在我的项目使用 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;
}
|
|