Πίνακας περιεχομένων:

Ανιχνευτής DTMF: 4 βήματα
Ανιχνευτής DTMF: 4 βήματα

Βίντεο: Ανιχνευτής DTMF: 4 βήματα

Βίντεο: Ανιχνευτής DTMF: 4 βήματα
Βίντεο: 4 Παλμικοί Ανιχνευτές σε ΕΝΑ! Ερευνάτε με Κίνηση ή Παλμικά? XP DEUS & XP ORX | Q&A - Επ. 18 2024, Νοέμβριος
Anonim
Image
Image

ΣΦΑΙΡΙΚΗ ΕΙΚΟΝΑ

Εμπνεύστηκα να δημιουργήσω αυτήν τη συσκευή από μια οικιακή εργασία στο διαδικτυακό μάθημα Digitalηφιακής επεξεργασίας σημάτων. Αυτός είναι ένας αποκωδικοποιητής DTMF που εφαρμόζεται με το Arduino UNO, ανιχνεύει ένα ψηφίο που πιέζεται σε ένα πληκτρολόγιο τηλεφώνου στη λειτουργία τόνου από τον ήχο που παράγει.

Βήμα 1: Κατανόηση του αλγορίθμου

Ο κώδικας
Ο κώδικας

Στο DTMF κάθε σύμβολο κωδικοποιείται με δύο συχνότητες σύμφωνα με τον πίνακα της εικόνας.

Η συσκευή καταγράφει την είσοδο από το μικρόφωνο και υπολογίζει πλάτη οκτώ συχνοτήτων. Δύο συχνότητες με μέγιστα πλάτη δίνουν μια γραμμή και μια στήλη του κωδικοποιημένου συμβόλου.

Απόκτηση δεδομένων

Για την εκτέλεση ανάλυσης φάσματος τα δείγματα πρέπει να λαμβάνονται σε μια συγκεκριμένη προβλέψιμη συχνότητα. Για να το πετύχω αυτό, χρησιμοποίησα τη λειτουργία ADC ελεύθερης λειτουργίας με μέγιστη ακρίβεια (προ-ζυγοστάτης 128) δίνει ρυθμό δειγματοληψίας 9615Hz. Ο παρακάτω κώδικας δείχνει πώς μπορείτε να διαμορφώσετε το ADC του Arduino.

void initADC () {

// Init ADC; f = (16MHz/prescaler)/13 κύκλοι/μετατροπή ADMUX = 0; // Πωλήσεις καναλιών, δεξιά-επέκταση, χρησιμοποιήστε καρφίτσα AREF ADCSRA = _BV (ADEN) | // ADC enable _BV (ADSC) | // ADC start _BV (ADATE) | // Αυτόματη σκανδάλη _BV (ADIE) | // Διακοπή ενεργοποίησης _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Λειτουργία ελεύθερης εκτέλεσης DIDR0 = _BV (0); // Απενεργοποίηση ψηφιακής εισόδου για καρφίτσα ADC TIMSK0 = 0; // Timer0 off} Και ο χειριστής διακοπών μοιάζει με αυτό το ISR (ADC_vect) {uint16_t sample = ADC; δείγματα [samplePos ++] = δείγμα - 400; εάν (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Η προσωρινή μνήμη είναι πλήρης, η διακοπή απενεργοποιείται}}

Ανάλυση φάσματος

Μετά τη συλλογή δειγμάτων υπολογίζω πλάτη 8 συχνοτήτων που κωδικοποιούν σύμβολα. Δεν χρειάζεται να εκτελέσω πλήρη FFT για αυτό, οπότε χρησιμοποίησα τον αλγόριθμο του Goertzel.

void goertzel (uint8_t *δείγματα, float *φάσμα) {

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])); float 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 = (float) (δείγματα ) + 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.

Το πλήρες σκίτσο φαίνεται ως εξής

/** * Συνδέσεις: * [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

#περιλαμβάνω

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

δείγματα uint8_t [N];

πτητικό uint16_t samplePos = 0;

float φάσμα [IX_LEN];

// Συχνότητες [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Υπολογίστηκε για 9615Hz 256 δείγματα const float cos_t [IX_LEN] PROGMEM = {0.8932243011955153, 0.8700869911087115, 0.8448535652497071, 0.8032075314806449, 0.6895405447370669, 0.63439639885, 0.63439639885, 0.634393603, 0.634393603, 0.63439.396393 0.63439.393 0.8639.396393 0.6343.986393 0.6343 Six. const float sin_t [IX_LEN] PROGMEM = {0.44961132965460654, 0.49289819222978404, 0.5349976198870972, 0.5956993044924334, 0.7242470829514669, 0.7730104533627369, 0.831924696, 0.831924986, 0.831924986, 0.83192496

typedef struct {

ψηφίο char? ευρετήριο uint8_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} };

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, 0x04, 0x04, 0x38, 0x40, 0x7c}, // 2 {0x00, 0x38, 0x04, 0x04, 0x18, 0x04, 0x44, 0x38}, // 3 {0x00, 0x04, 0x0c, 0x14, 0x24, 0x7e, 0x04, 0x04, 0x04, 0x04, 0x04, 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}, / / Β {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 () {

// Init ADC; f = (16MHz/prescaler)/13 κύκλοι/μετατροπή ADMUX = 0; // Πωλήσεις καναλιών, δεξιά-επέκταση, χρησιμοποιήστε καρφίτσα AREF ADCSRA = _BV (ADEN) | // ADC enable _BV (ADSC) | // ADC start _BV (ADATE) | // Αυτόματη σκανδάλη _BV (ADIE) | // Διακοπή ενεργοποίησης _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1 /13 = 9615 Hz ADCSRB = 0; // Λειτουργία ελεύθερης εκτέλεσης DIDR0 = _BV (0); // Απενεργοποίηση ψηφιακής εισόδου για καρφίτσα ADC TIMSK0 = 0; // Χρονοδιακόπτης 0 απενεργοποιημένος}

void goertzel (uint8_t *δείγματα, float *φάσμα) {

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])); float 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 = (float) (δείγματα ) + 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) {

float αποτέλεσμα =.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; } else {return -1; }}} return ix; }

void dete_digit (float *spectrum) {

float avg_row = μέσο (φάσμα, 4); float avg_col = avg (& φάσμα [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); if (row! = -1 && col! = -1 && avg_col> 200) {dete_digit.digit = pgm_read_byte (& (πίνακας [σειρά] [col])); Discover_digit.index = pgm_read_byte (& (char_indexes [σειρά] [col])); } else {dete_digit.digit = 0; }}

void drawSprite (byte* sprite) {

// Η μάσκα χρησιμοποιείται για να πάρει το bit της στήλης από τη μάσκα byte σειράς sprite = B10000000; για (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask)) ?

// μετακινήστε τη μάσκα κατά ένα pixel προς τα δεξιά

μάσκα = μάσκα >> 1; }

// επαναφορά μάσκας στήλης

μάσκα = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

Discover_digit.digit = 0;

}

ανυπόγραφο μακρύ z = 0;

void loop () {

ενώ (ADCSRA & _BV (ADIE)); // Περιμένετε να ολοκληρωθεί η δειγματοληψία ήχου για να τελειώσει το goertzel (δείγματα, φάσμα). dete_digit (φάσμα)?

εάν (dete_digit.digit! = 0) {

drawSprite (γραμματοσειρά [dete_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 δείγμα = ADC;

δείγματα [samplePos ++] = δείγμα - 400;

εάν (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Η προσωρινή μνήμη είναι πλήρης, η διακοπή απενεργοποιείται}}

Βήμα 3: Διαγράμματα

Σχήματα
Σχήματα

Πρέπει να γίνουν οι ακόλουθες συνδέσεις:

Μικρόφωνο στο Arduino

Έξω -> Α0

Vcc -> 3.3V Gnd -> Gnd

Είναι σημαντικό να συνδέσετε το AREF σε 3,3V

Εμφάνιση στο Arduino

Vcc -> 5V

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

Βήμα 4: Συμπέρασμα

Τι θα μπορούσε να βελτιωθεί εδώ; Χρησιμοποίησα δείγματα Ν = 256 με ρυθμό 9615Hz που έχει κάποια διαρροή φάσματος, αν Ν = 205 και ρυθμός είναι 8000Hz τότε οι επιθυμητές συχνότητες συμπίπτουν με το πλέγμα διακριτικότητας. Για αυτό το ADC θα πρέπει να χρησιμοποιείται σε λειτουργία υπερχείλισης χρονοδιακόπτη.

Συνιστάται: