Inhoudsopgave:
Video: DTMF-detector: 4 stappen
2024 Auteur: John Day | [email protected]. Laatst gewijzigd: 2024-01-30 11:17
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
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
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:
DTMF VIDEO STREAMING ROVER - Ajarnpa
DTMF VIDEO STREAMING ROVER: hallo na mijn LINUX TERMINAL GECONTROLEERDE ROVER en WIFI DTMF PC GECONTROLEERDE ROBOT dit is mijn derde robot. en net als de andere twee hier heb ik ook geen microcontroller of programmering gebruikt om het eenvoudig en gemakkelijk te maken te houden. Het streamt ook live video via wifi
Hoe maak je een eenvoudige DTMF (toon) telefoonlijndecoder - Ajarnpa
Hoe maak je een eenvoudige DTMF (toon) telefoonlijndecoder: Dit is een eenvoudig project waarmee je DTMF-signalen op vrijwel elke telefoonlijn kunt decoderen. In deze tutorial gebruiken we de decoder MT8870D. We gebruiken een vooraf gebouwde toondecoder omdat, geloof me, het lastig is om het te proberen met de
WIFI DTMF-ROBOT: 5 stappen
WIFI DTMF ROBOT: hallo in deze tutorial ga ik je laten zien hoe je een pc-gestuurde rover kunt maken zonder een microcontroller te gebruiken. kan volledig kijken
Hoe maak je een mobiel bestuurde robot - Op DTMF gebaseerd - Zonder Microcontroller & Programmering - Controle van overal ter wereld - RoboGeeks: 15 stappen
Hoe maak je een mobiel bestuurde robot | Op DTMF gebaseerd | Zonder Microcontroller & Programmering | Controle van overal ter wereld | RoboGeeks: Wil je een robot maken die overal ter wereld kan worden bestuurd, Lets do It
DTMF bestuurbare auto. Geen mobiele telefoons nodig: 3 stappen
DTMF bestuurbare auto. Geen mobiele telefoons nodig: Robots en Robo-auto's zijn in wezen het nieuwe speelgoed voor zowel tech-enthousiastelingen als wetenschappers over de hele wereld. ze vinden overal toepassingen. Hier in deze tutorial zal ik je uitleggen hoe je een DTMF-gestuurde robotauto maakt met behulp van arduino en