Sisällysluettelo:

DTMF -ilmaisin: 4 vaihetta
DTMF -ilmaisin: 4 vaihetta

Video: DTMF -ilmaisin: 4 vaihetta

Video: DTMF -ilmaisin: 4 vaihetta
Video: 1)SIM 800L DTMF НА 4 РЕЛЕ С ГОЛОСОВЫМ ОПОВЕЩЕНИЕМ И ТЕСТОМ ВКЛЮЧЕННЫХ УСТРОЙСТВ 2024, Heinäkuu
Anonim
Image
Image

Yleiskatsaus

Sain innoitukseni rakentaa tämä laite kotitehtävästä digitaalisen signaalin käsittelyn verkkokurssilla. Tämä on DTMF -dekooderi, joka on toteutettu Arduino UNO: lla, ja se tunnistaa numeron, jota puhelimen näppäimistö painaa äänitilassa sen tuottaman äänen perusteella.

Vaihe 1: Algoritmin ymmärtäminen

Koodi
Koodi

DTMF: ssä jokainen symboli on koodattu kahdella taajuudella kuvan taulukon mukaisesti.

Laite kerää mikrofonista tuloa ja laskee kahdeksan taajuuden amplitudit. Kaksi taajuutta, joilla on suurin amplitudi, antavat rivin ja sarakkeen koodatusta symbolista.

Tiedonkeruu

Spektrianalyysin suorittamiseksi näytteet on otettava tietyllä ennustettavalla taajuudella. Tämän saavuttamiseksi käytin vapaasti suoritettavaa ADC-tilaa mahdollisimman tarkasti (esiasetin 128), ja se antaa näytteenottotaajuuden 9615 Hz. Alla oleva koodi näyttää, miten Arduinon ADC määritetään.

void initADC () {

// Alkuperäinen ADC; f = (16 MHz/esiskaalain)/13 sykliä/muunnos ADMUX = 0; // Kanava sel, oikea-adj, käytä AREF-nastaista ADCSRA = _BV (ADEN) | // ADC käyttöön _BV (ADSC) | // ADC -aloitus _BV (ADATE) | // Automaattinen liipaisin _BV (ADIE) | // Keskeytyksen käyttöönotto _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Vapaakäyntitila DIDR0 = _BV (0); // Sammuta digitaalinen tulo ADC -nastalle TIMSK0 = 0; // Ajastin0 pois} Ja keskeytyksenkäsittelijä näyttää tältä: ISR (ADC_vect) {uint16_t sample = ADC; sample [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Puskuri täynnä, keskeytys pois päältä}}

Spektrianalyysi

Näytteiden keräämisen jälkeen lasken symboleja koodaavien 8 taajuuden amplitudit. Minun ei tarvitse suorittaa täyden FFT: n tätä varten, joten käytin Goertzelin algoritmia.

void goertzel (uint8_t *näytteet, float *spektri) {

kellua 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]))); kellua 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 = (kelluva) (näytteet ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektri [k] = amp; }}

Vaihe 2: Koodi

Yllä oleva kuva esittää esimerkin numeron 3 koodauksesta, jossa suurin amplitudi vastaa taajuuksia 697 Hz ja 1477 Hz.

Koko luonnos näyttää tältä

/** * Liitännät: * [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

#sisältää

#define CS_PIN 9

#määrittele N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t näytteet [N];

haihtuva uint16_t samplePos = 0;

kelluva spektri [IX_LEN];

// Taajuudet [697,0, 770,0, 852,0, 941,0, 1209,0, 1336,0, 1477,0, 1633,0]

// Laskettu 9615 Hz: lle 256 näytteelle const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.63439328416364561956605, 5656 const float sin_t [IX_LEN] OHJELMA = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025456 0.48

typedef structure {

char -numero; uint8_t -indeksi; } numero_t;

digit_t havaittu_numero;

const char -taulukko [4] [4] OHJELMA = {

{'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} };

tavu fontti [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 () {

// Alkuperäinen ADC; f = (16 MHz/esiskaalain)/13 sykliä/muunnos ADMUX = 0; // Kanava sel, oikea-adj, käytä AREF-nastaista ADCSRA = _BV (ADEN) | // ADC käyttöön _BV (ADSC) | // ADC -aloitus _BV (ADATE) | // Automaattinen liipaisin _BV (ADIE) | // Keskeytyksen käyttöönotto _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Vapaakäyntitila DIDR0 = _BV (0); // Sammuta digitaalinen tulo ADC -nastalle TIMSK0 = 0; // Ajastin0 pois päältä}

void goertzel (uint8_t *näytteet, float *spektri) {

kellua 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]))); kellua 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 = (kelluva) (näytteet ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektri [k] = amp; }}

float avg (float *a, uint16_t len) {

float -tulos =.0; for (uint16_t i = 0; i <len; i ++) {tulos+= a ; } palautustulos / len; }

int8_t get_single_index_above_threshold (kelluva *a, uint16_t len, kellukynnys) {

if (kynnys <THRESHOLD) {return -1; } int8_t ix = -1; (uint16_t i = 0; i kynnys) {if (ix == -1) {ix = i; } else {return -1; }}} return ix; }

void detect_digit (float *spektri) {

float avg_row = avg (spektri, 4); float avg_col = keskiarvo (& spektri [4], 4); int8_t rivi = get_single_index_above_threshold (spektri, 4, keskiarvo); int8_t col = get_single_index_above_threshold (& spektri [4], 4, keskim.) if (rivi! = -1 && col! = -1 && avg_col> 200) {havaittu_digit.digit = pgm_read_byte (& (taulukko [rivi] [sarake])); havaittu_dig.index = pgm_read_byte (& (char_indexes [rivi] [col])); } else {havaittu_numero.numero = 0; }}

void drawSprite (tavu* sprite) {

// Maskia käytetään sarakkeen bitin hakemiseen sprite -rivin tavumastista = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// siirrä naamio yhden pikselin verran oikealle

maski = maski >> 1; }

// nollaa pylväsmaski

naamio = B10000000; }}

void setup () {

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

Sarja.alku (115200);

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

havaittu_numero.numero = 0;

}

allekirjoittamaton pitkä z = 0;

void loop () {

kun taas (ADCSRA & _BV (ADIE)); // Odota ääninäytteenoton päättymistä goertzeliin (näytteet, spektri); tunnista_numero (spektri);

jos (havaittu_numero.luku! = 0) {

drawSprite (fontti [tunnistettu_numero.index]); lmd.display (); } jos (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektri ); Serial.print ("\ t"); } Sarja.println (); Sarja.println ((int) havaittu_numero.numero); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Jatka näytteenoton keskeytystä

}

ISR (ADC_vect) {

uint16_t näyte = ADC;

näytteet [samplePos ++] = näyte - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Puskuri täynnä, keskeytys pois päältä}}

Vaihe 3: Kaaviot

Kaaviot
Kaaviot

Seuraavat liitännät on tehtävä:

Mikrofoni Arduinolle

Ulos -> A0

Vcc -> 3.3V Gnd -> Gnd

On tärkeää liittää AREF 3,3 V: n jännitteeseen

Näytä Arduinolle

Vcc -> 5V

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

Vaihe 4: Johtopäätös

Mitä tässä voisi parantaa? Käytin N = 256 näytettä taajuudella 9615 Hz, jolla on jonkin verran spektrivuotoa, jos N = 205 ja nopeus on 8000 Hz, niin halutut taajuudet vastaavat diskreetointiruudukkoa. Tätä varten ADC: tä tulisi käyttää ajastimen ylivuototilassa.

Suositeltava: