Spisu treści:
Wideo: Detektor DTMF: 4 kroki
2024 Autor: John Day | [email protected]. Ostatnio zmodyfikowany: 2024-01-30 11:31
Przegląd
Do zbudowania tego urządzenia zainspirowało mnie zadanie domowe na kursie on-line Digital Signal Processing. Jest to dekoder DTMF zaimplementowany w Arduino UNO, wykrywa cyfrę naciśniętą na klawiaturze telefonu w trybie tonowym na podstawie wytwarzanego dźwięku.
Krok 1: Zrozumienie algorytmu
W DTMF każdy symbol kodowany jest dwiema częstotliwościami zgodnie z tabelą na rysunku.
Urządzenie przechwytuje sygnał wejściowy z mikrofonu i oblicza amplitudy ośmiu częstotliwości. Dwie częstotliwości o maksymalnych amplitudach dają rząd i kolumnę zakodowanego symbolu.
Pozyskiwanie danych
W celu przeprowadzenia analizy widma próbki powinny być zbierane z określoną przewidywalną częstotliwością. Aby to osiągnąć wykorzystałem tryb free-run ADC z maksymalną precyzją (preskaler 128) dający częstotliwość próbkowania 9615Hz. Poniższy kod pokazuje, jak skonfigurować ADC Arduino.
nieważne initADC() {
// Rozpocznij ADC; f = (16 MHz/preskaler) / 13 cykli/konwersja ADMUX = 0; // Wybór kanału, prawy-adj, użyj pinu AREF ADCSRA = _BV(ADEN) | // ADC włącz _BV(ADSC) | // ADC start _BV(ADATE) | // Automatyczne wyzwalanie _BV(ADIE) | // Przerwanie włączone _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Tryb swobodnej pracy DIDR0 = _BV(0); // Wyłącz wejście cyfrowe dla ADC pin TIMSK0 = 0; // Timer0 off } Obsługa przerwań wygląda tak ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(PrzykładPoz >= N) { ADCSRA &= ~_BV(ADIE); // Bufor pełny, przerwanie } }
Analiza widma
Po zebraniu próbek obliczam amplitudy symboli kodowania 8 częstotliwości. Nie muszę do tego uruchamiać pełnego FFT, więc użyłem algorytmu Goertzela.
void goertzel(uint8_t *samples, float *spectrum) {
pływak v_0, v_1, v_2; pływać 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])); pływak a = 2. * c; v_0 = v_1 = v_2 = 0; dla (uint16_t i = 0; i < N; i++) { v_0 = v_1; v_1 = v_2; v_2 = (zmiennoprzecinkowa)(próbki) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt(re * re + im * im); widmo[k] = amp; } }
Krok 2: Kodeks
Powyższy rysunek pokazuje przykład kodowania cyfry 3, gdzie maksymalna amplituda odpowiada częstotliwościom 697Hz i 1477Hz.
Cały szkic wygląda następująco
/** * Połączenia: * [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 */ #zawiera #zawiera
#włączać
#zdefiniuj CS_PIN 9
#zdefiniuj N 256
#define IX_LEN 8 #define PRÓG 20
Sterownik LEDMatrix lmd(1, CS_PIN);
próbki uint8_t[N];
lotny uint16_t samplePos = 0;
widmo zmiennoprzecinkowe[IX_LEN];
// Częstotliwości [697.0, 770.0, 852.0, 941.0, 1209.0, 1336,0, 1477,0, 1633.0]
// Obliczono dla 9615Hz 256 próbek const float cos_t[IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.555.05709930130823 } const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.773010453627369, 0.8314696123025451, 0.8819212643483549
struktura typedef {
cyfra znaku; indeks uint8_t; } cyfra_t;
digit_t wykryta_cyfra;
const char table[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} };
czcionka bajtowa[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} // * };
nieważne initADC() {
// Rozpocznij ADC; f = (16 MHz/preskaler) / 13 cykli/konwersja ADMUX = 0; // Wybór kanału, prawy-adj, użyj pinu AREF ADCSRA = _BV(ADEN) | // ADC włącz _BV(ADSC) | // ADC start _BV(ADATE) | // Automatyczne wyzwalanie _BV(ADIE) | // Przerwanie włączone _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Tryb swobodnej pracy DIDR0 = _BV(0); // Wyłącz wejście cyfrowe dla ADC pin TIMSK0 = 0; // Timer0 wyłączony }
void goertzel(uint8_t *samples, float *spectrum) {
pływak v_0, v_1, v_2; pływać 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])); pływak a = 2. * c; v_0 = v_1 = v_2 = 0; dla (uint16_t i = 0; i < N; i++) { v_0 = v_1; v_1 = v_2; v_2 = (zmiennoprzecinkowa)(próbki) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt(re * re + im * im); widmo[k] = amp; } }
float avg(float *a, uint16_t len) {
wynik zmiennoprzecinkowy = 0,0; for (uint16_t i = 0; i < len; i++) { wynik += a; } zwraca wynik / len; }
int8_t get_single_index_above_threshold(float *a, uint16_t len, float próg) {
if (próg < PRÓG) { return -1; } int8_t ix = -1; for (uint16_t i = 0; i próg) { if (ix == -1) { ix = i; } else { return -1; } } } return ix; }
void detect_digit(zmiennoprzecinkowa *widmo) {
float avg_row = avg(widmo, 4); float śr_kol = śr(&widmo[4], 4); int8_t wiersz = get_single_index_above_threshold(widmo, 4, avg_row); int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col); if (wiersz != -1 && col != -1 && avg_col > 200) { wykryta_cyfra.cyfra = pgm_read_byte(&(tabela[wiersz][kol])); wykryty_cyfra.index = pgm_read_byte(&(char_indexes[wiersz][kol])); } else { wykryta_cyfra.cyfra = 0; } }
void drawSprite(bajt* sprite) {
// Maska służy do pobrania bitu kolumny z maski bajtowej wiersza sprite = B10000000; for(int iy = 0; iy < 8; iy++) { for(int ix = 0; ix < 8; ix++) { lmd.setPixel(7 - iy, ix, (bool)(sprite[iy] i maska));
// przesuń maskę o jeden piksel w prawo
maska = maska >> 1; }
// zresetuj maskę kolumny
maska = B10000000; } }
pusta konfiguracja () {
cli(); initADC(); sei();
Serial.początek(115200);
lmd.setEnabled(prawda); lmd.setIntensity(2); lmd.wyczyść(); lmd.wyświetlacz();
wykryta_cyfra.cyfra = 0;
}
bez znaku długie z = 0;
pusta pętla () {
while(ADCSRA i _BV(ADIE)); // Czekaj na zakończenie próbkowania dźwięku goertzel(samples, spectrum); wykryj_cyfra(widmo);
if (wykryta_cyfra.cyfra != 0) {
drawSprite(font[wykryta_cyfra.indeks]); lmd.wyświetlacz(); } 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++;
próbkaPos = 0;
ADCSRA |= _BV(ADIE); // Wznów przerwanie próbkowania
}
ISR(ADC_wektor) {
próbka uint16_t = ADC;
próbki[przykładPoz++] = próbka - 400;
if(PrzykładPoz >= N) { ADCSRA &= ~_BV(ADIE); // Bufor pełny, przerwanie } }
Krok 3: Schematy
Należy wykonać następujące połączenia:
Mikrofon do Arduino
Out -> A0
Vcc -> 3.3V Gnd -> Gnd
Ważne jest, aby podłączyć AREF do 3,3V
Wyświetlacz do Arduino
Vcc -> 5V
Masa -> Masa DIN -> D11 CLK -> D13 CS -> D9
Krok 4: Wniosek
Co można tutaj poprawić? Użyłem N=256 próbek przy częstotliwości 9615Hz, która ma pewien wyciek widma, jeśli N=205 i częstotliwość 8000Hz to żądane częstotliwości pokrywają się z siatką dyskretyzacji. W tym celu ADC powinien być używany w trybie przepełnienia timera.
Zalecana:
Detektor wstrząsów teraźniejszości: 3 kroki
Detektor wstrząsów prezentu: W tym projekcie zamierzamy stworzyć urządzenie, które uruchomi alarm, jeśli ktoś potrząsnie prezentem/pudełkiem. Wpadłem na ten pomysł, kiedy otrzymaliśmy przesyłkę pocztową na Boże Narodzenie. Aby spróbować zgadnąć, co w nim było, oczywiście potrząsnęliśmy nim, tak jak wszyscy
Detektor czujnika płomienia: 3 kroki
Detektor czujnika płomienia: Ten projekt odkryje, jak działa czujnik płomienia i cel tego urządzenia sprzętowego. Jeśli uczysz się, jak działa to urządzenie sprzętowe, to jest to idealny projekt dla Ciebie. Gdy czujnik płomienia wykryje pożar (światło płomienia)
ROVER STREAMING WIDEO DTMF: 3 kroki
ŁADZIOWNIK DO STRUMIENIA WIDEO DTMF: cześć po moim łaziku sterowanym przez LINUX TERMINAL i ROBOCIE STEROWANYM Z PC WIFI DTMF, to jest mój trzeci robot. i podobnie jak dwa inne tutaj, również nie używałem żadnego mikrokontrolera ani programowania, aby było proste i łatwe do wykonania. Ponadto przesyła strumieniowo wideo na żywo przez Wi-Fi
Jak zrobić prosty dekoder linii telefonicznej DTMF (tonowy): 3 kroki
Jak zrobić prosty dekoder linii telefonicznej DTMF (ton): Jest to prosty projekt, który umożliwia dekodowanie sygnałów DTMF na praktycznie każdej linii telefonicznej. W tym samouczku używamy dekodera MT8870D. Używamy wstępnie wbudowanego dekodera tonów, ponieważ uwierz mi, to ból z tyłu próbować to zrobić za pomocą
Samochód sterowany DTMF. Nie są wymagane telefony komórkowe: 3 kroki
Samochód sterowany DTMF. Nie są wymagane telefony komórkowe: Roboty i samochody Robo to zasadniczo nowe zabawki zarówno dla entuzjastów technologii, jak i naukowców na całym świecie. znajdują zastosowanie wszędzie. W tym samouczku wyjaśnię, jak zrobić samochód robota sterowany DTMF za pomocą arduino i