STM32 и USB. Audio device class.

Всем доброго времени суток!

В сегодняшней статье мы еще раз вернемся к теме USB в микроконтроллерах STM32 (статьи, посвященные этому вопросу) и реализуем поддержку Audio device class. То есть по сути мы должны в результате нашей работы получить внешнюю звуковую карту =)

Звуковая карта на STM32

Для работы со звуком будем использовать отладочную плату STM32F4-Discovery, поскольку на ней уже установлено все необходимое. Мы попробуем запустить плату на воспроизведение звука, поэтому нам пригодится разъем 3.5 мм для наушников, смонтированный на Discovery. Ну и, конечно, же нельзя обойти вниманием цифровую часть работы с аудио, а именно микросхему CS43L22. Ее мы уже обсуждали на нашем сайте в одной из предыдущих статей, даже писали небольшой пример для воспроизведения звука — ссылка. Таким образом, с электронной частью данной задачи все понятно, давайте переходить в программным аспектам.

Эта статья будет построена не совсем традиционным образом для нашего сайта, по сути я просто выложу готовый и проверенный проект (проект для Keil, у меня лично Keil v4.70), и мы обсудим некоторые моменты, поясняющие работу этого проекта. Ну и, конечно же, в конце статьи попробуем подключить плату к ПК и посмотрим, что из этого получится 😉

Итак, вот упомянутый проект — USB_AudioClassProject

Давайте откроем проект и посмотрим, что там есть.

Во-первых, это, конечно, библиотеки для работы с USB от ST. Я поудалял оттуда лишнее, поэтому они не совсем в первозданном виде =)

Второй момент, на котором обязательно надо остановиться — это файлы stm32f4_discovery.c и  stm32f4_discovery_audio_codec.c, взятые на просторах интернета (эти мини-библиотеки также написали программисты ST, так что, в принципе, их можно считать официальными). В этих файлах, как уже понятно из их названия, реализована инициализация всех элементов платы STM32F4-Discovery, и, кроме того, приведены функции для работы с ними. Возьмем, например, светодиоды. Для них есть следующие функции:

void STM_EVAL_LEDInit(Led_TypeDef Led);
void STM_EVAL_LEDOn(Led_TypeDef Led);
void STM_EVAL_LEDOff(Led_TypeDef Led);
void STM_EVAL_LEDToggle(Led_TypeDef Led);

Получается, что весь спектр операций, которые поддерживают светодиоды, мы можем осуществить при помощи этих инструментов ;). На самом деле в файле stm32f4_discovery.c разобрана работа только с «базовыми» элементами платы — светодиодами и кнопками. Если мы хотим подключить к примеру акселерометр, то нужные файлы можно найти на сайте ST. В данной статье нас интересует работа со «звуковым» чипом CS43L22, поэтому мы включаем в наш проект файл stm32f4_discovery_audio_codec.c. И в этом файле есть все, что касается работы с этой микросхемой — от инициализации необходимых портов ввода-вывода и используемой периферии микроконтроллера до функций, непосредственно работающих с CS43L22 и соответствующих обработчиков прерываний:

/* High Layer codec functions */
static uint32_t Codec_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq);
static uint32_t Codec_DeInit(void);
static uint32_t Codec_Play(void);
static uint32_t Codec_PauseResume(uint32_t Cmd);
static uint32_t Codec_Stop(uint32_t Cmd);
static uint32_t Codec_VolumeCtrl(uint8_t Volume);
static uint32_t Codec_Mute(uint32_t Cmd);
/* Low layer codec functions */
static void     Codec_CtrlInterface_Init(void);
static void     Codec_CtrlInterface_DeInit(void);
static void     Codec_AudioInterface_Init(uint32_t AudioFreq);
static void     Codec_AudioInterface_DeInit(void);
static void     Codec_Reset(void);
static uint32_t Codec_WriteRegister(uint8_t RegisterAddr, uint8_t RegisterValue);
static uint32_t Codec_ReadRegister(uint8_t RegisterAddr);
static void     Codec_GPIO_Init(void);
static void     Codec_GPIO_DeInit(void);
static void     Delay(__IO uint32_t nCount);

С этим вопросом разобрались, давайте посмотрим как реализована поддержка Audio Device Class, и для этого откроем файл usbd_audio_out_if.c. Суть тут абсолютно такая же как и при работе с другими режимами USB в STM32 — есть ряд функций, которые нужно реализовать в соответствии с тем оборудованием, которое будет использоваться. В нашем случае это отладочная плата STM32F4-Discovery и CS43L22. Для этой связки, как мы уже обсудили, у нас есть готовый набор необходимых функций и по сути нам остается только правильным образом использовать их в данном файле. Рассмотрим, к примеру, функцию инициализации:

