Sommario:

Rilevatore DTMF: 4 passaggi
Rilevatore DTMF: 4 passaggi

Video: Rilevatore DTMF: 4 passaggi

Video: Rilevatore DTMF: 4 passaggi
Video: Changing the Sensor Membrane 2024, Luglio
Anonim
Image
Image

Panoramica

Sono stato ispirato a costruire questo dispositivo da un incarico a casa sul corso online di elaborazione del segnale digitale. Questo è un decoder DTMF implementato con Arduino UNO, rileva una cifra premuta sulla tastiera di un telefono in modalità tono dal suono che produce.

Passaggio 1: comprensione dell'algoritmo

Il codice
Il codice

In DTMF ogni simbolo è codificato con due frequenze secondo la tabella in figura.

Il dispositivo acquisisce l'input dal microfono e calcola le ampiezze di otto frequenze. Due frequenze con ampiezza massima danno una riga e una colonna del simbolo codificato.

Acquisizione dei dati

Per eseguire l'analisi dello spettro, i campioni dovrebbero essere catturati a una certa frequenza prevedibile. Per ottenere ciò ho utilizzato la modalità ADC a corsa libera con la massima precisione (prescaler 128) che fornisce una frequenza di campionamento di 9615 Hz. Il codice seguente mostra come configurare l'ADC di Arduino.

void initADC() {

// Inizia ADC; f = (16MHz/prescaler) / 13 cicli/conversione ADMUX = 0; // Channel sel, right-adj, usa il pin AREF ADCSRA = _BV(ADEN) | // Abilita ADC _BV(ADSC) | // Inizio ADC _BV(ADATE) | // Trigger automatico _BV(ADIE) | // Abilitazione interrupt _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Modalità di esecuzione libera DIDR0 = _BV(0); // Disattiva l'ingresso digitale per il pin ADC TIMSK0 = 0; // Timer0 off } E il gestore di interrupt assomiglia a questo ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer pieno, interruzione disattivata } }

Analisi dello spettro

Dopo aver raccolto i campioni calcolo le ampiezze di 8 frequenze che codificano i simboli. Non ho bisogno di eseguire FFT completo per questo, quindi ho usato l'algoritmo di Goertzel.

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

float v_0, v_1, v_2; galleggiante 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; for (uint16_t i = 0; i < N; i++) { v_0 = v_1; v_1 = v_2; v_2 = (float)(samples) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt(re * re + im * im); spettro[k] = amp; } }

Passaggio 2: il codice

L'immagine sopra mostra l'esempio di codifica della cifra 3 dove l'ampiezza massima corrisponde alle frequenze 697Hz e 1477Hz.

Lo schizzo completo appare come segue

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

#includere

#define CS_PIN 9

#definire N 256

#define IX_LEN 8 #define SOGLIA 20

LEDMatrixDriver lmd(, CS_PIN);

uint8_t campioni[N];

volatile uint16_t samplePos = 0;

spettro float[IX_LEN];

// Frequenze [697.0, 770.0, 852.0, 941,0, 1209.0, 1336,0, 1477,0, 1633.0]

// Calcolato per 9615Hz 256 campioni const float cos_t[IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555702330196023,5978}; const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.8819212643483549 };

typedef struttura {

cifra di caratteri; uint8_t indice; } cifra_t;

digit_t rilevato_cifra;

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

carattere di byte[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() {

// Inizia ADC; f = (16MHz/prescaler) / 13 cicli/conversione ADMUX = 0; // Channel sel, right-adj, usa il pin AREF ADCSRA = _BV(ADEN) | // Abilita ADC _BV(ADSC) | // Inizio ADC _BV(ADATE) | // Trigger automatico _BV(ADIE) | // Abilitazione interrupt _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Modalità di esecuzione libera DIDR0 = _BV(0); // Disattiva l'ingresso digitale per il pin ADC TIMSK0 = 0; // Timer0 disattivato }

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

float v_0, v_1, v_2; galleggiante 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; for (uint16_t i = 0; i < N; i++) { v_0 = v_1; v_1 = v_2; v_2 = (float)(samples) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt(re * re + im * im); spettro[k] = amp; } }

float avg(float *a, uint16_t len) {

risultato float =.0; for (uint16_t i = 0; i < len; i++) { risultato += a; } restituisce il risultato / lente; }

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

if (soglia < SOGLIA) { return -1; } int8_t ix = -1; for (uint16_t i = 0; i soglia) { if (ix == -1) { ix = i; } else { return -1; } } } restituisce ix; }

void detect_digit(float *spettro) {

float avg_row = avg(spettro, 4); float avg_col = avg(&spectrum[4], 4); int8_t riga = get_single_index_above_threshold(spettro, 4, avg_row); int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col); if (riga != -1 && col != -1 && avg_col > 200) { digit_rilevato.digit = pgm_read_byte(&(table[row][col])); valore_digit.index = pgm_read_byte(&(char_indexes[row][col])); } else { digit_digit.digit = 0; } }

void drawSprite(byte* sprite) {

// La maschera viene utilizzata per ottenere il bit di colonna dalla riga dello sprite 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));

// sposta la maschera di un pixel a destra

maschera = maschera >> 1; }

// ripristina la maschera di colonna

maschera = B10000000; } }

void setup() {

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

Serial.begin(115200);

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

digit_digit.digit = 0;

}

lungo senza segno z = 0;

ciclo vuoto() {

while(ADCSRA & _BV(ADIE)); // Attendi che il campionamento audio finisca goertzel(samples, Spectrum); detect_digit(spettro);

if (detected_digit.digit != 0) {

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

samplePos = 0;

ADCSRA |= _BV(ADIE); // Riprende l'interruzione del campionamento

}

ISR(ADC_vect) {

uint16_t campione = ADC;

campioni[campionePos++] = campione - 400;

if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer pieno, interruzione disattivata } }

Passaggio 3: schemi

Schematico
Schematico

Devono essere effettuati i seguenti collegamenti:

Dal microfono ad Arduino

Fuori -> A0

Vcc -> 3.3V Gnd -> Gnd

È importante collegare AREF a 3.3V

Visualizza su Arduino

Vcc -> 5V

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

Passaggio 4: conclusione

Cosa potrebbe essere migliorato qui? Ho usato N = 256 campioni alla frequenza 9615Hz che ha una certa dispersione dello spettro, se N = 205 e la frequenza è 8000Hz allora le frequenze desiderate coincidono con la griglia di discretizzazione. Per questo l'ADC dovrebbe essere utilizzato in modalità di overflow del timer.

Consigliato: