Talaan ng mga Nilalaman:

DTMF Detector: 4 na Hakbang
DTMF Detector: 4 na Hakbang

Video: DTMF Detector: 4 na Hakbang

Video: DTMF Detector: 4 na Hakbang
Video: Управление устройствами с сотового телефона. DTMF decoder. 2024, Nobyembre
Anonim
Image
Image

Pangkalahatang-ideya

May inspirasyon akong bumuo ng aparatong ito ng isang takdang-aralin sa bahay sa kurso sa online na Pagproseso ng Digital Signal. Ito ay isang decoder ng DTMF na ipinatupad sa Arduino UNO, nakita nito ang isang digit na pinindot sa isang keypad ng telepono sa tone mode ng tunog na ginagawa nito.

Hakbang 1: Pag-unawa sa Algorithm

Ang Code
Ang Code

Sa DTMF ang bawat simbolo ay naka-encode na may dalawang frequency alinsunod sa talahanayan sa larawan.

Nakukuha ng aparato ang input mula sa mikropono at kinakalkula ang mga amplitude ng walong mga frequency. Ang dalawang dalas na may maximum na amplitude ay nagbibigay ng isang hilera at isang haligi ng naka-encode na simbolo.

Pagkuha ng data

Upang maisagawa ang mga sample ng pagsusuri ng spectrum ay dapat makuha sa isang tiyak na hinuhulaan na dalas. Upang makamit ito, ginamit ko ang libreng-run na ADC mode na may maximum na katumpakan (prescaler 128) nagbibigay ito ng sampling rate na 9615Hz. Ipinapakita ng code sa ibaba kung paano i-configure ang Arduino's ADC.

