Índice:
Vídeo: Detector DTMF: 4 etapas
2024 Autor: John Day | [email protected]. Última modificação: 2024-01-30 11:38
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
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
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:
DTMF VIDEO STREAMING ROVER: 3 etapas
DTMF VIDEO STREAMING ROVER: olá, depois de meu ROVER CONTROLADO POR TERMINAL LINUX e ROBÔ CONTROLADO POR PC DTMF WIFI, este é meu terceiro robô. e como os outros dois aqui, também não usei nenhum microcontrolador ou programação para mantê-lo simples e fácil de fazer. Ele também transmite vídeo ao vivo por wi-fi
Como fazer um decodificador de linha telefônica DTMF (tom) simples: 3 etapas
Como fazer um decodificador simples de linha telefônica DTMF (tom): Este é um projeto simples que permite decodificar sinais DTMF em basicamente qualquer linha telefônica. Neste tutorial, estamos usando o decodificador MT8870D. Estamos usando um decodificador de tons pré-construído porque, acredite em mim, é um saco tentar fazer isso com
ROBÔ WIFI DTMF: 5 etapas
ROBÔ WIFI DTMF: olá neste tutorial vou mostrar como você pode fazer um rover controlado por PC sem usar micro controlador, isso significa que neste projeto não há código de alto nível envolvido, você só precisa de conhecimento básico sobre como fazer a página html. pode assistir na íntegra
Como fazer um robô controlado por celular. Baseado em DTMF - Sem microcontrolador e programação - Controle de qualquer lugar do mundo - RoboGeeks: 15 etapas
Como fazer um robô controlado por celular. Baseado em DTMF | Sem microcontrolador e programação | Controle de qualquer lugar do mundo | RoboGeeks: Quer fazer um robô que possa ser controlado de qualquer lugar do mundo, vamos fazer isso
Detector de fumaça IOT: Atualizar o detector de fumaça existente com IOT: 6 etapas (com imagens)
Detector de fumaça IOT: atualize o detector de fumaça existente com o IOT: Lista de colaboradores, Inventor: Tan Siew Chin, Tan Yit Peng, Tan Wee Heng Supervisor: Dra. Chia Kim Seng, Departamento de Engenharia Mecatrônica e Robótica, Faculdade de Engenharia Elétrica e Eletrônica, Universiti Tun Hussein Onn Malaysia.Distribut