İçindekiler:

DTMF Dedektörü: 4 Adım
DTMF Dedektörü: 4 Adım

Video: DTMF Dedektörü: 4 Adım

Video: DTMF Dedektörü: 4 Adım
Video: Fibaro Su Basma Sensörü - Sisteme Ekleme 2024, Kasım
Anonim
Image
Image

genel bakış

Dijital Sinyal İşleme çevrimiçi kursunda bir ev ödevi ile bu cihazı oluşturmak için ilham aldım. Bu Arduino UNO ile uygulanan bir DTMF kod çözücüdür, ürettiği sesle ton modunda bir telefon tuş takımında basılan bir rakamı algılar.

Adım 1: Algoritmayı Anlamak

kod
kod

DTMF'de her sembol, resimdeki tabloya göre iki frekansla kodlanmıştır.

Cihaz, mikrofondan girdi alır ve sekiz frekansın genliğini hesaplar. Maksimum genliğe sahip iki frekans, kodlanmış sembolün bir satırını ve bir sütununu verir.

Veri toplama

Spektrum analizi yapabilmek için numunelerin belirli bir öngörülebilir frekansta yakalanması gerekir. Bunu başarmak için maksimum hassasiyetle serbest çalışan ADC modunu kullandım (ön ölçekleyici 128), örnekleme hızı 9615Hz veriyor. Aşağıdaki kod, Arduino'nun ADC'sinin nasıl yapılandırılacağını gösterir.

geçersiz initADC() {

// ADC'yi başlat; f = (16MHz/ön ölçekleyici) / 13 döngü/dönüşüm ADMUX = 0; // Kanal sel, sağa ayar, AREF pinini kullan ADCSRA = _BV(ADEN) | // ADC etkinleştir _BV(ADSC) | // ADC başlangıcı _BV(ADATE) | // Otomatik tetik _BV(ADIE) | // Kesinti etkinleştirme _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Serbest çalışma modu DIDR0 = _BV(0); // ADC pini TIMSK0 için dijital girişi kapatın = 0; // Timer0 off } Ve kesme işleyicisi şöyle görünür ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Arabellek dolu, kesinti kapalı } }

spektrum analizi

Örnekleri topladıktan sonra, sembolleri kodlayan 8 frekansın genliğini hesaplarım. Bunun için tam FFT çalıştırmam gerekmiyor, bu yüzden Goertzel'in algoritmasını kullandım.

void goertzel(uint8_t *örnekler, kayan nokta *spektrum) {

float v_0, v_1, v_2; float yeniden, im, amp; for (uint8_t k = 0; k < IX_LEN; k++) { kayan nokta c = pgm_read_float(&(cos_t[k])); kayan nokta s = pgm_read_float(&(sin_t[k])); kayan nokta 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 = (kayan)(örnekler) + a * v_1 - v_0; } yeniden = c * v_2 - v_1; im = s * v_2; amp = sqrt(yeniden * yeniden + im * im); spektrum[k] = amp; } }

2. Adım: Kod

Yukarıdaki resim, maksimum genliğin 697Hz ve 1477Hz frekanslarına karşılık geldiği 3. basamak kodlama örneğini göstermektedir.

Tam kroki aşağıdaki gibi görünüyor

/** * Bağlantılar: * [Mic to Arduino] * - Out -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [Display to Arduino] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 */ #include #include

#Dahil etmek

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define EŞİK 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t örnekleri[N];

uçucu uint16_t samplePos = 0;

kayan spektrum[IX_LEN];

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

// 9615Hz için hesaplanmıştır 256 örnek const float cos_t[IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5556825301960; const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.831463596123025451, 0.88192}1264348;

typedef yapısı {

karakter rakamı; uint8_t dizini; } digit_t;

digit_t tespit edilen_digit;

const karakter tablosu[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} };

bayt yazı tipi[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} // * };

geçersiz initADC() {

// ADC'yi başlat; f = (16MHz/ön ölçekleyici) / 13 döngü/dönüşüm ADMUX = 0; // Kanal sel, sağa ayar, AREF pinini kullan ADCSRA = _BV(ADEN) | // ADC etkinleştir _BV(ADSC) | // ADC başlangıcı _BV(ADATE) | // Otomatik tetik _BV(ADIE) | // Kesinti etkinleştirme _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Serbest çalışma modu DIDR0 = _BV(0); // ADC pini TIMSK0 için dijital girişi kapatın = 0; // Zamanlayıcı0 kapalı }

void goertzel(uint8_t *örnekler, kayan nokta *spektrum) {

kayan nokta v_0, v_1, v_2; float yeniden, im, amp; for (uint8_t k = 0; k < IX_LEN; k++) { kayan nokta c = pgm_read_float(&(cos_t[k])); kayan nokta s = pgm_read_float(&(sin_t[k])); kayan nokta 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 = (kayan)(örnekler) + a * v_1 - v_0; } yeniden = c * v_2 - v_1; im = s * v_2; amp = sqrt(re * yeniden + im * im); spektrum[k] = amp; } }

float avg(float *a, uint16_t len) {

kayan nokta sonucu =.0; for (uint16_t i = 0; i < len; i++) { sonuç += a; } sonuç / len; }

int8_t get_single_index_above_threshold(float *a, uint16_t len, kayan nokta eşiği) {

if (eşik < EŞİK) { dönüş -1; } int8_t ix = -1; for (uint16_t i = 0; i eşiği) { if (ix == -1) { ix = i; } başka { dönüş -1; } } } ix'i döndür; }

void algılama_digit(float *spektrum) {

float avg_row = avg(spektrum, 4); float avg_col = ortalama(&spektrum[4], 4); int8_t satır = get_single_index_above_threshold(spektrum, 4, ort_row); int8_t sütun = get_single_index_above_threshold(&spektrum[4], 4, avg_col); if (satır != -1 && sütun != -1 && avg_col > 200) {Detected_digit.digit = pgm_read_byte(&(table[row][col])); algılanan_digit.index = pgm_read_byte(&(char_indexes[satır][sütun])); } else { algılanan_digit.digit = 0; } }

void drawSprite(byte* hareketli grafiği) {

// Maske, hareketli grafik satırı bayt maskesinden sütun bitini almak için kullanılır = B10000000; for(int iy = 0; iy < 8; iy++) { for(int ix = 0; ix < 8; ix++) { lmd.setPixel(7 - iy, ix, (bool)(sprite[iy] & mask));

// maskeyi bir piksel sağa kaydır

maske = maske >> 1; }

// sütun maskesini sıfırla

maske = B10000000; } }

geçersiz kurulum() {

kli(); initADC(); ben();

Seri.başla(115200);

lmd.setEnabled(doğru); lmd.setYoğunluk(2); lmd.clear(); lmd.display();

algılanan_digit.digit = 0;

}

işaretsiz uzun z = 0;

boşluk döngüsü () {

while(ADCSRA & _BV(ADIE)); // Goertzel(örnekler, spektrum); algılama_digit(spektrum);

if (tespit edilen_digit.digit != 0) {

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

örnekPos = 0;

ADCSRA |= _BV(ADIE); // Örnekleme kesmesini devam ettir

}

ISR(ADC_vect) {

uint16_t örnek = ADC;

örnekler[samplePos++] = örnek - 400;

if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Arabellek dolu, kesinti kapalı } }

Adım 3: Şemalar

şemalar
şemalar

Aşağıdaki bağlantılar yapılmalıdır:

Arduino'ya Mikrofon

Çıkış -> A0

Vcc -> 3.3V Gnd -> Gnd

AREF'i 3.3V'a bağlamak önemlidir

Arduino'ya Görüntüle

Vcc -> 5V

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

4. Adım: Sonuç

Burada ne geliştirilebilir? 9615Hz hızında N = 256 örnek kullandım, bu durumda bir miktar spektrum sızıntısı var, eğer N = 205 ve oran 8000Hz ise, istenen frekanslar ayrıklaştırma ızgarası ile çakışıyor. Bunun için timer overflow modunda ADC kullanılmalıdır.

Önerilen: