본문 바로가기

[STM32F746G-DISCO] USB Audio 사용하기 (Device Mode)

by rudals.kim 2024. 7. 17. 댓글 개
반응형
아래 테스트는 STM32CubeIDE 1.6.1/STM32Cube_FW_F7_V1.16.1를 사용하여 테스트 되었습니다.


STM32F746G-DISCO 보드에서 USB Device모드를 사용하여 Audio Device Class를 테스트해 보았습니다.

Audio Device Class가 어떤 것인가 하면 이어폰/스피커/헤드폰의 연결이 USB로 되어 있어서 PC의 USB 단자에 꽂으면 오디오를 들을 수 있는 제품이 있습니다. 이러한 오디오 입/출력 기능을 STM32F746G-DISCO 보드를 사용하여 구현할 수 있으며, 이때 사용되는 것이 USB의 Audio Class를 사용하면 가능합니다.

STM32F746G-DISCO 보드의 펌웨어에서 제공되는 AUDIO_Standalone 예제를 참고하여 테스트되었습니다.
(예제 위치 : STM32Cube_FW_F7_V1.16.1/Projects/STM32756G_EVAL/Applications/USB_Device/CDC_Standalone)

STM32CubeIDE에서 새 프로젝트를 생성합니다.
핀 초기화 후 RCC/NVIC/SYS/USART1/SAI2(SAI A)/USB_OTG_FS/USB_DEVICE를 설정하였습니다.

USB_OTG_FS는 Device모드로 설정하였고 Class는 Audio Device Class로 설정하였습니다.

테스트하면서 이상한 점이 하나 있는데 Audio Device Class의 Device Descriptor 설정 시 기본 PID가 22336으로 설정이 되는데 이 PID로는 윈도우 PC에서 장치를 인식하지 못합니다. 예제에 적혀있는 0x5730(22320) 값을 사용해야만 윈도우 PC에서 정상적인 장치로 인식되어졌습니다.

Audio를 위해서는 SAI2가 사용되는데 아래 회로도에 맞춰서 핀 설정을 하였으며 SAI2 -> SAI_A의 모드는 Master with Master Clock Out으로 설정하였습니다.

코드를 생성 후 USB_DEVICE/App/usbd_audio_if.c 을 예제 파일을 참고하여 수정합니다.

static int8_t AUDIO_Init_FS(uint32_t AudioFreq, uint32_t Volume, uint32_t options)
{
  /* USER CODE BEGIN 0 */
  if(BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_AUTO, Volume, AudioFreq) == AUDIO_OK)
     printf("[Info] BSP_AUDIO initailized.\r\n");
  else
    printf("[Error] BSP_AUDIO failed to initailized. @line:%d\r\n", __LINE__);

  /* Update the Audio frame slot configuration to match the PCM standard instead of TDM */
  BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02);
  return (USBD_OK);
  /* USER CODE END 0 */
}

/**
  * @brief  De-Initializes the AUDIO media low layer
  * @param  options: Reserved for future use
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_DeInit_FS(uint32_t options)
{
  /* USER CODE BEGIN 1 */
  BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW);
  return (USBD_OK);
  /* USER CODE END 1 */
}

/**
  * @brief  Handles AUDIO command.
  * @param  pbuf: Pointer to buffer of data to be sent
  * @param  size: Number of data to be sent (in bytes)
  * @param  cmd: Command opcode
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_AudioCmd_FS(uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
  /* USER CODE BEGIN 2 */
  switch(cmd)
  {
    case AUDIO_CMD_START:
      BSP_AUDIO_OUT_Play((uint16_t *)pbuf, 2*size);
    break;

    case AUDIO_CMD_PLAY:
      BSP_AUDIO_OUT_ChangeBuffer((uint16_t *)pbuf, 2*size);
    break;
  }
  UNUSED(pbuf);
  UNUSED(size);
  UNUSED(cmd);
  return (USBD_OK);
  /* USER CODE END 2 */
}

/**
  * @brief  Controls AUDIO Volume.
  * @param  vol: volume level (0..100)
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_VolumeCtl_FS(uint8_t vol)
{
  /* USER CODE BEGIN 3 */
  BSP_AUDIO_OUT_SetVolume(vol);
  return (USBD_OK);
  /* USER CODE END 3 */
}

/**
  * @brief  Controls AUDIO Mute.
  * @param  cmd: command opcode
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_MuteCtl_FS(uint8_t cmd)
{
  /* USER CODE BEGIN 4 */
  BSP_AUDIO_OUT_SetMute(cmd);
  return (USBD_OK);
  /* USER CODE END 4 */
}

/**
  * @brief  AUDIO_PeriodicT_FS
  * @param  cmd: Command opcode
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_PeriodicTC_FS(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
  /* USER CODE BEGIN 5 */
  UNUSED(pbuf);
  UNUSED(size);
  UNUSED(cmd);
  return (USBD_OK);
  /* USER CODE END 5 */
}

/**
  * @brief  Gets AUDIO State.
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_GetState_FS(void)
{
  /* USER CODE BEGIN 6 */
  return (USBD_OK);
  /* USER CODE END 6 */
}

/**
  * @brief  Manages the DMA full transfer complete event.
  * @retval None
  */
void TransferComplete_CallBack_FS(void)
{
  /* USER CODE BEGIN 7 */
  USBD_AUDIO_Sync(&hUsbDeviceFS, AUDIO_OFFSET_FULL);
  /* USER CODE END 7 */
}

/**
  * @brief  Manages the DMA Half transfer complete event.
  * @retval None
  */
void HalfTransfer_CallBack_FS(void)
{
  /* USER CODE BEGIN 8 */
  USBD_AUDIO_Sync(&hUsbDeviceFS, AUDIO_OFFSET_HALF);
  /* USER CODE END 8 */
}


Core/Src/stm32f7xx_it.c에 아래 내용을 추가합니다.

extern SAI_HandleTypeDef haudio_out_sai;
void DMA2_Stream4_IRQHandler(void)
{
  HAL_DMA_IRQHandler(haudio_out_sai.hdmatx);
}


예제용 파일을 참고하여 코드를 추가 및 변경하여 적용시킵니다.
적용이 다 되었으면 빌드를 한 후 보드에 다운로드를 합니다.
스피커를 보드에 연결한 후 Micro USB 케이블을 CN13(USB_FS) 커넥터에 꽂습니다.

그러면 아래 그림처럼 '사운드, 비디오 및 게임 컨트롤러'에 'STM32 Audio Class' 장치가 추가됩니다.

윈도우 PC에서 비디오/음악 파일을 재생을 하면 PC의 Audio 출력이 USB를 거쳐 STM32F746G-DISCO 보드의 스피커로 출력됩니다.

(유튜브에 동영상을 업로드하니 음악 관련 저작권 문제가 있다고 나오네요. 한 10초 분량의 가요를 녹화했는데 정확한 곡명과 가수를 찾아서 저작권 문제가 있다고 알려줍니다. 그래서 다시 무료 음악을 다운로드 받아 재녹화를 해서 업로드하였습니다.)

 

반응형

댓글