Sadržaj:

DTMF detektor: 4 koraka
DTMF detektor: 4 koraka

Video: DTMF detektor: 4 koraka

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

Pregled

Za izradu ovog uređaja inspirirao me domaći zadatak na online kursu Digitalna obrada signala. Ovo je DTMF dekoder implementiran sa Arduino UNO, on detektuje cifru pritisnutu na tastaturi telefona u tonskom režimu zvukom koji proizvodi.

Korak 1: Razumevanje algoritma

Kodeks
Kodeks

U DTMF -u je svaki simbol kodiran s dvije frekvencije prema tablici na slici.

Uređaj snima ulaz sa mikrofona i izračunava amplitude osam frekvencija. Dvije frekvencije s maksimalnim amplitudama daju red i stupac kodiranog simbola.

Prikupljanje podataka

Kako bi se izvršila analiza spektra, uzorke treba uhvatiti na određenoj predvidljivoj frekvenciji. Da bih to postigao, koristio sam ADC način rada sa maksimalnom preciznošću (predskaler 128) koji daje frekvenciju uzorkovanja 9615Hz. Donji kod prikazuje kako konfigurirati Arduino ADC.

void initADC () {

// Init ADC; f = (16MHz/predskaler)/13 ciklusa/konverzija ADMUX = 0; // Sel channel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // Omogućivanje ADC -a _BV (ADSC) | // ADC start _BV (ADATE) | // Automatski okidač _BV (ADIE) | // Omogućivanje prekida _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Slobodni način rada DIDR0 = _BV (0); // Isključivanje digitalnog ulaza za ADC pin TIMSK0 = 0; // Timer0 off} A rukovatelj prekida izgleda ovako: ISR (ADC_vect) {uint16_t sample = ADC; uzorci [samplePos ++] = uzorak - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bafer pun, prekid isključen}}

Analiza spektra

Nakon prikupljanja uzoraka izračunavam amplitude 8 frekvencija koje kodiraju simbole. Za ovo mi ne treba pokretanje punog FFT -a, pa sam upotrijebio Goertzelov algoritam.

void goertzel (uint8_t *uzorci, float *spektar) {

float v_0, v_1, v_2; float 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])); float a = 2. * c; v_0 = v_1 = v_2 = 0; za (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (uzorci ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektar [k] = amp; }}

Korak 2: Kodeks

Na gornjoj slici prikazan je primjer kodiranja znamenke 3 gdje maksimalna amplituda odgovara frekvencijama 697Hz i 1477Hz.

Kompletna skica izgleda ovako

/** * Priključci: * [Mikrofon na Arduino] * - Izlaz -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [Prikaz na Arduinu] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 */ #include #include

#include

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t uzorci [N];

volatile uint16_t samplePos = 0;

plutajući spektar [IX_LEN];

// Frekvencije [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Izračunato za 9615Hz 256 uzoraka const float cos_t [IX_LEN] PROGMEM = {0,8932243011955153, 0,8700869911087115, 0,8448535652497071, 0,8032075314806449, 0,6895405447370669, 0,634393284163645639, 0,56036, 0,62336 const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.8314696123025451;

typedef struct {

znak znamenke; uint8_t indeks; } digit_t;

digit_t otkrivena_digit;

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

font bajta [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 () {

// Init ADC; f = (16MHz/predskaler)/13 ciklusa/konverzija ADMUX = 0; // Sel channel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // Omogućivanje ADC -a _BV (ADSC) | // ADC start _BV (ADATE) | // Automatski okidač _BV (ADIE) | // Omogućivanje prekida _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Slobodni način rada DIDR0 = _BV (0); // Isključivanje digitalnog ulaza za ADC pin TIMSK0 = 0; // Tajmer0 isključen}

void goertzel (uint8_t *uzorci, float *spektar) {

float v_0, v_1, v_2; float 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])); float a = 2. * c; v_0 = v_1 = v_2 = 0; za (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (uzorci ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektar [k] = amp; }}

float avg (float *a, uint16_t len) {

plutajući rezultat =, 0; for (uint16_t i = 0; i <len; i ++) {rezultat+= a ; } return result / len; }

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

if (prag <THRESHOLD) {return -1; } int8_t ix = -1; za (uint16_t i = 0; i prag) {if (ix == -1) {ix = i; } else {return -1; }}} return ix; }

void detektiraj_digit (float *spektar) {

float avg_row = avg (spektar, 4); float avg_col = avg (& spektar [4], 4); int8_t red = get_single_index_above_threshold (spektar, 4, prosjek_row); int8_t col = get_single_index_above_threshold (& spektar [4], 4, avg_col); if (red! = -1 && col! = -1 && avg_col> 200) {otkrivena_digit.digit = pgm_čitana_bajt (& (tabela [red] [col])); otkriven_digit.index = pgm_čitani_bajt (& (char_indexes [red] [col])); } else {otkrivena_digit.digit = 0; }}

void drawSprite (byte* sprite) {

// Maska se koristi za dobivanje bita kolone iz sprite red byte mask = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// pomaknuti masku za jedan piksel udesno

maska = maska >> 1; }

// poništavanje maske kolone

maska = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

otkrivena_digit.digit = 0;

}

nepotpisano dugo z = 0;

void loop () {{100} {101}

while (ADCSRA & _BV (ADIE)); // Sačekajte da audio uzorkovanje završi goertzel (uzorci, spektar); detektiraj_cifru (spektar);

if (otkrivena_digit.digit! = 0) {

drawSprite (font [otkriven_digit.index]); lmd.display (); } if (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektar ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) detection_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Nastavak prekida uzorkovanja

}

ISR (ADC_vect) {

uint16_t uzorak = ADC;

uzorci [samplePos ++] = uzorak - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bafer pun, prekid isključen}}

Korak 3: Sheme

Sheme
Sheme

Treba uspostaviti sljedeće veze:

Mikrofon na Arduino

Izlaz -> A0

Vcc -> 3.3V Gnd -> Gnd

Važno je spojiti AREF na 3.3V

Prikaži na Arduinu

Vcc -> 5V

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

Korak 4: Zaključak

Šta bi se tu moglo poboljšati? Koristio sam N = 256 uzoraka pri brzini 9615Hz koja ima neko curenje u spektru, ako je N = 205 i brzina je 8000Hz tada se željene frekvencije podudaraju s diskrecijskom mrežom. Za to bi ADC trebao biti korišten u modu preljevanja timera.

Preporučuje se: