4、基础驱动代码:使用Arduino控制H桥正反转、PWM调速原理、死区时间的软件实现
好,咱们直接进入正题。前面我们把H桥的硬件原理讲透了,现在该让代码跑起来了。
我个人习惯用Arduino来做原型验证,原因很简单:上手快,调试方便。你想想看,如果每次调个PWM都要翻半天手册配寄存器,那多耽误事儿。先用Arduino把逻辑跑通,再移植到STM32或者其它MCU上,这是我最常用的套路。
4.1 基础正反转控制
先来个最简单的。控制两个电机引脚的高低电平,就能让电机转起来。
看下面这段代码,我故意把注释写得很详细,方便你理解:
// H桥电机控制 - 基础正反转
// 引脚定义
#define MOTOR_IN1 9 // H桥输入1
#define MOTOR_IN2 10 // H桥输入2
void setup() {
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
// 初始状态:电机停止
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
}
void loop() {
// 正转:IN1高,IN2低
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
delay(2000); // 转2秒
// 停止
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delay(1000); // 停1秒
// 反转:IN1低,IN2高
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
delay(2000); // 转2秒
// 停止
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delay(1000); // 停1秒
}
小提示:千万别让IN1和IN2同时为高!那就是短路,我见过有人把驱动芯片烧冒烟的。嗯,真事儿。
4.2 PWM调速原理
光能正反转还不够,速度也得能调。这时候就要请出PWM了。
PWM,说白了就是快速开关电源。你想想看,如果一秒钟开关1000次,每次开的时间长一点,电机得到的平均电压就高,转得就快。反之就慢。
这个比例叫占空比。占空比50%,相当于给电机一半的电压。占空比100%,就是全速。
Arduino的analogWrite()函数就是干这个的。取值范围0到255,对应0%到100%的占空比。
// PWM调速示例
#define MOTOR_IN1 9
#define MOTOR_IN2 10
#define PWM_PIN 9 // 用带PWM功能的引脚
void setup() {
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
}
void loop() {
// 正转,速度逐渐加快
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
for (int speed = 0; speed <= 255; speed += 5) {
analogWrite(PWM_PIN, speed);
delay(100);
}
// 全速转2秒
delay(2000);
// 减速停止
for (int speed = 255; speed >= 0; speed -= 5) {
analogWrite(PWM_PIN, speed);
delay(100);
}
delay(1000);
}
关键点:PWM频率不能太低。我试过用50Hz去驱动电机,结果电机嗡嗡响,跟蚊子叫似的。一般电机驱动用1kHz到20kHz比较合适。Arduino默认的PWM频率是490Hz或980Hz,够用。
4.3 死区时间的软件实现
这是个大坑,我得好好说说。
死区时间是什么?就是H桥上下两个管子切换时,故意插入的一段「都关断」的时间。为什么要这么做?
因为MOS管开关需要时间。你让上管关断的同时让下管导通,上管还没完全关断呢,下管已经通了——瞬间短路!这叫「直通」,电流会大得吓人。
我曾经在一个项目中忽略了这个问题,结果驱动板上的MOS管烫得能煎鸡蛋。从那以后,我写驱动代码必加死区。
软件实现死区其实很简单:在切换方向时,先让两个管子都关断,等一小会儿,再打开目标管子。
// 带死区时间的H桥控制
#define MOTOR_IN1 9
#define MOTOR_IN2 10
#define DEAD_TIME 100 // 死区时间,单位微秒
void motor_forward(int speed) {
// 先全部关断
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delayMicroseconds(DEAD_TIME); // 死区等待
// 设置正转
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
analogWrite(MOTOR_IN1, speed);
}
void motor_reverse(int speed) {
// 先全部关断
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delayMicroseconds(DEAD_TIME); // 死区等待
// 设置反转
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
analogWrite(MOTOR_IN2, speed);
}
void motor_stop() {
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
}
void setup() {
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
motor_stop();
}
void loop() {
motor_forward(200); // 正转,速度200
delay(3000);
motor_stop();
delay(1000);
motor_reverse(200); // 反转,速度200
delay(3000);
motor_stop();
delay(1000);
}
警告:死区时间不是越长越好。太长了会影响电机响应速度,而且会产生噪音。一般1到10微秒就够了。具体多少,得看你用的MOS管型号和驱动电路。
4.4 知识结构图
下面这张图,把本章的核心逻辑串起来了:
4.5 实际项目中的避坑指南
- 电源要稳:电机启动瞬间电流很大,我见过Arduino直接被拉复位的情况。建议电机和逻辑电路分开供电。
- 地线要粗:大电流回路的地线,别用细线。我曾经用杜邦线接过电机,结果线都烧化了。
- PWM频率别太低:低于100Hz电机就会抖动,听着像在打拍子。
- 死区时间别省:省了死区,省下的钱全得买新MOS管。
我的习惯:调试时先用小电压(比如5V)测试逻辑,确认正反转和PWM都正常了,再接上电机的大电源。这样就算代码写错了,也不至于炸管子。
好了,这一章的内容就到这儿。代码都给你了,找个板子试试吧。记住,理论再漂亮,不如亲手跑一遍来得实在。