2025 Muallif: John Day | [email protected]. Oxirgi o'zgartirilgan: 2025-01-13 06:58
Sharh
Menga ushbu qurilmani uydan raqamli signallarni qayta ishlash bo'yicha onlayn kurs bo'yicha topshiriq ilhomlantirdi. Bu Arduino UNO yordamida amalga oshirilgan DTMF dekoderi, u telefon klaviaturasida ohang rejimida bosilgan raqamni u chiqaradigan tovush bilan aniqlaydi.
1 -qadam: Algoritmni tushunish
DTMFda har bir belgi rasmdagi jadvalga muvofiq ikkita chastota bilan kodlangan.
Qurilma mikrofondan kirishni yozib oladi va sakkiz chastota amplitudasini hisoblab chiqadi. Maksimal amplitudali ikkita chastota kodlangan belgining qatorini va ustunini beradi.
Ma'lumot olish
Spektrni tahlil qilish uchun namunalarni ma'lum bir chastotada olish kerak. Bunga erishish uchun men ADC rejimini maksimal aniqlikda ishlatardim (prescaler 128), bu 9615 Gts namuna olish tezligini beradi. Quyidagi kod Arduino ADC -ni qanday sozlashni ko'rsatadi.
bekor ADAD () {
// ADCni ishga tushirish; f = (16 MGts/oldindan hisoblagich)/13 tsikl/konvertatsiya ADMUX = 0; // Channel sel, right-adj, AREF pin ADCSRA = _BV (ADEN) dan foydalaning | // ADC _BV (ADSC) ni yoqish | // ADC boshlanishi _BV (ADATE) | // Avtomatik ishga tushirish _BV (ADIE) | // Tanaffusni yoqish _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Erkin ishlash rejimi DIDR0 = _BV (0); // ADC pin uchun raqamli kirishni o'chirish TIMSK0 = 0; // Taymer0 o'chirilgan} Va uzilish ishlovchisi ISR (ADC_vect) ga o'xshaydi {uint16_t sample = ADC; sample [samplePos ++] = namuna - 400; agar (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bufer to'lgan, uzilish o'chirilgan}}
Spektrni tahlil qilish
Namunalarni yig'ib bo'lgach, men 8 ta chastotali kodlash belgilarining amplitudalarini hisoblayman. Buning uchun menga to'liq FFT ishga tushirish shart emas, shuning uchun men Goertzel algoritmidan foydalandim.
bo'sh gersel (uint8_t *namunalari, float *spektri) {
suzuvchi v_0, v_1, v_2; float re, im, amp; uchun (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; uchun (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (namunalar ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektr [k] = amp; }}
2 -qadam: Kod
Yuqoridagi rasmda maksimal amplitudasi 697 Gts va 1477 Gts chastotalarga mos keladigan 3 -raqamli kodlash misoli ko'rsatilgan.
To'liq eskiz quyidagicha ko'rinadi
/** * Ulanishlar: * [Micro to Arduino] * - Chiqish -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [Arduino displeyi] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 */ #include #include
#qo'shing
#CS_PIN 9 ni aniqlang
#aniqlang N 256
#IX_LEN 8 ni belgilang #TAHRISI 20 ni aniqlang
LEDMatrixDriver lmd (1, CS_PIN);
uint8_t namunalari [N];
uchuvchi uint16_t samplePos = 0;
suzuvchi spektr [IX_LEN];
// Chastotalar [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]
// 9615 Gts 256 namunali const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.68954054473706396666666666666666666666666666686, 0,8639 const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.7730104533627369, 0.7730104533627369
typedef tuzilishi {
char raqami; uint8_t indeksi; } raqam_t;
raqam_t aniqlangan_ raqam;
const char jadvali [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} };
bayt shrift [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} // *};
bekor initADC () {
// ADCni ishga tushirish; f = (16 MGts/oldindan hisoblagich)/13 tsikl/konvertatsiya ADMUX = 0; // Channel sel, right-adj, AREF pin ADCSRA = _BV (ADEN) dan foydalaning | // ADC _BV (ADSC) ni yoqish | // ADC boshlanishi _BV (ADATE) | // Avtomatik ishga tushirish _BV (ADIE) | // Tanaffusni yoqish _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Erkin ishlash rejimi DIDR0 = _BV (0); // ADC pin uchun raqamli kirishni o'chirish TIMSK0 = 0; // Taymer 0 o'chirilgan}
bo'sh gersel (uint8_t *namunalari, float *spektri) {
suzuvchi v_0, v_1, v_2; float re, im, amp; uchun (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; uchun (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (namunalar ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); spektr [k] = amp; }}
suzuvchi o'rtacha (float *a, uint16_t len) {
float natijasi =.0; uchun (uint16_t i = 0; i <len; i ++) {natija+= a ; } natijani qaytarish / len; }
int8_t get_single_index_above_threshold (float *a, uint16_t len, float pol) {
if (pol <THRESHOLD) {qaytish -1; } int8_t ix = -1; uchun (uint16_t i = 0; i ostona) {if (ix == -1) {ix = i; } boshqa {qaytish -1; }}} qaytarish ix; }
void detect_digit (float *spektr) {
suzuvchi avg_row = o'rtacha (spektr, 4); suzuvchi avg_col = avg (& spektr [4], 4); int8_t qator = get_single_index_above_threshold (spektr, 4, avg_row); int8_t col = get_single_index_above_threshold (& spektr [4], 4, avg_col); agar (qator! = -1 && col! = -1 && avg_col> 200) {aniqlangan_digit.digit = pgm_read_byte (& (jadval [qator] [col])); aniqlangan_digit.index = pgm_read_byte (& (char_indexes [qator] [col])); } boshqa {aniqlangan_digit.digit = 0; }}
void drawSprite (bayt* sprite) {
// Niqob sprite qator bayt niqobidan ustun bitini olish uchun ishlatiladi = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));
// niqobni bir pikselga o'ngga siljiting
niqob = niqob >> 1; }
// ustun niqobini tiklash
niqob = B10000000; }}
bo'sh o'rnatish () {
cli (); initADC (); sei ();
Serial.begin (115200);
lmd.setEnabled (haqiqiy); lmd.setIntensity (2); lmd.clear (); lmd.display ();
aniqlangan_digit.digit = 0;
}
belgisiz uzun z = 0;
void loop () {
esa (ADCSRA & _BV (ADIE)); // Ovozli namuna olish tugaguncha kuting (namunalar, spektr); aniqlash_digit (spektr);
agar (aniqlangan_digit.digit! = 0) {
drawSprite (shrift [detect_digit.index]); lmd.display (); } if (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (spektr ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) detect_digit.digit); } z ++;
namunaPos = 0;
ADCSRA | = _BV (ADIE); // Namuna olishni to'xtatishni davom ettiring
}
ISR (ADC_vect) {
uint16_t namuna = ADC;
namunalar [samplePos ++] = namuna - 400;
agar (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bufer to'ldirildi, uzilish o'chirildi}}
3 -qadam: sxemalar
Quyidagi aloqalarni o'rnatish kerak:
Arduino uchun mikrofon
Chiqish -> A0
Vcc -> 3.3V Gnd -> Gnd
AREF -ni 3.3V ga ulash muhim
Arduino -da ko'rsatish
Vcc -> 5V
Gnd -> Gnd DIN -> D11 CLK -> D13 CS -> D9
4 -qadam: Xulosa
Bu erda nimani yaxshilash mumkin? Men N = 256 namunalarini 9615 Gts tezlikda ishlatardim, ularda spektr oqimi bor edi, agar N = 205 va tezligi 8000 Gts bo'lsa, kerakli chastotalar diskretizatsiya tarmog'iga to'g'ri keladi. Buning uchun ADC taymerni to'ldirish rejimida ishlatilishi kerak.