# 引言 在工业控制、电机驱动和精密测量中,STM32的定时器PWM结合DMA是生成多路高精度脉冲序列的常用方案。然而,当DMA半传输中断(HT)与PWM更新事件(UEV)在时间上重叠时,可能导致DMA传输时序错乱,进而产生波形毛刺。本文将分析竞争根源,并给出实用的规避方案。 ## 一、竞争机制剖析 ### 1.1 DMA+PWM工作原理 - **PWM生成**:定时器(如TIM1)的CCR寄存器决定占空比,更新事件(UEV)在每个周期结束时触发,将影子寄存器值加载到实际寄存器。 - **DMA传输**:DMA在更新事件触发下,从内存搬运新的占空比数据到CCR寄存器,实现动态调制。 - **半传输中断**:当DMA传输完成一半时触发HT中断,常用于双缓冲切换,以提前准备下一批数据。 ### 1.2 竞争场景 - 当DMA传输数据量较大时,HT中断可能在某个PWM周期中间触发。 - 若HT中断服务函数(ISR)执行时间过长,或与UEV中断(若开启)同时到达,可能导致DMA传输暂停或数据错位。 - 更关键的是:HT中断中若修改DMA配置(如切换内存地址),可能发生在UEV触发DMA传输的瞬间,造成传输冲突,产生毛刺。 ## 二、规避策略 ### 策略1:双缓冲 + 延迟切换 - **原理**:使用两个内存缓冲区,DMA交替读取。HT中断仅设置标志位,不在中断中直接切换DMA地址,而是通过主循环或定时器轮询在安全时刻切换。 - **实现**: - 配置DMA循环模式,缓冲区大小设为2*N(N为单次脉冲数)。 - HT中断置位`flag_half`,TC中断置位`flag_complete`。 - 在主循环中检查标志,调用`HAL_DMA_Start_IT`重新配置源地址(需先停止DMA)。 ### 策略2:中断优先级与临界区保护 - **原理**:将DMA中断优先级设为最高,PWM更新中断(若使用)设为次高,确保DMA传输不被抢占。同时,在HT ISR中关闭PWM更新中断(TIMx->DIER &= ~TIM_DIER_UIE),操作完成后再恢复。 - **注意**:关闭UEV中断会导致PWM周期内无中断,但DMA仍由硬件触发,不影响传输。 ### 策略3:使用DMA的FIFO和突发传输 - **原理**:配置DMA的FIFO模式,并启用突发传输(Burst),一次传输多个数据,减少DMA占用总线的时间,降低与UEV冲突概率。 - **实现**:设置`DMA_InitTypeDef`中的`DMA_FIFOMode`和`DMA_BurstSize`。 ## 三、完整代码示例(以STM32F407为例) 以下代码演示策略2(中断优先级+临界区)的实现。 ### 3.1 定时器与DMA配置 ```c // 定时器PWM配置(TIM1,通道1-4) void PWM_Init(void) { TIM_HandleTypeDef htim1; htim1.Instance = TIM1; htim1.Init.Prescaler = 84-1; // 84MHz/84=1MHz htim1.Init.Period = 1000-1; // 1kHz PWM htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(&htim1); TIM_OC_InitTypeDef sConfigOC; sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; for (int i=1; i<=4; i++) { HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, i*2-1); // 通道1,2,3,4 } // 使能更新中断(用于同步) HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1); } // DMA配置(以TIM1_CH1为例,其他通道类似) void DMA_Init(void) { DMA_HandleTypeDef hdma_tim1_ch1; hdma_tim1_ch1.Instance = DMA1_Stream2; // 根据实际映射 hdma_tim1_ch1.Init.Channel = DMA_CHANNEL_6; // TIM1_CH1映射 hdma_tim1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tim1_ch1.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tim1_ch1.Init.MemInc = DMA_MINC_ENABLE; hdma_tim1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_tim1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_tim1_ch1.Init.Mode = DMA_CIRCULAR; // 循环模式 hdma_tim1_ch1.Init.Priority = DMA_PRIORITY_HIGH; // 高优先级 hdma_tim1_ch1.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&hdma_tim1_ch1); // 连接DMA到定时器 __HAL_LINKDMA(&htim1, hdma[TIM_DMA_ID_CC1], hdma_tim1_ch1); } ``` ### 3.2 中断处理与临界区 ```c volatile uint8_t pwm_update_enabled = 1; // DMA半传输中断回调 void HAL_TIM_PWM_PulseFinishedHalfCpltCallback(TIM_HandleTypeDef *htim) { // 进入临界区:关闭PWM更新中断 __HAL_TIM_DISABLE_IT(&htim1, TIM_IT_UPDATE); pwm_update_enabled = 0; // 处理半缓冲数据(例如预加载下一批) // 注意:此处不切换DMA地址,仅做数据准备 // 退出临界区:恢复更新中断 __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE); pwm_update_enabled = 1; } // PWM更新中断回调(用于同步) void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { // 可在此处进行精确同步操作,但需保持简短 } // 主循环中安全切换DMA缓冲区(策略1的补充) void ProcessDMAFlags(void) { static uint8_t use_half = 0; if (flag_half) { flag_half = 0; // 停止DMA,切换源地址,重新启动 HAL_DMA_Abort(&hdma_tim1_ch1); HAL_DMA_Start_IT(&hdma_tim1_ch1, (uint32_t)buffer_half, (uint32_t)&htim1.Instance->CCR1, N); use_half = 1; } if (flag_complete) { flag_complete = 0; HAL_DMA_Abort(&hdma_tim1_ch1); HAL_DMA_Start_IT(&hdma_tim1_ch1, (uint32_t)buffer_full, (uint32_t)&htim1.Instance->CCR1, N); use_half = 0; } } ``` ### 3.3 启动输出 ```c int main(void) { HAL_Init(); PWM_Init(); DMA_Init(); // 启动DMA传输(循环模式) HAL_DMA_Start_IT(&hdma_tim1_ch1, (uint32_t)buffer_full, (uint32_t)&htim1.Instance->CCR1, N); // 使能定时器DMA请求 __HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_CC1); HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); while(1) { ProcessDMAFlags(); } } ``` ## 四、注意事项 - **中断优先级**:务必设置DMA中断优先级高于任何可能抢占它的中断(如外部中断),但低于系统节拍(若使用RTOS)。 - **临界区保护**:在HT ISR中关闭UEV中断时,需确保操作时间极短(<1us),否则会影响PWM精度。 - **缓冲区对齐**:DMA缓冲区建议按32字节对齐,避免缓存一致性问题(若使用D-Cache)。 - **调试技巧**:使用逻辑分析仪观察PWM波形,同时监控DMA的NDTR寄存器,确认传输无错位。 - **多通道扩展**:多路PWM时,每个通道独立DMA流,但共享定时器,需注意DMA带宽,必要时降低PWM频率或使用FIFO。 ## 五、总结 通过合理配置中断优先级、使用临界区保护以及双缓冲延迟切换,可以有效规避DMA半传输中断与PWM更新事件的竞争,消除波形毛刺。实际项目中,建议结合具体硬件和实时性要求,组合使用上述策略,并充分测试极端负载情况。