İçindekiler:
Video: DTMF Dedektörü: 4 Adım
2024 Yazar: John Day | [email protected]. Son düzenleme: 2024-01-30 13:20
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
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
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:
Raspberry Pi - TMD26721 Kızılötesi Dijital Yakınlık Dedektörü Java Eğitimi: 4 Adım
Raspberry Pi - TMD26721 Kızılötesi Dijital Yakınlık Dedektörü Java Eğitimi: TMD26721, tek bir 8 pimli yüzeye montaj modülünde eksiksiz bir yakınlık algılama sistemi ve dijital arayüz mantığı sağlayan bir kızılötesi dijital yakınlık dedektörüdür. Yakınlık algılama, gelişmiş sinyal-gürültü ve kesinlik. Profesyonel
Su Seviye Dedektörü: 7 Adım
Su Seviyesi Dedektörü: Ultrasonik sensör, radar sistemi ile aynı prensipte çalışır. Ultrasonik bir sensör, elektrik enerjisini akustik dalgalara dönüştürebilir ve bunun tersi de mümkündür. Ünlü HC SR04 ultrasonik sensör, 40kHz frekansında ultrasonik dalgalar üretir.Tipik
Zigbee Yatak Varlık Dedektörü: 8 Adım
Zigbee Yatak Varlığı Dedektörü: Bir süredir yatakta olduğumuzu algılamanın bir yolunu arıyordum. Bu, bu bilgiyi Homeassistant'ta kullanmak içindir. Bu bilgilerle geceleri ışıkları kapatmak için otomasyonlar yapabilirim veya örneğin evimde bir alarm sistemini aktif hale getirebilirim
Duman Dedektörü: 13 Adım
Duman Dedektörü: Merhaba arkadaşlar bugün duman dedektörüne bir bakalım Bir çoğunuz AVM'lere gittiniz çoğunlukla duman dedektörü denen bu cihaz dumanı algılar ve sprinkleri açar ve yangını durdurur.Fakat bu projede bu ufak bir değişiklik Bunun yerine
Mevcut Sarsıntı Dedektörü: 3 Adım
Present Shake Detector: Bu projede, birisi bir hediyeyi/kutuyu salladığında alarm çalan bir cihaz yapacağız. Bu fikre Noel için postadan bir paket geldiği zaman ulaştım. Denemek ve içinde ne olduğunu tahmin etmek için tabii ki herkes gibi salladık