/**
* @brief  Init
*         Initialize and configures all required resources for audio play function.
* @param  AudioFreq: Statrtup audio frequency. 
* @param  Volume: Startup volume to be set.
* @param  options: specific options passed to low layer function.
* @retval AUDIO_OK if all operations succeed, AUDIO_FAIL else.
*/
static uint8_t  Init         (uint32_t AudioFreq, 
                              uint32_t Volume, 
                              uint32_t options)
{
  static uint32_t Initialized = 0;
 
  /* Check if the low layer has already been initialized */
  if (Initialized == 0)
  {
    /* Call low layer function */
    if (EVAL_AUDIO_Init(OUTPUT_DEVICE_HEADPHONE, Volume, AudioFreq) != 0)
    {
      AudioState = AUDIO_STATE_ERROR;
      return AUDIO_FAIL;
    }
 
    /* Set the Initialization flag to prevent reinitializing the interface again */
    Initialized = 1;
  }
 
  /* Update the Audio state machine */
  AudioState = AUDIO_STATE_ACTIVE;
 
  return AUDIO_OK;
}

Как видите, в этой функции мы вызвали нужную нам функцию инициализации EVAL_AUDIO_Init() из файла stm32f4_discovery_audio_codec.c. Аналогичным образом все сделано и для остальных функций. Для примера посмотрим, как реализована обработка команды остановки воспроизведения в AudioCmd(), вот часть этой функции:

/* Process the STOP command ----------------------------*/
case AUDIO_CMD_STOP:
  if (AudioState != AUDIO_STATE_PLAYING)
  {
    /* Unsupported command */
    return AUDIO_FAIL;
  }
  else if (EVAL_AUDIO_Stop(CODEC_PDWN_SW) != 0)
  {
    AudioState = AUDIO_STATE_ERROR;
    return AUDIO_FAIL;
  }
  else
  {
    AudioState = AUDIO_STATE_STOPPED;
    return AUDIO_OK;
  }

Итак, мы получили команду остановки — сразу же проверяем, активно ли воспроизведение (ведь чтобы его остановить оно должно по крайней мере быть запущено ;)):

if (AudioState != AUDIO_STATE_PLAYING)

В случае, если все в порядке и воспроизведение активно — вызываем функцию остановки:

EVAL_AUDIO_Stop(CODEC_PDWN_SW)

В случае успеха операции меняем текущее состояние на AUDIO_STATE_STOPPED, если на каком-то из этих этапов возникла ошибка, то сигнализируем об этом:

AudioState = AUDIO_STATE_ERROR;
return AUDIO_FAIL;

Все организовано крайне логично ) Давайте соберем проект и проверим его на практике, запрограммировав микроконтроллер.

В диспетчере устройств у нас появляется новое звуковое устройство:

Аудио на STM32

Осталось совсем немного — идем в панель управления и в разделе Звук активируем появившееся устройство. После этого открываем какой-нибудь аудио-файл (не забыв подключить наушники к Discovery =)) и видим (а точнее слышим), что наша программа работает правильно 😉

На сегодня на этом заканчиваем, до скорой встречи в новых статьях!

Понравилась статья? Поделись с друзьями!

STM32 и USB. Audio device class.: 15 комментариев
  1. Спасибо. А когда появится урок по реализации usb audio через CubeMx? Очень надо именно этот класс реализовать, не получается, а примеров нигде нет.

    • Я на самом деле через Cube сначала и хотел, но это довольно утомительно оказалось…) В плане USB там такая же суть как и в этом проекте, просто надо отдельно через Cube реализовывать подключение CS43L22 — все интерфейсы, прерывания итд.

  2. Пере-компилировал, заработало на Stm32f401c-disco,
    За статью спасибо, чуть по-раньше бы её, а то уже на готовой «Audio device class» микросхеме PCM2705 собрал.

  3. Здравствуйте, решил проверить на своей плате с чипом stm32f407, проект собрался без ошибок, залился, но как новое устройство не отображается. В чем может быть причина?

  4. А какая функция передаёт полученные аудио данные в i2s? Никак не могу понять где можно посмотреть полученные по аудио биты.

    • Там через ДМА отправляется, соответственно, данные можно посмотреть по адресам, которые в ДМА указываются.

  5. Hi
    many many thanks for this great project and good explanation.
    i have a question why when i compile the project with the Keil V5,16a it dose’t compile and gives me many errors but compiles well in the V4 .
    please do you know what is the problem.
    thank you in advance

  6. Hi
    Thank you for your fast reply
    Ok i understand that there is a big difference between both versions
    but in your opinion is it possible to migrate this project to V5 or i must make a big change in the files to make it work.

      • У меня V5, и там ошибки множественного определения , вроде этой: .\USB_AudioClassProject.axf: Error: L6200E: Symbol SystemCoreClock multiply defined (by system_stm32f4xx_1.o and system_stm32f4xx.o).
        Во всех случаях конфликтуют system_stm32f4xx_1.o и system_stm32f4xx.o. system_stm32f4xx.c я нашел, а вот где system_stm32f4xx_1.с непонятно. Если удалить system_stm32f4xx.c, то проект компилится, но звуков плата не издает, хотя выключает их на ПК

        • 5-ый Кейл насколько я помню сам автоматически включает библиотеки в проект, из-за этого и проблемы видимо.

  7. Спасибо за статью. Скомпилировал, собрал под STM32f4Discovery. Звук есть. Всё работает, но есть лишний шум в виде «тиканья». Не получается от него избавится. Вчём может быть проблема? И ещё вопрос, как можно увеличить разрядность звука?

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

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