You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:45:15