Isi kandungan:

Pengesan DTMF: 4 Langkah
Pengesan DTMF: 4 Langkah

Video: Pengesan DTMF: 4 Langkah

Video: Pengesan DTMF: 4 Langkah
Video: 🫣😱See what high voltage does #shorts 2024, November
Anonim
Image
Image

Gambaran keseluruhan

Saya mendapat inspirasi untuk membina peranti ini dengan tugasan rumah dalam kursus dalam talian Pemprosesan Isyarat Digital. Ini adalah penyahkod DTMF yang dilaksanakan dengan Arduino UNO, ia mengesan digit yang ditekan pada papan kekunci telefon dalam mod nada dengan suara yang dihasilkannya.

Langkah 1: Memahami Algoritma

Kod tersebut
Kod tersebut

Di DTMF setiap simbol dikodkan dengan dua frekuensi mengikut jadual pada gambar.

Peranti menangkap input dari mikrofon dan mengira amplitud lapan frekuensi. Dua frekuensi dengan amplitud maksimum memberikan baris dan lajur simbol yang dikodkan.

Perolehan data

Untuk melakukan analisis spektrum, sampel harus diambil pada frekuensi yang dapat diramalkan. Untuk mencapai ini, saya menggunakan mod ADC lari bebas dengan ketepatan maksimum (prescaler 128) ia memberikan kadar pensampelan 9615Hz. Kod di bawah menunjukkan cara mengkonfigurasi ADC Arduino.

