В этой статье (перевод [1]) будет показано, как воспроизвести звук с помощью микроконтроллера STM32F103. В статье [2] рассматривалось немного теории о том, как генерируются аналоговые сигналы с помощью ШИМ, и теперь посмотрим, как это можно реализовать на практике.
В основном нужно выполнить следующие 3 шага:
• Настроить таймер для генерации сигнала ШИМ (PWM). • Настроить контроллер DMA для копирования выборок звука. • Сконфигурировать второй таймер, чтобы он периодически запускал DMA.
В этом примере для генерации сигнала PWM используется таймер 2. Он тактируется на максимально возможной частоте 72 МГц (главная частота). В статье [2] было показано, что существует компромисс между точностью (разрешающей способностью) ШИМ и частотой ШИМ. Обычно звук записывается с разрешающей способностью 16 бит (качество CD), однако максимальная частота ШИМ окажется слишком низкой: 72 МГц/(2^16) = 1098 Гц. Поэтому для синтеза звука автором была выбрана разрешающая способность 8 бит, которая дает частоту ШИМ 72 МГц/(2^8)=281250 Гц (в режиме обычного ШИМ, или Fast PWM). Это очень хороший выбор частоты ШИМ, потому частота помех оказывается намного выше максимальной слышимой человеком частоты звука. Другое достоинство выбранного метода ШИМ состоит в том, что 8-битные выборки звука занимают 1 байт, что упрощает программирование. Если бы наши выборки были бы 10-битные, то их приходилось бы хранить в 2 байтах, что в 2 раза увеличило бы затраты памяти.
После настройки таймера для генерации ШИМ конфигурируется контроллер DMA. Аббревиатура DMA обозначает direct memory access, т. е. прямой доступ к памяти (этому термину соответствует полузабытая русская аббревиатура ПДП). Контроллер DMA является периферийным устройством микроконтроллера STM32. Его основное назначение - копировать данные из одного места в адресном пространстве в другое место. Большое достоинство DMA состоит в том, что процесс копирования, будучи запущенным, уже не зависит от вычислительного ядра (CPU) микроконтроллера, освобождая его процессорное время для других задач. Контроллер DMA работает следующим образом: мы указываем ему, откуда брать данные (адрес источника, source), и куда их нужно копировать (адрес места назначения, destination). Затем мы должны сконфигурировать, что будет запускать передачу данных (момент, когда начнется процесс копирования), т. е. триггер DMA.
В нашем случае источником, откуда будут выбираться данные, служит массив, где хранятся 8-битные выборки звука. Местом назначения служит регистр таймера 2, который содержит значения сравнения для генерируемого ШИМ (PWM compare value). Это значение определяет скважность формируемых периодов ШИМ. Транзакция DMA должна запускаться с той же частотой, с которой был записан звук (sample rate). В этом примере используется стандартная частота 44.1 кГц. Таким образом, каждые 1/441000 секунды новая выборка выводится с помощью ШИМ, получается PWM DAC (ЦАП на основе ШИМ).
Исходный код проекта опубликован на GitHub (см. [3, 5], каталог PWM_DAC_SOUND). В нем не использовались библиотеки HAL, потому что автор ставил перед собой задачу научиться работать с ресурсами микроконтроллера STM32 напрямую, просто путем изучения документации на микроконтроллер. Приятная возможность - контроллер DMA и таймер сами проигрывают звук, и при этом CPU никак не используется! Это видно в исходном коде, главный бесконечный цикл функции main совершенно пустой.
/* MAIN LOOP */
while(1){
// do nothing
}
Для проверки были созданы 2 звуковых файла. В одном была записана простая таблица (lookup-table) для синтеза синусоиды 5 кГц. Чтобы проверить, как будет воспроизводиться реальный голос, через микрофон была записана фраза "Hello test". Для преобразования звуковых данных использовался хорошо известный, бесплатный звуковой редактор Audacity, в нем звук был экспортирован в файл как сырые данные "8bit unsigned RAW". Это просто двоичный файл, где находятся 8-битные выборки оцифрованного звука. Чтобы запрограммировать этот файл в микроконтроллер, была создана простая программа на C++, которая преобразовывала двоичные данные файла в удобный для использования заголовочный файл языка C (см. [4, 5], каталог 8bit_raw_audio_2_c_header_converter). Остается только подключить этот заголовочный файл к проекту, и использовать эти данные в качестве источника данных звука.
Для фильтрации импульсов ШИМ на отладочной плате собрана простая схема двухкаскадного ФНЧ. Операционный усилитель MCP6002 удобен тем, что он поддерживает прохождение сигнала rail-to-rail со входа на выход даже при питании от низких напряжений (3.3V, используемые на плате микроконтроллера STM32). На выход были подключены обычные стереонаушники (правый и левый канал соединены последовательно для увеличения сопротивления нагрузки).
На картинке ниже показан скриншот синтезированного сигнала синуса 5 кГц (экран программы осциллографа PicoScope 2204A). Красная линия здесь показывает сигнал ШИМ, а синяя выходной сигнал после ФНЧ.
Результат очень хороший, даже если учесть, что использовалось всего 9 выборок на период синтезируемого сигнала. При исследовании спектра сигнала хорошо видны гармоники ШИМ:
Основной пик находится на частоте 5 кГц, это синусоидальный полезный сигнал. Следующий по уровню пик находится на частоте 280 кГц, это частота ШИМ. Следующий пик 560 кГц, удвоенная частота ШИМ. Эти гармоники не представляют никакой проблемы, потому что их частота находится далеко выше порога слышимости, или даже частоты, которую могут воспроизводить обычные акустические системы.
С воспроизведением голоса фразы "Hello test" все получилось лучше, чем можно было ожидать от качества 8 бит. Конечно же, звук не HiFi, однако чистый и разборчивый. Результат получился весьма обнадеживающий, однако огорчает малый объем памяти микроконтроллера, куда не получится записать звук длиной больше нескольких секунд, 64 килобайта FLASH-памяти программ STM32F103 быстро переполняются. По этой причине было решено попробовать подключить к микроконтроллеру внешнюю FLASH-память [6].
[Ссылки]
1. Create audio signals with PWM site:marcelmg.github.io. 2. Сравнение режимов ШИМ - обычного и центрированного. 3. MarcelMG / STM32F103C8T6 site:github.com. 4. MarcelMG / Miscellaneous site:github.com. 5. 200305Left-vs-Centered-PWM.zip - исходный код. 6. W25Q64JV SPI flash memory chip as external memory site:github.com. |