使用 STM32F767 的 DMA 进行 I2S 音频数据传输,但发现偶尔会出现数据丢失和音频爆音的问题。
发现
使用逻辑分析仪监测 I2S 和 DMA 信号,发现 DMA 传输完成中断有时会延迟
检查 DMA 配置,发现没有启用错误中断和传输完成中断
分析代码,发现 DMA 传输缓冲区管理不完善
需要修改软件
1启用 DMA 错误中断和传输完成中断
2优化 DMA 缓冲区管理,使用双缓冲技术
3增加 DMA 传输错误处理机制
// DMA和I2S初始化I2S_HandleTypeDef hi2s3;DMA_HandleTypeDef hdma_spi3_tx;DMA_HandleTypeDef hdma_spi3_rx;#define AUDIO_BUFFER_SIZE 1024uint16_t audio_tx_buffer[AUDIO_BUFFER_SIZE];uint16_t audio_rx_buffer[AUDIO_BUFFER_SIZE];void MX_DMA_Init(void){ // DMA控制器时钟使能 __HAL_RCC_DMA1_CLK_ENABLE(); // DMA中断配置 HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn); HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);}void MX_I2S3_Init(void){ hi2s3.Instance = SPI3; hi2s3.Init.Mode = I2S_MODE_MASTER_TX; hi2s3.Init.Standard = I2S_STANDARD_PHILIPS; hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B; hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE; hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_48K; hi2s3.Init.CPOL = I2S_CPOL_LOW; hi2s3.Init.ClockSource = I2S_CLOCK_PLL; hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE; if (HAL_I2S_Init(&hi2s3) != HAL_OK) { Error_Handler(); }}// 启动I2S DMA传输void Start_I2S_DMA_Transfer(void){ // 初始化音频缓冲区 memset(audio_tx_buffer, 0, sizeof(audio_tx_buffer)); memset(audio_rx_buffer, 0, sizeof(audio_rx_buffer)); // 启动I2S DMA全双工传输 if (HAL_I2S_TransmitReceive_DMA(&hi2s3, (uint16_t*)audio_tx_buffer, (uint16_t*)audio_rx_buffer, AUDIO_BUFFER_SIZE/2) != HAL_OK) { Error_Handler(); }}// DMA传输完成回调函数void HAL_I2S_TxRxCpltCallback(I2S_HandleTypeDef *hi2s){ if(hi2s->Instance == SPI3) { // 处理接收到的音频数据 Process_Audio_Data(audio_rx_buffer, AUDIO_BUFFER_SIZE/2); // 准备下一次发送的音频数据 Prepare_Audio_Data(audio_tx_buffer, AUDIO_BUFFER_SIZE/2); }}// DMA传输错误回调函数void HAL_I2S_ErrorCallback(I2S_HandleTypeDef *hi2s){ if(hi2s->Instance == SPI3) { // 记录错误信息 printf("I2S DMA Error!\r\n"); // 停止I2S DMA传输 HAL_I2S_DMAStop(&hi2s3); // 重新启动I2S DMA传输 Start_I2S_DMA_Transfer(); }}
|
|