Innholdsfortegnelse:

DTMF -detektor: 4 trinn
DTMF -detektor: 4 trinn

Video: DTMF -detektor: 4 trinn

Video: DTMF -detektor: 4 trinn
Video: Как придать Объём волосам Пошагово дома | 4 способа Укладки волос! Как научиться укладывать волосы! 2024, November
Anonim
Image
Image

Oversikt

Jeg ble inspirert til å bygge denne enheten av et hjemmeopgave på Digital Signal Processing online -kurs. Dette er en DTMF -dekoder implementert med Arduino UNO, den oppdager et siffer som trykkes på et tastatur på telefonen i tonemodus av lyden den produserer.

Trinn 1: Forstå algoritmen

Koden
Koden

I DTMF er hvert symbol kodet med to frekvenser i henhold til tabellen på bildet.

Enheten fanger inngang fra mikrofonen og beregner amplituder på åtte frekvenser. To frekvenser med maksimale amplituder gir en rad og en kolonne med det kodede symbolet.

Datainnsamling

For å utføre spektrumanalyse bør prøver tas med en viss forutsigbar frekvens. For å oppnå dette brukte jeg frikjør ADC-modus med maksimal presisjon (prescaler 128) det gir samplingsfrekvens 9615Hz. Koden nedenfor viser hvordan du konfigurerer Arduinos ADC.

ugyldig initADC () {

// Init ADC; f = (16MHz/prescaler)/13 sykluser/konvertering ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC aktiver _BV (ADSC) | // ADC start _BV (ADATE) | // Autotrigger _BV (ADIE) | // Avbryt aktiver _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Friløpsmodus DIDR0 = _BV (0); // Slå av digital inngang for ADC pin TIMSK0 = 0; // Timer0 off} Og avbryterbehandleren ser slik ut: ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; hvis (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, avbryt}}

Spektrumanalyse

Etter å ha samlet prøver beregner jeg amplituder på 8 frekvenser som koder for symboler. Jeg trenger ikke å kjøre full FFT for dette, så jeg brukte Goertzels algoritme.

void goertzel (uint8_t *samples, float *spectrum) {

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])); flyte a = 2. * c; v_0 = v_1 = v_2 = 0; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prøver ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}

Trinn 2: Koden

Bildet ovenfor viser eksemplet på koding av siffer 3 hvor maksimal amplitude tilsvarer frekvensene 697Hz og 1477Hz.

Hele skissen ser slik ut

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

#inkludere

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t prøver [N];

flyktig uint16_t samplePos = 0;

flytespekter [IX_LEN];

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

// Beregnet for 9615Hz 256 sampler const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555; const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.88

typedef struct {

røye siffer; uint8_t indeks; } digit_t;

digit_t detect_digit;

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

byte skrift [16] [8] = {

{0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38}, // 0 {0x04, 0x0c, 0x14, 0x24, 0x04, 0x04, 0x04, 0x04}, // 1 {0x00, 0x30, 0x48, 0x0, 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} // *};

ugyldig initADC () {

// Init ADC; f = (16MHz/prescaler)/13 sykluser/konvertering ADMUX = 0; // Kanalsel, høyre-adj, bruk AREF-pin ADCSRA = _BV (ADEN) | // ADC aktiver _BV (ADSC) | // ADC start _BV (ADATE) | // Autotrigger _BV (ADIE) | // Avbryt aktiver _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Friløpsmodus DIDR0 = _BV (0); // Slå av digital inngang for ADC pin TIMSK0 = 0; // Timer0 off}

void goertzel (uint8_t *samples, float *spectrum) {

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])); flyte a = 2. * c; v_0 = v_1 = v_2 = 0; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prøver ) + 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) {

flyte resultat =.0; for (uint16_t i = 0; i <len; i ++) {resultat+= a ; } returresultat / len; }

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

if (terskel <THRESHOLD) {return -1; } int8_t ix = -1; for (uint16_t i = 0; i terskel) {if (ix == -1) {ix = i; } annet {retur -1; }}} returner ix; }

void detect_digit (float *spectrum) {

float avg_row = avg (spektrum, 4); float avg_col = avg (& spectrum [4], 4); int8_t rad = get_single_index_above_threshold (spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectrum [4], 4, avg_col); hvis (rad! = -1 && col! = -1 && avg_col> 200) {detect_digit.digit = pgm_read_byte (& (tabell [rad] [col])); detect_digit.index = pgm_read_byte (& (char_indexes [rad] [kol])); } annet {detect_digit.digit = 0; }}

void drawSprite (byte* sprite) {

// Masken brukes til å hente søylebiten fra sprite row 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));

// skift masken med en piksel til høyre

maske = maske >> 1; }

// tilbakestill kolonnemaske

maske = B10000000; }}

ugyldig oppsett () {

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

Serial.begin (115200);

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

detect_digit.digit = 0;

}

usignert lang z = 0;

void loop () {

mens (ADCSRA & _BV (ADIE)); // Vent på at lydsampling er ferdig med goertzel (prøver, spektrum); detect_digit (spektrum);

hvis (detect_digit.digit! = 0) {

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

samplePos = 0;

ADCSRA | = _BV (ADIE); // Fortsett prøvetaking avbryt

}

ISR (ADC_vect) {

uint16_t sample = ADC;

prøver [samplePos ++] = sample - 400;

hvis (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, avbryt}}

Trinn 3: Skjemaer

Skjemaer
Skjemaer

Følgende tilkoblinger bør gjøres:

Mikrofon til Arduino

Ut -> A0

Vcc -> 3.3V Gnd -> Gnd

Det er viktig å koble AREF til 3,3V

Display til Arduino

Vcc -> 5V

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

Trinn 4: Konklusjon

Hva kan forbedres her? Jeg brukte N = 256 prøver med en hastighet på 9615Hz som har noe spektrumlekkasje, hvis N = 205 og frekvensen er 8000Hz, faller de ønskede frekvensene sammen med diskretiseringsnettet. For at ADC skal brukes i timeroverløpsmodus.

Anbefalt: