Хабрахабр

Определение цифры на слух: реализация на Arduino

В этой статье я продолжу воплощать свое вдохновение лабораторной работой №3 уже в железе. Речь пойдет о детектировании цифры по звуку в тоновом режиме набора на Arduino с помощью алгоритма Герцеля.
Для реализации задуманного я использовал Arduino UNO, электретный микрофон (adafruit) и дисплей 8х8 с драйвером MAX7219.

План действий

  • Дискретизировать достаточное количество отсчетов (с помощью программы из предыдущей статьи я убедился, что 256 достаточно).
  • Найти амплитуды АЧХ, соответствующие искомым частотам, кодирующим символы.
  • Два максимальных значения амплитуды дадут индексы строки и столбца искомого символа, так, например, выглядит цифра 3.
    image

Реализация

Перед тем как браться за реализацию, ответим на вопрос — хватит ли нам производительности Arduino UNO?

Тактовая частота: 16МГц
Один цикл дискретизации занимает 13 тактов
Значение прескейлера, обеспечивающего наибольшую точность: 128

8кГц. Получается 16МГц / 13 / 128 ~ 9615Гц — искомая частота дискретизации, значит, можно работать с частотами до 4.

Настройка АЦП

Есть несколько режимов работы АЦП, ниже приведены наиболее интересные (полный список в datasheet по ключевому слову ADCSRB)

  • single read — с помощью метода analogRead(), но это блокирующий вызов, который занимает 100µs, и используя его невозможно обеспечить постоянную частоту дискретизации
  • free-run mode — в этом режиме следующий цикл дискретизации начинается сразу после окончания предыдущего и обеспечивается максимальная частота дискретизации
  • timer overflow — дискретизация начинается по переполнению таймера, это позволяет точно настроить частоту дискретизации

Код настройки АЦП, для простоты я использовал 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

Обработка сигнала

Как только наберется полный массив сэмплов, выключаем прерывание по АЦП и вычисляем амплитуды спектра с помощью алгоритма Герцеля. Не буду соперничать в описании алгоритма с этим исчерпывающим ресурсом, но приведу свою реализацию:

void goertzel(uint8_t *samples, float *spectrum) re = cos * v_2 - v_1; im = sin * v_2; amp = sqrt(re * re + im * im); spectrum[k] = amp; } }

Синусы и косинусы были предварительно рассчитаны для отсчетов, соответствующих искомым частотам. Полная версия кода находится здесь.

Выводы

Самое главное, что получилось и ресурсов Arduino UNO хватает для простой обработки звука.

Главный урок, который я извлек, что АЦП — штука чувствительная, после успешного распознавания и отправки символа на консоль, я потратил неделю на отладку, чтобы все работало и с дисплеем, потому что соединил землю микрофона и max7219 и все сэмплы сразу превращались в шум.

Да, более правильно было бы подобрать частоту дискретизации и количество сэмплов так, чтобы искомые частоты совпадали с решеткой дискретизации, это бы предотвратило растекание спектра. Можно ли было сделать еще лучше? Такие параметры уже есть f = 8кГц, N = 205, в таком случае надо запускать ADC не в режиме free-run, а timer overflow, и разница была бы очевидна.

Курс подходит к концу, но идей еще много.
Спасибо за внимание.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть