4. GPIO控制电机正反转:用ESP32让窗帘电机听话
好,咱们进入实操环节。
前面讲了H桥的原理,说白了就是四个开关管,控制电流方向。那谁来控制这些开关管?就是ESP32的GPIO引脚。我刚开始做这个的时候,以为直接接上就能转,结果电机纹丝不动——嗯,后来才发现是逻辑电平没对上。
4.1 硬件连接:GPIO怎么接H桥
拿常用的L298N模块举例。这个模块有两个使能端和四个输入脚。控制一个电机,你需要两个GPIO。
| 电机动作 | IN1 (GPIO) | IN2 (GPIO) | ENA (使能) |
|---|---|---|---|
| 正转 | 高电平 (1) | 低电平 (0) | 高电平 (PWM) |
| 反转 | 低电平 (0) | 高电平 (1) | 高电平 (PWM) |
| 停止 | 低电平 (0) | 低电平 (0) | 低电平 |
| 刹车 | 高电平 (1) | 高电平 (1) | 高电平 |
我个人习惯把IN1接GPIO12,IN2接GPIO14。为什么选这两个?因为ESP32有些引脚上电瞬间会输出高电平,容易让电机乱转。GPIO12和14是相对安全的。
⚠️ 避坑指南:
我曾经踩过一个坑——把电机驱动引脚接到了GPIO0上。结果每次下载程序时电机都会突然转一下。后来查手册才发现,GPIO0是下载模式选择脚,上电时序有特殊要求。所以,尽量避开GPIO0、GPIO2、GPIO15这些特殊引脚。
4.2 基础控制代码:让电机转起来
代码其实很简单。你想想看,就是控制两个引脚的电平高低。
// 定义引脚
#define MOTOR_IN1 12
#define MOTOR_IN2 14
void setup() {
// 设置引脚为输出模式
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
// 初始状态:电机停止
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
Serial.begin(115200);
Serial.println("电机控制初始化完成");
}
void loop() {
// 正转2秒
Serial.println("正转");
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
delay(2000);
// 停止1秒
Serial.println("停止");
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delay(1000);
// 反转2秒
Serial.println("反转");
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
delay(2000);
// 停止1秒
Serial.println("停止");
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
delay(1000);
}
💡 我的小技巧:
实际项目中,我不会用delay(),因为会阻塞程序。我会用定时器或者millis()来做非阻塞延时。但初学阶段,delay()最直观,先跑通再说。
4.3 封装成函数:让代码更优雅
每次写digitalWrite太啰嗦了。我习惯封装成函数,调用起来一目了然。
// 电机控制函数
void motorForward() {
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
Serial.println("电机正转");
}
void motorReverse() {
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
Serial.println("电机反转");
}
void motorStop() {
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, LOW);
Serial.println("电机停止");
}
void motorBrake() {
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, HIGH);
Serial.println("电机制动");
}
void setup() {
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
motorStop(); // 初始停止
Serial.begin(115200);
}
void loop() {
motorForward();
delay(2000);
motorStop();
delay(1000);
motorReverse();
delay(2000);
motorStop();
delay(1000);
}
你看,这样代码可读性就好多了。以后要改逻辑,直接调函数就行。
4.4 使能控制:用PWM调速
上面代码里,ENA使能脚我直接接了高电平。但实际窗帘电机需要调速——开窗时慢一点,关窗时快一点。这时候就要用PWM了。
ENA接一个支持PWM的GPIO,比如GPIO13。然后这样写:
#define MOTOR_ENA 13
void setup() {
pinMode(MOTOR_IN1, OUTPUT);
pinMode(MOTOR_IN2, OUTPUT);
pinMode(MOTOR_ENA, OUTPUT);
// 设置PWM频率和分辨率
ledcSetup(0, 5000, 8); // 通道0,5kHz,8位分辨率
ledcAttachPin(MOTOR_ENA, 0);
motorStop();
}
void motorForwardWithSpeed(int speed) {
digitalWrite(MOTOR_IN1, HIGH);
digitalWrite(MOTOR_IN2, LOW);
ledcWrite(0, speed); // speed范围0-255
}
void motorReverseWithSpeed(int speed) {
digitalWrite(MOTOR_IN1, LOW);
digitalWrite(MOTOR_IN2, HIGH);
ledcWrite(0, speed);
}
void loop() {
// 慢速正转
motorForwardWithSpeed(100);
delay(2000);
// 快速正转
motorForwardWithSpeed(255);
delay(2000);
motorStop();
delay(1000);
}
🔑 关键点:
PWM频率我选5kHz,这是电机驱动的常用值。频率太低电机会嗡嗡响,频率太高MOS管开关损耗大。5kHz是个折中值,我在多个项目里验证过。
4.5 实际项目中的注意事项
- 电源要分开:电机驱动电源和ESP32电源不要共用。电机启动瞬间电流很大,会把ESP32拉复位。我吃过这个亏,后来老老实实加了隔离。
- 加续流二极管:虽然L298N内部有,但如果是自己搭H桥,一定要在电机两端反向并联二极管,否则关断瞬间的反向电动势会烧管子。
- 逻辑电平匹配:ESP32是3.3V逻辑,有些H桥模块需要5V逻辑。这时候要加电平转换,或者选3.3V兼容的模块。
- 启动电流:窗帘电机堵转时电流可能到1-2A。选H桥模块时留足余量,别用那种芝麻大的驱动板。
好了,代码写完了,上传到ESP32试试。如果电机不转,先检查接线,再用万用表量GPIO电平。我当年调试时,有半天时间都耗在了一根杜邦线接触不良上——嗯,这种经历估计你也会遇到。