Taula de continguts:

Detector DTMF: 4 passos
Detector DTMF: 4 passos

Vídeo: Detector DTMF: 4 passos

Vídeo: Detector DTMF: 4 passos
Vídeo: How to use a Motorola Razr V3 to control an Arduino with DTMF 2024, De novembre
Anonim
Image
Image

Visió general

Em va inspirar construir aquest dispositiu amb una assignació a casa al curs en línia de processament de senyal digital. Es tracta d’un descodificador DTMF implementat amb Arduino UNO, que detecta un dígit premut en un teclat del telèfon en mode de to pel so que produeix.

Pas 1: entendre l'algorisme

El codi
El codi

En DTMF cada símbol es codifica amb dues freqüències segons la taula de la imatge.

El dispositiu capta l'entrada del micròfon i calcula amplituds de vuit freqüències. Dues freqüències amb amplituds màximes donen una fila i una columna del símbol codificat.

Adquisició de dades

Per realitzar anàlisis d'espectre, s'han de capturar mostres a una freqüència previsible determinada. Per aconseguir-ho, he utilitzat el mode ADC de funcionament lliure amb la màxima precisió (prescaler 128) que proporciona una freqüència de mostreig de 9615Hz. El codi següent mostra com configurar l’ADC d’Arduino.

void initADC () {

// Iniciar ADC; f = (16 MHz / prescaler) / 13 cicles / conversió ADMUX = 0; // Selecció de canal, ajustat a la dreta, utilitza el pin AREF ADCSRA = _BV (ADEN) | // ADC habilita _BV (ADSC) | // Inici ADC _BV (ADATE) | // Activador automàtic _BV (ADIE) | // Interrupt habilitar _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Mode de funcionament lliure DIDR0 = _BV (0); // Desactiveu l'entrada digital per al pin ADC TIMSK0 = 0; // Temporitzador0 desactivat} I el controlador d'interrupcions té aquest aspecte ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer ple, interrupció desactivada}}

Anàlisi de l'espectre

Després de recollir mostres, calculo amplituds de 8 freqüències que codifiquen símbols. No necessito executar FFT complet per a això, així que he utilitzat l'algorisme de Goertzel.

voertz goertzel (mostres uint8_t *, espectre flotant *) {

flotador v_0, v_1, v_2; float re, im, amp; per a (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); flotar a = 2. * c; v_0 = v_1 = v_2 = 0; per a (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); espectre [k] = amplificador; }}

Pas 2: el codi

La imatge superior mostra l'exemple de codificació del dígit 3 on l'amplitud màxima correspon a les freqüències 697Hz i 1477Hz.

L’esbós complet té el següent aspecte

/ ** * Connexions: * [Mic a Arduino] * - Sortida -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [Visualització a Arduino] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 * / #include #include

#incloure

#defineix CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t mostres [N];

volàtil uint16_t samplePos = 0;

espectre flotant [IX_LEN];

// Freqüències [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Calculat per a 9615Hz 256 mostres const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555, 0.5555; const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.83146961230252456, 0.88

typedef struct {

dígit de caràcter; índex uint8_t; } dígit_t;

dígit_t dígit_ detectat;

taula de const const [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 de bytes [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, 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} // *};

void initADC () {

// Iniciar ADC; f = (16 MHz / prescaler) / 13 cicles / conversió ADMUX = 0; // Selecció de canal, ajustat a la dreta, utilitza el pin AREF ADCSRA = _BV (ADEN) | // ADC habilita _BV (ADSC) | // Inici ADC _BV (ADATE) | // Activador automàtic _BV (ADIE) | // Interrupt habilitar _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Mode de funcionament lliure DIDR0 = _BV (0); // Desactiveu l'entrada digital per al pin ADC TIMSK0 = 0; // Temporitzador0 desactivat}

voertz goertzel (mostres uint8_t *, espectre flotant *) {

flotador v_0, v_1, v_2; float re, im, amp; per a (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); flotar a = 2. * c; v_0 = v_1 = v_2 = 0; per a (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); espectre [k] = amplificador; }}

float avg (float * a, uint16_t len) {

resultat flotant =, 0; for (uint16_t i = 0; i <len; i ++) {result + = a ; } retornar el resultat / len; }

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

if (llindar <LIMI) {return -1; } int8_t ix = -1; per a (uint16_t i = 0; llindar i) {if (ix == -1) {ix = i; } else {return -1; }}} tornar ix; }

void detect_digit (float * spectre) {

float avg_row = avg (espectre, 4); float avg_col = avg (& spectre [4], 4); int8_t fila = get_single_index_above_threshold (espectre, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectre [4], 4, avg_col); if (fila! = -1 && col! = -1 && avg_col> 200) {digit_digit.digit = pgm_read_byte (& (taula [fila] [col])); digital_digit.index = pgm_read_byte (& (char_indexes [fila] [col])); } else {detectat_digit.digit = 0; }}

void drawSprite (byte * sprite) {

// La màscara s’utilitza per obtenir el bit de columna de la màscara de bytes de fila sprite = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// canvieu la màscara per un píxel cap a la dreta

màscara = màscara >> 1; }

// restableix la màscara de columna

màscara = B10000000; }}

configuració nul·la () {

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

Serial.begin (115200);

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

digital_digit.digit = 0;

}

llarg sense signe z = 0;

bucle buit () {

mentre que (ADCSRA & _BV (ADIE)); // Espereu que el mostreig d’àudio acabi el goertzel (mostres, espectre); detect_digit (espectre);

if (digital_digit.digit! = 0) {

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

samplePos = 0;

ADCSRA | = _BV (ADIE); // Reprèn la interrupció de mostreig

}

ISR (ADC_vect) {

uint16_t sample = ADC;

samples [samplePos ++] = sample - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer ple, interrupció desactivada}}

Pas 3: Esquemes

Esquemes
Esquemes

Cal establir les connexions següents:

Micròfon a Arduino

Fora -> A0

Vcc -> 3,3 V Gnd -> Gnd

És important connectar AREF a 3,3V

Mostra a Arduino

Vcc -> 5V

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

Pas 4: Conclusió

Què es podria millorar aquí? He utilitzat N = 256 mostres a una velocitat de 9615Hz, que té alguna fuita d’espectre, si N = 205 i la velocitat és de 8000Hz, les freqüències desitjades coincideixen amb la graella de discretització. Per a això, s'ha d'utilitzar ADC en mode de desbordament del temporitzador.

Recomanat: