Índice:

Detector DTMF: 4 etapas
Detector DTMF: 4 etapas

Vídeo: Detector DTMF: 4 etapas

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

Visão geral

Fiquei inspirado para construir este dispositivo com uma tarefa em casa no curso online de Processamento de Sinal Digital. Este é um decodificador DTMF implementado com Arduino UNO, ele detecta um dígito pressionado no teclado de um telefone no modo de tom pelo som que ele produz.

Etapa 1: Compreendendo o Algoritmo

O código
O código

Em DTMF cada símbolo é codificado com duas frequências de acordo com a tabela da imagem.

O dispositivo captura a entrada do microfone e calcula amplitudes de oito frequências. Duas frequências com amplitudes máximas fornecem uma linha e uma coluna do símbolo codificado.

Aquisição de dados

Para realizar a análise de espectro, as amostras devem ser capturadas em uma certa frequência previsível. Para conseguir isso, usei o modo ADC de execução livre com precisão máxima (prescaler 128), que fornece taxa de amostragem de 9615Hz. O código abaixo mostra como configurar o ADC do Arduino.

void initADC () {

// Init ADC; f = (16 MHz / prescaler) / 13 ciclos / conversão ADMUX = 0; // Seleção de canal, ajuste à direita, use o pino AREF ADCSRA = _BV (ADEN) | // Habilitar ADC _BV (ADSC) | // ADC start _BV (ADATE) | // Acionamento automático _BV (ADIE) | // Habilitar interrupção _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Modo de execução livre DIDR0 = _BV (0); // Desativa a entrada digital para o pino ADC TIMSK0 = 0; // Timer0 off} E o manipulador de interrupção se parece com este ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer cheio, interrupção desligada}}

Análise de espectro

Depois de coletar as amostras, calculo as amplitudes de 8 símbolos de codificação de frequências. Não preciso executar o FFT completo para isso, então usei o algoritmo de Goertzel.

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

float v_0, v_1, v_2; float 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])); float 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 = (flutuante) (amostras ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); espectro [k] = amp; }}

Etapa 2: O Código

A imagem acima mostra o exemplo de codificação do dígito 3 onde a amplitude máxima corresponde às frequências 697Hz e 1477Hz.

O esboço completo é o seguinte

/ ** * Conexões: * [Mic para Arduino] * - Saída -> A0 * - Vcc -> 3,3 V * - Gnd -> Gnd * - Arduino: AREF -> 3,3 V * [Exibir para 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);

amostras uint8_t [N];

volátil uint16_t samplePos = 0;

espectro de flutuação [IX_LEN];

// Frequências [697,0, 770,0, 852,0, 941,0, 1209,0, 1336,0, 1477,0, 1633,0]

// Calculado para 9615 Hz 256 amostras const float cos_t [IX_LEN] PROGMEM = {0,8932243011955153, 0,8700869911087115, 0,8448535652497071, 0,8032075314806449, 0,6895405447370669, 0,63439328416456, 0,6343932842347193, 0,63439328416456, 0,6560134513; const float sin_t [IX_LEN] PROGMEM = {0,44961132965460654, 0,49289819222978404, 0,5349976198870972, 0,5956993044924334, 0,7242470829514669, 0,7730104533627369, 0,8314696123025492

typedef struct {

dígito char; uint8_t index; } digit_t;

digit_tected_digit;

tabela 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} };

fonte de 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, 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 = (16 MHz / prescaler) / 13 ciclos / conversão ADMUX = 0; // Seleção de canal, ajuste à direita, use o pino AREF ADCSRA = _BV (ADEN) | // Habilitar ADC _BV (ADSC) | // ADC start _BV (ADATE) | // Acionamento automático _BV (ADIE) | // Habilitar interrupção _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Modo de execução livre DIDR0 = _BV (0); // Desativa a entrada digital para o pino ADC TIMSK0 = 0; // Timer0 desligado}

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

float v_0, v_1, v_2; float 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])); float 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 = (flutuante) (amostras ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); espectro [k] = amp; }}

float avg (float * a, uint16_t len) {

resultado flutuante =.0; para (uint16_t i = 0; i <len; i ++) {resultado + = a ; } resultado de retorno / len; }

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

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

void detect_digit (float * spectrum) {

float avg_row = avg (espectro, 4); float avg_col = avg (& espectro [4], 4); int8_t row = get_single_index_above_threshold (espectro, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectrum [4], 4, avg_col); if (row! = -1 && col! = -1 && avg_col> 200) {ected_digit.digit = pgm_read_byte (& (table [row] [col])); detectado_digit.index = pgm_read_byte (& (char_indexes [linha] [col])); } else {ected_digit.digit = 0; }}

void drawSprite (byte * sprite) {

// A máscara é usada para obter o bit da coluna da 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] & máscara));

// desloca a máscara um pixel para a direita

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

// redefinir a máscara da coluna

máscara = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

dígitos_detectados.digit = 0;

}

z longo sem sinal = 0;

void loop () {

enquanto (ADCSRA & _BV (ADIE)); // Aguarde o término da amostragem de áudio goertzel (amostras, espectro); detect_digit (espectro);

if (ected_digit.digit! = 0) {

drawSprite (fonte [detector_digit.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)ected_digit.digit detectado); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Retomar a interrupção da amostragem

}

ISR (ADC_vect) {

amostra uint16_t = ADC;

amostras [samplePos ++] = amostra - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer cheio, interrupção desligada}}

Etapa 3: esquemas

Esquemas
Esquemas

As seguintes conexões devem ser feitas:

Mic para Arduino

Out -> A0

Vcc -> 3,3V Gnd -> Gnd

É importante conectar o AREF a 3,3V

Exibir para Arduino

Vcc -> 5V

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

Etapa 4: Conclusão

O que pode ser melhorado aqui? Eu usei N = 256 amostras na taxa de 9615 Hz que tem algum vazamento de espectro, se N = 205 e a taxa é 8000 Hz então as frequências desejadas coincidem com a grade de discretização. Para isso, o ADC deve ser usado no modo de estouro do temporizador.

Recomendado: