# 基于STM32G4的HRTIM高分辨率定时器实现多路交错PWM的相位抖动抑制技巧 ## 引言 在电机控制、数字电源和逆变器应用中,多路交错PWM能有效降低电流纹波和EMI,但相位抖动(Phase Jitter)会破坏交错效果,导致谐波增加。STM32G4的HRTIM(High-Resolution Timer)拥有184皮秒的时间分辨率,理论上可生成极致精度的PWM,但实际工程中,抖动问题依然突出。本文从原理出发,结合实战代码,教你如何压制相位抖动。 ## 1. 相位抖动的根源 ### 1.1 时钟源的不确定性 HRTIM的时钟源通常为PLL输出,而PLL的抖动(Jitter)会直接耦合到PWM输出。STM32G4的PLL在宽频带下抖动可达几十皮秒,虽小,但在高分辨率应用中不可忽视。 ### 1.2 死区插入的偏差 多路交错PWM常需互补输出和死区,死区发生器基于HRTIM内部时钟计数,若死区时间不是时钟周期的整数倍,会产生量化误差,导致相位偏移。 ### 1.3 负载突变与占空比更新 当占空比更新时,若更新时刻与PWM周期不同步,会引入瞬时相位跳变,尤其在连续调制模式下,这种抖动呈周期性。 ## 2. 硬件层面的抑制策略 ### 2.1 使用专用时钟源 优先选择外部高精度晶振(如TCXO)作为PLL输入,避免内部RC振荡器。STM32G4的PLL支持分频和倍频,配置时尽量使HRTIM时钟为整数倍频,减少分频误差。 ```c // 配置PLL为HRTIM提供168MHz时钟(假设外部晶振8MHz) RCC_OscInitTypeDef osc = {0}; osc.OscillatorType = RCC_OSCILLATORTYPE_HSE; osc.HSEState = RCC_HSE_ON; osc.PLL.PLLState = RCC_PLL_ON; osc.PLL.PLLSource = RCC_PLLSOURCE_HSE; osc.PLL.PLLM = 1; // 8MHz / 1 = 8MHz osc.PLL.PLLN = 21; // 8MHz * 21 = 168MHz osc.PLL.PLLP = RCC_PLLP_DIV2; // 168MHz / 2 = 84MHz (HRTIM时钟) HAL_RCC_OscConfig(&osc); ``` ### 2.2 启用HRTIM的DLL校准 HRTIM内置延迟锁定环(DLL),可自动校准内部延迟线,补偿温度漂移。务必在初始化时使能DLL,并定期触发校准。 ```c // 使能HRTIM DLL校准 HRTIM_TimeBaseCfgTypeDef sTimeBaseCfg = {0}; sTimeBaseCfg.Period = 1000; // 周期值 sTimeBaseCfg.RepetitionCounter = 0; sTimeBaseCfg.Prescaler = HRTIM_PRESCALERRATIO_DIV1; sTimeBaseCfg.Mode = HRTIM_MODE_CONTINUOUS; HAL_HRTIM_TimeBaseInit(&hhrtim, HRTIM_TIMERINDEX_MASTER, &sTimeBaseCfg); // 启动DLL校准(需在HRTIM使能前调用) HAL_HRTIM_DLLCalibrationStart(&hhrtim, HRTIM_DLLCALIBRATION_RATE_1); ``` ## 3. 软件层面的补偿技巧 ### 3.1 同步更新占空比 所有通道的占空比更新应使用HRTIM的同步更新机制,避免异步写入导致相位偏移。通过设置`HRTIM_CR1`的`UDIS`位,在周期中断中统一更新。 ```c // 在周期中断中更新所有通道占空比 void HRTIM_PeriodElapsedCallback(HRTIM_HandleTypeDef *hhrtim) { if (hhrtim->Instance == HRTIM1) { // 锁定更新,防止中途修改 __HAL_HRTIM_DISABLE(hhrtim, HRTIM_TIMERINDEX_MASTER); // 更新通道A和B的占空比 HAL_HRTIM_WaveformCompareSet(hhrtim, HRTIM_TIMERINDEX_TIMER_A, 500); HAL_HRTIM_WaveformCompareSet(hhrtim, HRTIM_TIMERINDEX_TIMER_B, 500); // 解锁并触发同步更新 __HAL_HRTIM_ENABLE(hhrtim, HRTIM_TIMERINDEX_MASTER); __HAL_HRTIM_SET_UPDATE(hhrtim, HRTIM_TIMERINDEX_MASTER); } } ``` ### 3.2 死区补偿算法 当死区时间不是HRTIM时钟周期的整数倍时,通过软件微调比较值来补偿。例如,死区要求100ns,时钟周期为11.9ns(84MHz),则死区计数器值为8(95.2ns),剩余4.8ns误差可通过调整相邻通道的相位来抵消。 ```c // 死区补偿示例:调整通道B的相位偏移 #define DEADTIME_NS 100 #define HRTIM_CLK_MHZ 84 #define DEADTIME_COUNT (DEADTIME_NS * HRTIM_CLK_MHZ / 1000) // 8 #define DEADTIME_ERROR_NS (DEADTIME_NS - DEADTIME_COUNT * 1000 / HRTIM_CLK_MHZ) // 4.8ns // 计算补偿值(以时钟周期为单位) #define COMPENSATION_COUNT (DEADTIME_ERROR_NS * HRTIM_CLK_MHZ / 1000) // 0.4 -> 0 // 实际中可累积误差,动态调整 ``` ### 3.3 相位抖动滤波算法 对于周期性抖动,可采用移动平均滤波或锁相环(PLL)思想。在软件中,通过检测周期误差,调整下一次更新的相位偏移。 ```c // 简单的比例积分(PI)补偿器 float integral = 0.0f; float kp = 0.1f, ki = 0.01f; int32_t phase_error; void PhaseCompensate(int32_t measured_phase) { int32_t target_phase = 0; // 期望相位 phase_error = target_phase - measured_phase; integral += phase_error; int32_t adjustment = (int32_t)(kp * phase_error + ki * integral); // 调整HRTIM比较值 HAL_HRTIM_WaveformCompareSet(&hhrtim, HRTIM_TIMERINDEX_TIMER_A, base_compare + adjustment); } ``` ## 4. 完整代码示例 以下代码初始化HRTIM1生成两路交错PWM(相位差180度),并应用上述抑制技巧。 ```c #include "stm32g4xx_hal.h" HRTIM_HandleTypeDef hhrtim; void HRTIM_Init(void) { // 使能时钟 __HAL_RCC_HRTIM1_CLK_ENABLE(); // 配置HRTIM基本参数 hhrtim.Instance = HRTIM1; hhrtim.Init.HRTIMInterruptResquests = HRTIM_IT_MASTER_PER; hhrtim.Init.SyncOptions = HRTIM_SYNC_NONE; HAL_HRTIM_Init(&hhrtim); // 配置主定时器(时基) HRTIM_TimeBaseCfgTypeDef sTimeBaseCfg = {0}; sTimeBaseCfg.Period = 1000; // 周期值,决定PWM频率 sTimeBaseCfg.RepetitionCounter = 0; sTimeBaseCfg.Prescaler = HRTIM_PRESCALERRATIO_DIV1; sTimeBaseCfg.Mode = HRTIM_MODE_CONTINUOUS; HAL_HRTIM_TimeBaseInit(&hhrtim, HRTIM_TIMERINDEX_MASTER, &sTimeBaseCfg); // 配置定时器A和B(输出PWM) HRTIM_TimerCfgTypeDef sTimerCfg = {0}; sTimerCfg.InterruptRequests = HRTIM_TIM_IT_CMP1; sTimerCfg.DACTrigger = HRTIM_TRIGGER_NONE; sTimerCfg.UpdateGating = HRTIM_UPDATEGATING_INDEPENDENT; sTimerCfg.ResetUpdate = HRTIM_TIMUPDATE_RESET_MASTER; sTimerCfg.StartSync = HRTIM_TIMSYNC_MASTER_START; sTimerCfg.PreloadEnable = HRTIM_PRELOAD_ENABLE; sTimerCfg.UpdateTimer = HRTIM_TIMUPDATE_MASTER; sTimerCfg.ResetTimer = HRTIM_TIMRESET_MASTER; HAL_HRTIM_TimerConfig(&hhrtim, HRTIM_TIMERINDEX_TIMER_A, &sTimerCfg); HAL_HRTIM_TimerConfig(&hhrtim, HRTIM_TIMERINDEX_TIMER_B, &sTimerCfg); // 配置比较值(初始占空比50%) HAL_HRTIM_WaveformCompareSet(&hhrtim, HRTIM_TIMERINDEX_TIMER_A, 500); HAL_HRTIM_WaveformCompareSet(&hhrtim, HRTIM_TIMERINDEX_TIMER_B, 500); // 配置输出(使能PWM输出) HRTIM_OutputCfgTypeDef sOutputCfg = {0}; sOutputCfg.Polarity = HRTIM_OUTPUTPOLARITY_HIGH; sOutputCfg.SetSource = HRTIM_OUTPUTSET_TIMCMP1; sOutputCfg.ResetSource = HRTIM_OUTPUTRESET_TIMPER; sOutputCfg.IdleMode = HRTIM_OUTPUTIDLEMODE_NONE; sOutputCfg.IdleLevel = HRTIM_OUTPUTIDLELEVEL_INACTIVE; HAL_HRTIM_OutputConfig(&hhrtim, HRTIM_TIMERINDEX_TIMER_A, &sOutputCfg); HAL_HRTIM_OutputConfig(&hhrtim, HRTIM_TIMERINDEX_TIMER_B, &sOutputCfg); // 使能DLL校准 HAL_HRTIM_DLLCalibrationStart(&hhrtim, HRTIM_DLLCALIBRATION_RATE_1); // 启动HRTIM HAL_HRTIM_Start(&hhrtim, HRTIM_TIMERINDEX_MASTER); HAL_HRTIM_OutputStart(&hhrtim, HRTIM_OUTPUT_TIMER_A | HRTIM_OUTPUT_TIMER_B); // 使能中断 HAL_HRTIM_TimeBase_Start_IT(&hhrtim, HRTIM_TIMERINDEX_MASTER); } // 中断回调:周期更新时同步调整相位 void HAL_HRTIM_TimeBase_MasterPeriodElapsedCallback(HRTIM_HandleTypeDef *hhrtim) { static uint32_t counter = 0; counter++; // 每1000个周期进行一次相位校正(示例) if (counter >= 1000) { counter = 0; // 假设检测到相位误差为-2个时钟周期 int32_t error = -2; // 调整通道B的相位(通过修改比较值) uint32_t current_cmp = HAL_HRTIM_WaveformCompareGet(hhrtim, HRTIM_TIMERINDEX_TIMER_B); HAL_HRTIM_WaveformCompareSet(hhrtim, HRTIM_TIMERINDEX_TIMER_B, current_cmp + error); } } ``` ## 5. 注意事项 - **初始化顺序**:必须先配置时基和定时器,再配置输出,最后启动,否则可能产生意外波形。 - **DLL校准时机**:DLL校准应在HRTIM使能前完成,且校准期间不能输出PWM。 - **中断优先级**:若使用中断更新占空比,应确保中断优先级足够高,避免被其他中断延迟。 - **测量验证**:使用示波器或逻辑分析仪测量实际相位差,建议使用差分探头减少测量噪声。 - **温度影响**:DLL校准可补偿温度漂移,但若环境温度变化剧烈,建议周期性重校准。 ## 结语 通过硬件时钟优化、DLL校准和软件补偿,可显著降低STM32G4 HRTIM多路交错PWM的相位抖动。实际应用中,需根据系统需求选择合适的策略,并利用示波器验证效果。希望本文能帮助你在高精度PWM控制领域更进一步。