Table des matières:

Détecteur DTMF : 4 étapes
Détecteur DTMF : 4 étapes

Vidéo: Détecteur DTMF : 4 étapes

Vidéo: Détecteur DTMF : 4 étapes
Vidéo: Balise Radio DTMF - Décodeur DTMF MT8870 - v1.0 - Arduino UNO 2024, Novembre
Anonim
Image
Image

Aperçu

J'ai été inspiré pour construire cet appareil par un devoir à domicile sur le cours en ligne de traitement du signal numérique. Il s'agit d'un décodeur DTMF implémenté avec Arduino UNO, il détecte un chiffre pressé sur un clavier téléphonique en mode tonalité par le son qu'il produit.

Étape 1: Comprendre l'algorithme

Le code
Le code

En DTMF, chaque symbole est codé avec deux fréquences selon le tableau sur l'image.

L'appareil capture l'entrée du microphone et calcule les amplitudes de huit fréquences. Deux fréquences avec des amplitudes maximales donnent une ligne et une colonne du symbole codé.

L'acquisition des données

Afin d'effectuer une analyse de spectre, les échantillons doivent être capturés à une certaine fréquence prévisible. Pour y parvenir, j'ai utilisé le mode ADC à exécution libre avec une précision maximale (prescaler 128), il donne un taux d'échantillonnage de 9615 Hz. Le code ci-dessous montre comment configurer l'ADC d'Arduino.

void initADC() {

// Init ADC; f = (16MHz/prescaler) / 13 cycles/conversion ADMUX = 0; // Channel sel, right-adj, utiliser la broche AREF ADCSRA = _BV(ADEN) | // Activer l'ADC _BV(ADSC) | // Démarrage ADC _BV(ADATE) | // Déclenchement automatique _BV(ADIE) | // Activation d'interruption _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Mode libre DIDR0 = _BV(0); // Désactiver l'entrée numérique pour la broche ADC TIMSK0 = 0; // Timer0 off } Et le gestionnaire d'interruption ressemble à ceci ISR(ADC_vect) { uint16_t sample = ADC;samples[samplePos++] = sample - 400; if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Tampon plein, interruption désactivée } }

Analyse de spectre

Après avoir collecté des échantillons, je calcule les amplitudes de 8 fréquences encodant des symboles. Je n'ai pas besoin d'exécuter une FFT complète pour cela, j'ai donc utilisé l'algorithme de Goertzel.

void goertzel (uint8_t *échantillons, float *spectre) {

flottant v_0, v_1, v_2; flotteur 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])); flotteur a = 2. * c; v_0 = v_1 = v_2 = 0; pour (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); spectre[k] = amp; } }

Étape 2: le code

L'image ci-dessus montre l'exemple d'encodage du chiffre 3 où l'amplitude maximale correspond aux fréquences 697Hz et 1477Hz.

Le croquis complet se présente comme suit

/** * Connexions: * [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

#comprendre

#définir CS_PIN 9

#définir N 256

#définir IX_LEN 8 #définir SEUIL 20

LEDMatrixDriver lmd(1, CS_PIN);

uint8_t échantillons[N];

volatile uint16_t samplePos = 0;

spectre flottant[IX_LEN];

// Fréquences [697.0, 770.0, 852.0, 941.0, 1209.0, 1336,0, 1477.0, 1633.0]

// Calculé pour 9615Hz 256 échantillons const float cos_t[IX_LEN] PROGMEM = { 0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.555570233019606723, 0. const float sin_t[IX_LEN] PROGMEM = { 0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.881921}2643483549

struct typedef {

chiffre de caractère; uint8_t index; } chiffre_t;

chiffre_t chiffre_détecté;

table de caractères 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} };

police d'octets[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() {

// Init ADC; f = (16MHz/prescaler) / 13 cycles/conversion ADMUX = 0; // Channel sel, right-adj, utiliser la broche AREF ADCSRA = _BV(ADEN) | // Activer l'ADC _BV(ADSC) | // Démarrage ADC _BV(ADATE) | // Déclenchement automatique _BV(ADIE) | // Activation d'interruption _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz ADCSRB = 0; // Mode libre DIDR0 = _BV(0); // Désactiver l'entrée numérique pour la broche ADC TIMSK0 = 0; // Minuterie 0 désactivée }

void goertzel (uint8_t *échantillons, float *spectre) {

flottant v_0, v_1, v_2; flotteur 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])); flotteur a = 2. * c; v_0 = v_1 = v_2 = 0; pour (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); spectre[k] = amp; } }

float avg(float *a, uint16_t len) {

résultat flottant =.0; for (uint16_t i = 0; i < len; i++) { result += a; } renvoie le résultat / len; }

int8_t get_single_index_above_threshold (float *a, uint16_t len, seuil flottant) {

if (seuil < SEUIL) { return -1; } int8_t ix = -1; pour (uint16_t i = 0; seuil i) { if (ix == -1) { ix = i; } else { return -1; } } } renvoie ix; }

void detect_digit(float *spectre) {

float avg_row = avg(spectre, 4); float col_moy = moy(&spectrum[4], 4); int8_t row = get_single_index_above_threshold (spectre, 4, avg_row); int8_t col = get_single_index_above_threshold(&spectrum[4], 4, avg_col); if (ligne != -1 && col != -1 && col_moy > 200) {digit_détecté.chiffre = pgm_read_byte(&(table[ligne][col])); detect_digit.index = pgm_read_byte(&(char_indexes[row][col])); } else { détecté_chiffre.chiffre = 0; } }

void drawSprite(byte* sprite) {

// Le masque est utilisé pour obtenir le bit de colonne à partir du 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] & mask));

// décaler le masque d'un pixel vers la droite

masque = masque >> 1; }

// réinitialiser le masque de colonne

masque = B10000000; } }

void setup() {

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

Serial.begin(115200);

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

chiffre_détecté.chiffre = 0;

}

z long non signé = 0;

boucle vide() {

while(ADCSRA & _BV(ADIE)); // Attend la fin de l'échantillonnage audio goertzel(samples, spectrum); detect_digit(spectre);

if (chiffre_détecté.chiffre != 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++;

ExemplePos = 0;

ADCSRA |= _BV(ADIE); // Reprise de l'interruption d'échantillonnage

}

ISR(ADC_vect) {

uint16_t échantillon = ADC;

échantillons[samplePos++] = échantillon - 400;

if(samplePos >= N) { ADCSRA &= ~_BV(ADIE); // Tampon plein, interruption désactivée } }

Étape 3: Schémas

Schémas
Schémas

Les connexions suivantes doivent être effectuées:

Micro vers Arduino

Sortie -> A0

Vcc -> 3.3V Gnd -> Gnd

Il est important de connecter AREF à 3.3V

Afficher sur Arduino

Vcc -> 5V

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

Étape 4: Conclusion

Qu'est-ce qui pourrait être amélioré ici? J'ai utilisé N = 256 échantillons à une fréquence de 9615 Hz qui présente une certaine fuite spectrale. Si N = 205 et une fréquence de 8000 Hz, les fréquences souhaitées coïncident avec la grille de discrétisation. Pour cela, l'ADC doit être utilisé en mode débordement de la minuterie.

Conseillé: