תוכן עניינים:

גלאי DTMF: 4 שלבים
גלאי DTMF: 4 שלבים

וִידֵאוֹ: גלאי DTMF: 4 שלבים

וִידֵאוֹ: גלאי DTMF: 4 שלבים
וִידֵאוֹ: PAL-ES GSM-KEY MAKING | Avtomatika.online 2024, יולי
Anonim
Image
Image

סקירה כללית

קיבלתי השראה לבנות מכשיר זה על ידי מטלה ביתית בקורס מקוון לעיבוד אותות דיגיטליים. זהו מפענח DTMF המיושם עם Arduino UNO, הוא מזהה ספרה שנלחץ על לוח המקשים של הטלפון במצב הטון על ידי הצליל שהוא מייצר.

שלב 1: הבנת האלגוריתם

הקוד
הקוד

ב- DTMF כל סמל מקודד בשני תדרים בהתאם לטבלה שבתמונה.

המכשיר לוכד קלט מהמיקרופון ומחשב אמפליטודות של שמונה תדרים. שני תדרים עם אמפליטודות מרביות נותנים שורה ועמודה של הסמל המקודד.

רכישת נתונים

על מנת לבצע ניתוח ספקטרום דגימות יש ללכוד בתדירות צפויה מסוימת. כדי להשיג זאת השתמשתי במצב ADC בהפעלה חופשית עם דיוק מרבי (מראש מסדר 128) הוא נותן קצב דגימה 9615Hz. הקוד להלן מראה כיצד להגדיר את ה- ADC של Arduino.

initADC void () {

// Init ADC; f = (16MHz/prcaler)/13 מחזורים/ADMUX המרה = 0; // ערוץ sel, ימין- adj, השתמש בסיכת AREF 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 TIMSK0 = 0; // טיימר 0 כבוי} והמטפל להפריע נראה כך ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; אם (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // מאגר מלא, הפרעה מושבתת}}

ניתוח ספקטרום

לאחר איסוף דגימות אני מחשב אמפליטודות של 8 תדרים המקודדים סמלים. אני לא צריך להריץ FFT מלא בשביל זה, אז השתמשתי באלגוריתם של גרצל.

goertzel void (uint8_t *דוגמאות, float *ספקטרום) {

לצוף v_0, v_1, v_2; float re, im, amp; עבור (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k]))); float s = pgm_read_float (& (sin_t [k])); לצוף a = 2. * c; 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; amp = sqrt (re * re + im * im); ספקטרום [k] = אמפר; }}

שלב 2: הקוד

התמונה למעלה מציגה את דוגמת הקידוד של הספרה 3 שבה המשרעת המרבית מתאימה לתדרים 697Hz ו -1477Hz.

המערכון המלא נראה כך

/** * חיבורים: * [מיקרופון ל- 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

#לִכלוֹל

#הגדר CS_PIN 9

#הגדר N 256

#הגדר 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 דוגמאות const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.6343932841636456, 0.5365 const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.8314696123025451;

typedef struct {

ספרת חרס; אינדקס uint8_t; } digit_t;

digit_t זיהוי_ספרה;

const char table [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, 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} // *};

initADC void () {

// Init ADC; f = (16MHz/prescaler)/13 מחזורים/ADMUX המרה = 0; // ערוץ sel, ימין- adj, השתמש בסיכת AREF 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 TIMSK0 = 0; // טיימר 0 כבוי}

goertzel void (uint8_t *דוגמאות, float *ספקטרום) {

לצוף v_0, v_1, v_2; float re, im, amp; עבור (uint8_t k = 0; k <IX_LEN; k ++) {float c = pgm_read_float (& (cos_t [k])); float s = pgm_read_float (& (sin_t [k])); לצוף a = 2. * c; 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; amp = sqrt (re * re + im * im); ספקטרום [k] = אמפר; }}

ממוצע צף (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, סף צף) {

if (סף <THRESHOLD) {return -1; } int8_t ix = -1; עבור (uint16_t i = 0; i סף) {if (ix == -1) {ix = i; } אחר {החזרה -1; }}} החזר ix; }

void detect_digit (float *spectrum) {

צף avg_row = ממוצע (ספקטרום, 4); float avg_col = avg (& spectrum [4], 4); int8_t row = get_single_index_above_threshold (ספקטרום, 4, avg_row); int8_t col = get_single_index_above_threshold (& spectrum [4], 4, avg_col); אם (שורה! = -1 && col! = -1 && avg_col> 200) {detect_digit.digit = pgm_read_byte (& (טבלה [שורה] [col])); detect_digit.index = pgm_read_byte (& (char_indexes [row] [col])); } אחר {detect_digit.digit = 0; }}

void drawSprite (בייט* ספרייט) {

// המסכה משמשת להוצאת ביט העמודה ממסכת בתים של שורת ספרייט = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & מסכה));

// העבר את המסכה בפיקסל אחד ימינה

מסכה = מסכה >> 1; }

// אפס את מסכת העמודות

מסכה = B10000000; }}

הגדרת בטל () {

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

Serial.begin (115200);

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

detect_digit.digit = 0;

}

לא חתום z = 0;

לולאת חלל () {

בעוד (ADCSRA & _BV (ADIE)); // המתן עד שדגימת האודיו תסיים את goertzel (דוגמאות, ספקטרום); detect_digit (ספקטרום);

אם (detect_digit.digit! = 0) {

drawSprite (פונט [detected_digit.index]); lmd.display (); } if (z % 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (ספקטרום ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) detect_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // המשך הפסקת הדגימה

}

ISR (ADC_vect) {

uint16_t sample = ADC;

דוגמאות [samplePos ++] = מדגם - 400;

אם (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // מאגר מלא, הפרעה מושבתת}}

שלב 3: תרשימים

סכמטים
סכמטים

יש לבצע את החיבורים הבאים:

מיקרופון לארדואינו

החוצה -> A0

Vcc -> 3.3V Gnd -> Gnd

חשוב לחבר את AREF ל 3.3V

תצוגה ל- Arduino

Vcc -> 5V

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

שלב 4: מסקנה

מה ניתן לשפר כאן? השתמשתי ב- N = 256 דגימות בקצב 9615Hz שיש לה דליפת ספקטרום מסוימת, אם N = 205 והקצב הוא 8000Hz אז התדרים הרצויים חופפים לרשת הדיסקרטיזציה. לשם כך יש להשתמש ב- ADC במצב הצפת טיימר.

מוּמלָץ: