STM32F103ZET6【HAL库实战】STM32CubeMX配置高级定时器实现三相电机驱动PWM

张开发
2026/4/6 20:40:56 15 分钟阅读

分享文章

STM32F103ZET6【HAL库实战】STM32CubeMX配置高级定时器实现三相电机驱动PWM
1. 为什么需要带死区的互补PWM在驱动三相无刷电机时最头疼的问题就是上下桥臂直通。想象一下如果同一个桥臂的上下两个MOS管同时导通电源正负极就直接短路了轻则烧MOS管重则整个电路板冒烟。我当年第一次调电机驱动时就因为这个烧了三块板子后来才明白死区时间的重要性。STM32F103ZET6的高级定时器TIM1/TIM8有个特别实用的功能——可以自动生成带死区的互补PWM。这个功能相当于给你的电机驱动加了道保险具体来说互补输出比如CH1输出高电平时CH1N自动输出低电平死区插入在电平切换时自动插入一段双方都关闭的安全时间硬件实现完全由定时器硬件完成不占用CPU资源2. 硬件准备与CubeMX工程创建2.1 硬件选型要点虽然标题用的是STM32F103ZET6但其实这个配置方法适用于所有带高级定时器的STM32型号。选型时要注意定时器资源至少需要1个高级定时器TIM1或TIM8引脚分配查看数据手册确认PWM输出引脚是否可用驱动电路建议使用预驱芯片如IR2104直接推MOS管风险较大2.2 CubeMX基础配置打开CubeMX新建工程时有几点容易踩坑时钟树配置先配置好系统时钟我一般用72MHz主频Debug接口一定要开启SWD调试否则下载一次就锁芯片GPIO设置高级定时器的PWM引脚是固定的不能随意映射// 正确的时钟配置示例HSE 8MHz RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL RCC_PLL_MUL9;3. 定时器参数详细配置3.1 定时器基础参数在CubeMX的定时器配置界面新手最容易搞错的几个参数Prescaler分频系数决定定时器时钟频率Counter Mode必须选择Up向上计数Counter PeriodARR寄存器的值决定PWM频率AutoReload Preload建议启用假设我们要生成16kHz的PWM计算步骤如下系统时钟72MHz预分频设为0不分频ARR值 72000000/16000 - 1 44993.2 PWM通道配置每个PWM通道有四个关键参数参数名推荐值说明Pulse可变占空比初始设为ARR值的一半ModePWM mode 1标准PWM模式Fast ModeDisable普通模式即可CH PolarityHigh有效电平为高特别注意CHxN通道的极性要和CHx相反比如CH1设为HighCH1N就要设为Low。3.3 死区时间计算死区时间设置是个精细活太短会直通太长会影响驱动效率。计算公式死区时钟周期 定时器时钟周期 × DTG[7:0] × 预分频系数举个例子如果要设置1us死区定时器时钟 72MHz → 周期≈13.89nsDTG值 1000ns / 13.89ns ≈ 72对应寄存器值 72 | 0x80因为72在0-127范围内在CubeMX中直接填写1000ns1us即可软件会自动计算寄存器值。4. 代码生成与调试技巧4.1 生成的代码分析CubeMX生成的初始化代码里有几个关键点需要检查// 定时器基础配置 htim1.Instance TIM1; htim1.Init.Prescaler 0; htim1.Init.CounterMode TIM_COUNTERMODE_UP; htim1.Init.Period 4499; htim1.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter 0; htim1.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_ENABLE; // 死区配置 sDeadTimeConfig.DeadTime 72; // 1us sDeadTimeConfig.LockLevel TIM_LOCKLEVEL_OFF; sDeadTimeConfig.OSSIState TIM_OSSI_ENABLE; sDeadTimeConfig.OSSRState TIM_OSSR_ENABLE;4.2 启动PWM的正确姿势原始文章给的启动代码其实可以优化。我推荐这样写// 更安全的启动顺序 HAL_TIM_PWM_Start(htim1, TIM_CHANNEL_1); HAL_TIMEx_PWMN_Start(htim1, TIM_CHANNEL_1); // 重复2、3通道... HAL_TIMEx_BDTRConfig(htim1, sBreakDeadTimeConfig); __HAL_TIM_MOE_ENABLE(htim1);关键点先启动主通道再启动互补通道最后配置刹车死区并使能主输出这个顺序可以避免启动时的毛刺4.3 示波器调试实战用示波器观察时建议这样连接通道1CH1信号通道2CH1N信号数学运算CH1-CH1N正常应该看到互补信号始终相反跳变沿之间有明显的死区时间占空比变化时死区时间保持不变如果发现死区时间不稳定可能是定时器时钟配置错误预分频系数计算错误寄存器写入顺序有问题5. 实际电机驱动中的注意事项5.1 软件保护机制硬件死区只是第一道防线软件上还需要过流保护在ADC中断中检测电流堵转检测监测反电动势软启动PWM占空比缓慢增加// 简单的软启动实现 for(int i0; i1000; i10) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, i); HAL_Delay(1); }5.2 常见问题排查遇到电机不转或异常振动时按这个顺序检查用万用表测量各相电压是否平衡断开电机用示波器看PWM波形检查预驱芯片的输入输出测量MOS管栅极波形我遇到过最诡异的问题是PCB布局不当导致交叉干扰表现为空载正常带载后波形畸变电机抖动严重解决方法是在MOS管栅极加10-100Ω电阻并缩短走线长度。6. 进阶应用正弦波驱动对于PMSM电机可以用定时器的PWM模式生成SPWM预先计算正弦表const uint16_t sin_table[360] { ... };在定时器中断中更新CCRvoid HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint16_t index 0; __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, sin_table[index]); index (index 1) % 360; }配合SVPWM算法可以实现FOC控制这种方案比纯硬件PWM更灵活但对CPU性能有一定要求。如果要做高性能驱动建议上STM32F4或者F7系列。

更多文章