Innholdsfortegnelse:
2025 Forfatter: John Day | [email protected]. Sist endret: 2025-01-13 06:58
Oversikt
Jeg ble inspirert til å bygge denne enheten av et hjemmeopgave på Digital Signal Processing online -kurs. Dette er en DTMF -dekoder implementert med Arduino UNO, den oppdager et siffer som trykkes på et tastatur på telefonen i tonemodus av lyden den produserer.
Trinn 1: Forstå algoritmen
I DTMF er hvert symbol kodet med to frekvenser i henhold til tabellen på bildet.
Enheten fanger inngang fra mikrofonen og beregner amplituder på åtte frekvenser. To frekvenser med maksimale amplituder gir en rad og en kolonne med det kodede symbolet.
Datainnsamling
For å utføre spektrumanalyse bør prøver tas med en viss forutsigbar frekvens. For å oppnå dette brukte jeg frikjør ADC-modus med maksimal presisjon (prescaler 128) det gir samplingsfrekvens 9615Hz. Koden nedenfor viser hvordan du konfigurerer Arduinos ADC.
ugyldig initADC () {
// Init ADC; f = (16MHz/prescaler)/13 sykluser/konvertering ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC aktiver _BV (ADSC) | // ADC start _BV (ADATE) | // Autotrigger _BV (ADIE) | // Avbryt aktiver _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Friløpsmodus DIDR0 = _BV (0); // Slå av digital inngang for ADC pin TIMSK0 = 0; // Timer0 off} Og avbryterbehandleren ser slik ut: ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; hvis (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, avbryt}}
Spektrumanalyse
Etter å ha samlet prøver beregner jeg amplituder på 8 frekvenser som koder for symboler. Jeg trenger ikke å kjøre full FFT for dette, så jeg brukte Goertzels algoritme.
void goertzel (uint8_t *samples, float *spectrum) {
float v_0, v_1, v_2; float 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])); flyte a = 2. * c; v_0 = v_1 = v_2 = 0; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prøver ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}
Trinn 2: Koden
Bildet ovenfor viser eksemplet på koding av siffer 3 hvor maksimal amplitude tilsvarer frekvensene 697Hz og 1477Hz.
Hele skissen ser slik ut
/** * Tilkoblinger: * [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
#inkludere
#define CS_PIN 9
#define N 256
#define IX_LEN 8 #define THRESHOLD 20
LEDMatrixDriver lmd (1, CS_PIN);
uint8_t prøver [N];
flyktig uint16_t samplePos = 0;
flytespekter [IX_LEN];
// Frekvenser [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]
// Beregnet for 9615Hz 256 sampler const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5555; const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.88
typedef struct {
røye siffer; uint8_t indeks; } digit_t;
digit_t detect_digit;
const char tabell [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 skrift [16] [8] = {
{0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38}, // 0 {0x04, 0x0c, 0x14, 0x24, 0x04, 0x04, 0x04, 0x04}, // 1 {0x00, 0x30, 0x48, 0x0, 0x04, 0x38, 0x40, 0x7c}, // 2 {0x00, 0x38, 0x04, 0x04, 0x18, 0x04, 0x44, 0x38}, // 3 {0x00, 0x04, 0x0c, 0x14, 0x24, 0x7e, 0x04, 0x0 }, // 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} // *};
ugyldig initADC () {
// Init ADC; f = (16MHz/prescaler)/13 sykluser/konvertering ADMUX = 0; // Kanalsel, høyre-adj, bruk AREF-pin ADCSRA = _BV (ADEN) | // ADC aktiver _BV (ADSC) | // ADC start _BV (ADATE) | // Autotrigger _BV (ADIE) | // Avbryt aktiver _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Friløpsmodus DIDR0 = _BV (0); // Slå av digital inngang for ADC pin TIMSK0 = 0; // Timer0 off}
void goertzel (uint8_t *samples, float *spectrum) {
float v_0, v_1, v_2; float 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])); flyte a = 2. * c; v_0 = v_1 = v_2 = 0; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prøver ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}
float avg (float *a, uint16_t len) {
flyte resultat =.0; for (uint16_t i = 0; i <len; i ++) {resultat+= a ; } returresultat / len; }
int8_t get_single_index_above_threshold (float *a, uint16_t len, float terskel) {
if (terskel <THRESHOLD) {return -1; } int8_t ix = -1; for (uint16_t i = 0; i terskel) {if (ix == -1) {ix = i; } annet {retur -1; }}} returner ix; }
void detect_digit (float *spectrum) {
float avg_row = avg (spektrum, 4); float avg_col = avg (& spectrum [4], 4); int8_t rad = get_single_index_above_threshold (spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectrum [4], 4, avg_col); hvis (rad! = -1 && col! = -1 && avg_col> 200) {detect_digit.digit = pgm_read_byte (& (tabell [rad] [col])); detect_digit.index = pgm_read_byte (& (char_indexes [rad] [kol])); } annet {detect_digit.digit = 0; }}
void drawSprite (byte* sprite) {
// Masken brukes til å hente søylebiten fra 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));
// skift masken med en piksel til høyre
maske = maske >> 1; }
// tilbakestill kolonnemaske
maske = B10000000; }}
ugyldig oppsett () {
cli (); initADC (); sei ();
Serial.begin (115200);
lmd.setEnabled (true); lmd.setIntensity (2); lmd.clear (); lmd.display ();
detect_digit.digit = 0;
}
usignert lang z = 0;
void loop () {
mens (ADCSRA & _BV (ADIE)); // Vent på at lydsampling er ferdig med goertzel (prøver, spektrum); detect_digit (spektrum);
hvis (detect_digit.digit! = 0) {
drawSprite (font [detect_digit.index]); lmd.display (); } hvis (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektrum ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) detect_digit.digit); } z ++;
samplePos = 0;
ADCSRA | = _BV (ADIE); // Fortsett prøvetaking avbryt
}
ISR (ADC_vect) {
uint16_t sample = ADC;
prøver [samplePos ++] = sample - 400;
hvis (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, avbryt}}
Trinn 3: Skjemaer
Følgende tilkoblinger bør gjøres:
Mikrofon til Arduino
Ut -> A0
Vcc -> 3.3V Gnd -> Gnd
Det er viktig å koble AREF til 3,3V
Display til Arduino
Vcc -> 5V
Gnd -> Gnd DIN -> D11 CLK -> D13 CS -> D9
Trinn 4: Konklusjon
Hva kan forbedres her? Jeg brukte N = 256 prøver med en hastighet på 9615Hz som har noe spektrumlekkasje, hvis N = 205 og frekvensen er 8000Hz, faller de ønskede frekvensene sammen med diskretiseringsnettet. For at ADC skal brukes i timeroverløpsmodus.