Daftar Isi:

Detektor DTMF: 4 Langkah
Detektor DTMF: 4 Langkah

Video: Detektor DTMF: 4 Langkah

Video: Detektor DTMF: 4 Langkah
Video: GSM/GPRS-модуль SIM800L (#4) - все о DTMF: парсинг, управление, безопасность 2024, November
Anonim
Image
Image

Gambaran

Saya terinspirasi untuk membangun perangkat ini dengan tugas rumah pada kursus online Digital Signal Processing. Ini adalah dekoder DTMF yang diimplementasikan dengan Arduino UNO, mendeteksi digit yang ditekan pada keypad telepon dalam mode nada dengan suara yang dihasilkannya.

Langkah 1: Memahami Algoritma

Kode
Kode

Dalam DTMF setiap simbol dikodekan dengan dua frekuensi sesuai tabel pada gambar.

Perangkat menangkap input dari mikrofon dan menghitung amplitudo delapan frekuensi. Dua frekuensi dengan amplitudo maksimum memberikan baris dan kolom dari simbol yang dikodekan.

akuisisi data

Untuk melakukan analisis spektrum, sampel harus ditangkap pada frekuensi tertentu yang dapat diprediksi. Untuk mencapai ini, saya menggunakan mode ADC free-run dengan presisi maksimum (prescaler 128) yang memberikan laju sampling 9615Hz. Kode di bawah ini menunjukkan cara mengkonfigurasi ADC Arduino.

batalkan initADC() {

// Inisi ADC; f = (16MHz/prascaler) / 13 siklus/konversi ADMUX = 0; // Channel sel, adj kanan, gunakan pin AREF ADCSRA = _BV(ADEN) | // ADC mengaktifkan _BV(ADSC) | // ADC mulai _BV(ADATE) | // Pemicu otomatis _BV(ADIE) | // Interupsi aktifkan _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Mode lari bebas DIDR0 = _BV(0); // Matikan input digital untuk pin ADC TIMSK0 = 0; // Timer0 off } Dan pengendali interupsi terlihat seperti ini ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer penuh, interupsi mati } }

Analisis spektrum

Setelah mengumpulkan sampel, saya menghitung amplitudo dari 8 simbol pengkodean frekuensi. Saya tidak perlu menjalankan FFT penuh untuk ini, jadi saya menggunakan algoritma Goertzel.

void goertzel(uint8_t *sampel, float *spektrum) {

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

Langkah 2: Kode

Gambar di atas menunjukkan contoh pengkodean digit 3 di mana amplitudo maksimum sesuai dengan frekuensi 697Hz dan 1477Hz.

Sketsa lengkapnya terlihat sebagai berikut:

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

#termasuk

#tentukan CS_PIN 9

#tentukan N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t sampel[N];

volatil uint16_t samplePos = 0;

spektrum float[IX_LEN];

// Frekuensi [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.6343932841636456, 0.55557.023301960}; const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.88491921};

struktur typedef {

angka karakter; indeks uint8_t; } angka_t;

digit_t terdeteksi_digit;

tabel 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} };

byte font[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 }, // 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} // * };

batalkan initADC() {

// Inisi ADC; f = (16MHz/prascaler) / 13 siklus/konversi ADMUX = 0; // Channel sel, adj kanan, gunakan pin AREF ADCSRA = _BV(ADEN) | // ADC mengaktifkan _BV(ADSC) | // ADC mulai _BV(ADATE) | // Pemicu otomatis _BV(ADIE) | // Interupsi aktifkan _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Mode lari bebas DIDR0 = _BV(0); // Matikan input digital untuk pin ADC TIMSK0 = 0; // Timer0 mati }

void goertzel(uint8_t *sampel, float *spektrum) {

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

float rata-rata(float *a, uint16_t len) {

hasil float =.0; for (uint16_t i = 0; i < len; i++) { hasil += a; } mengembalikan hasil / len; }

int8_t get_single_index_above_threshold(float *a, uint16_t len, float threshold) {

if (ambang < THRESHOLD) { kembali -1; } int8_t ix = -1; for (uint16_t i = 0; i threshold) { if (ix == -1) { ix = i; } else { kembali -1; } } } kembali ix; }

void detect_digit(float *spektrum) {

float avg_row = avg(spektrum, 4); float avg_col = avg(&spektrum[4], 4); int8_t baris = get_single_index_above_threshold(spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold(&spektrum[4], 4, avg_col); if (baris != -1 && col != -1 && avg_col > 200) { terdeteksi_digit.digit = pgm_read_byte(&(tabel[baris][kol])); terdeteksi_digit.index = pgm_read_byte(&(char_indexes[baris][kol])); } else { terdeteksi_digit.digit = 0; } }

void drawSprite(byte* sprite) {

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

// geser topeng satu piksel ke kanan

topeng = topeng >> 1; }

// setel ulang topeng kolom

topeng = B10000000; } }

batalkan pengaturan() {

kli(); initADC(); sei();

Serial.begin(115200);

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

terdeteksi_digit.digit = 0;

}

unsigned panjang z = 0;

lingkaran kosong() {

while(ADCSRA & _BV(ADIE)); // Tunggu pengambilan sampel audio selesai goertzel(sampel, spektrum); deteksi_digit(spektrum);

if (terdeteksi_digit.digit != 0) {

drawSprite(font[terdeteksi_digit.index]); lmd.display(); } if (z % 5 == 0) { for (int i = 0; i < IX_LEN; i++) { Serial.print(spectrum); Serial.print("\t"); } Serial.println(); Serial.println((int)detected_digit.digit); } z++;

sampelPos = 0;

ADCSRA |= _BV(ADIE); // Lanjutkan interupsi pengambilan sampel

}

ISR(ADC_vect) {

uint16_t sampel = ADC;

sampel[samplePos++] = sampel - 400;

if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer penuh, interupsi mati } }

Langkah 3: Skema

skema
skema

Koneksi berikut harus dibuat:

Mikrofon ke Arduino

Keluar -> A0

Vcc -> 3.3V Gnd -> Gnd

Penting untuk menghubungkan AREF ke 3.3V

Tampilan ke Arduino

Vcc -> 5V

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

Langkah 4: Kesimpulan

Apa yang bisa diperbaiki di sini? Saya menggunakan N = 256 sampel pada laju 9615Hz yang memiliki beberapa kebocoran spektrum, jika N = 205 dan laju adalah 8000Hz maka frekuensi yang diinginkan bertepatan dengan kisi diskritisasi. Untuk itu ADC sebaiknya digunakan dalam mode timer overflow.

Direkomendasikan: