STM32F746G-Discovery音频采集程序卡在DMA中断无法退出
STM32F746G-Discovery WM8994 MEMS麦克风采集中断卡死问题
开发背景
在STM32F746G-Discovery开发板上开展音频采集开发,调用BSP库读取板载MEMS麦克风的音频数据,板卡搭载WM8994音频Codec,硬件连接可参考开发板官方原理图。
测试代码
核心工程代码如下:
/* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "stm32746g_discovery_audio.h" #include "wm8994.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ #define AUDIO_BLOCK_SIZE ((uint32_t)0xFFFE) #define AUDIO_NB_BLOCKS ((uint32_t)4) /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c3; SAI_HandleTypeDef hsai_BlockA2; SAI_HandleTypeDef hsai_BlockB2; DMA_HandleTypeDef hdma_sai2_b; DMA_HandleTypeDef hdma_sai2_a; UART_HandleTypeDef huart1; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); void PeriphCommonClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_SAI2_Init(void); static void MX_I2C3_Init(void); static void MX_USART1_UART_Init(void); /* USER CODE BEGIN PFP */ void BSP_AUDIO_IN_TransferComplete_CallBack(void); void BSP_AUDIO_IN_HalfTransfer_CallBack(void); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ uint32_t audio_rec_buffer_state; /* USER CODE END 0 */ /** * @brief 程序入口 * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint8_t res = 0; uint16_t internal_buffer[AUDIO_BLOCK_SIZE]; uint32_t bytesread = 0; /* USER CODE END 1 */ /* 使能I-Cache---------------------------------------------------------*/ SCB_EnableICache(); /* 使能D-Cache---------------------------------------------------------*/ SCB_EnableDCache(); /* MCU配置--------------------------------------------------------*/ HAL_Init(); /* 系统时钟配置 */ SystemClock_Config(); PeriphCommonClock_Config(); /* 初始化所有配置的外设 */ MX_GPIO_Init(); MX_DMA_Init(); MX_SAI2_Init(); MX_I2C3_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ audio_rec_buffer_state = BUFFER_OFFSET_NONE; res = BSP_AUDIO_IN_Init(INPUT_DEVICE_DIGITAL_MICROPHONE_2, 35, AUDIO_FREQUENCY_44K); if(!res) { res = BSP_AUDIO_IN_Record(internal_buffer, AUDIO_BLOCK_SIZE); for (int block_number = 0; block_number < AUDIO_NB_BLOCKS; ++block_number) { while(audio_rec_buffer_state != BUFFER_OFFSET_HALF); audio_rec_buffer_state = BUFFER_OFFSET_NONE; while(audio_rec_buffer_state != BUFFER_OFFSET_FULL); audio_rec_buffer_state = BUFFER_OFFSET_NONE; } BSP_AUDIO_IN_Stop(CODEC_PDWN_SW); BSP_AUDIO_OUT_Init(OUTPUT_DEVICE_HEADPHONE, 35, AUDIO_FREQUENCY_44K); BSP_AUDIO_OUT_SetAudioFrameSlot(CODEC_AUDIOFRAME_SLOT_02); res = BSP_AUDIO_OUT_Play(internal_buffer, AUDIO_BLOCK_SIZE); BSP_AUDIO_OUT_Stop(CODEC_PDWN_SW); } /* USER CODE END 2 */ /* 死循环 */ while (1) { } } // 回调函数 void BSP_AUDIO_IN_TransferComplete_CallBack(void) { audio_rec_buffer_state = BUFFER_OFFSET_FULL; return; } void BSP_AUDIO_IN_HalfTransfer_CallBack(void) { audio_rec_buffer_state = BUFFER_OFFSET_HALF; return; }
故障现象
调试时程序进入DMA中断函数后无法正常退出,涉及的中断处理函数如下:
void DMA2_Stream7_IRQHandler(void) { /* USER CODE BEGIN DMA2_Stream7_IRQn 0 */ /* USER CODE END DMA2_Stream7_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_sai2_b); /* USER CODE BEGIN DMA2_Stream7_IRQn 1 */ /* USER CODE END DMA2_Stream7_IRQn 1 */ }
故障排查与解决方法
按优先级从高到低排查以下问题:
- 栈溢出故障(最高概率):代码中
internal_buffer定义在main函数的局部变量区,单块大小为0xFFFE个uint16_t,总大小约128KB。STM32F7默认主栈大小通常配置为4KB~16KB,该数组直接占满栈空间并破坏栈内的返回地址、其他变量内存,DMA写入数据时触发内存访问错误,导致中断卡死。
解决方法:将internal_buffer定义为全局变量,或增加static修饰将其分配到静态存储区,禁止将KB级以上的大数组定义为局部变量放在栈上。 - D-Cache一致性故障:代码中开启了D-Cache,DMA传输直接操作SRAM物理地址,CPU访问的是Cache缓存数据,二者数据不同步会导致DMA传输异常、内存访问错误。
解决方法:将音频缓冲区分配到DTCM RAM(该区域不经过Cache),或配置缓冲区所在SRAM区域为非Cache属性,也可以在每次半传输、全传输中断触发时,调用SCB_InvalidateDCache_by_Addr对缓冲区对应地址范围做Cache无效操作。 - 重复初始化外设导致配置冲突:代码中先通过CubeMX生成的
MX_SAI2_Init、MX_DMA_Init、MX_I2C3_Init初始化了音频相关外设,之后调用的BSP_AUDIO_IN_Init接口本身也会重新初始化I2C、SAI、DMA外设并配置中断,两次初始化的参数(比如DMA数据宽度、SAI音频槽、时钟源、中断优先级)如果冲突,会导致DMA传输错误,反复触发中断无法退出。
解决方法:二选一做初始化,要么完全依赖BSP库的初始化接口,删除自己生成的SAI、DMA、I2C初始化代码;要么自己实现完整的音频底层驱动,不要混用BSP库的音频接口。 - DMA错误标志未清除:调试时可查看中断触发时
hdma_sai2_b的ErrorCode成员值,如果存在FIFO错误、传输错误、直接模式错误标志,说明DMA配置和SAI外设的数据宽度、地址增量模式不匹配,对应修正参数即可。
额外逻辑问题:当前代码中录音完成后调用
BSP_AUDIO_OUT_Play立刻调用BSP_AUDIO_OUT_Stop是错误的,播放接口为异步非阻塞接口,调用后会立刻返回,不会等待播放完成,直接Stop会导致音频完全无法输出,需要等播放完成回调触发后再执行停止操作。
内容的提问来源于stack exchange,提问作者akoluaciklinux
相关产品推荐
相关产品推荐

