Mục lục:

DTMF Detector: 4 bước
DTMF Detector: 4 bước

Video: DTMF Detector: 4 bước

Video: DTMF Detector: 4 bước
Video: Relay control via phone call | DTMF Decoding 2024, Tháng bảy
Anonim
Image
Image

Tổng quat

Tôi được truyền cảm hứng để chế tạo thiết bị này nhờ một bài tập về nhà trong khóa học trực tuyến Xử lý tín hiệu kỹ thuật số. Đây là bộ giải mã DTMF được triển khai với Arduino UNO, nó phát hiện một chữ số được nhấn trên bàn phím điện thoại ở chế độ âm báo bằng âm thanh mà nó tạo ra.

Bước 1: Tìm hiểu thuật toán

Mật mã
Mật mã

Trong DTMF mỗi ký hiệu được mã hóa với hai tần số theo bảng trên hình.

Thiết bị thu nhận đầu vào từ micrô và tính toán biên độ của tám tần số. Hai tần số có biên độ cực đại cho một hàng và một cột của biểu tượng được mã hóa.

Thu thập dữ liệu

Để thực hiện phân tích phổ, các mẫu cần được thu nhận ở một tần số có thể dự đoán được. Để đạt được điều này, tôi đã sử dụng chế độ ADC chạy tự do với độ chính xác tối đa (bộ định mức 128), nó cho tốc độ lấy mẫu 9615Hz. Đoạn mã dưới đây cho biết cách định cấu hình Arduino’s ADC.

void initADC () {

// Nhập ADC; f = (16MHz / bộ định mức) / 13 chu kỳ / chuyển đổi ADMUX = 0; // Kênh sel, right-adj, sử dụng chân AREF ADCSRA = _BV (ADEN) | // Kích hoạt ADC _BV (ADSC) | // ADC start _BV (ADATE) | // Tự động kích hoạt _BV (ADIE) | // Kích hoạt ngắt _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Chế độ chạy tự do DIDR0 = _BV (0); // Tắt đầu vào số cho chân ADC TIMSK0 = 0; // Timer0 tắt} Và trình xử lý ngắt có dạng như sau ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bộ đệm đầy, ngắt}}

Phân tích phổ

Sau khi thu thập mẫu, tôi tính toán biên độ của ký hiệu mã hóa 8 tần số. Tôi không cần chạy FFT đầy đủ cho việc này, vì vậy tôi đã sử dụng thuật toán của Goertzel.

void goertzel (uint8_t * mẫu, float * phổ) {

float v_0, v_1, v_2; float re, im, amp; for (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; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (mẫu ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); quang phổ [k] = amp; }}

Bước 2: Mã

Hình trên cho thấy ví dụ về mã hóa của chữ số 3 trong đó biên độ tối đa tương ứng với các tần số 697Hz và 1477Hz.

Bản phác thảo hoàn chỉnh trông như sau

/ ** * Kết nối: * [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

#bao gồm

#define CS_PIN 9

#define N 256

#define IX_LEN 8 #define THRESHOLD 20

LEDMatrixDriver lmd (1, CS_PIN);

uint8_t mẫu [N];

dễ bay hơi uint16_t samplePos = 0;

phổ float [IX_LEN];

// Tần suất [697.0, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0]

// Được tính cho 9615Hz 256 mẫu const float cos_t [IX_LEN] PROGMEM = {0,8932243011955153, 0,8700869911087115, 0,8448535652497071, 0,8032075314806449, 0,6895405447370669, 0,634399978841636456, 0,5555702347991960} 0,5555702347991960} 0,55557023 const float sin_t [IX_LEN] PROGMEM = {0,44961132965460654, 0,49289819222978404, 0,5349976198870972, 0,5956993044924334, 0,7242470829514669, 0,7730104533627369, 0,8314696123025451, 0,883549126434835491264348354912643;

typedef struct {

ký số char; chỉ mục uint8_t; } số_t;

số_t phát hiện_digit;

bảng const 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} };

phông chữ byte [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} // *};

void initADC () {

// Nhập ADC; f = (16MHz / bộ định mức) / 13 chu kỳ / chuyển đổi ADMUX = 0; // Kênh sel, right-adj, sử dụng chân AREF ADCSRA = _BV (ADEN) | // Bật ADC _BV (ADSC) | // ADC start _BV (ADATE) | // Tự động kích hoạt _BV (ADIE) | // Kích hoạt ngắt _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Hz ADCSRB = 0; // Chế độ chạy tự do DIDR0 = _BV (0); // Tắt đầu vào số cho chân ADC TIMSK0 = 0; // Timer0 tắt}

void goertzel (uint8_t * mẫu, float * phổ) {

float v_0, v_1, v_2; float re, im, amp; for (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; for (uint16_t i = 0; i <N; i ++) {v_0 = v_1; v_1 = v_2; v_2 = (float) (mẫu ) + a * v_1 - v_0; } re = c * v_2 - v_1; im = s * v_2; amp = sqrt (re * re + im * im); quang phổ [k] = amp; }}

float avg (float * a, uint16_t len) {

kết quả float =.0; for (uint16_t i = 0; i <len; i ++) {result + = a ; } trả về kết quả / len; }

int8_t get_single_index_above_threshold (float * a, uint16_t len, float ngưỡng) {

if (ngưỡng <THRESHOLD) {return -1; } int8_t ix = -1; for (uint16_t i = 0; i ngưỡng) {if (ix == -1) {ix = i; } else {return -1; }}} trả về ix; }

void explore_digit (float * phổ) {

float avg_row = avg (phổ, 4); float avg_col = avg (& phổ [4], 4); int8_t row = get_single_index_above_threshold (phổ, 4, avg_row); int8_t col = get_single_index_above_threshold (& phổ [4], 4, avg_col); if (row! = -1 && col! = -1 && avg_col> 200) {secure_digit.digit = pgm_read_byte (& (table [row] [col])); Discover_digit.index = pgm_read_byte (& (char_indexes [row] [col])); } else {secure_digit.digit = 0; }}

void drawSprite (byte * sprite) {

// Mặt nạ được sử dụng để lấy bit cột từ sprite hàng byte mask = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (sprite [iy] & mask));

// di chuyển mặt nạ một pixel sang bên phải

mask = mặt nạ >> 1; }

// đặt lại mặt nạ cột

mặt nạ = B10000000; }}

void setup () {

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

Serial.begin (115200);

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

phát hiện_digit.digit = 0;

}

dài không dấu z = 0;

void loop () {

trong khi (ADCSRA & _BV (ADIE)); // Chờ lấy mẫu âm thanh kết thúc goertzel (mẫu, phổ); dò_dấu (quang phổ);

if (secure_digit.digit! = 0) {

drawSprite (font [found_digit.index]); lmd.display (); } if (z% 5 == 0) {for (int i = 0; i <IX_LEN; i ++) {Serial.print (phổ ); Serial.print ("\ t"); } Serial.println (); Serial.println ((int) found_digit.digit); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Tiếp tục ngắt lấy mẫu

}

ISR (ADC_vect) {

uint16_t mẫu = ADC;

mẫu [samplePos ++] = mẫu - 400;

if (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Bộ đệm đầy, ngắt}}

Bước 3: Sơ đồ

Sơ đồ
Sơ đồ

Các kết nối sau nên được thực hiện:

Mic để Arduino

Hết -> A0

Vcc -> 3.3V Gnd -> Gnd

Điều quan trọng là phải kết nối AREF với 3.3V

Hiển thị với Arduino

Vcc -> 5V

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

Bước 4: Kết luận

Điều gì có thể được cải thiện ở đây? Tôi đã sử dụng N = 256 mẫu ở tốc độ 9615Hz có một số rò rỉ phổ, nếu N = 205 và tốc độ là 8000Hz thì các tần số mong muốn trùng với lưới tùy ý. Đối với ADC đó nên được sử dụng trong chế độ tràn bộ hẹn giờ.

Đề xuất: