Оглавление:

Детектор DTMF: 4 шага
Детектор DTMF: 4 шага

Видео: Детектор DTMF: 4 шага

Видео: Детектор DTMF: 4 шага
Видео: GSM/GPRS-модуль SIM800L (#4) - все о DTMF: парсинг, управление, безопасность 2024, Ноябрь
Anonim
Image
Image

Обзор

На создание этого устройства меня вдохновило домашнее задание по онлайн-курсу «Цифровая обработка сигналов». Это декодер DTMF, реализованный с Arduino UNO, он определяет цифру, нажатую на клавиатуре телефона в тональном режиме, по издаваемому звуку.

Шаг 1: понимание алгоритма

Код
Код

В DTMF каждый символ кодируется двумя частотами согласно таблице на картинке.

Устройство принимает сигнал с микрофона и вычисляет амплитуды восьми частот. Две частоты с максимальными амплитудами дают строку и столбец кодированного символа.

Получение данных

Для проведения спектрального анализа образцы должны собираться с определенной предсказуемой частотой. Чтобы добиться этого, я использовал режим автономного АЦП с максимальной точностью (предварительный делитель 128), он дает частоту дискретизации 9615 Гц. В приведенном ниже коде показано, как настроить АЦП Arduino.

void initADC () {

// Инициализируем АЦП; f = (16 МГц / предварительный делитель) / 13 циклов / преобразование ADMUX = 0; // Выбор канала, регулировка вправо, использование вывода AREF ADCSRA = _BV (ADEN) | // включение АЦП _BV (ADSC) | // запуск АЦП _BV (ADATE) | // Автоматический запуск _BV (ADIE) | // Разрешение прерывания _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Гц ADCSRB = 0; // Автономный режим DIDR0 = _BV (0); // Отключить цифровой вход для вывода АЦП TIMSK0 = 0; // Timer0 off} И обработчик прерывания выглядит так: ISR (ADC_vect) {uint16_t sample = ADC; samples [samplePos ++] = sample - 400; если (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Буфер заполнен, прерывание отключено}}

Спектральный анализ

После сбора образцов я вычисляю амплитуды 8 символов кодирования частот. Для этого мне не нужно выполнять полное БПФ, поэтому я использовал алгоритм Герцеля.

void goertzel (uint8_t * samples, float * Spectrum) {

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] = amp; }}

Шаг 2: Код

На рисунке выше показан пример кодирования цифры 3, где максимальная амплитуда соответствует частотам 697 Гц и 1477 Гц.

Полный эскиз выглядит следующим образом

/ ** * Подключения: * [Микрофон к Arduino] * - Выход -> A0 * - Vcc -> 3,3 В * - Gnd -> Gnd * - Arduino: AREF -> 3,3 В * [Отображение на 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;

спектр с плавающей запятой [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.5602368570 const float sin_t [IX_LEN] PROGMEM = {0,44961132965460654, 0,49289819222978404, 0,5349976198870972, 0,5956993044924334, 0,7242470829514669, 0,7730104533627369, 0,831469612301924512, 0,831469612301924512, 0,831469612301924512;

typedef struct {

символьная цифра; uint8_t index; } digit_t;

digit_t обнаруженная_digit;

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, 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 () {

// Инициализируем АЦП; f = (16 МГц / предварительный делитель) / 13 циклов / преобразование ADMUX = 0; // Выбор канала, регулировка вправо, использование вывода AREF ADCSRA = _BV (ADEN) | // включение АЦП _BV (ADSC) | // запуск АЦП _BV (ADATE) | // Автоматический запуск _BV (ADIE) | // Разрешение прерывания _BV (ADPS2) | _BV (ADPS1) | _BV (ADPS0); // 128: 1/13 = 9615 Гц ADCSRB = 0; // Автономный режим DIDR0 = _BV (0); // Отключить цифровой вход для вывода АЦП TIMSK0 = 0; // Таймер 0 выключен}

void goertzel (uint8_t * samples, float * Spectrum) {

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] = amp; }}

float avg (float * a, uint16_t len) {

float result =.0; для (uint16_t я = 0; я <len; я ++) {результат + = а [я]; } вернуть результат / лен; }

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

если (порог <ПОРОГ) {возврат -1; } int8_t ix = -1; for (uint16_t i = 0; i порог) {if (ix == -1) {ix = i; } else {возврат -1; }}} return ix; }

void detect_digit (float * спектр) {

float avg_row = avg (спектр, 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); если (строка! = -1 && col! = -1 && avg_col> 200) {обнаруженный_digit.digit = pgm_read_byte (& (таблица [строка] [столбец])); Detected_digit.index = pgm_read_byte (& (char_indexes [строка] [столбец])); } еще {Detected_digit.digit = 0; }}

void drawSprite (byte * sprite) {

// Маска используется для получения бита столбца из байтовой маски строки спрайта = B10000000; for (int iy = 0; iy <8; iy ++) {for (int ix = 0; ix <8; ix ++) {lmd.setPixel (7 - iy, ix, (bool) (спрайт [iy] & маска));

// сдвигаем маску на один пиксель вправо

маска = маска >> 1; }

// сбросить маску столбца

маска = B10000000; }}

void setup () {

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

Serial.begin (115200);

lmd.setEnabled (правда); lmd.setIntensity (2); lmd.clear (); lmd.display ();

Detected_digit.digit = 0;

}

беззнаковый длинный z = 0;

void loop () {

в то время как (ADCSRA & _BV (ADIE)); // Дождемся завершения выборки аудио goertzel (samples, Spectrum); detect_digit (спектр);

if (Detected_digit.digit! = 0) {

drawSprite (шрифт [detect_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 ((целое) обнаруженная цифра. Цифра); } z ++;

samplePos = 0;

ADCSRA | = _BV (ADIE); // Возобновляем прерывание выборки

}

ISR (ADC_vect) {

uint16_t sample = ADC;

образцы [samplePos ++] = sample - 400;

если (samplePos> = N) {ADCSRA & = ~ _BV (ADIE); // Буфер заполнен, прерывание отключено}}

Шаг 3: схемы

Схемы
Схемы

Необходимо выполнить следующие подключения:

Микрофон к Arduino

Выход -> A0

Vcc -> 3.3 В Gnd -> Gnd

Важно подключить AREF к 3,3 В

Отображение в Arduino

Vcc -> 5 В

Земля -> Земля DIN -> D11 CLK -> D13 CS -> D9

Шаг 4: Заключение

Что здесь можно улучшить? Я использовал N = 256 отсчетов на частоте 9615 Гц, которая имеет некоторую утечку спектра, если N = 205 и частота 8000 Гц, то желаемые частоты совпадают с сеткой дискретизации. Для этого следует использовать АЦП в режиме переполнения таймера.

Рекомендуемые: