You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用CMSIS CORE实现DMA数组拷贝?STM32F103RC调试异常求助

1. 如何使用CMSIS CORE通过DMA实现数组拷贝(内存到内存传输)

针对STM32F103RC这类芯片,用CMSIS CORE直接操作寄存器实现DMA内存到内存拷贝,步骤很清晰:

  • 第一步:开启DMA时钟
    首先得给DMA控制器上电,比如用DMA2(F1系列中DMA2原生支持内存到内存传输,DMA1也能模拟但步骤稍麻烦),通过RCC寄存器开启时钟:

    RCC->AHBENR |= RCC_AHBENR_DMA2EN; // 启用DMA2时钟
    
  • 第二步:配置DMA通道参数
    选一个空闲的通道(比如DMA2_Channel1),先关闭通道再配置,避免配置过程中误触发:

    // 先关闭通道,确保配置安全
    DMA2_Channel1->CCR &= ~DMA_CCR_EN;
    
    // 开启内存到内存模式
    DMA2_Channel1->CCR |= DMA_CCR_MEM2MEM;
    
    // 设置数据宽度(比如数组是uint32_t就用32位,根据实际类型调整)
    DMA2_Channel1->CCR |= DMA_CCR_PSIZE_1 | DMA_CCR_PSIZE_0; // 外设端数据宽度32位
    DMA2_Channel1->CCR |= DMA_CCR_MSIZE_1 | DMA_CCR_MSIZE_0; // 内存端数据宽度32位
    
    // 开启地址自增:源和目标地址每次传输后自动偏移
    DMA2_Channel1->CCR |= DMA_CCR_PINC | DMA_CCR_MINC;
    
    // 设置源数组和目标数组的地址
    DMA2_Channel1->CPAR = (uint32_t)sourceArr; // 源地址(外设端地址,MEM2MEM模式下作为数据源)
    DMA2_Channel1->CMAR = (uint32_t)destArr;   // 目标地址(内存端地址)
    
    // 设置要传输的元素个数
    DMA2_Channel1->CNDTR = sizeof(sourceArr)/sizeof(sourceArr[0]);
    
  • 第三步:启动传输(可选中断/轮询)
    如果不需要中断,直接轮询完成标志即可:

    // 开启DMA通道,开始传输
    DMA2_Channel1->CCR |= DMA_CCR_EN;
    
    // 等待传输完成
    while(!(DMA2->ISR & DMA_ISR_TCIF1));
    
    // 清除完成标志,避免后续误触发
    DMA2->IFCR |= DMA_IFCR_CTCIF1;
    

    如果需要用中断通知传输完成,还要配置NVIC和中断服务函数:

    // 开启传输完成中断
    DMA2_Channel1->CCR |= DMA_CCR_TCIE;
    
    // 配置NVIC,启用DMA2通道1中断
    NVIC_EnableIRQ(DMA2_Channel1_IRQn);
    NVIC_SetPriority(DMA2_Channel1_IRQn, 1); // 合理设置优先级,避免被抢占
    
    // 启动传输
    DMA2_Channel1->CCR |= DMA_CCR_EN;
    

    对应的中断服务函数:

    void DMA2_Channel1_IRQHandler(void)
    {
        if(DMA2->ISR & DMA_ISR_TCIF1)
        {
            // 这里写传输完成后的操作,比如你要的LED闪烁
            GPIOx->ODR ^= GPIO_Pin_x; // 翻转LED引脚电平
    
            // 必须清除中断标志!否则会一直触发中断
            DMA2->IFCR |= DMA_IFCR_CTCIF1;
        }
    }
    

2. STM32F103RC DMA调试问题:LED闪烁次数不符的排查建议

你说预设主函数4次、中断1次共5次闪烁,但实际只有2次,咱们可以从这些方向逐步查:

  • 先确认主函数的闪烁逻辑是否正常
    先把DMA相关代码注释掉,单独跑主函数里的LED闪烁,看能不能正常闪4次。如果这一步就有问题,那锅不在DMA,而是LED的GPIO配置、延时函数或者主循环逻辑出了问题。

  • 检查DMA时钟是否真的开了
    很多人容易漏开DMA时钟!比如你用DMA1的话,要加RCC->AHBENR |= RCC_AHBENR_DMA1EN;,没开时钟的话DMA完全不会工作,中断更别谈触发。

  • 验证DMA通道配置的细节

    • 内存到内存模式是否正确:如果用的是DMA1,它不支持原生MEM2MEM,得把传输方向设为内存到外设,再把源/目标都设为内存地址;如果是DMA2,要确认CCR寄存器的MEM2MEM位已经置1。
    • 地址自增是否开启:PINCMINC要是没开,DMA会一直读写同一个地址,不仅传输错误,还可能触发异常。
    • 传输数量是否正确:CNDTR的值要和数组元素个数一致,设小了传输提前结束,设大了会访问越界。
    • 数据宽度是否匹配:比如数组是uint8_t但你设成32位宽度,会一次传4个字节,导致传输次数计算错误。
  • 排查中断配置的问题

    • 确认DMA通道的TCIE(传输完成中断)是否开启:CCR寄存器的TCIE位要设为1。
    • NVIC是否正确配置:要调用NVIC_EnableIRQ(对应通道的IRQn);,优先级设置也要合理,别被其他高优先级中断抢占导致中断函数没机会执行。
    • 中断标志是否清除:中断服务函数里必须清除TCIF标志,不然中断会一直触发,或者下次无法正常触发。
  • 确认DMA是否真的完成了传输
    可以在主函数里轮询TCIF标志,或者调试时看DMA寄存器:

    • CNDTR的值是否递减到0,如果一直不变,说明DMA根本没工作。
    • ISR寄存器的TCIF位是否置位,要是没置位,说明传输没完成,中断自然不会触发。
  • 检查LED闪烁的冲突逻辑
    比如主函数和中断用的是同一个LED引脚?会不会主函数的延时和中断触发时机重叠,导致闪烁次数看起来不对?可以给中断单独用一个LED引脚,或者加个全局计数变量(比如irq_trigger_count,中断触发时加1),主函数里打印这个变量的值,确认中断到底有没有触发。

  • 排查数组的存储位置问题
    如果sourceArrconst修饰被存在Flash里,要确认DMA是否能正常访问Flash(F1的DMA是支持的,但配置要正确);另外可以给数组加volatile修饰,避免编译器优化把数组放到奇怪的位置,导致DMA访问出错。

内容的提问来源于stack exchange,提问作者Devjeet Mandal

火山引擎 最新活动