walang bisa initADC () {

// Init ADC; f = (16MHz / prescaler) / 13 cycle / conversion ADMUX = 0; // Channel sel, right-adj, gamitin ang AREF pin ADCSRA = _BV (ADEN) | // ADC paganahin ang _BV (ADSC) | // ADC start _BV (ADATE) | // Auto trigger _BV (ADIE) | // Makagambala paganahin ang _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Free-run mode DIDR0 = _BV (0); // Patayin ang digital input para sa ADC pin TIMSK0 = 0; // Timer0 off} At ang gumagambala ng handler ay ganito ang ISR (ADC_vect) {uint16_t sample = ADC; mga sample [samplePos ++] = sample - 400; kung (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, interrupt off}}

Pagsusuri sa Spectrum

Pagkatapos ng pagkolekta ng mga sample kinakalkula ko ang mga amplitude ng 8 mga frequency na simbolo ng pag-encode. Hindi ko kailangang patakbuhin ang buong FFT para dito, kaya ginamit ko ang algorithm ni Goertzel.

void goertzel (uint8_t * mga sample, float * spectrum) {

float v_0, v_1, v_2; float re, im, amp; para sa (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); float a = 2. * c; v_0 = v_1 = v_2 = 0; para sa (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (mga sample ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spectrum [k] = amp; }}

Hakbang 2: Ang Code

Ipinapakita ng larawan sa itaas ang halimbawa ng pag-encode ng digit 3 kung saan ang maximum na amplitude ay tumutugma sa mga frequency na 697Hz at 1477Hz.

Ang kumpletong sketch ay ang hitsura ng mga sumusunod

/ ** * Mga Koneksyon: * [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 * / # isama ang # isama

# isama

# tukuyin ang CS_PIN 9

# tukuyin ang N 256

# tukuyin ang IX_LEN 8 # tukuyin ang THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t mga sample [N];

pabagu-bago ng isip uint16_t samplePos = 0;

float spectrum [IX_LEN];

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

// Kinalkula para sa 9615Hz 256 na mga halimbawa ng const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.634393284163645613, 0.56083681378 Const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451, 0.83019454, 0.8301945496, 0.8301945496, 0.830194519451, 0.83019459794, 0.828191930451, 0.8301913054

typedef struct {

char digit; uint8_t index; } digit_t;

nakita ang digit_t_digit;

talahanayan ng char char [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} // *};

walang bisa initADC () {

// Init ADC; f = (16MHz / prescaler) / 13 cycle / conversion ADMUX = 0; // Channel sel, right-adj, gamitin ang AREF pin ADCSRA = _BV (ADEN) | // ADC paganahin ang _BV (ADSC) | // ADC start _BV (ADATE) | // Auto trigger _BV (ADIE) | // Makagambala paganahin ang _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Free-run mode DIDR0 = _BV (0); // Patayin ang digital input para sa ADC pin TIMSK0 = 0; // Naka-off ang Timer0}

void goertzel (uint8_t * mga sample, float * spectrum) {

float v_0, v_1, v_2; float re, im, amp; para sa (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); float a = 2. * c; v_0 = v_1 = v_2 = 0; para sa (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (mga sample ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spectrum [k] = amp; }}

float avg (float * a, uint16_t len) {

resulta ng float =.0; para sa (uint16_t i = 0; i <len; i ++) {resulta + = a ; } ibalik ang resulta / len; }

int8_t makakuha ng_single_index_above_threshold (float * a, uint16_t len, float threshold) {

kung (threshold <THRESHOLD) {return -1; } int8_t ix = -1; para sa (uint16_t i = 0; i threshold) {if (ix == -1) {ix = i; } iba pa {bumalik -1; }}} ibalik ang ix; }

walang bisa ang detect_digit (float * spectrum) {

float avg_row = avg (spectrum, 4); float avg_col = avg (& spectrum [4], 4); int8_t row = get_single_index_above_threshold (spectrum, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectrum [4], 4, avg_col); kung (row! = -1 && col! = -1 && avg_col> 200) {napansin_digit.digit = pgm_read_byte (& (table [row] [col])); napansin_digit.index = pgm_read_byte (& (char_indexes [row] [col])); } iba pa {napansin_digit.digit = 0; }}

void drawSprite (byte * sprite) {

// Ang mask ay ginagamit upang makuha ang haligi ng haligi mula sa sprite row byte mask = B10000000; para sa (int iy = 0; iy <8; iy ++) {para sa (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// shift the mask by one pixel to the right

mask = mask >> 1; }

// reset ang mask ng haligi

mask = B10000000; }}

walang bisa ang pag-setup () {

Tala (); initADC (); sei ();

Serial.begin (115200);

lmd.setEn pinagana (totoo); lmd.setIntensity (2); lmd.clear (); lmd.display ();

napansin_digit.digit = 0;

}

unsigned mahabang z = 0;

void loop () {

habang (ADCSRA & _BV (ADIE)); // Maghintay para sa audio sampling upang matapos ang goertzel (mga sample, spectrum); makita_digit (spectrum);

kung (napansin_digit.digit! = 0) {

drawSprite (font [nakita_digit.index]); lmd.display (); } kung (z% 5 == 0) {para sa (int i = 0; i <IX_LEN; i ++) {Serial.print (spectrum ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) napansin_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Ipagpatuloy ang pag-sampal ng sampling

}

ISR (ADC_vect) {

uint16_t sample = ADC;

mga sample [samplePos ++] = sample - 400;

kung (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Buffer full, interrupt off}}

Hakbang 3: Mga Skematika

Mga Skema
Mga Skema

Ang mga sumusunod na koneksyon ay dapat gawin:

Mic kay Arduino

Lumabas -> A0

Vcc -> 3.3V Gnd -> Gnd

Mahalagang ikonekta ang AREF sa 3.3V

Ipakita kay Arduino

Vcc -> 5V

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

Hakbang 4: Konklusyon

Ano ang maaaring mapabuti dito? Gumamit ako ng N = 256 na mga sample sa rate 9615Hz na mayroong ilang spectrum leakage, kung N = 205 at rate ay 8000Hz kung gayon ang nais na mga frequency ay kasabay ng discretisation grid. Para sa ADC na iyon ay dapat gamitin sa mode ng overflow ng timer.

Inirerekumendang: