Obsah:

DTMF detektor: 4 kroky
DTMF detektor: 4 kroky

Video: DTMF detektor: 4 kroky

Video: DTMF detektor: 4 kroky
Video: 😱Хакерская SDR Прослушка На Xiaomi 🔥 2024, Červenec
Anonim
Image
Image

Přehled

K sestavení tohoto zařízení mě inspiroval domácí úkol v online kurzu zpracování digitálního signálu. Toto je dekodér DTMF implementovaný s Arduino UNO, který detekuje číslici stisknutou na klávesnici telefonu v tónovém režimu podle zvuku, který vydává.

Krok 1: Porozumění algoritmu

Kód
Kód

V DTMF je každý symbol kódován dvěma frekvencemi podle tabulky na obrázku.

Zařízení zachycuje vstup z mikrofonu a vypočítává amplitudy osmi frekvencí. Dvě frekvence s maximální amplitudou udávají řádek a sloupec zakódovaného symbolu.

Sběr dat

Aby bylo možné provádět analýzu spektra, měly by být vzorky zachyceny na určité předvídatelné frekvenci. K dosažení tohoto cíle jsem použil volně běžící režim ADC s maximální přesností (prescaler 128), který dává vzorkovací frekvenci 9615 Hz. Níže uvedený kód ukazuje, jak konfigurovat ADC Arduina.

zrušit initADC () {

// Zahájit ADC; f = (16 MHz/předzesilovač)/13 cyklů/převod ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC povolit _BV (ADSC) | // Začátek ADC _BV (ADATE) | // Automatické spuštění _BV (ADIE) | // Povolit přerušení _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Režim volného chodu DIDR0 = _BV (0); // Vypněte digitální vstup pro pin ADC TIMSK0 = 0; // Timer0 off} A obsluha přerušení vypadá takto ISR (ADC_vect) {uint16_t sample = ADC; sample [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Vyrovnávací paměť plná, přerušení vypnuto}}

Spektrální analýza

Po shromáždění vzorků vypočítám amplitudy kódujících symbolů 8 frekvencí. Nepotřebuji k tomu provozovat plný FFT, takže jsem použil Goertzelův algoritmus.

neplatný goertzel (uint8_t *vzorky, float *spektrum) {

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

Krok 2: Kód

Obrázek výše ukazuje příklad kódování číslice 3, kde maximální amplituda odpovídá frekvencím 697 Hz a 1477 Hz.

Kompletní skica vypadá následovně

/** * Připojení: * [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

#zahrnout

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t vzorky [N];

volatile uint16_t samplePos = 0;

float spektrum [IX_LEN];

// Frekvence [697,0, 770,0, 852,0, 941,0, 1209,0, 1336,0, 1477,0, 1633,0]

// Vypočteno pro 9615Hz 256 vzorků const float cos_t [IX_LEN] PROGMEM = {0,8932243011955153, 0,8700869911087115, 0,8448535652497071, 0,8032075314806449, 0,6895405447370669, 0,6343932841636459, 0,523570 const float sin_t [IX_LEN] PROGMEM = {0,44961132965460654, 0,49289819222978404, 0,5349976198870972, 0,5956993044924334, 0,7242470829514669, 0,7730104533627369, 0,8314696123025451, 0,4814664464 0,88

typedef struct {

znaková číslice; uint8_t index; } digit_t;

digit_t detekován_číslice;

tabulka konstant [4] [4] PROGMEM = {

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

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

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

bajtové písmo [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, 0x0 }, // 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} // *};

zrušit initADC () {

// Zahájit ADC; f = (16 MHz/předzesilovač)/13 cyklů/převod ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC povolit _BV (ADSC) | // Začátek ADC _BV (ADATE) | // Automatické spuštění _BV (ADIE) | // Povolit přerušení _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Režim volného chodu DIDR0 = _BV (0); // Vypněte digitální vstup pro pin ADC TIMSK0 = 0; // Časovač 0 vypnut}

neplatný goertzel (uint8_t *vzorky, float *spektrum) {

float v_0, v_1, v_2; float re, im, amp; pro (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); float a = 2. * c; v_0 = v_1 = v_2 = 0; pro (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (vzorky ) + a * v_1 - v_0; } re = 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) {

float result =, 0; for (uint16_t i = 0; i <len; i ++) {result+= a ; } vrátit výsledek / len; }

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

if (práh <THRESHOLD) {return -1; } int8_t ix = -1; for (uint16_t i = 0; i práh) {if (ix == -1) {ix = i; } else {return -1; }}} vrátit ix; }

neplatné detect_digit (float *spektrum) {

float avg_row = avg (spektrum, 4); float avg_col = avg (& spektrum [4], 4); int8_t řádek = get_single_index_above_threshold (spektrum, 4, prům._row); int8_t col = get_single_index_above_threshold (& spektrum [4], 4, avg_col); if (řádek! = -1 && col! = -1 && avg_col> 200) {detekován_digit.digit = pgm_read_byte (& (tabulka [řádek] [sloupec))); detekovaný_digit.index = pgm_read_byte (& (char_indexes [řádek] [sloupec])); } else {detekovany_digit.digit = 0; }}

void drawSprite (byte* sprite) {

// Maska se používá k získání bitu sloupce z masky sprite byte byte = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// posune masku o jeden pixel doprava

maska = maska >> 1; }

// reset masky sloupce

maska = B10000000; }}

neplatné nastavení () {

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

Serial.begin (115200);

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

detekovaný_digit.digit = 0;

}

bez znaménka dlouhý z = 0;

prázdná smyčka () {

while (ADCSRA & _BV (ADIE)); // Počkejte na dokončení vzorkování zvuku goertzel (vzorky, spektrum); detect_digit (spektrum);

pokud (zjištěno_číslo.digit! = 0) {

drawSprite (písmo [detekován_digit.index]); lmd.display (); } if (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektrum ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) detekován_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Obnovte přerušení vzorkování

}

ISR (ADC_vect) {

uint16_t sample = ADC;

vzorky [samplePos ++] = vzorek - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Vyrovnávací paměť plná, přerušení vypnuto}}

Krok 3: Schémata

Schémata
Schémata

Je třeba provést následující připojení:

Z mikrofonu na Arduino

Out -> A0

Vcc -> 3,3 V Gnd -> Gnd

Je důležité připojit AREF na 3,3 V

Zobrazit Arduino

Vcc -> 5V

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

Krok 4: Závěr

Co by se zde dalo zlepšit? Použil jsem N = 256 vzorků s frekvencí 9615 Hz, která má určitý únik spektra, pokud N = 205 a rychlost je 8 000 Hz, pak se požadované frekvence shodují s diskretizační mřížkou. K tomu by měl být ADC použit v režimu přetečení časovače.

Doporučuje: