Turinys:

DTMF detektorius: 4 žingsniai
DTMF detektorius: 4 žingsniai

Video: DTMF detektorius: 4 žingsniai

Video: DTMF detektorius: 4 žingsniai
Video: Создание DTMF звонка для Вашей радиостанции 2024, Lapkritis
Anonim
Image
Image

Apžvalga

Sukurti šį įrenginį mane įkvėpė namų užduotis skaitmeninio signalo apdorojimo internetiniuose kursuose. Tai DTMF dekoderis, įdiegtas naudojant „Arduino UNO“, jis atpažįsta skaitmenį, nuspaustą telefono klaviatūroje toniniu režimu pagal jo skleidžiamą garsą.

1 žingsnis: supraskite algoritmą

Kodas
Kodas

DTMF kiekvienas simbolis yra užkoduotas dviem dažniais pagal paveikslėlyje pateiktą lentelę.

Įrenginys fiksuoja įvestį iš mikrofono ir apskaičiuoja aštuonių dažnių amplitudę. Du dažniai su maksimaliomis amplitudėmis suteikia užkoduoto simbolio eilutę ir stulpelį.

Duomenų surinkimas

Norint atlikti spektro analizę, mėginiai turėtų būti paimami tam tikru nuspėjamu dažniu. Norėdami tai pasiekti, aš naudoju laisvo paleidimo ADC režimą su maksimaliu tikslumu (prescaler 128), kuris suteikia mėginių ėmimo dažnį 9615 Hz. Žemiau pateiktas kodas parodo, kaip sukonfigūruoti „Arduino“ADC.

void initADC () {

// Pradinė ADC; f = (16MHz/prescaler)/13 ciklų/konversija ADMUX = 0; // Kanalas sel, dešinysis, naudokite AREF kaištį ADCSRA = _BV (ADEN) | // ADC įgalinti _BV (ADSC) | // ADC pradžia _BV (ADATE) | // Automatinis paleidiklis _BV (ADIE) | // Nutraukti įgalinimą _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Laisvo paleidimo režimas DIDR0 = _BV (0); // Išjungti skaitmeninį įvestį ADC kaiščiui TIMSK0 = 0; // Laikmatis0 išjungtas} Ir pertraukimo tvarkytojas atrodo taip: ISR (ADC_vect) {uint16_t sample = ADC; sample [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buferis pilnas, pertrauka išjungta}}

Spektro analizė

Surinkęs mėginius apskaičiuoju 8 dažnių, koduojančių simbolius, amplitudes. Man nereikia paleisti viso FFT, todėl naudojau Goertzelio algoritmą.

void goertzel (uint8_t *mėginiai, plūdės *spektras) {

plūdė v_0, v_1, v_2; plūdė re, im, amp; for (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k]))); plūdė s = pgm_read_float (& (sin_t [k]))); plūdė a = 2. * c; v_0 = v_1 = v_2 = 0; už (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (plūdė) (pavyzdžiai ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektras [k] = amp; }}

2 žingsnis: kodas

Aukščiau esančiame paveikslėlyje parodytas 3 skaitmens kodavimo pavyzdys, kai maksimali amplitudė atitinka 697 Hz ir 1477 Hz dažnius.

Visas eskizas atrodo taip

/** * Ryšiai: * [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

#įtraukti

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t mėginiai [N];

nepastovus uint16_t samplePos = 0;

plūdės spektras [IX_LEN];

// Dažniai [697,0, 770,0, 852,0, 941,0, 1209,0, 1336,0, 1477,0, 1633,0]

// Apskaičiuota 9615 Hz 256 mėginiams 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 {

char skaitmuo; indeksas uint8_t; } skaitmenų_t;

skaitmenų_t aptiktas_skaitmuo;

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

baitų šriftas [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 () {

// Pradinė ADC; f = (16MHz/prescaler)/13 ciklų/konversija ADMUX = 0; // Kanalas sel, dešinysis, naudokite AREF kaištį ADCSRA = _BV (ADEN) | // ADC įgalinti _BV (ADSC) | // ADC pradžia _BV (ADATE) | // Automatinis paleidiklis _BV (ADIE) | // Nutraukti įgalinimą _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Laisvo paleidimo režimas DIDR0 = _BV (0); // Išjungti skaitmeninį įvestį ADC kaiščiui TIMSK0 = 0; // Laikmatis0 išjungtas}

void goertzel (uint8_t *mėginiai, plūdės *spektras) {

plūdė v_0, v_1, v_2; plūdė re, im, amp; for (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k]))); plūdė s = pgm_read_float (& (sin_t [k]))); plūdė a = 2. * c; v_0 = v_1 = v_2 = 0; už (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (plūdė) (pavyzdžiai ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektras [k] = amp; }}

plūdė vid. (plūdė *a, uint16_t len) {

plūdės rezultatas =.0; už (uint16_t i = 0; i <len; i ++) {rezultatas+= a ; } grąžinimo rezultatas / len; }

int8_t get_single_index_above_threshold (plūdė *a, uint16_t len, plūdės slenkstis) {

if (slenkstis <THRESHOLD) {return -1; } int8_t ix = -1; už (uint16_t i = 0; i slenkstis) {if (ix == -1) {ix = i; } else {return -1; }}} return ix; }

void detect_digit (plūdės *spektras) {

plūdė avg_row = avg (spektras, 4); plūdė avg_col = vid. (& spektras [4], 4); int8_t eilutė = get_single_index_above_threshold (spektras, 4, vid. eilutė); int8_t col = get_single_index_above_threshold (& spektras [4], 4, avg_col); if (eilutė! = -1 && col! = -1 && avg_col> 200) {aptiktas_skaitmuo.skaitmuo = pgm_read_byte (& (lentelė [eilutė] [col])); aptikta_skaitmen.index = pgm_read_byte (& (char_indexes [eilutė] [col])); } else {aptiktas_skaitmuo.skaitmuo = 0; }}

void drawSprite (baitas* spritas) {

// Kaukė naudojama norint gauti stulpelio bitą iš sprite eilutės baitų kaukės = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// perkelkite kaukę vienu pikseliu į dešinę

kaukė = kaukė >> 1; }

// iš naujo nustatyti stulpelio kaukę

kaukė = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

aptiktas_skaitmuo.skaitmuo = 0;

}

nepasirašytas ilgas z = 0;

void loop () {

tuo tarpu (ADCSRA & _BV (ADIE)); // Palaukite, kol garso atranka baigs goertzel (mėginiai, spektras); aptikti_skaitmenis (spektras);

jei (aptiktas_skaitmuo.ženklas! = 0) {

drawSprite (šriftas [aptiktas_ženklas.indeksas]); lmd.display (); } if (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektras ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) aptiktas_skaitmuo.skaitmuo); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Tęsti mėginių ėmimo pertraukimą

}

ISR (ADC_vect) {

uint16_t pavyzdys = ADC;

mėginiai [samplePos ++] = mėginys - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buferis pilnas, pertrauka išjungta}}

3 žingsnis: schemos

Schemos
Schemos

Reikėtų atlikti šiuos ryšius:

Mikrofonas „Arduino“

Išėjimas -> A0

Vcc -> 3.3V Gnd -> Gnd

Svarbu prijungti AREF prie 3.3V

Rodyti „Arduino“

Vcc -> 5V

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

4 žingsnis: Išvada

Ką čia būtų galima patobulinti? Aš naudoju N = 256 mėginius 9615Hz dažniu, kuris turi tam tikrą spektro nuotėkį, jei N = 205 ir dažnis yra 8000 Hz, tada norimi dažniai sutampa su diskretizacijos tinkleliu. Tam ADC turėtų būti naudojamas laikmačio perpildymo režimu.

Rekomenduojamas: