[STM32H7] 让 WM8994 通过 STM32H745I-DISCO 开发板上的 SAI 发声

[复制链接]
84|0
柴库柴库 发表于 2025-10-30 17:15 | 显示全部楼层 |阅读模式
在我的项目使用 HAL 库,是用 STM32CubeIDE 制作的。
按照示例中的配置来设置所有内容。
具体来说 ——SAI 的时钟设置为 4.096MHz。对 SAI1 的配置如下(如图所示):

9879569032bc0d0e6b.png 200369032bcade044.png
代码如下
#include "sai.h"

static int16_t* Audio_Buffer = NULL;
static size_t Audio_Buffer_Size = 0;
static AUDIO_Drv_t* Audio_Drv = NULL;
static WM8994_Init_t codec_init;

/**
  * @brief  Register Bus IOs if component ID is OK
  * @retval error status
  */
static int32_t WM8994_Probe()
{
  int32_t ret = BSP_ERROR_NONE;
  WM8994_IO_t              IOCtx;
  static WM8994_Object_t   WM8994Obj;
  uint32_t id;

  /* Configure the audio driver */
  IOCtx.Address     = AUDIO_I2C_ADDRESS;
  IOCtx.Init        = BSP_I2C4_Init;
  IOCtx.DeInit      = BSP_I2C4_DeInit;
  IOCtx.ReadReg     = BSP_I2C4_ReadReg16;
  IOCtx.WriteReg    = BSP_I2C4_WriteReg16;
  IOCtx.GetTick     = BSP_GetTick;

  if(WM8994_RegisterBusIO (&WM8994Obj, &IOCtx) != WM8994_OK)
  {
    ret = BSP_ERROR_BUS_FAILURE;
  }
  else
  {
    /* Reset the codec */
    if(WM8994_Reset(&WM8994Obj) != WM8994_OK)
    {
      ret = BSP_ERROR_COMPONENT_FAILURE;
    }
    else if(WM8994_ReadID(&WM8994Obj, &id) != WM8994_OK)
    {
      ret = BSP_ERROR_COMPONENT_FAILURE;
    }
    else if(id != WM8994_ID)
    {
      ret = BSP_ERROR_UNKNOWN_COMPONENT;
    }
    else
    {
      Audio_Drv = (AUDIO_Drv_t *) &WM8994_Driver;
      Audio_CompObj = &WM8994Obj;
    }
  }
  return ret;
}

/**
* @fn int16_t generate_sine*(uint32_t, double, double, double, size_t*)
* @brief Generates a sine wave.
* @param sampleRate Sample rate in samples per second.
* @param time Duration of the PCM data .
* @param frequency Frequency of the generated wave [Hz].
* @param level Amplitude [dB]. Max 0dB.
* @param size A pointer to the generated data size in bytes.
* @return A pointer to the buffer containing the generated wave.
*/
static int16_t* generate_sine(uint32_t sampleRate, double time, double frequency, double level, size_t* size)
{
  if (sampleRate < 8000 || sampleRate < 2 * frequency || !time || level > 0) return NULL;
  const size_t sampleCount = ceil(sampleRate * time);
  *size = sampleCount * sizeof(int16_t);
  int16_t* buffer = (int16_t*)malloc(*size);
  const double periodSamplesCount = sampleRate / frequency;
  const double a = pow(10, 0.1 * level);
  for (size_t i = 0; i < sampleCount; i++)
  {
    double x = 6.283185307179586476925286766559 * i / periodSamplesCount;
    double y = sin(x) * a;
    buffer = round(0x7fff * y);
  }
  return buffer;
}

HAL_StatusTypeDef Audio_Init()
{
  if (WM8994_Probe() != BSP_ERROR_NONE) return HAL_ERROR;
  if (!Audio_CompObj) return HAL_ERROR;
  codec_init.Resolution   = WM8994_RESOLUTION_16b;
  codec_init.Frequency    = WM8994_FREQUENCY_22K;
  codec_init.InputDevice  = WM8994_IN_NONE;
  codec_init.OutputDevice = AUDIO_OUT_DEVICE_SPEAKER;
  codec_init.Volume       = VOLUME_OUT_CONVERT(100);
  if (Audio_Drv->Init(Audio_CompObj, &codec_init) != 0) return HAL_ERROR;
  if (Audio_Drv->Play(Audio_CompObj) < 0) return HAL_ERROR;
  Audio_Buffer = generate_sine(WM8994_FREQUENCY_22K, 1.0, 1000.0, 0, &Audio_Buffer_Size);
  if (!Audio_Buffer || !Audio_Buffer_Size) return HAL_ERROR;
  return HAL_OK;
}

HAL_StatusTypeDef Audio_Test()
{
  if (HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t*)Audio_Buffer, Audio_Buffer_Size) != HAL_OK) return HAL_ERROR;
  return HAL_OK;
}
在主程序启动的最后,我调用了 Audio_Init() 函数,若它返回 HAL_OK,则继续调用 Audio_Test()。
这两个函数都返回了 HAL_OK,说明所有被调用的相关函数都执行成功了。
我将 “左扬声器(SPK left)” 输出通过两个 2.2μF 电容连接到示波器,以测试是否有信号输出。电容串联在示波器和两个引脚之间,这杨两个引脚都不是接地的,我不确定这样的连接是否正确。如果没有扬声器或耳机,还有其他测试方法吗?
示波器上没有任何波形。
我的目标是在一个或是两个扬声器输出端看到正弦波。既然 WM8994 驱动初始化成功了,我怀疑 SAI 根本没有工作。或者,是我对 PCM 数据的理解有遗漏?我原以为 PCM 数据就是一个个样本,每个样本是 16 位带符号数。但话说回来,就算我把数据搞错了,只要提供了变化的数值 —— 示波器上至少也该有一些信号才对。可运行代码后,完全没有任何波形。
以下是生成的代码:
static void MX_SAI1_Init(void)
{

  /* USER CODE BEGIN SAI1_Init 0 */

  /* USER CODE END SAI1_Init 0 */

  /* USER CODE BEGIN SAI1_Init 1 */

  /* USER CODE END SAI1_Init 1 */
  hsai_BlockA1.Instance = SAI1_Block_A;
  hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_TX;
  hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
  hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE;
  hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
  hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF;
  hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_22K;
  hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
  hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
  hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
  hsai_BlockA1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
  if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_PCM_SHORT, SAI_PROTOCOL_DATASIZE_16BIT, 4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SAI1_Init 2 */

  /* USER CODE END SAI1_Init 2 */

}

HAL_StatusTypeDef HAL_SAI_InitProtocol(SAI_HandleTypeDef *hsai, uint32_t protocol, uint32_t datasize, uint32_t nbslot)
{
  HAL_StatusTypeDef status;

  /* Check the parameters */
  assert_param(IS_SAI_SUPPORTED_PROTOCOL(protocol));
  assert_param(IS_SAI_PROTOCOL_DATASIZE(datasize));

  switch (protocol)
  {
    case SAI_I2S_STANDARD :
    case SAI_I2S_MSBJUSTIFIED :
    case SAI_I2S_LSBJUSTIFIED :
      status = SAI_InitI2S(hsai, protocol, datasize, nbslot);
      break;
    case SAI_PCM_LONG :
    case SAI_PCM_SHORT :
      status = SAI_InitPCM(hsai, protocol, datasize, nbslot);
      break;
    default :
      status = HAL_ERROR;
      break;
  }

  if (status == HAL_OK)
  {
    status = HAL_SAI_Init(hsai);
  }

  return status;
}

static HAL_StatusTypeDef SAI_InitPCM(SAI_HandleTypeDef *hsai, uint32_t protocol, uint32_t datasize, uint32_t nbslot)
{
  HAL_StatusTypeDef status = HAL_OK;

  hsai->Init.Protocol            = SAI_FREE_PROTOCOL;
  hsai->Init.FirstBit            = SAI_FIRSTBIT_MSB;
  /* Compute ClockStrobing according AudioMode */
  if ((hsai->Init.AudioMode == SAI_MODEMASTER_TX) || (hsai->Init.AudioMode == SAI_MODESLAVE_TX))
  {
    /* Transmit */
    hsai->Init.ClockStrobing     = SAI_CLOCKSTROBING_RISINGEDGE;
  }
  else
  {
    /* Receive */
    hsai->Init.ClockStrobing     = SAI_CLOCKSTROBING_FALLINGEDGE;
  }
  hsai->FrameInit.FSDefinition   = SAI_FS_STARTFRAME;
  hsai->FrameInit.FSPolarity     = SAI_FS_ACTIVE_HIGH;
  hsai->FrameInit.FSOffset       = SAI_FS_BEFOREFIRSTBIT;
  hsai->SlotInit.FirstBitOffset  = 0;
  hsai->SlotInit.SlotNumber      = nbslot;
  hsai->SlotInit.SlotActive      = SAI_SLOTACTIVE_ALL;

  if (protocol == SAI_PCM_SHORT)
  {
      hsai->FrameInit.ActiveFrameLength = 1;
  }
  else
  {
    /* SAI_PCM_LONG */
      hsai->FrameInit.ActiveFrameLength = 13;
  }

  switch (datasize)
  {
    case SAI_PROTOCOL_DATASIZE_16BIT:
      hsai->Init.DataSize = SAI_DATASIZE_16;
      hsai->FrameInit.FrameLength = 16U * nbslot;
      hsai->SlotInit.SlotSize = SAI_SLOTSIZE_16B;
      break;
    case SAI_PROTOCOL_DATASIZE_16BITEXTENDED :
      hsai->Init.DataSize = SAI_DATASIZE_16;
      hsai->FrameInit.FrameLength = 32U * nbslot;
      hsai->SlotInit.SlotSize = SAI_SLOTSIZE_32B;
      break;
    case SAI_PROTOCOL_DATASIZE_24BIT :
      hsai->Init.DataSize = SAI_DATASIZE_24;
      hsai->FrameInit.FrameLength = 32U * nbslot;
      hsai->SlotInit.SlotSize = SAI_SLOTSIZE_32B;
      break;
    case SAI_PROTOCOL_DATASIZE_32BIT:
      hsai->Init.DataSize = SAI_DATASIZE_32;
      hsai->FrameInit.FrameLength = 32U * nbslot;
      hsai->SlotInit.SlotSize = SAI_SLOTSIZE_32B;
      break;
    default :
      status = HAL_ERROR;
      break;
  }

  return status;
}



您需要登录后才可以回帖 登录 | 注册

本版积分规则

69

主题

89

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部