Детектор Tayloe и цифровая обработка сигналов #6 передаем аудио на ПК через USB Audio Class

Автор: | 12.11.2025

В прошлый раз передавали аудио поток через виртуальный COM порт, и средствами среды Python его анализировали. Но можно же использовать наш STM32H723ZGT6 как аудиокарту, т.е. чтобы ПК, далее по тексту — хост, определял наше устройство как входное аудио устройство USB Audio Class, дело в том, что на хосте в системе уже есть все необходимые драйвера для обработки аудио потока как 2-канала, 16-бит и с частотой дискретизации 48 кГц, как раз, что нам и нужно для обработки IQ аудио потока от SDR. Почему не 96 кГц или 192 кГц? USB на борту STM32H723ZGT6 может работать только в Full Speed (FS) режиме, т.к. имеет только встроенный PHY (произносится «фай», от Physical Layer — физический уровень) — это аналоговая схема внутри кристалла, подключённая к пинам DP/DM, чтобы работать в High Speed (HS) режиме нужно подключать внешний PHY — отдельная микросхема (например, USB3300), подключённая через ULPI или UTMI+.

Приступим… В проекте я использую платку FK723M1-ZGT6

Создадим новый минимальный проект для нашей стмки, включим тактирование от внешнего резонатора

для визуальной отладки понадобится светик размещенный на плате и управляемый пином PG7, включим ему режим GPIO_Output и назначим метку LED_PCB, светик будет моргать когда аудио данные будут отправляться на хост

включаем USB_OTG_HS в FS режиме

включаем режим USB Audio Device Class

далее настроим необходимые клоки

можно увеличить минимальный размер кучи и стека

Собираем проект…

И тут начинается самое интересное, а именно то, что «из коробки» USB Audio Class минимально настроен как выходное аудио устройство, которое на хосте определяется как «Динамик», прошиваем платку, подключаем и проверяем

все как и ожидалось.

Чтобы настроить наше устройство так, чтобы оно определялось как входное аудио устройство на хосте, пришлось погрузиться в изучение USB, для описания как работает USB, можно целую книгу издать! Замечу, что мне, в том числе, помогли разобраться материалы Дмитрия RD9F. Если коротко, то каждое USB аудио устройство подключаемое к хосту описывается набором дескрипторов, подробно расписывать не буду, проект в конце записи, а то много буковок получится.

В дескрипторах я поменял устройство на входное, двухканальное (стерео), 16 бит и 48 кГц, тип устройства я выбрал как Radio Receiver с кодом 0x0710, про типы устройств и коды есть тут. Все дескрипторы и определения конечных точек описываются в файлах usbd_audio.h и usbd_audio.c, текстовые определения класса в файле usbd_desc.c.

Меняем, прошиваем, подключаем, смотрим что получилось

Устройство определилось, а входного устройства, так называемой конечной точки нет, не порядок….. 

А все просто, хост по умолчанию отключает, при первом подключении, некоторые типы устройств (спасибо разработчику SDR техники Геннадию Завидовскому за подсказку!), смотрим

Стоит отметить, что хост, а именно Windows кэширует у себя информацию о подключенных устройствах, если в коде постоянно меняем дескрипторы, то устройство может и не верно определиться после очередной прошивке, чтобы этого избежать, достаточно удалить устройство аудио класса в диспетчере устройств и переподключить его заново, тогда хост определит как новое устройство с новыми дескрипторами, даже если мы в коде не будем менять идентификаторы VID/PID. Включаем наш «Радиоприемник» и смотрим его свойства

Все как надо, два канала, 16 бит и 48 кГц!

Далее для проверки научим наше устройство передавать аудио поток в виде синусоидального сигнала частотой 1 кГц. Для этой цели сделал библиотечку с тремя видами описания синуса:

  • синус в два канала 1 кГц, 16 бит, 48 кГц, уровнем 0 dB
  • синус в два канала 1 кГц, 16 бит, 48 кГц, уровнем -73 dB (S9)
  • IQ сигнал 1 кГц, 16 бит, 48 кГц, уровнем 0 dB

вся эта библиотека располагается в двух файлах sine_1kHz_48k_stereo.h и sine_1kHz_48k_stereo.c.

Как происходит передача аудио на хост? Хост определив, что к нему подключили через USB входное аудио устройство, и если какая то программа хочет получить поток входных аудио данных с этого устройства, начинает забрасывать устройство запросами, мол давай мне поток данных, устройство в свою очередь должно правильно обработать поступивший запрос и выдать порцию данных, если устройство будет молчать, то хост отключит запросы, для этого:

  • в функции static uint8_t USBD_AUDIO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) мы инициализируем нашу конечную точку;
  • в функции static uint8_t USBD_AUDIO_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) деинициализируем конечную точку при отключении устройства от порта USB;
  • в функции static uint8_t USBD_AUDIO_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) добавляем обработку запросов от хоста;
  • в функции static uint8_t USBD_AUDIO_SOF(USBD_HandleTypeDef *pdev) выполняем отправку пустого пакета для синхронизации;
  • в функции static uint8_t USBD_AUDIO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) отправляем наш синус на хост.

Меняем, прошиваем, подключаем и проверяем, первый вариант — синус в оба канала уровнем 0 dB

второй вариант — синус в оба канала, уровнем -73 dB

третий вариант — IQ сигнал, проверим на HDSDR, для этого выберем в нем наше аудиоустройство

как видим, чистый сигнал, без зеркалки, что и требовалось!

Далее будем подключать АЦП для оцифровки IQ сигнала…

Проект под STM32CibeIDE v1.19.0

Продолжение следует…

73!

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

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