جدول المحتويات:

كاشف DTMF: 4 خطوات
كاشف DTMF: 4 خطوات

فيديو: كاشف DTMF: 4 خطوات

فيديو: كاشف DTMF: 4 خطوات
فيديو: dtmf1 2024, شهر نوفمبر
Anonim
Image
Image

ملخص

لقد ألهمني تصميم هذا الجهاز من خلال مهمة منزلية في دورة معالجة الإشارات الرقمية عبر الإنترنت. هذا هو وحدة فك ترميز DTMF مطبقة مع Arduino UNO ، فهي تكتشف رقمًا مضغوطًا على لوحة مفاتيح الهاتف في وضع النغمة من خلال الصوت الذي تنتجه.

الخطوة 1: فهم الخوارزمية

الرمز
الرمز

في DTMF يتم ترميز كل رمز بترددين حسب الجدول الموجود في الصورة.

يلتقط الجهاز المدخلات من الميكروفون ويحسب سعة ثمانية ترددات. يعطي ترددان ذو سعة قصوى صفًا وعمودًا من الرمز المشفر.

الحصول على البيانات

من أجل إجراء تحليل الطيف ، ينبغي التقاط عينات عند تردد معين يمكن التنبؤ به. لتحقيق ذلك ، استخدمت وضع ADC للتشغيل الحر بأقصى دقة (مقياس مسبق 128) ، فإنه يعطي معدل أخذ العينات 9615 هرتز. يوضح الكود أدناه كيفية تكوين ADC في Arduino.

initADC () باطلة {

// التهيئة ADC ؛ f = (16 ميجا هرتز / مقياس مسبق) / 13 دورة / تحويل ADMUX = 0 ؛ // قناة sel ، يمينًا ، استخدم AREF pin ADCSRA = _BV (ADEN) | // ADC تمكين _BV (ADSC) | // ADC start _BV (ADATE) | // مشغل تلقائي _BV (ADIE) | // تمكين المقاطعة _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0) ؛ // 128: 1/13 = 9615 هرتز ADCSRB = 0 ؛ // وضع التشغيل الحر DIDR0 = _BV (0) ؛ // إيقاف تشغيل الإدخال الرقمي لـ ADC pin TIMSK0 = 0 ؛ // Timer0 off} ويبدو معالج المقاطعة مثل ISR (ADC_vect) {uint16_t sample = ADC؛ sample [samplePos ++] = sample - 400 ؛ إذا (samplePos> = N) {ADCSRA & = ~ _BV (ADIE) ؛ // المخزن المؤقت ممتلئ ، المقاطعة متوقفة}}

تحليل الطيف

بعد جمع العينات ، أحسب اتساع رموز ترميز 8 ترددات. لست بحاجة إلى تشغيل FFT بالكامل لهذا الغرض ، لذلك استخدمت خوارزمية Goertzel.

goertzel باطل (uint8_t * عينات ، طيف تعويم *) {

تعويم v_0 ، v_1 ، v_2 ؛ تعويم إعادة ، الدردشة ، أمبير ؛ لـ (uint8_t k = 0؛ k <IX_LEN؛ k ++) {float c = pgm_read_float (& (cos_t [k])) ؛ float s = pgm_read_float (& (sin_t [k])) ؛ تعويم أ = 2. * ج ؛ v_0 = v_1 = v_2 = 0 ؛ لـ (uint16_t i = 0 ؛ i <N ؛ i ++) {v_0 = v_1 ؛ v_1 = v_2 ؛ v_2 = (تعويم) (عينات ) + a * v_1 - v_0 ؛ } re = c * v_2 - v_1 ؛ im = s * v_2 ؛ أمبير = sqrt (re * re + im * im) ؛ الطيف [ك] = أمبير ؛ }}

الخطوة الثانية: الكود

توضح الصورة أعلاه مثال ترميز الرقم 3 حيث يتوافق السعة القصوى مع الترددات 697 هرتز و 1477 هرتز.

يبدو الرسم الكامل على النحو التالي

/ ** * التوصيلات: * [Mic to Arduino] * - Out -> A0 * - Vcc -> 3.3V * - Gnd -> Gnd * - Arduino: AREF -> 3.3V * [عرض على Arduino] * - Vcc - > 5V * - Gnd -> Gnd * - DIN -> D11 * - CLK -> D13 * - CS -> D9 * / # تضمين # تضمين

#يشمل

#define CS_PIN 9

# تعريف شمال 256

#define IX_LEN 8 # تعريف THRESHOLD 20

LEDMatrixDriver lmd (1 ، CS_PIN) ؛

عينات uint8_t [N] ؛

uint16_t samplePos = 0 ؛

طيف تعويم [IX_LEN] ؛

// الترددات [697.0، 770.0، 852.0، 941.0، 1209.0، 1336.0، 1477.0، 1633.0]

// محسوبة لـ 9615 هرتز 256 عينة عائمة cos_t [IX_LEN] PROGMEM = {0.8932243011955153 ، 0.8700869911087115 ، 0.8448535652497071 ، 0.8032075314806449 ، 0.6895405447370669 ، 0.6343932841636456 ، 0.55553619 ، const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654، 0.49289819222978404، 0.5349976198870972، 0.5956993044924334، 0.7242470829514669، 0.7730104533627369، 0.8314696123025612}؛

بنية typedef {

رقم الحرف فهرس uint8_t ؛ } digit_t؛

تم الكشف عن digit_t_digit ؛

جدول الحرف الثابت [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} };

خط بايت [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} ، / / / 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} // *} ؛

