- PVSM.RU - https://www.pvsm.ru -
В этой статье я продолжу [1] воплощать свое вдохновение лабораторной работой №3 уже в железе. Речь пойдет о детектировании цифры по звуку в тоновом режиме набора на Arduino с помощью алгоритма Герцеля.
Для реализации задуманного я использовал Arduino UNO, электретный микрофон (adafruit [2]) и дисплей 8х8 с драйвером MAX7219.
Перед тем как браться за реализацию, ответим на вопрос — хватит ли нам производительности Arduino UNO?
Тактовая частота: 16МГц
Один цикл дискретизации занимает 13 тактов
Значение прескейлера, обеспечивающего наибольшую точность: 128
Получается 16МГц / 13 / 128 ~ 9615Гц — искомая частота дискретизации, значит, можно работать с частотами до 4.8кГц.
Есть несколько режимов работы АЦП, ниже приведены наиболее интересные (полный список в datasheet [3] по ключевому слову ADCSRB)
Код настройки АЦП, для простоты я использовал free-run mode.
ADMUX = 0; // Channel sel, right-adj, use AREF pin
ADCSRA = _BV(ADEN) | // ADC enable
_BV(ADSC) | // ADC start
_BV(ADATE) | // Auto trigger
_BV(ADIE) | // Interrupt enable
_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
ADCSRB = 0; // Free-run mode
DIDR0 = _BV(0); // Turn off digital input for ADC pin
TIMSK0 = 0; // Timer0 off
Как только наберется полный массив сэмплов, выключаем прерывание по АЦП и вычисляем амплитуды спектра с помощью алгоритма Герцеля. Не буду соперничать в описании алгоритма с этим исчерпывающим ресурсом [4], но приведу свою реализацию:
void goertzel(uint8_t *samples, float *spectrum) {
float v_0, v_1, v_2;
float re, im, amp;
for (uint8_t k = 0; k < IX_LEN; k++) {
float cos = pgm_read_float(&(cos_t[k]));
float sin = pgm_read_float(&(sin_t[k]));
float a = 2. * cos;
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)(samples[i]) + a * v_1 - v_0;
}
re = cos * v_2 - v_1;
im = sin * v_2;
amp = sqrt(re * re + im * im);
spectrum[k] = amp;
}
}
Синусы и косинусы были предварительно рассчитаны для отсчетов, соответствующих искомым частотам. Полная версия кода находится здесь [5].
Самое главное, что получилось и ресурсов Arduino UNO хватает для простой обработки звука.
Главный урок, который я извлек, что АЦП — штука чувствительная, после успешного распознавания и отправки символа на консоль, я потратил неделю на отладку, чтобы все работало и с дисплеем, потому что соединил землю микрофона и max7219 и все сэмплы сразу превращались в шум.
Можно ли было сделать еще лучше? Да, более правильно было бы подобрать частоту дискретизации и количество сэмплов так, чтобы искомые частоты совпадали с решеткой дискретизации, это бы предотвратило растекание спектра. Такие параметры уже есть f = 8кГц, N = 205, в таком случае надо запускать ADC не в режиме free-run, а timer overflow, и разница была бы очевидна.
Курс [6] подходит к концу, но идей еще много.
Спасибо за внимание.
Автор: zjor
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/301585
Ссылки в тексте:
[1] продолжу: https://habr.com/post/429700/
[2] adafruit: https://www.adafruit.com/product/1063
[3] datasheet: https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf
[4] ресурсом: http://ru.dsplib.org/content/goertzel/goertzel.html
[5] здесь: https://github.com/zjor/dtmf-detector/tree/master/arduino/dtmf-detector
[6] Курс: https://openedu.ru/course/eltech/DSP/
[7] Источник: https://habr.com/post/432498/?utm_source=habrahabr&utm_medium=rss&utm_campaign=432498
Нажмите здесь для печати.