| 
 
| 在我的项目使用 HAL 库,是用 STM32CubeIDE 制作的。 按照示例中的配置来设置所有内容。
 具体来说 ——SAI 的时钟设置为 4.096MHz。对 SAI1 的配置如下(如图所示):
 
 
     代码如下
 #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;
 }
 
 
 
 
 | 
 |