initADC () باطلة {

// التهيئة ADC ؛ f = (16 ميجا هرتز / مقياس مسبق) / 13 دورة / تحويل ADMUX = 0 ؛ // قناة sel ، يمينًا ، استخدم AREF pin ADCSRA = _BV (ADEN) | // ADC تمكين _BV (ADSC) | // ADC start _BV (ADATE) | // مشغل تلقائي _BV (ADIE) | // تمكين المقاطعة _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0) ؛ // 128: 1/13 = 9615 هرتز ADCSRB = 0 ؛ // وضع التشغيل الحر DIDR0 = _BV (0) ؛ // إيقاف تشغيل الإدخال الرقمي لـ ADC pin TIMSK0 = 0 ؛ // Timer0 off}

goertzel باطل (uint8_t * عينات ، طيف تعويم *) {

تعويم v_0 ، v_1 ، v_2 ؛ تعويم إعادة ، الدردشة ، أمبير ؛ لـ (uint8_t k = 0؛ k <IX_LEN؛ k ++) {float c = pgm_read_float (& (cos_t [k])) ؛ float s = pgm_read_float (& (sin_t [k])) ؛ تعويم أ = 2. * ج ؛ v_0 = v_1 = v_2 = 0 ؛ لـ (uint16_t i = 0 ؛ i <N ؛ i ++) {v_0 = v_1 ؛ v_1 = v_2 ؛ v_2 = (تعويم) (عينات ) + a * v_1 - v_0 ؛ } re = c * v_2 - v_1 ؛ im = s * v_2 ؛ أمبير = sqrt (re * re + im * im) ؛ الطيف [ك] = أمبير ؛ }}

float avg (float * a، uint16_t len) {

نتيجة تعويم =.0 ؛ لـ (uint16_t i = 0؛ i <len؛ i ++) {result + = a ؛ } نتيجة الإرجاع / len ؛ }

int8_t get_single_index_above_threshold (float * a، uint16_t len، float threshold) {

إذا (عتبة <THRESHOLD) {إرجاع -1 ؛ } int8_t ix = -1 ؛ لـ (uint16_t i = 0 ؛ i الحد الأدنى) {if (ix == -1) {ix = i؛ } آخر {عودة -1؛ }}} عودة ix؛ }

كشف باطل رقم (طيف * طيف) {

تعويم avg_row = avg (طيف ، 4) ؛ تعويم avg_col = متوسط (& طيف [4] ، 4) ؛ int8_t row = get_single_index_above_threshold (طيف ، 4 ، avg_row) ؛ int8_t col = get_single_index_above_threshold (& الطيف [4]، 4، avg_col) ؛ إذا (row! = -1 && col! = -1 && avg_col> 200) {discovery_digit.digit = pgm_read_byte (& (table [row] [col])) ؛ كشف_digit.index = pgm_read_byte (& (char_indexes [row] [col])) ؛ } else {discovery_digit.digit = 0 ؛ }}

drawSprite باطل (بايت * sprite) {

// يتم استخدام القناع للحصول على بت العمود من قناع بايت صف العفريت = B10000000 ؛ لـ (int iy = 0؛ iy <8؛ iy ++) {لـ (int ix = 0؛ ix <8؛ ix ++) {lmd.setPixel (7 - iy، ix، (bool) (sprite [iy] & mask)) ؛

// انقل القناع بمقدار بكسل واحد إلى اليمين

قناع = قناع >> 1 ؛ }

// إعادة تعيين قناع العمود

قناع = B10000000 ؛ }}

الإعداد باطل() {

cli () ؛ initADC () ، سي () ؛

Serial.begin (115200) ؛

lmd.setEnabled (صحيح) ، lmd.set كثافة (2) ؛ lmd.clear () ، lmd.display () ،

Detective_digit.digit = 0 ؛

}

طويل بدون توقيع z = 0 ؛

حلقة فارغة() {

بينما (ADCSRA & _BV (ADIE)) ؛ // انتظر حتى ينتهي أخذ العينات الصوتية من goertzel (العينات ، الطيف) ؛ كشف الرقم (الطيف) ؛

إذا (اكتشفت_digit.digit! = 0) {

drawSprite (الخط [المكتشف_digit.index]) ؛ lmd.display () ، } if (z٪ 5 == 0) {for (int i = 0؛ i <IX_LEN؛ i ++) {Serial.print (range ) ؛ Serial.print ("\ t") ؛ } Serial.println () ، Serial.println ((int) detect_digit.digit) ؛ } z ++ ؛

samplePos = 0 ؛

ADCSRA | = _BV (ADIE) ، // استئناف مقاطعة أخذ العينات

}

ISR (ADC_vect) {

عينة uint16_t = ADC ؛

عينات [samplePos ++] = عينة - 400 ؛

إذا (samplePos> = N) {ADCSRA & = ~ _BV (ADIE) ؛ // المخزن المؤقت ممتلئ ، المقاطعة متوقفة}}

الخطوة 3: المخططات

المخططات
المخططات

يجب عمل التوصيلات التالية:

ميكروفون لاردوينو

خارج -> A0

Vcc -> 3.3V Gnd -> Gnd

من المهم توصيل AREF بـ 3.3V

عرض لاردوينو

Vcc -> 5V

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

الخطوة 4: الخاتمة

ما الذي يمكن تحسينه هنا؟ لقد استخدمت N = 256 عينة بمعدل 9615 هرتز الذي يحتوي على بعض تسرب الطيف ، إذا كان N = 205 ومعدل 8000 هرتز ، فإن الترددات المرغوبة تتطابق مع شبكة التقدير. لذلك يجب استخدام ADC في وضع تجاوز سعة المؤقت.

موصى به: