Звуковая карта USB на STM32. Часть 1: Используем I2S-кодек
С момента публикации материала о реализации составного устройства USB на STM32 прошло полтора года. Данная статья в двух частях будет своеобразным отчётом о проделанной за это время работе.
Напомню, что описанное в предыдущей публикации решение состоит из двухканального звукового устройства USB и виртуального COM-порта. Разрабатывалось составное устройство USB для применения в составе любительской SDR-радиостанции.
Далее речь пойдёт об аппаратно-программной доработке двухканального звукового устройства USB в полноценную звуковую карту USB.
В предыдущих выпусках мы подробно разобрали, как организовать составное устройство USB на STM32 в среде разработки STM32CubeIDE. Это уместилось в четыре публикации:
Затем последовала публикация про использование виртуального COM-порта для организации CAT-интерфейса любительской радиостанции — CAT-интерфейс для трансивера «Радио-76».
▍ Очень краткая вводная часть
За полтора года проект претерпел изменения. В целях удешевления аппаратной части достаточно дорогой MCUSTM32F446ZET6 был заменён более дешёвым STM32F411CEU6. Многие знают этот микроконтроллер по применению в составе популярной отладочной платы Black Pill.
Сначала в описываемом решении использовалась Black Pill. Именно её хорошо видно на КДПВ публикации про CAT-интерфейс.
Дальнейшее развитие аппаратной части было обусловлено тем фактом, что из частоты 25 МГц кварцевого резонатора на плате Black Pill получить частоту 48 МГц для интерфейса USB достаточно легко, сложно из неё получить сигнал MCLK с частотой ровно 12 288 кГц для качественного тактирования шины I2S.
Решение лежало буквально на поверхности: проект радиостанции изначально подразумевал наличие синтезатора частоты. Таким образом, тактовый сигнал MCU и стал формироваться на одном из выходов синтезатора Si5351a. Тактовой частотой была выбрана частота 24 576 кГц, но об этом позже.
Чтобы не разводить «сопли» и «гадюшник», для опытного образца радиостанции была разработана печатная плата размером 100×60 мм. На плате размещаются и звуковая плата, и контроллер, и дисплей, и локальные органы управления, и малосигнальная часть радиочастотного тракта разрабатываемой любительской SDR-радиостанции.
На плате, показанной на КДПВ, собрана только аппаратная часть описываемого в публикации решения, остальные компоненты на плату не установлены, чтобы не отвлекать внимание.
Программное обеспечение описываемого решения размещено в репозитории.
Необходимо отметить, что решение с STM32F411CEU6 правильно работает только с версией STM32Cube FW_F4 V1.26.2. На более ранних версиях не работают прерывания от DMA, на более поздних версиях изменена структура устройства USB.
Чтобы разработанное решение продолжало работать как составное устройство USB, после каждого запуска генерации кода из проекта STM32CubeMX необходимо изменять три строчки дескриптора, как указано в разделе »Неочевидный нюанс 2» публикации Составное устройство USB на STM32. Часть 4: Два-в-одном.
▍ Аппаратная обвязка MCU
В описываемом решении MCU «обвязан» синтезатором частоты Si5351A-B-GT и кодеком TLV320AIC3104IRHBT с интерфейсом I2S. Всё включено по типовым схемам без каких-либо особенностей:
Кодек TLV320AIC3104IRHBT был выбран по ряду причин:
- он недорогой,
- он имеет по два дифференциальных входа и выхода, что очень удобно для обработки квадратурных сигналов (I, Q) радиотракта,
- у него есть микрофонный вход с определением подключения микрофона и нажатия на кнопку гарнитуры,
- у него есть «усиленный» выход для стереотелефонов.
И синтезатор, и кодек управляются по шине I2C. В проекте это интерфейс I2C1. Кодек обменивается данными по интерфейсу I2S3. Настройки интерфейса приведены на экране ниже:
Вывод GPIO с меткой I2S3_RES используется для аппаратного сброса микросхемы кодека в процессе инициализации.
Обмен данными с шиной I2S3 производится по каналам DMA. Оба канала имеют идентичные настройки как на экране ниже:
Хотелось бы заострить внимание на том, что в каналах DMA используются циклические буферы, обслуживаемые по прерываниям.
▍ Формирование сигнала тактирования
На экране с настройками интерфейса I2S3 сразу бросается в глаза отсутствие ошибки тактовой частоты. Об этом и пойдёт речь в этом разделе.
Все устройства в описываемом решении тактируются сигналами, сформированными от одного единственного опорного генератора. На схеме в этом качестве присутствует TCXO FOX924B. Его можно заменить на любой менее дорогой кварцевый генератор с частотой 25 000 кГц, выходом CMOS и напряжением питания 3.3 В.
Используемый в решении синтезатор Si5351a при подключении к нему кварцевого резонатора с частотой 25 или 27 МГц тоже может работать в качестве опорного генератора. Решений много. Главное, чтобы на MCU с выхода синтезатора поступал тактовый сигнал частотой ровно 24 576 кГц.
При частоте HSE 24 576 кГц с достаточной точностью формируется и тактовая частота интерфейса USB, и тактовая частота интерфейса I2S. Настойки тактирования показаны на рисунке ниже:
Решение имеет «подводный камень». До того, как на MCU будет подан сигнал с выхода синтезатора, синтезатор надо как-то настроить.
Это сделать несложно, если знать тот факт, что сразу после включения MCU работает от внутреннего источника HSI RC. Сигнал тактовой частоты 16 МГц от него подаётся на ядро и шины периферии, следовательно, и на интерфейс I2C.
Для доступа к управлению синтезатором макросом включаем тактирование порта B, к выводам которого подключён I2C. Затем инициализируем I2C запуском функции MX_I2C1_Init (), и только затем запускаем функцию инициализации Si5351:
/* USER CODE BEGIN Init */
#if (SI5351)
__HAL_RCC_GPIOB_CLK_ENABLE ();
MX_I2C1_Init ();
DSP_Set_HSE (); /* Set HSE = 24.576 MHz */
#endif /* SI5351 */
/* USER CODE END Init */
Ключ SI5351 устанавливается в файле main.h. Если ключ не установлен, считается, что тактирование MCU производится от какого-либо внешнего независимого источника сигнала с частотой 24 576 кГц.
Для точной установки частоты опорного генератора синтезатор генерирует на двух остальных выходах сигнал с частотой 9 997 кГц и фазовым сдвигом 90° относительно друг друга.
Записать точное значение частоты опорного генератора нужно в файл si5351a.h. Процедура определения и установки частоты опорного генератора подробно описана в публикации Радиолюбительские измерения: когда нет частотомера.
На рисунке ниже показан приём на частоте 9 996 кГц сигналов точного времени радиостанции RWM и сигналов с частотой 9 997 кГц с выхода синтезатора после калибровки:
▍ Синхронизация потоков
Как бы точно ни были выставлены частоты, потоки данных от разных источников потребуют синхронизации. Синхронизация в проекте осуществляется обработкой взаиморасположения указателей чтения и записи буферов DSP.
Поскольку частота MCLK выставлена предельно точно, синхронизация требуется нечасто. Производится она следующим образом: если запись опережает по скорости чтение, указатель записи смещается по буферу на два отсчёта назад, а если запись от чтения отстаёт, указатель записи смещается на два отсчёта вперёд.
Реализацию такого механизма синхронизации можно посмотреть в файле dsp_if.c в теле функций DSP_Out_Buff_Write () и DSP_In_Buff_Write (). Размер буферов DSP должен быть как минимум в четыре раза больше пакета данных, которое звуковое устройство получает по интерфейсу USB. В данном проекте ёмкость буфера DSP составляет восемь таких пакетов.
▍ Тестируем результат
Для проверки полученной звуковой карты USB воспользуемся бесплатной версией программы RMAA.
Сначала приведём проект в состояние, описанное в части 4 публикации про составное устройство USB. Для этого нужно сбросить ключ DSP в файле main.h:
/* USER CODE BEGIN Private defines */
/* SI5351 = 1 if si5351 is used */
#define SI5351 1
/* DSP = 0 if DSP buff is bypassed */
#define DSP 0
/* We may set CODEC and EMBED_ADC if DSP = 1 only */
#if (DSP)
/* ADC = 1 if used Built-In ADC */
#define EMBED_ADC 0
/* CODEC = 0 if codec is dumb */
#define CODEC 1
#endif /* DSP */
/* USER CODE END Private defines */
Поток данных, поступающих на звуковую карту по USB, теперь передаётся назад без обработки. Проводим тестирование, как указано в руководстве пользователя программы RMAA. Результаты сохраняем в файл.
Затем устанавливаем ключ DSP и подключаем линейный вход звуковой карты USB к линейному выходу. Проводим тестирование, сохраняем результаты и сравниваем их с результатами предыдущего тестирования:
Наблюдаем неплохое соотношение сигнал/шум -89.5 дБ и неплохой динамический диапазон 89.3 дБ. Это более чем хорошо для создания недорогой любительской SDR-радиостанции.
▍ Удешевление недорогого
Второй части этой публикации могло бы и не быть, если бы не было проекта QCX-SSB радиолюбителя Guido (PE1NNZ) из Нидерландов.
Ничто до этого не вызывало у меня в профессиональном плане настолько смешанных чувств: создать DSP для обработки сигналов SSB-трансивера на Arduino Uno!
Позвольте, но ведь STM32F411CEU6 это…
…продолжение следует
При использовании кодека и синтезатора частоты в составе звуковой платы USB получен неплохой результат. На основе такой звуковой платы и организованного через виртуальный COM-порт CAT-интерфейса уже собран и испытывается малосигнальный тракт любительской SDR-радиостанции.
В следующей части публикации будет рассказ о попытке использовать встроенный в STM32F411CEU6 АЦП в качестве линейного входа звуковой карты USB.
Оставайтесь на связи!
73! de RD9F
Telegram-канал с полезностями и уютный чат