Tabla de contenido:

Detector DTMF: 4 pasos
Detector DTMF: 4 pasos

Video: Detector DTMF: 4 pasos

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

Visión general

Me inspiré para construir este dispositivo con una tarea en casa del curso en línea Procesamiento de señales digitales. Este es un decodificador DTMF implementado con Arduino UNO, detecta un dígito presionado en el teclado de un teléfono en el modo de tono por el sonido que produce.

Paso 1: comprensión del algoritmo

El código
El código

En DTMF, cada símbolo está codificado con dos frecuencias de acuerdo con la tabla de la imagen.

El dispositivo captura la entrada del micrófono y calcula amplitudes de ocho frecuencias. Dos frecuencias con amplitudes máximas dan una fila y una columna del símbolo codificado.

Adquisición de datos

Para realizar el análisis del espectro, las muestras deben capturarse a una cierta frecuencia predecible. Para lograr esto, utilicé el modo ADC de ejecución libre con la máxima precisión (prescaler 128) que proporciona una frecuencia de muestreo de 9615Hz. El siguiente código muestra cómo configurar el ADC de Arduino.

void initADC () {

// Init ADC; f = (16MHz / prescaler) / 13 ciclos / conversión ADMUX = 0; // Sel de canal, ajuste a la derecha, use el pin AREF ADCSRA = _BV (ADEN) | // ADC habilita _BV (ADSC) | // Inicio de ADC _BV (ADATE) | // Disparador automático _BV (ADIE) | // Habilitación de interrupciones _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Modo de ejecución libre DIDR0 = _BV (0); // Apague la entrada digital para el pin ADC TIMSK0 = 0; // Timer0 off} Y el controlador de interrupciones se ve así ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Búfer lleno, interrumpir}}

Análisis de espectro

Después de recolectar muestras, calculo amplitudes de 8 frecuencias que codifican símbolos. No necesito ejecutar FFT completo para esto, así que utilicé el algoritmo de Goertzel.

void goertzel (uint8_t * muestras, flotante * espectro) {

flotar v_0, v_1, v_2; flotar re, im, amp; para (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; para (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (flotante) (muestras ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); espectro [k] = amperio; }}

Paso 2: el código

La imagen de arriba muestra el ejemplo de codificación del dígito 3 donde la amplitud máxima corresponde a las frecuencias 697Hz y 1477Hz.

El boceto completo se ve como sigue

/ ** * Conexiones: * [Mic a Arduino] * - Salida -> 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

#incluir

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t muestras [N];

volátil uint16_t samplePos = 0;

espectro flotante [IX_LEN];

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

// Calculado para 9615 Hz 256 muestras const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.634393284167025713, 0.59978 const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451;, 0.883549212643

typedef struct {

dígito char; uint8_t index; } digit_t;

digito_t digito_detectado;

tabla de caracteres 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} };

fuente 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, 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 / prescaler) / 13 ciclos / conversión ADMUX = 0; // Sel de canal, ajuste a la derecha, use el pin AREF ADCSRA = _BV (ADEN) | // ADC habilita _BV (ADSC) | // Inicio de ADC _BV (ADATE) | // Disparador automático _BV (ADIE) | // Habilitación de interrupciones _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Modo de ejecución libre DIDR0 = _BV (0); // Apague la entrada digital para el pin ADC TIMSK0 = 0; // Temporizador0 apagado}

void goertzel (uint8_t * muestras, flotante * espectro) {

flotar v_0, v_1, v_2; flotar re, im, amp; para (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; para (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (flotante) (muestras ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); espectro [k] = amperio; }}

float avg (float * a, uint16_t len) {

resultado flotante =.0; para (uint16_t i = 0; i <len; i ++) {resultado + = a ; } return result / len; }

int8_t get_single_index_above_threshold (flotar * a, uint16_t len, flotar umbral) {

if (umbral <UMBRAL) {retorno -1; } int8_t ix = -1; para (uint16_t i = 0; i umbral) {if (ix == -1) {ix = i; } else {return -1; }}} return ix; }

void detect_digit (flotante * espectro) {

flotar avg_row = avg (espectro, 4); flotar avg_col = avg (& espectro [4], 4); int8_t fila = get_single_index_above_threshold (espectro, 4, avg_row); int8_t col = get_single_index_above_threshold (& espectro [4], 4, avg_col); if (fila! = -1 && col! = -1 && avg_col> 200) {dígitos_detectados.digito = pgm_read_byte (& (tabla [fila] [col])); dígitos_detectados.index = pgm_read_byte (& (char_indexes [fila] [col])); } más {dígitos_detectados. dígitos = 0; }}

void drawSprite (byte * sprite) {

// La máscara se usa para obtener el bit de columna del byte de la fila del sprite mask = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] y máscara));

// desplaza la máscara un píxel a la derecha

máscara = máscara >> 1; }

// restablecer la máscara de columna

máscara = B10000000; }}

configuración vacía () {

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

Serial.begin (115200);

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

dígitos_detectados.digito = 0;

}

unsigned long z = 0;

bucle vacío () {

while (ADCSRA & _BV (ADIE)); // Espere a que termine el muestreo de audio goertzel (muestras, espectro); detect_digit (espectro);

si (dígitos_detectados. dígitos! = 0) {

drawSprite (fuente [dígitos_detectados.index]); lmd.display (); } if (z% 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (espectro ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) dígitos_detectados.digito); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Reanudar la interrupción del muestreo

}

ISR (ADC_vect) {

uint16_t sample = ADC;

samples [samplePos ++] = sample - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Búfer lleno, interrumpir}}

Paso 3: esquemas

Esquemas
Esquemas

Deben realizarse las siguientes conexiones:

Micrófono a Arduino

Fuera -> A0

Vcc -> 3.3V Gnd -> Gnd

Es importante conectar AREF a 3.3V

Mostrar a Arduino

Vcc -> 5 V

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

Paso 4: Conclusión

¿Qué se podría mejorar aquí? Usé N = 256 muestras a una tasa de 9615Hz que tiene alguna fuga de espectro, si N = 205 y la tasa es de 8000Hz, entonces las frecuencias deseadas coinciden con la cuadrícula de discretización. Para eso, el ADC debe usarse en modo de desbordamiento del temporizador.

Recomendado: