STM32接收十六进制数据时DMA计数器值异常且无法进入中断
STM32 DMA2接收异常问题排查与修复
核心问题分析
你的代码存在以下关键错误,直接导致无法进入DMA中断、数据计数值异常:
1. 中断服务函数名称错误
STM32F4中DMA2 Stream5对应的中断服务函数是DMA2_Stream5_IRQHandler,而非DMA2_IRQHandler,名称不匹配会导致中断无法触发。
2. 未配置DMA中断的NVIC
即使开启了DMA中断,若未在NVIC中注册该中断通道,CPU不会响应中断请求。
3. 外设地址配置错误
DMA_PeripheralBaseAddr需指向USART的数据寄存器(DR),而非USART基地址,正确地址应为(uint32_t)&USART1->DR。
4. 外设数据大小参数非法
DMA_PeripheralDataSize = 8是非法值,必须使用标准宏定义DMA_PeripheralDataSize_Byte,位宽不匹配会导致传输计数异常。
5. Normal模式下DMA完成后未重启
Normal模式下DMA完成一次传输后会自动停止,若需连续接收,需在中断中重新配置并开启DMA。
修复后的代码示例
1. 完善DMA初始化函数(添加NVIC配置)
void bsp_USART_dma_init() { DMA_DeInit(DMA2_Stream5); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE); USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); DMA_InitTypeDef dma_initstructure; dma_initstructure.DMA_Channel = DMA_Channel_4; // 修正:指向USART1的数据寄存器DR dma_initstructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR; dma_initstructure.DMA_Memory0BaseAddr = (uint32_t)usart_buffer; dma_initstructure.DMA_DIR = DMA_DIR_PeripheralToMemory; dma_initstructure.DMA_BufferSize = 4; dma_initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma_initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 修正:使用标准宏定义配置外设数据位宽 dma_initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; dma_initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dma_initstructure.DMA_Mode = DMA_Mode_Normal; dma_initstructure.DMA_Priority = DMA_Priority_VeryHigh; dma_initstructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_Init(DMA2_Stream5, &dma_initstructure); // 添加NVIC中断配置 NVIC_InitTypeDef nvic_init; nvic_init.NVIC_IRQChannel = DMA2_Stream5_IRQn; nvic_init.NVIC_IRQChannelPreemptionPriority = 2; nvic_init.NVIC_IRQChannelSubPriority = 2; nvic_init.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic_init); DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, ENABLE); DMA_Cmd(DMA2_Stream5, ENABLE); while(DMA_GetCmdStatus(DMA2_Stream5) != ENABLE); }
2. 修正中断服务函数
extern uint8_t flag; uint8_t usart_buffer[4]; uint8_t flag = 0; void DMA2_Stream5_IRQHandler() { // 检查传输完成中断标志 if(DMA_GetITStatus(DMA2_Stream5, DMA_IT_TCIF5) != RESET) { DMA_ClearITPendingBit(DMA2_Stream5, DMA_IT_TCIF5); flag = 1; // Normal模式下,重启DMA以接收下一组数据 DMA_SetCurrDataCounter(DMA2_Stream5, 4); DMA_Cmd(DMA2_Stream5, ENABLE); } }
3. 主函数保持原有逻辑即可
int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); bsp_USART_init(); bsp_USART_dma_init(); while(1) { bsp_Usart_SendByte(USART1, DMA_GetCurrDataCounter(DMA2_Stream5)); delay_ms(1000); if(flag == 1) { bsp_Usart_SendByte(USART1, usart_buffer[0]); bsp_Usart_SendByte(USART1, usart_buffer[1]); bsp_Usart_SendByte(USART1, usart_buffer[2]); bsp_Usart_SendByte(USART1, usart_buffer[3]); flag = 0; // 重置标志位,避免重复发送 } } }
额外注意事项
DMA_GetCurrDataCounter返回的是剩余待传输字节数,初始值为4,每接收1字节减1,传输完成后变为0。- 若需连续接收,除了在中断中重启DMA,也可将
DMA_Mode改为DMA_Mode_Circular(循环模式),无需手动重启。 - 确保
bsp_Usart_SendByte函数能正确发送单个字节,避免因发送逻辑错误导致调试信息异常。
内容的提问来源于stack exchange,提问作者Jasper
相关产品推荐
相关产品推荐

