3. 定时器与PWM电机驱动
各位同学,今天我们来聊聊电动牙刷里最核心的驱动部分——定时器与PWM。说实话,我刚开始做嵌入式那会儿,对定时器的理解也就停留在“能计时”这个层面。直到有一次做电机控制项目,电机嗡嗡响就是不转,查了两天才发现是PWM频率没配好。嗯,从那以后,我对定时器的敬畏心就上来了。
3.1 定时器工作原理
定时器说白了就是一个会数数的计数器。它内部有个寄存器,每来一个时钟脉冲就加1。当计数值达到我们设定的上限时,就会产生一个中断或者触发某个事件。
我习惯把定时器分成三类:
- 基本定时器:只能计时,功能最简单。适合做系统心跳、延时等。
- 通用定时器:除了计时,还能输出PWM、捕获输入信号。电动牙刷里最常用这种。
- 高级定时器:功能最全,支持互补输出、死区插入、刹车功能。做电机驱动时,我强烈建议用这个。
你想想看,电动牙刷的电机需要以特定速度旋转,同时还要根据刷牙模式切换转速。如果没有定时器,靠软件延时去控制?那CPU啥也别干了,光在那数数吧。
核心概念:自动重装载
定时器计到最大值后,会自动回到0重新开始。这个最大值就是自动重装载值(ARR)。ARR越大,定时周期越长。
3.2 PWM生成原理
PWM,脉冲宽度调制。名字听着高大上,其实就是控制一个方波里高电平占多少比例。这个比例叫占空比。
定时器生成PWM的方式是这样的:
- 定时器从0开始向上计数
- 到达比较值(CCR)时,输出电平翻转
- 到达自动重装载值(ARR)时,再次翻转并清零
- 如此循环,就得到了一个周期固定的PWM波
举个例子,ARR设为1000,CCR设为300。那么前300个计数周期输出高电平,后700个输出低电平。占空比就是30%。
我的经验:电动牙刷的PWM频率一般选20kHz左右。太低会有可听见的噪音,太高会增加MOS管的开关损耗。20kHz刚好在人耳听觉范围之上,电机响应也跟得上。
3.3 电机调速实现
直流电机的转速和两端电压成正比。PWM调速的本质,就是通过改变占空比来改变平均电压。
占空比越大,平均电压越高,电机转得越快。这个关系在大部分工作区间内是线性的,但要注意:
- 占空比太低(比如低于10%),电机可能转不起来。因为启动需要克服静摩擦力。
- 占空比太高(接近100%),调速效果就不明显了。
我在项目中遇到过一个问题:电动牙刷在低档位时电机抖动得厉害。后来发现是PWM频率太低,电机在每个周期里都有明显的启停动作。把频率从1kHz提到20kHz,问题就解决了。
// 以STM32为例,配置定时器输出PWM
// 假设系统时钟72MHz,预分频器设为71,则定时器时钟为1MHz
// 想要20kHz的PWM,ARR = 1000000 / 20000 - 1 = 49
// 占空比50%,CCR = 49 * 50% = 24
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 预分频
htim2.Init.Period = 49; // 自动重装载值
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&htim2);
TIM_OC_InitTypeDef sConfigOC;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 24; // 比较值,控制占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
3.4 占空比与转速关系
占空比和转速不是严格的正比关系。我整理了一个实测数据表,供大家参考:
| 占空比 | 理论平均电压 | 实测转速(RPM) | 备注 |
|---|---|---|---|
| 10% | 0.37V | 0 | 启动失败 |
| 20% | 0.74V | 1200 | 勉强启动 |
| 50% | 1.85V | 8500 | 正常工作区 |
| 80% | 2.96V | 15200 | 接近满速 |
| 100% | 3.70V | 18000 | 满速 |
为什么会这样?因为电机内部有电阻和电感,还有反电动势。低占空比时,电流还没建立起来,周期就结束了。所以实际应用中,我一般会设置一个最小占空比阈值,比如15%。低于这个值就直接关闭输出,避免电机嗡嗡响不转。
3.5 死区保护
死区保护,这是做电机驱动时绝对不能跳过的一步。
电动牙刷用的是H桥电路驱动电机。H桥有四个MOS管,上下两个不能同时导通,否则会短路烧管子。但MOS管的开关需要时间,关断比开通慢。如果上管还没完全关断,下管就开通了,那就直通了。
死区时间就是在上下管切换时,故意插入一段“谁都不导通”的时间。比如上管关断后,等500纳秒,再开通下管。
警告:我曾经在一个项目中忽略了死区设置,结果上电瞬间MOS管直接冒烟。后来查了数据手册,发现那个MOS管的关断延迟是120纳秒,开通延迟是80纳秒。如果不加死区,切换时会有40纳秒的重叠时间,足够让管子烧掉了。
高级定时器一般都支持硬件死区插入。你只需要设置死区时间,硬件会自动处理。以STM32高级定时器为例:
// 配置死区时间为500ns
// 定时器时钟1MHz,每个计数周期1us
// 500ns需要0.5个计数周期,但硬件只能整数配置
// 所以实际配置为1个计数周期,即1us
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 1; // 死区时间 = DeadTime * 定时器时钟周期
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
我的建议:死区时间不是越大越好。太大会影响PWM精度,导致电机效率下降。一般取MOS管关断延迟的2-3倍就够用了。比如关断延迟100纳秒,死区设300纳秒。留点余量,但别太夸张。
好了,这一章的内容就到这里。定时器和PWM是嵌入式电机控制的基石,理解透了,后面做闭环控制、速度平滑这些就水到渠成了。下一章我们聊聊ADC采样和电池电量检测,那也是电动牙刷里很关键的一环。