功能描述:利用STM32H7B0的I2S(用DMA)接口发送数据给DAC(每秒发一次,一次持续发送100毫秒),发完数据后就停止I2S和DMA,等下一秒再启动发送数据。
存在的问题:每次启动I2S发送数据,就出现一次破音,请问这是什么问题,如何解决?
1、DMA配置如下:
// 初始化dma
uint8 dma_i2s_init_dma()
{
HAL_StatusTypeDef halFLag = HAL_ERROR;
// 使能dma时钟
__HAL_RCC_DMA1_CLK_ENABLE();
// 失能DMA-TX
HAL_DMA_DeInit(&g_dmaTxHandle);
// 初始化DMA发送通道
g_dmaTxHandle.Instance = DMA1_Stream1;
g_dmaTxHandle.Init.Request = DMA_REQUEST_SPI1_TX; // 请求源
g_dmaTxHandle.Init.Direction = DMA_MEMORY_TO_PERIPH; // 存储器到外设
g_dmaTxHandle.Init.PeriphInc = DMA_PINC_DISABLE;
g_dmaTxHandle.Init.MemInc = DMA_MINC_ENABLE;
g_dmaTxHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
g_dmaTxHandle.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
g_dmaTxHandle.Init.Mode = DMA_NORMAL; // 普通模式
g_dmaTxHandle.Init.Priority = DMA_PRIORITY_MEDIUM;
g_dmaTxHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
g_dmaTxHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // 16字节
g_dmaTxHandle.Init.MemBurst = DMA_MBURST_INC4; // 一次取16字节
g_dmaTxHandle.Init.PeriphBurst = DMA_PBURST_SINGLE;
halFLag = HAL_DMA_Init(&g_dmaTxHandle);
if (halFLag != HAL_OK)
{
return 0;
}
// 链接I2S句柄和Dma句柄
__HAL_LINKDMA(&g_i2sHandle, hdmatx, g_dmaTxHandle);
// DMA中断使能
HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
return 1;
}
2、I2S配置如下(MCLK:12.288MHz,LRCK:48KHz):
// 初始化i2s外设
uint8 dma_i2s_init_i2s()
{
HAL_StatusTypeDef halFLag = HAL_ERROR;
dma_i2s_init_clock(); // 初始化时钟
dma_i2s_init_gpio(); // 初始化i2s的管脚
dma_i2s_init_dma(); // 初始化DMA
// 失能I2S
HAL_I2S_DeInit(&g_i2sHandle);
// 组织I2S发送通信参数
g_i2sHandle.Instance = SPI1; // 复用SPI1
g_i2sHandle.Init.Mode = I2S_MODE_MASTER_TX; // 主控制器
g_i2sHandle.Init.Standard = I2S_STANDARD_PHILIPS; // 飞利浦协议
g_i2sHandle.Init.DataFormat = I2S_DATAFORMAT_24B; // 24bit
g_i2sHandle.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; // 主时钟输出
g_i2sHandle.Init.AudioFreq = I2S_AUDIOFREQ_48K; // 采样率
g_i2sHandle.Init.CPOL = I2S_CPOL_LOW; // 空闲状态,时钟为低电平
g_i2sHandle.Init.FirstBit = I2S_FIRSTBIT_MSB; // 大端模式,高字节在前
g_i2sHandle.Init.WSInversion = I2S_WS_INVERSION_ENABLE; // LRCK翻转,左低右高
g_i2sHandle.Init.Data24BitAlignment = I2S_DATA_24BIT_ALIGNMENT_LEFT;
g_i2sHandle.Init.MasterKeepIOState = I2S_MASTER_KEEP_IO_STATE_DISABLE;
// 初始化I2S句柄
halFLag = HAL_I2S_Init(&g_i2sHandle);
if (halFLag != HAL_OK)
{
return 0;
}
return 1;
}
3、停止DMA和I2S
// I2S发送完成的回调函数
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
__HAL_DMA_CLEAR_FLAG(hi2s, DMA_FLAG_TCIF1_5); // 清除中断标识
uint32 dwFactor = 4096;
uint32 dwFlag = 0; // I2S发送完成标识符,非0标识发送完成
SPI_TypeDef *pSpi = (SPI_TypeDef *)hi2s->Instance; // SPI外设
// 等待i2s移位寄存器发送完成
while (1)
{
dwFlag = pSpi->SR & dwFactor;
if (dwFlag)
{
HAL_I2S_DMAStop(hi2s); // 停止I2S发送
g_byI2sCpltFlag = 0; // 信号已发完
break;
}
}
return;
}
|
签到
1. 音源中存在有破音的信号, 可以跳过MCU来验证是否有破音产生。(换一个接口设备连接验证一下信号)
2. 功率转换时产生的,那么给功放延迟几十毫秒。
音源确认过没有问题,DAC的输入数据和时钟也确认过没有问题;另外,目前还没有接PA,但在DAC后面观察到了破音。
我的应用是发送100毫秒数据,之后就停止dma和i2s,等900毫秒后再启动dma和i2s发送数据,一直这样循环往复下去,这个破音每秒就出现一次。
同常来说,左右声道的信号应该是连续传输的。
我把信号做成全0也有这个破音。单步跟踪,发现在
HAL_I2S_Transmit_DMA
()这个函数中执行完以下这行代码,破音就出来了。有没有可能是I2S“使能发送”后产生一个冲击噪声。执行这个有可能就是恢复了时钟。通常时钟应该保持连续,突然间开启时钟容易造成破音出来。
可以反向验证定位一下,不做停止,时钟保持所有时钟,这样破音是不是就不存在了。
若不关闭I2S(I2S时钟保持,I2S的TX使能保持),只通过`__HAL_DMA_ENABLE(&g_dmaTxHandle)来使能DMA,这时数据就不会发送(分析原因应该是没有来自I2S的DMA请求),也不会进DMA发送完成中断。
时钟保持,没有数据(数据线上拉)可以验证。
否则就只有MUTE了
可能话,不妨跟硬件工程师 尤其对音响有经验的兄弟聊聊。
应该排查一下,定义的I2S的格式和DMA突发大小是否相同,然后DMA传输的I2S音频数据的 buff缓存大小数量是否正确,然后地址是否 4字节对齐
MCLK、LRCK和BCK三个时钟都是MCU的I2S接口控制的,软件拿不到控制权,没法保持呀。