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电平。我当年调试时,有半天时间都耗在了一根杜邦线接触不良上——嗯,这种经历估计你也会遇到。