Satura rādītājs:

DTMF detektors: 4 soļi
DTMF detektors: 4 soļi

Video: DTMF detektors: 4 soļi

Video: DTMF detektors: 4 soļi
Video: GSM/GPRS-модуль SIM800L (#4) - все о DTMF: парсинг, управление, безопасность 2024, Jūlijs
Anonim
Image
Image

Pārskats

Būvēt šo ierīci mani iedvesmoja mājas uzdevums digitālā signāla apstrādes tiešsaistes kursā. Šis ir DTMF dekodētājs, kas ieviests ar Arduino UNO, un tas rada ciparu, kas toņa režīmā tiek nospiests uz tālruņa tastatūras, pēc tā radītās skaņas.

1. darbība. Izprotiet algoritmu

Kods
Kods

DTMF sistēmā katrs simbols ir kodēts ar divām frekvencēm saskaņā ar tabulu attēlā.

Ierīce uztver mikrofona ievadi un aprēķina astoņu frekvenču amplitūdas. Divas frekvences ar maksimālo amplitūdu dod kodēta simbola rindu un kolonnu.

Datu ieguve

Lai veiktu spektra analīzi, paraugi jāuztver noteiktā paredzamā frekvencē. Lai to panāktu, es ar maksimālu precizitāti izmantoju brīvā skrējiena ADC režīmu (prescaler 128), tas nodrošina paraugu ņemšanas ātrumu 9615 Hz. Zemāk esošais kods parāda, kā konfigurēt Arduino ADC.

void initADC () {

// Init ADC; f = (16MHz/prescaler)/13 cikli/konversija ADMUX = 0; // Kanāls sel, labais-adj, izmantojiet AREF tapu ADCSRA = _BV (ADEN) | // ADC iespējot _BV (ADSC) | // ADC sākums _BV (ADATE) | // Automātiskais sprūda _BV (ADIE) | // Pārtraukšanas iespējošana _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Brīvās darbības režīms DIDR0 = _BV (0); // Izslēgt ciparu ievadi ADC tapai TIMSK0 = 0; // Taimeris0 izslēgts} Un pārtraukuma apstrādātājs izskatās šādi: ISR (ADC_vect) {uint16_t paraugs = ADC; paraugi [samplePos ++] = paraugs - 400; ja (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buferis pilns, pārtraukums izslēgts}}

Spektra analīze

Pēc paraugu savākšanas es aprēķinu 8 frekvenču amplitūdas, kas kodē simbolus. Man nav nepieciešams palaist pilnu FFT, tāpēc es izmantoju Gertzela algoritmu.

void goertzel (uint8_t *paraugi, pludiņa *spektrs) {

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

2. darbība: kods

Iepriekš redzamajā attēlā parādīts 3. ciparu kodēšanas piemērs, kur maksimālā amplitūda atbilst frekvencēm 697 Hz un 1477 Hz.

Pilna skice izskatās šādi

/** * Savienojumi: * [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

#iekļaut

#define CS_PIN 9

#definēt N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t paraugi [N];

gaistošs uint16_t samplePos = 0;

pludiņa spektrs [IX_LEN];

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

// Aprēķināts 9615 Hz 256 paraugiem const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.634393284163645619566056 const float sin_t [IX_LEN] PROGMĒMA = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025456 0.48

typedef structure {

char cipars; indekss uint8_t; } ciparu_t;

ciparu_t atklāts_cipars;

const char tabula [4] [4] PROGMĒMA = {

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

const uint8_t char_indexes [4] [4] PROGMĒMA = {

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

baitu fonts [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} // *};

void initADC () {

// Sākotnējais ADC; f = (16MHz/prescaler)/13 cikli/konversija ADMUX = 0; // Kanāls sel, labais-adj, izmantojiet AREF tapu ADCSRA = _BV (ADEN) | // ADC iespējot _BV (ADSC) | // ADC sākums _BV (ADATE) | // Automātiskais sprūda _BV (ADIE) | // Pārtraukšanas iespējošana _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Brīvās darbības režīms DIDR0 = _BV (0); // Izslēgt ciparu ievadi ADC tapai TIMSK0 = 0; // Taimeris0 izslēgts}

void goertzel (uint8_t *paraugi, pludiņa *spektrs) {

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

float avg (float *a, uint16_t len) {

pludiņa rezultāts =.0; par (uint16_t i = 0; i <len; i ++) {rezultāts+= a ; } atgriešanās rezultāts / len; }

int8_t get_single_index_above_threshold (pludiņš *a, uint16_t len, pludiņa slieksnis) {

if (slieksnis <THRESHOLD) {return -1; } int8_t ix = -1; par (uint16_t i = 0; i slieksnis) {ja (ix == -1) {ix = i; } cits {atgriezties -1; }}} atgriezties ix; }

void detect_digit (pludiņa *spektrs) {

peldēt avg_row = avg (spektrs, 4); pludiņš avg_col = avg (& spektrs [4], 4); int8_t rinda = get_single_index_above_threshold (spektrs, 4, avg_row); int8_t col = get_single_index_above_threshold (& spektrs [4], 4, avg_col); ja (rinda! = -1 && col! = -1 && avg_col> 200) {atklāts_cipars.cipars = pgm_read_byte (& (tabula [rinda] [kolonna])); atklāts_digits.index = pgm_read_byte (& (char_indexes [rinda] [kol.])); } cits {atklāts_cipars.cipars = 0; }}

void drawSprite (baits* sprite) {

// Maska tiek izmantota, lai iegūtu kolonnas bitu no sprite rindas baitu maskas = B10000000; par (int iy = 0; iy <8; iy ++) {par (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// pārbīdiet masku par vienu pikseļu pa labi

maska = maska >> 1; }

// atiestatīt kolonnu masku

maska = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

atklāts_cipars.cipars = 0;

}

neparakstīts garais z = 0;

void loop () {

kamēr (ADCSRA & _BV (ADIE)); // Pagaidiet, līdz audio paraugu ņemšana pabeigs goertzel (paraugi, spektrs); atklāt_ciparu (spektrs);

ja (atklāts_cipars.cipars! = 0) {

drawSprite (fonts [atklāts_ciparu.indekss]); lmd.display (); } ja (z % 5 == 0) {par (int i = 0; i <IX_LEN; i ++) {Sērijas nospiedums (spektrs ); Serial.print ("\ t"); } Sērijas.println (); Serial.println ((int) atklāts_cipars.cipars); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Atsākt paraugu ņemšanas pārtraukšanu

}

ISR (ADC_vect) {

uint16_t paraugs = ADC;

paraugi [samplePos ++] = paraugs - 400;

ja (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buferis pilns, pārtraukums izslēgts}}

3. darbība. Shēmas

Shēmas
Shēmas

Jāizveido šādi savienojumi:

Mikrofons Arduino

Ārā -> A0

Vcc -> 3.3V Gnd -> Gnd

Ir svarīgi savienot AREF ar 3.3V

Parādiet Arduino

Vcc -> 5V

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

4. solis. Secinājums

Ko šeit varētu uzlabot? Es izmantoju N = 256 paraugus ar frekvenci 9615Hz, kam ir zināma spektra noplūde, ja N = 205 un ātrums ir 8000Hz, tad vēlamās frekvences sakrīt ar diskretizācijas režģi. Šim nolūkam ADC jāizmanto taimera pārpildes režīmā.

Ieteicams: