Table des matières:
Vidéo: Détecteur DTMF : 4 étapes
2024 Auteur: John Day | [email protected]. Dernière modifié: 2024-01-30 09:08
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
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
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é:
Raspberry Pi - Tutoriel Java du détecteur de proximité numérique infrarouge TMD26721 : 4 étapes
Raspberry Pi - Détecteur de proximité numérique infrarouge TMD26721 Tutoriel Java : Le TMD26721 est un détecteur de proximité numérique infrarouge qui fournit un système de détection de proximité complet et une logique d'interface numérique dans un seul module de montage en surface à 8 broches. La détection de proximité comprend un rapport signal/bruit amélioré et précision. Un pro
Détecteur de niveau d'eau : 7 étapes
Détecteur de niveau d'eau : Le capteur à ultrasons fonctionne sur les mêmes principes qu'un système radar. Un capteur à ultrasons peut convertir l'énergie électrique en ondes acoustiques et vice versa. Le célèbre capteur à ultrasons HC SR04 génère des ondes ultrasonores à une fréquence de 40 kHz. Typique
Détecteur de présence de lit Zigbee : 8 étapes
Détecteur de présence au lit Zigbee : Depuis quelque temps, je cherchais un moyen de détecter quand nous sommes au lit. Ceci pour utiliser ces informations dans Homeassistant. Avec ces informations, je pourrais faire des automatisations pour éteindre les lumières la nuit ou par exemple activer un système d'alarme dans ma maison
Détecteur de fumée : 13 étapes
Détecteur de fumée : Salut les amis, voyons aujourd'hui à propos du détecteur de fumée. Beaucoup d'entre vous sont allés dans des centres commerciaux, la plupart du temps, vous pouvez voir cet appareil appelé détecteur de fumée. au lieu
Détecteur de fumée IOT : mettre à jour le détecteur de fumée existant avec IOT : 6 étapes (avec photos)
Détecteur de fumée IOT : Mettre à jour le détecteur de fumée existant avec IOT : Liste des contributeurs, Inventeur : Tan Siew Chin, Tan Yit Peng, Tan Wee Heng Superviseur : Dr Chia Kim Seng Département de génie mécatronique et robotique, Faculté de génie électrique et électronique, Universiti Tun Hussein Onn Malaisie.Distribuer