Sommario:
- Passaggio 1: comprensione dell'algoritmo
- Passaggio 2: il codice
- Passaggio 3: schemi
- Passaggio 4: conclusione
Video: Rilevatore DTMF: 4 passaggi
2024 Autore: John Day | [email protected]. Ultima modifica: 2024-01-30 10:03
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
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
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:
Raspberry Pi - Rilevatore di prossimità digitale a infrarossi TMD26721 Tutorial Java: 4 passaggi
Raspberry Pi - TMD26721 Rilevatore di prossimità digitale a infrarossi Java Tutorial: TMD26721 è un rilevatore di prossimità digitale a infrarossi che fornisce un sistema di rilevamento di prossimità completo e logica di interfaccia digitale in un singolo modulo a montaggio superficiale a 8 pin. Il rilevamento di prossimità include un migliore rapporto segnale-rumore e precisione. Un professionista
Rilevatore del livello dell'acqua: 7 passaggi
Rilevatore del livello dell'acqua: il sensore a ultrasuoni funziona secondo gli stessi principi di un sistema radar. Un sensore a ultrasuoni può convertire l'energia elettrica in onde acustiche e viceversa. Il famoso sensore a ultrasuoni HC SR04 genera onde ultrasoniche a una frequenza di 40kHz. Tipico
Rilevatore di presenza letto Zigbee: 8 passaggi
Rilevatore presenza letto Zigbee: da un po' di tempo stavo cercando un modo per rilevare quando siamo a letto. Questo per utilizzare queste informazioni in Homeassistant. Con queste informazioni potrei realizzare automazioni per spegnere le luci di notte o ad esempio attivare un sistema di allarme nella mia casa
Rilevatore di fumo: 13 passaggi
Rilevatore di fumo: Ciao amici oggi parliamo del rilevatore di fumo Molti di voi sono andati in centri commerciali nei centri commerciali per lo più puoi vedere questo dispositivo chiamato rilevatore di fumo che rileverà il fumo e accenderà l'irrigatore e fermerà il fuoco. Ma in questo progetto c'è un leggero cambiamento Invece
Rilevatore di fumo IOT: aggiorna il rilevatore di fumo esistente con IOT: 6 passaggi (con immagini)
Rilevatore di fumo IOT: aggiorna il rilevatore di fumo esistente con IOT: elenco dei contributori, Inventore: Tan Siew Chin, Tan Yit Peng, Tan Wee Heng Supervisore: Dr Chia Kim Seng Dipartimento di ingegneria meccatronica e robotica, Facoltà di ingegneria elettrica ed elettronica, Universiti Tun Hussein Onn Malaysia.Distribuzione