Inhaltsverzeichnis:

DTMF-Detektor - Gunook
DTMF-Detektor - Gunook

Video: DTMF-Detektor - Gunook

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

Überblick

Ich wurde inspiriert, dieses Gerät durch eine Hausaufgabe im Online-Kurs Digitale Signalverarbeitung zu bauen. Dies ist ein mit Arduino UNO implementierter DTMF-Decoder, der eine auf einer Telefontastatur gedrückte Ziffer im Tonmodus anhand des erzeugten Tons erkennt.

Schritt 1: Den Algorithmus verstehen

Der Code
Der Code

Bei DTMF wird jedes Symbol mit zwei Frequenzen entsprechend der Tabelle auf dem Bild codiert.

Das Gerät erfasst Eingaben vom Mikrofon und berechnet Amplituden von acht Frequenzen. Zwei Frequenzen mit maximalen Amplituden ergeben eine Zeile und eine Spalte des codierten Symbols.

Datenerfassung

Um eine Spektralanalyse durchzuführen, sollten Proben mit einer bestimmten vorhersagbaren Frequenz erfasst werden. Um dies zu erreichen, habe ich den Freilauf-ADC-Modus mit maximaler Präzision (Prescaler 128) verwendet, der eine Abtastrate von 9615 Hz ergibt. Der folgende Code zeigt, wie Sie den ADC von Arduino konfigurieren.

void initADC() {

// ADC initiieren; f = (16MHz/Prescaler) / 13 Zyklen/Wandlung ADMUX = 0; // Kanalauswahl, rechts-adj, AREF-Pin verwenden ADCSRA = _BV(ADEN) | // ADC-Freigabe _BV(ADSC) | // ADC-Start _BV(ADATE) | // Autotrigger _BV(ADIE) | // Unterbrechungsfreigabe _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128: 1 / 13 = 9615 Hz ADCSRB = 0; // Freilaufmodus DIDR0 = _BV(0); // Digitaleingang für ADC-Pin ausschalten TIMSK0 = 0; // Timer0 off } Und der Interrupt-Handler sieht so aus ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Puffer voll, Unterbrechung aus } }

Spektrumanalyse

Nach dem Sammeln von Samples berechne ich Amplituden von 8 Frequenzen, die Symbole kodieren. Ich muss dafür keine vollständige FFT ausführen, also habe ich den Algorithmus von Goertzel verwendet.

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

schweben v_0, v_1, v_2; Schwimmer re, im, Ampere; 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])); Schwimmer a = 2. * c; v_0 = v_1 = v_2 = 0; für (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); Spektrum[k] = Ampere; } }

Schritt 2: Der Code

Das obige Bild zeigt das Beispiel der Kodierung von Ziffer 3, wobei die maximale Amplitude den Frequenzen 697 Hz und 1477 Hz entspricht.

Die komplette Skizze sieht wie folgt aus

/** * Anschlüsse: * [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

#enthalten

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define SCHWELLENWERT 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t Proben[N];

flüchtig uint16_t samplePos = 0;

Float-Spektrum[IX_LEN];

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

// Berechnet für 9615Hz 256 Samples const float cos_t[IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555702330196023, 0.25994778 const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.88192126;43483549}

typedef-Struktur {

Zeichenziffer; uint8_t-Index; } Ziffer_t;

Stelle_t erkannte_Stelle;

const char-Tabelle[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} };

Byte-Schriftart[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() {

// ADC initiieren; f = (16MHz/Prescaler) / 13 Zyklen/Wandlung ADMUX = 0; // Kanalauswahl, rechts-adj, AREF-Pin verwenden ADCSRA = _BV(ADEN) | // ADC-Freigabe _BV(ADSC) | // ADC-Start _BV(ADATE) | // Autotrigger _BV(ADIE) | // Unterbrechungsfreigabe _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128: 1 / 13 = 9615 Hz ADCSRB = 0; // Freilaufmodus DIDR0 = _BV(0); // Digitaleingang für ADC-Pin ausschalten TIMSK0 = 0; // Timer0 aus }

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

schweben v_0, v_1, v_2; Schwimmer re, im, Ampere; 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])); Schwimmer a = 2. * c; v_0 = v_1 = v_2 = 0; für (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); Spektrum[k] = Ampere; } }

float avg(float *a, uint16_t len) {

Float-Ergebnis =.0; for (uint16_t i = 0; i < len; i++) { Ergebnis += a; } Ergebnis zurückgeben / len; }

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

if (threshold < THRESHOLD) { return -1; } int8_tix = -1; für (uint16_t i = 0; i Schwellenwert) { if (ix == -1) { ix = i; aufrechtzuerhalten. Sonst { return -1; } } } return ix; }

void Detect_digit (Float * Spektrum) {

float avg_row = avg(Spektrum, 4); float avg_col = avg(&spectrum[4], 4); int8_t Zeile = get_single_index_above_threshold (Spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col); if (row != -1 && col != -1 && avg_col > 200) {Detected_digit.digit = pgm_read_byte(&(table[row][col])); erkannte_ziffer.index = pgm_read_byte(&(char_indexes[row][col])); aufrechtzuerhalten. Else {Detected_digit.digit = 0; } }

void drawSprite(byte* sprite) {

// Die Maske wird verwendet, um das Spaltenbit aus der Spritezeile zu erhalten 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));

// die Maske um ein Pixel nach rechts verschieben

Maske = Maske >> 1; }

// Spaltenmaske zurücksetzen

Maske = B10000000; } }

Leere Einrichtung () {

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

Serial.begin(115200);

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

erkannte_ziffer.ziffer = 0;

}

vorzeichenloses langes z = 0;

Leere Schleife () {

while(ADCSRA & _BV(ADIE)); // Warten Sie, bis das Audio-Sampling abgeschlossen ist goertzel (samples, Spectrum); Detect_digit (Spektrum);

if (erkannte_ziffer.ziffer != 0) {

drawSprite(font[detected_digit.index]); lmd.display(); aufrechtzuerhalten. Wenn (z % 5 == 0) { für (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); // Abtastunterbrechung fortsetzen

}

ESR(ADC_vect) {

uint16_t Probe = ADC;

Proben[samplePos++] = Probe - 400;

if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Puffer voll, Unterbrechung aus } }

Schritt 3: Schaltpläne

Schema
Schema

Folgende Verbindungen sollten hergestellt werden:

Mikrofon zu Arduino

Aus -> A0

Vcc -> 3.3V Masse -> Masse

Es ist wichtig, AREF an 3.3V anzuschließen

Anzeige auf Arduino

Vcc -> 5V

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

Schritt 4: Fazit

Was könnte hier verbessert werden? Ich habe N = 256 Samples mit einer Rate von 9615 Hz verwendet, was eine gewisse Spektrumsleckage aufweist. Wenn N = 205 und die Rate 8000 Hz beträgt, stimmen die gewünschten Frequenzen mit dem Diskretisierungsraster überein. Dazu sollte der ADC im Timer-Überlaufmodus verwendet werden.