batal initADC () {

// Init ADC; f = (16MHz / prescaler) / 13 kitaran / penukaran ADMUX = 0; // Sel sel, kanan, gunakan pin AREF ADCSRA = _BV (ADEN) | // ADC aktifkan _BV (ADSC) | // ADC bermula _BV (ADATE) | // Pencetus automatik _BV (ADIE) | // Selang aktifkan _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Mod jalan bebas DIDR0 = _BV (0); // Matikan input digital untuk pin ADC TIMSK0 = 0; // Timer0 off} Dan pengendali interrupt kelihatan seperti ISR ini (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; jika (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Penampan penuh, gangguan dimatikan}}

Analisis spektrum

Setelah mengumpulkan sampel saya mengira amplitud 8 frekuensi simbol pengekodan. Saya tidak perlu menjalankan FFT sepenuhnya untuk ini, jadi saya menggunakan algoritma Goertzel.

batal goertzel (sampel uint8_t *, spektrum apungan *) {

terapung v_0, v_1, v_2; float re, im, amp; untuk (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); terapung s = pgm_read_float (& (sin_t [k])); apungan a = 2. * c; v_0 = v_1 = v_2 = 0; untuk (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (terapung) (sampel ) + a * v_1 - v_0; } semula = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}

Langkah 2: Kodnya

Gambar di atas menunjukkan contoh pengekodan digit 3 di mana amplitud maksimum sepadan dengan frekuensi 697Hz dan 1477Hz.

Lakaran yang lengkap kelihatan seperti berikut

/ ** * Sambungan: * [Mic ke Arduino] * - Keluar -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [Paparan ke Arduino] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 * / #include #include

#sertakan

#tentukan CS_PIN 9

#tentukan N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t sampel [N];

uint16_t samplePos yang tidak menentu = 0;

spektrum terapung [IX_LEN];

// Kekerapan [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Dihitung untuk 9615Hz 256 sampel const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.63439328416366456258238358258 const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025121

typedef struktur {

digit char; indeks uint8_t; } digit_t;

digit_t dikesan_digit;

jadual const char [4] [4] PROGMEM = {

{'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', ' C '}, {' * ',' 0 ',' # ',' D '}};

const uint8_t char_indexes [4] [4] PROGMEM = {

{1, 2, 3, 10}, {4, 5, 6, 11}, {7, 8, 9, 12}, {15, 0, 14, 13} };

fon bait [16] [8] = {

{0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38}, // 0 {0x04, 0x0c, 0x14, 0x24, 0x04, 0x04, 0x04, 0x04}, // 1 {0x00, 0x30, 0x48, 0x04, 0x04, 0x38, 0x40, 0x7c}, // 2 {0x00, 0x38, 0x04, 0x04, 0x18, 0x04, 0x44, 0x38}, // 3 {0x00, 0x04, 0x0c, 0x14, 0x24, 0x7e, 0x04, 0x04, 0x04, 0x04 }, // 4 {0x00, 0x7c, 0x40, 0x40, 0x78, 0x04, 0x04, 0x38}, // 5 {0x00, 0x38, 0x40, 0x40, 0x78, 0x44, 0x44, 0x38}, // 6 {0x00, 0x7c, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10}, // 7 {0x00, 0x3c, 0x44, 0x44, 0x38, 0x44, 0x44, 0x78}, // 8 {0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x04, 0x78}, // 9 {0x00, 0x1c, 0x22, 0x42, 0x42, 0x7e, 0x42, 0x42}, // A {0x00, 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x7c}, / / B {0x00, 0x3c, 0x44, 0x40, 0x40, 0x40, 0x44, 0x7c}, // C {0x00, 0x7c, 0x42, 0x42, 0x42, 0x42, 0x44, 0x78}, // D {0x00, 0x0a, 0x7f, 0x14, 0x28, 0xfe, 0x50, 0x00}, // # {0x00, 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10} // *};

batal initADC () {

// Init ADC; f = (16MHz / prescaler) / 13 kitaran / penukaran ADMUX = 0; // Sel sel, kanan, gunakan pin AREF ADCSRA = _BV (ADEN) | // ADC aktifkan _BV (ADSC) | // ADC bermula _BV (ADATE) | // Pencetus automatik _BV (ADIE) | // Selang aktifkan _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Mod jalan bebas DIDR0 = _BV (0); // Matikan input digital untuk pin ADC TIMSK0 = 0; // Pemasa0 dimatikan}

batal goertzel (sampel uint8_t *, spektrum apungan *) {

terapung v_0, v_1, v_2; float re, im, amp; untuk (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); terapung s = pgm_read_float (& (sin_t [k])); apungan a = 2. * c; v_0 = v_1 = v_2 = 0; untuk (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (terapung) (sampel ) + a * v_1 - v_0; } semula = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}

float avg (float * a, uint16_t len) {

hasil apungan =.0; untuk (uint16_t i = 0; i <len; i ++) {hasil + = a ; } keputusan pulangan / len; }

int8_t get_single_index_above_threshold (apungan * a, uint16_t len, ambang apungan) {

jika (ambang <THRESHOLD) {return -1; } int8_t ix = -1; untuk (uint16_t i = 0; i ambang) {if (ix == -1) {ix = i; } lain {return -1; }}} pulangkan ix; }

void detect_digit (float * spektrum) {

float avg_row = avg (spektrum, 4); terapung avg_col = avg (& spektrum [4], 4); int8_t row = get_single_index_above_threshold (spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold (& spektrum [4], 4, avg_col); jika (baris! = -1 && col! = -1 && avg_col> 200) {dikesan_digit.digit = pgm_read_byte (& (jadual [baris] [col])); dikesan_digit.index = pgm_read_byte (& (char_indexes [baris] [col])); } lain {dikesan_digit.digit = 0; }}

kekosongan drawSprite (byte * sprite) {

// Topeng digunakan untuk mendapatkan bit lajur dari sprite baris byte mask = B10000000; untuk (int iy = 0; iy <8; iy ++) {untuk (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// alihkan topeng dengan satu piksel ke kanan

topeng = topeng >> 1; }

// tetapkan semula topeng lajur

topeng = B10000000; }}

batal persediaan () {

cli (); initADC (); sei ();

Serial.begin (115200);

lmd.setEnabled (benar); lmd.setIntensity (2); lmd.clear (); lmd.display ();

dikesan_digit.digit = 0;

}

panjang tidak bertanda z = 0;

gelung kosong () {

sementara (ADCSRA & _BV (ADIE)); // Tunggu persampelan audio untuk menyelesaikan goertzel (sampel, spektrum); mengesan_digit (spektrum);

jika (dikesan_digit.digit! = 0) {

drawSprite (fon [dikesan_digit.index]); lmd.display (); } jika (z% 5 == 0) {untuk (int i = 0; i <IX_LEN; i ++) {Serial.print (spektrum ); Serial.print ("\ t"); } Bersiri.println (); Serial.println ((int) dikesan_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Sambung semula sampel mengganggu

}

ISR (ADC_vect) {

uint16_t sample = ADC;

sampel [samplePos ++] = sampel - 400;

jika (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Penampan penuh, gangguan dimatikan}}

Langkah 3: Skematik

Skematik
Skematik

Sambungan berikut harus dibuat:

Mic ke Arduino

Keluar -> A0

Vcc -> 3.3V Gnd -> Gnd

Penting untuk menghubungkan AREF ke 3.3V

Paparan ke Arduino

Vcc -> 5V

Gnd -> Gnd DIN -> D11 CLK -> D13 CS -> D9

Langkah 4: Kesimpulannya

Apa yang boleh diperbaiki di sini? Saya menggunakan sampel N = 256 pada kadar 9615Hz yang mempunyai beberapa kebocoran spektrum, jika N = 205 dan kadarnya 8000Hz maka frekuensi yang diinginkan bertepatan dengan grid diskritisasi. Untuk itu ADC harus digunakan dalam mod overflow pemasa.

Disyorkan: