3、直流有刷电机开环控制:PWM生成、死区时间设置、电机正反转、速度开环控制实验
好,我们正式开始直流有刷电机的开环控制。这一章是电机控制的「敲门砖」。你想想看,如果连让电机转起来都搞不定,后面那些花里胡哨的算法就全是空中楼阁。
我个人习惯,学任何新东西,先让它动起来再说。哪怕只是嗡嗡响,也比纸上谈兵强一百倍。今天我们就来搞定三件事:怎么让电机转、怎么让它反转、怎么控制它转多快。
3.1 PWM生成:电机的「油门」
直流有刷电机,说白了就是一个线圈加一个换向器。给它通上电,它就转。但问题来了——怎么控制速度?
你可能会想:「调电压呗!」没错,但调电压不是让你真的去拧一个电位器。在嵌入式世界里,我们用的是PWM(脉宽调制)。
PWM的原理很简单:
把电源当成一个开关。开关一直开着,电机全速跑;开关一直关着,电机停。那如果开关开一会儿、关一会儿呢?电机就会「半速」跑。
这个「开」的时间占整个周期的比例,就叫占空比。占空比50%,电机大概跑一半的速度。当然,实际转速和占空比不是严格的线性关系,这个我们后面再聊。
核心公式:
占空比 = Ton / (Ton + Toff) × 100%
电机平均电压 = 电源电压 × 占空比
我在项目中遇到过一个问题:用STM32的定时器输出PWM,结果电机一启动就「吱吱」叫。后来发现是PWM频率太低了,落在了人耳可听范围内。嗯,这里要注意——PWM频率一般选在10kHz以上,既听不到噪音,又能保证电机响应够快。
下面是一个典型的PWM初始化代码(以STM32为例):
// 定时器2,通道1输出PWM,频率20kHz
void PWM_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 使能时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// PA0配置为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 定时器配置:72MHz / (72 * 50) = 20kHz
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = 3; // 预分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// PWM模式1,占空比初始为0
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
代码写完了,怎么调占空比?很简单:
// 设置占空比为50%
TIM_SetCompare1(TIM2, 500); // 500 / 1000 = 50%
小技巧:我习惯把占空比用「千分比」来表示。比如500就是50%,1000就是100%。这样计算方便,而且精度够用。你如果喜欢用百分比,也可以自己封装一个函数。
3.2 死区时间设置:别让上下管「打架」
如果你用的是H桥驱动电路(比如L298N、TB6612),那死区时间就是个绕不开的话题。
什么是死区时间?
H桥有四个开关管,上管和下管不能同时导通。一旦同时导通,电源直接短路,电流瞬间飙升——管子烧了,板子冒烟了,项目凉了。
我曾经就吃过这个亏。调试一个电机驱动板,手一抖,上下管同时开了0.5微秒。就这0.5微秒,MOS管直接炸了,还带走了旁边的电容。嗯,从那以后,我再也不敢忽视死区时间了。
死区时间的设置,说白了就是在切换上下管时,插入一段「都关断」的时间。比如上管关掉后,等1微秒,再打开下管。这1微秒就是死区时间。
死区时间怎么设?
- 太短:有短路风险,管子可能烧
- 太长:电机换向不流畅,效率降低,甚至产生噪音
一般建议:死区时间 = 开关管关断时间 × 1.5 ~ 2倍。比如你的MOS管关断时间是200ns,那死区时间设300~400ns就够。
在STM32的高级定时器里,死区时间是这样配置的:
// 死区时间设置:100个时钟周期
// 假设定时器时钟72MHz,100个周期 ≈ 1.39微秒
TIM_BreakDeadTimeConfig(TIM1, 0xFF, 0x0D, TIM_Break_Enable,
TIM_AutomaticOutput_Enable, TIM_OSSRIState_Enable,
TIM_OSSRState_Enable);
警告:如果你用的是现成的驱动模块(比如L298N),它内部已经处理了死区时间,你不需要额外设置。但如果你是自己搭H桥,死区时间必须自己算清楚。别问我怎么知道的——我烧过三块板子才记住这个教训。
3.3 电机正反转:H桥的「换向」逻辑
直流有刷电机要反转,其实就一句话:把电源的正负极对调。
在H桥里,这个操作通过控制四个开关管来实现:
| 模式 | Q1(左上) | Q2(右上) | Q3(左下) | Q4(右下) |
|---|---|---|---|---|
| 正转 | ON | OFF | OFF | ON |
| 反转 | OFF | ON | ON | OFF |
| 刹车 | OFF | OFF | OFF | OFF |
| 滑行 | OFF | OFF | OFF | OFF |
注意看,正转和反转其实就是两组对角管交替导通。而刹车和滑行看起来一样,但实际效果不同——刹车是让电机端子短路,利用反电动势快速制动;滑行是让电机自由旋转,靠摩擦力慢慢停。
代码实现也很直接:
// 电机正转
void Motor_Forward(void) {
GPIO_SetBits(GPIOB, GPIO_Pin_0); // IN1 = 1
GPIO_ResetBits(GPIOB, GPIO_Pin_1); // IN2 = 0
}
// 电机反转
void Motor_Reverse(void) {
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // IN1 = 0
GPIO_SetBits(GPIOB, GPIO_Pin_1); // IN2 = 1
}
// 电机刹车
void Motor_Brake(void) {
GPIO_SetBits(GPIOB, GPIO_Pin_0); // IN1 = 1
GPIO_SetBits(GPIOB, GPIO_Pin_1); // IN2 = 1
}
// 电机滑行
void Motor_Coast(void) {
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // IN1 = 0
GPIO_ResetBits(GPIOB, GPIO_Pin_1); // IN2 = 0
}
个人经验:我建议你在切换正反转时,先让电机刹车50ms,再切换方向。为什么?因为电机有惯性,直接反向通电会产生很大的冲击电流,容易烧驱动。先刹车让电机停下来,再反转,电流就平滑多了。
3.4 速度开环控制实验:让电机「听话」
好了,前面都是铺垫。现在我们来做一个完整的实验:让电机以指定的速度旋转。
开环控制是什么意思?就是只发指令,不检测实际转速。你给50%占空比,电机就跑50%的速度——至于它到底跑了多少转,我们不管。
实验步骤很简单:
- 初始化PWM和GPIO
- 设置占空比(比如50%)
- 设置方向(正转)
- 等待一段时间
- 改变占空比或方向
完整代码示例:
int main(void) {
// 初始化
PWM_Init();
GPIO_Init();
while(1) {
// 正转,50%速度,持续3秒
Motor_Forward();
TIM_SetCompare1(TIM2, 500);
Delay_ms(3000);
// 正转,100%速度,持续3秒
TIM_SetCompare1(TIM2, 1000);
Delay_ms(3000);
// 刹车,停1秒
Motor_Brake();
TIM_SetCompare1(TIM2, 0);
Delay_ms(1000);
// 反转,75%速度,持续3秒
Motor_Reverse();
TIM_SetCompare1(TIM2, 750);
Delay_ms(3000);
// 滑行,停2秒
Motor_Coast();
TIM_SetCompare1(TIM2, 0);
Delay_ms(2000);
}
}
你运行这个程序,会看到电机先慢速正转,再快速正转,然后刹车停住,接着反转,最后滑行停下。
实验要点:
- 占空比从0到1000,对应0%到100%
- 正反转切换前,建议先刹车50ms
- 滑行和刹车的区别:滑行是自由停止,刹车是强制停止
- 开环控制下,电机实际转速受负载影响很大——你用手捏住轴,转速就会下降
说到负载影响,我当年做第一个电机项目时就踩过坑。我设了50%占空比,空载时转速很稳。结果一装上轮子,转速直接掉到30%。我当时还以为是代码写错了,查了半天。后来才明白——开环控制不抗负载扰动。这就是为什么后面我们要学闭环控制。
3.5 本章小结
这一章我们干了三件事:
- PWM生成:用定时器输出PWM波,控制电机平均电压
- 死区时间设置:防止H桥上下管直通,保护硬件
- 正反转控制:通过H桥切换电流方向,实现电机换向
- 开环速度控制:用占空比控制速度,简单但精度有限
下一章,我们会引入编码器,开始做闭环速度控制。到时候你会发现,有了反馈,电机才能真正「听话」。
嗯,今天就到这里。去动手试试吧——让电机转起来,比看十遍文章都管用。