Innehållsförteckning:

DTMF -detektor: 4 steg
DTMF -detektor: 4 steg

Video: DTMF -detektor: 4 steg

Video: DTMF -detektor: 4 steg
Video: Audio Spectrogram & StegCracker Passwords - DownUnderCTF 2024, Juli
Anonim
Image
Image

Översikt

Jag inspirerades att bygga den här enheten genom ett hemuppdrag på onlinekurs för digital signalbehandling. Detta är en DTMF -avkodare implementerad med Arduino UNO, den känner av en siffra som trycks på en knappsats på telefonen i tonläget av det ljud som den producerar.

Steg 1: Förstå algoritmen

Koden
Koden

I DTMF kodas varje symbol med två frekvenser enligt tabellen på bilden.

Enheten fångar inmatning från mikrofonen och beräknar amplituder på åtta frekvenser. Två frekvenser med maximala amplituder ger en rad och en kolumn med den kodade symbolen.

Datainsamling

För att kunna utföra spektrumanalys bör prover tas med en viss förutsägbar frekvens. För att uppnå detta använde jag frikörning ADC-läge med maximal precision (förskalare 128) det ger samplingshastighet 9615Hz. Koden nedan visar hur du konfigurerar Arduinos ADC.

ogiltig initADC () {

// Init ADC; f = (16MHz/förkalkning)/13 cykler/konvertering ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC aktivera _BV (ADSC) | // ADC start _BV (ADATE) | // Auto trigger _BV (ADIE) | // Avbryt aktivering _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Free-run mode DIDR0 = _BV (0); // Stäng av digital ingång för ADC -stift TIMSK0 = 0; // Timer0 av} Och avbrottshanteraren ser ut så här ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; om (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffert full, avbryt}}

Spektrumanalys

Efter att ha samlat in prover beräknar jag amplituder på 8 frekvenser som kodar symboler. Jag behöver inte köra full FFT för detta, så jag använde Goertzels algoritm.

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

float v_0, v_1, v_2; float re, im, amp; för (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); flyta a = 2. * c; v_0 = v_1 = v_2 = 0; för (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prover ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektrum [k] = amp; }}

Steg 2: Koden

Bilden ovan visar exemplet på kodning av siffra 3 där maximal amplitud motsvarar frekvenserna 697Hz och 1477Hz.

Hela skissen ser ut så här

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

#omfatta

#define CS_PIN 9

#definiera N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t prover [N];

flyktigt uint16_t samplePos = 0;

flottörspektrum [IX_LEN];

// Frekvenser [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Beräknat för 9615Hz 256 sampel const float cos_t [IX_LEN] PROGMEM = {0,8932243011955153, 0,877869911087115, 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ödningssiffra; uint8_t index; } digit_t;

digit_t detekterade_siffran;

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 font [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} // *};

ogiltig initADC () {

// Init ADC; f = (16MHz/förkalkning)/13 cykler/konvertering ADMUX = 0; // Channel sel, right-adj, use AREF pin ADCSRA = _BV (ADEN) | // ADC aktivera _BV (ADSC) | // ADC start _BV (ADATE) | // Auto trigger _BV (ADIE) | // Avbryt aktivering _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Free-run mode DIDR0 = _BV (0); // Stäng av digital ingång för ADC -stift TIMSK0 = 0; // Timer0 av}

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

float v_0, v_1, v_2; float re, im, amp; för (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); flyta a = 2. * c; v_0 = v_1 = v_2 = 0; för (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (prover ) + 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) {

float resultat =.0; för (uint16_t i = 0; i <len; i ++) {resultat+= a ; } returresultat / len; }

int8_t get_single_index_above_threshold (float *a, uint16_t len, float tröskel) {

if (tröskel <THRESHOLD) {return -1; } int8_t ix = -1; för (uint16_t i = 0; i tröskel) {if (ix == -1) {ix = i; } annat {return -1; }}} returnera ix; }

void detect_digit (float *spectrum) {

float avg_row = avg (spektrum, 4); float avg_col = avg (& spektrum [4], 4); int8_t rad = get_single_index_above_threshold (spektrum, 4, avg_row); int8_t col = get_single_index_above_threshold (& spektrum [4], 4, avg_col); if (rad! = -1 && col! = -1 && avg_col> 200) {detect_digit.digit = pgm_read_byte (& (tabell [rad] [col])); detekterad_digit.index = pgm_read_byte (& (char_indexes [rad] [kol])); } annat {detect_digit.digit = 0; }}

void drawSprite (byte* sprite) {

// Masken används för att få kolumnbiten från sprite row byte mask = B10000000; för (int iy = 0; iy <8; iy ++) {för (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// flytta masken med en pixel åt höger

mask = mask >> 1; }

// återställ kolumnmask

mask = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

detekterad_digit.digit = 0;

}

osignerad lång z = 0;

void loop () {

medan (ADCSRA & _BV (ADIE)); // Vänta på ljudsampling för att slutföra goertzel (samplingar, spektrum); detect_digit (spektrum);

om (detect_digit.digit! = 0) {

drawSprite (font [detekterad_digit.index]); lmd.display (); } if (z % 5 == 0) {för (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); // Återuppta provtagningsavbrott

}

ISR (ADC_vect) {

uint16_t sample = ADC;

prover [samplePos ++] = prov - 400;

om (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffert full, avbryt}}

Steg 3: Scheman

Scheman
Scheman

Följande anslutningar bör göras:

Mic till Arduino

Ut -> A0

Vcc -> 3.3V Gnd -> Gnd

Det är viktigt att ansluta AREF till 3,3V

Display till Arduino

Vcc -> 5V

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

Steg 4: Slutsats

Vad kan förbättras här? Jag använde N = 256 sampler med en hastighet av 9615Hz som har ett visst spektrumläckage, om N = 205 och hastigheten är 8000Hz sammanfaller de önskade frekvenserna med diskretiseringsnätet. För att ADC ska användas i timeröverflödesläge.

Rekommenderad: