Inhoudsopgave:

DTMF-detector: 4 stappen
DTMF-detector: 4 stappen

Video: DTMF-detector: 4 stappen

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

Overzicht

Ik werd geïnspireerd om dit apparaat te bouwen door een thuisopdracht over de online cursus Digitale signaalverwerking. Dit is een DTMF-decoder geïmplementeerd met Arduino UNO, het detecteert een cijfer dat op een telefoontoetsenbord wordt gedrukt in de toonmodus door het geluid dat het produceert.

Stap 1: Het algoritme begrijpen

De code
De code

In DTMF is elk symbool gecodeerd met twee frequenties volgens de tabel op de afbeelding.

Het apparaat vangt input van de microfoon op en berekent amplitudes van acht frequenties. Twee frequenties met maximale amplitudes geven een rij en een kolom van het gecodeerde symbool.

Data-acquisitie

Om spectrumanalyse uit te voeren, moeten monsters worden genomen met een bepaalde voorspelbare frequentie. Om dit te bereiken heb ik de free-run ADC-modus gebruikt met maximale precisie (prescaler 128), het geeft een bemonsteringsfrequentie van 9615Hz. De onderstaande code laat zien hoe Arduino's ADC te configureren.

ongeldig initADC() {

// Init-ADC; f = (16MHz/prescaler) / 13 cycli/conversie ADMUX = 0; // Kanaal sel, rechts-adj, gebruik AREF pin ADCSRA = _BV(ADEN) | // ADC inschakelen _BV(ADSC) | // ADC start _BV(ADATE) | // Automatische trigger _BV(ADIE) | // Interrupt inschakelen _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Free-run-modus DIDR0 = _BV (0); // Schakel de digitale ingang uit voor ADC-pin TIMSK0 = 0; // Timer0 uit} En de interrupt-handler ziet er zo uit ISR (ADC_vect) { uint16_t sample = ADC; samples [samplePos ++] = sample - 400; if (voorbeeldPos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer vol, onderbreking uit } }

Spectrumanalyse

Na het verzamelen van monsters bereken ik amplitudes van 8 frequenties die symbolen coderen. Ik hoef hiervoor geen volledige FFT te draaien, dus heb ik het algoritme van Goertzel gebruikt.

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

zweven v_0, v_1, v_2; float re, im, versterker; 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])); zweven a = 2. * c; v_0 = v_1 = v_2 = 0; voor (uint16_t ik = 0; ik <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); spectrum[k] = versterker; } }

Stap 2: De code

De afbeelding hierboven toont het voorbeeld van codering van cijfer 3 waarbij de maximale amplitude overeenkomt met de frequenties 697Hz en 1477Hz.

De volledige schets ziet er als volgt uit:

/** * Aansluitingen: * [Mic naar 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

#erbij betrekken

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t voorbeelden[N];

vluchtig uint16_t samplePos = 0;

zwevend spectrum [IX_LEN];

// Frequenties [697,0, 770,0, 852,0, 941,0, 1209.0, 1336,0, 1477,0, 1633,0]

// Berekend voor 9615 Hz 256 samples const float cos_t [IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555.4783196023,68; const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.492898192222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.88192}12643483549;

typedef struct {

char cijfer; uint8_t-index; } cijfer_t;

cijfer_t gedetecteerd_cijfer;

const char tabel [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-lettertype[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} // * };

ongeldig initADC() {

// Init-ADC; f = (16MHz/prescaler) / 13 cycli/conversie ADMUX = 0; // Kanaal sel, rechts-adj, gebruik AREF pin ADCSRA = _BV(ADEN) | // ADC inschakelen _BV(ADSC) | // ADC start _BV(ADATE) | // Automatische trigger _BV(ADIE) | // Interrupt inschakelen _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Free-run-modus DIDR0 = _BV (0); // Schakel de digitale ingang uit voor ADC-pin TIMSK0 = 0; // Timer0 uit}

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

zweven v_0, v_1, v_2; float re, im, versterker; 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])); zweven a = 2. * c; v_0 = v_1 = v_2 = 0; voor (uint16_t ik = 0; ik <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); spectrum[k] = versterker; } }

float avg(float *a, uint16_t len) {

float resultaat =.0; for (uint16_t i = 0; i <len; i++) { resultaat += a; } resultaat / len; }

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

if (drempel < THRESHOLD) { return -1; } int8_t ix = -1; for (uint16_t i = 0; i drempel) { if (ix == -1) { ix = i; } anders { retourneer -1; } } } retourneer ix; }

void detect_digit(float *spectrum) {

float avg_row = avg(spectrum, 4); float avg_col = avg(&spectrum[4], 4); int8_t rij = get_single_index_above_threshold (spectrum, 4, avg_row); int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col); if (rij != -1 && col != -1 && avg_col > 200) {discovery_digit.digit = pgm_read_byte(&(table[row][col])); detect_digit.index = pgm_read_byte(&(char_indexes[row][col])); } else { gedetecteerd_cijfer.cijfer = 0; } }

void drawSprite(byte* sprite) {

// Het masker wordt gebruikt om de kolombit uit de sprite-rij te halen 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));

// verschuif het masker één pixel naar rechts

masker = masker >> 1; }

// reset kolommasker

masker = B10000000; } }

ongeldige setup() {

cli(); initADC(); zie();

Serieel.begin(115200);

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

gedetecteerd_cijfer.cijfer = 0;

}

niet-ondertekende lange z = 0;

lege lus() {

while(ADCSRA & _BV(ADIE)); // Wacht tot de audiosampling klaar is met goertzel (samples, spectrum); detect_digit(spectrum);

if (detected_digit.digit != 0) {

drawSprite (lettertype [gedetecteerde_cijfers.index]); lmd.display(); } if (z % 5 == 0) { for (int i = 0; i < IX_LEN; i++) { Serial.print(spectrum); Serieel.print("\t"); } Serieel.println(); Serial.println((int)detected_digit.digit); } z++;

monsterPos = 0;

ADCSRA |= _BV(ADIE); // Hervat de bemonsteringsonderbreking

}

ISR(ADC_vect) {

uint16_t voorbeeld = ADC;

samples[samplePos++] = sample - 400;

if (voorbeeldPos >= N) { ADCSRA &= ~_BV(ADIE); // Buffer vol, onderbreking uit } }

Stap 3: Schema's

Schema's
Schema's

De volgende aansluitingen moeten worden gemaakt:

Microfoon naar Arduino

Uit -> A0

Vcc -> 3.3V Gnd -> Gnd

Het is belangrijk om AREF aan te sluiten op 3.3V

Weergeven aan Arduino

Vcc -> 5V

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

Stap 4: Conclusie

Wat zou hier verbeterd kunnen worden? Ik heb N = 256 samples gebruikt met een snelheid van 9615 Hz die enige spectrumlekkage heeft, als N = 205 en de snelheid is 8000 Hz, dan vallen de gewenste frequenties samen met het discretisatieraster. Daarvoor moet ADC worden gebruikt in de timer-overloopmodus.

Aanbevolen: