Sisukord:

DTMF -detektor: 4 sammu
DTMF -detektor: 4 sammu

Video: DTMF -detektor: 4 sammu

Video: DTMF -detektor: 4 sammu
Video: GSM/GPRS-модуль SIM800L (#4) - все о DTMF: парсинг, управление, безопасность 2024, November
Anonim
Image
Image

Ülevaade

Mind inspireeris selle seadme loomine digitaalse signaalitöötluse veebikursuse kodusest ülesandest. See on DTMF -dekooder, mis on rakendatud koos Arduino UNO -ga, tuvastab helitugevuse režiimis telefoni klaviatuuril vajutatud numbri.

Samm: mõistke algoritmi

Kood
Kood

DTMF -is on iga sümbol kodeeritud kahe sagedusega vastavalt pildil olevale tabelile.

Seade salvestab mikrofonist sisendi ja arvutab kaheksa sageduse amplituudid. Kaks maksimaalse amplituudiga sagedust annavad kodeeritud sümboli rea ja veeru.

Andmete kogumine

Spektrianalüüsi tegemiseks tuleks proove koguda teatud prognoositava sagedusega. Selle saavutamiseks kasutasin maksimaalse täpsusega vabalt käivitatavat ADC-režiimi (eelskaala 128), mis annab diskreetimissageduse 9615 Hz. Allolev kood näitab, kuidas konfigureerida Arduino ADC.

tühine initADC () {

// Init ADC; f = (16MHz/prescaler)/13 tsüklit/teisendus ADMUX = 0; // Kanal sel, parem-adj, kasutage AREF-i tihvti ADCSRA = _BV (ADEN) | // ADC lubamine _BV (ADSC) | // ADC algus _BV (ADATE) | // Automaatne päästik _BV (ADIE) | // Katkestamise lubamine _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Vabakäigu režiim DIDR0 = _BV (0); // Lülita ADC pin pinge sisend välja TIMSK0 = 0; // Taimer0 väljas} Ja katkestuste käitleja näeb välja selline ISR (ADC_vect) {uint16_t sample = ADC; sample [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Puhver täis, katkestus välja}}

Spektri analüüs

Pärast proovide kogumist arvutan sümboleid kodeeriva 8 sageduse amplituudid. Selleks ei pea ma täielikku FFT -d käivitama, seega kasutasin Goertzeli algoritmi.

tühine goertzel (uint8_t *proovid, ujuk *spekter) {

ujuk v_0, v_1, v_2; float re, im, amp; jaoks (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k]))); float s = pgm_read_float (& (sin_t [k]))); ujuk a = 2. * c; v_0 = v_1 = v_2 = 0; jaoks (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (hõljuk) (proovid ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spekter [k] = amp; }}

2. samm: kood

Ülaloleval pildil on näide numbri 3 kodeerimisest, kus maksimaalne amplituud vastab sagedustele 697Hz ja 1477Hz.

Kogu eskiis näeb välja järgmine

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

#kaasake

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t proovid [N];

lenduv uint16_t samplePos = 0;

ujuv spekter [IX_LEN];

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

// Arvutatud 9615 Hz 256 proovi jaoks 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] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025456 0.48

typedef structure {

sümbol; indeks uint8_t; } number_t;

digit_t tuvastatud_digit;

const char tabel [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} };

baitide font [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} // *};

tühine initADC () {

// Init ADC; f = (16MHz/prescaler)/13 tsüklit/teisendus ADMUX = 0; // Kanal sel, parem-adj, kasutage AREF-i tihvti ADCSRA = _BV (ADEN) | // ADC lubamine _BV (ADSC) | // ADC algus _BV (ADATE) | // Automaatne päästik _BV (ADIE) | // Katkestamise lubamine _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Vabakäigu režiim DIDR0 = _BV (0); // Lülita ADC pin pinge sisend välja TIMSK0 = 0; // Taimer0 välja lülitatud}

tühine goertzel (uint8_t *proovid, ujuk *spekter) {

ujuk v_0, v_1, v_2; float re, im, amp; jaoks (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k]))); float s = pgm_read_float (& (sin_t [k]))); ujuk a = 2. * c; v_0 = v_1 = v_2 = 0; jaoks (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (hõljuk) (proovid ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spekter [k] = amp; }}

float avg (float *a, uint16_t len) {

ujukitulemus =.0; jaoks (uint16_t i = 0; i <len; i ++) {tulemus+= a ; } tagastamise tulemus / len; }

int8_t get_single_index_above_threshold (float *a, uint16_t len, float künnis) {

if (lävi <THRESHOLD) {return -1; } int8_t ix = -1; jaoks (uint16_t i = 0; i lävi) {if (ix == -1) {ix = i; } muu {tagastamine -1; }}} return ix; }

tühine detekteerimisnumber (ujuk *spekter) {

ujuk avg_row = keskm (spekter, 4); float avg_col = keskm. (& spekter [4], 4); int8_t rida = get_single_index_above_threshold (spekter, 4, keskm. rida); int8_t col = get_single_index_above_threshold (& spekter [4], 4, keskm.) if (rida! = -1 && col! = -1 && avg_col> 200) {tuvastatud_digit.digit = pgm_read_byte (& (tabel [rida] [col])); tuvastatud_dig.index = pgm_read_byte (& (char_indexes [rida] [col])); } muu {tuvastatud_dig.digit = 0; }}

void drawSprite (bait* sprite) {

// Maski kasutatakse veerbiti saamiseks sprite rea baidimaskilt = B10000000; jaoks (int iy = 0; iy <8; iy ++) {jaoks (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// nihutage maski ühe piksli võrra paremale

mask = mask >> 1; }

// veergumaski lähtestamine

mask = B10000000; }}

tühine seadistus () {

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

Seriaalne algus (115200);

lmd.setEnabled (tõene); lmd.setIntensity (2); lmd.clear (); lmd.display ();

tuvastatud_digit.digit = 0;

}

allkirjata pikk z = 0;

void loop () {

samas (ADCSRA & _BV (ADIE)); // Oodake, kuni heliproovide võtmine goertzel lõpetab (proovid, spekter); detect_digit (spekter);

kui (tuvastatud_dig.digit! = 0) {

drawSprite (font [tuvastatud_dig.index]); lmd.display (); } kui (z % 5 == 0) {jaoks (int i = 0; i <IX_LEN; i ++) {jadatrükk (spekter ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) tuvastatud_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Proovivõtmise katkestamise jätkamine

}

ISR (ADC_vect) {

uint16_t proov = ADC;

proovid [samplePos ++] = proov - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Puhver täis, katkestus välja}}

3. samm: skeemid

Skeemid
Skeemid

Tuleb teha järgmised ühendused:

Mikrofon Arduinole

Väljas -> A0

Vcc -> 3.3V Gnd -> Gnd

Oluline on ühendada AREF 3.3V -ga

Kuva Arduinole

Vcc -> 5V

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

4. samm: järeldus

Mida saaks siin parandada? Kasutasin N = 256 proovi sagedusel 9615Hz, millel on mõningane spektri leke, kui N = 205 ja sagedus on 8000Hz, langevad soovitud sagedused kokku diskreetimisvõrguga. Selleks tuleks ADC -d kasutada taimeri ülevoolu režiimis.

Soovitan: