Skip to content

附录 B:风扇(H 桥)控制失效的常见原因与排查

本附录以 D3 小车的风扇为例(正转 GP18、反转 GP19)。使用《跬步少年编程基础教具包》的学员请注意:教具包的风扇接 GP16(反转)与 GP17(正转),引脚号不同,但原理和排查方法完全一样,对照着换两个引脚号即可。

一、什么叫"失效"

程序让风扇正转,它不正转;让它停,它不停;让它转慢一点,它却全速狂转……总之,电机的实际表现和程序的设计目的对不上,都叫失效

遇到失效先别急着怀疑代码写错了。按下面两类原因顺序排查,九成问题都能解决。

二、原因一:线松了(GND / INA / INB 任一断路)

H 桥模块一共接四根线:VCC、GND、INA、INB。失效时电机往往还在转(只是不受控),这说明 VCC 通常是通的,重点排查剩下三根:GND、INA(D3 接 GP18)、INB(D3 接 GP19)——任意一根接触不良或断路,控制信号就会异常。

排查方法(需要万用表)

1. 查 GND:万用表红笔接 Pico 扩展板的 VCC 输出,黑笔接 H 桥模块的 GND 引脚。显示 3.3V 或 5V,说明 GND 回路正常。

2. 查 GP18(INA):上传下面这段测试程序(GP18 输出高电平、GP19 输出低电平),红笔接 GP18、黑笔接 GND,显示 3.3V,说明 GP18 线路连通。

cpp
void setup() {
    pinMode(18, OUTPUT);
    pinMode(19, OUTPUT);
}

void loop() {
    digitalWrite(18, HIGH);
    digitalWrite(19, LOW);
}

3. 查 GP19(INB):换一段程序,让 GP19 输出高电平、GP18 输出低电平,同样方法测 GP19。

cpp
void setup() {
    pinMode(18, OUTPUT);
    pinMode(19, OUTPUT);
}

void loop() {
    digitalWrite(18, LOW);
    digitalWrite(19, HIGH);
}

排查通断只需要输出固定的高/低电平,不用测 PWM。哪根线不通,重新插紧或换一根杜邦线即可。

三、原因二:引脚模式"挂挡"失败(用米思齐 Mixly 的学员高发)

先看两张图找不同

同样是"正转 5 秒 → 停 2 秒 → 反转 5 秒"的积木程序,分别用 Mind+ 和米思齐(Mixly)来自动生成 Arduino 代码:

Mind+ 生成的风扇控制代码

米思齐生成的风扇控制代码

对比可以看出:积木一模一样,生成的代码却有一处关键差异:米思齐在 setup() 里自动多写了 pinMode(18, OUTPUT); pinMode(19, OUTPUT); 两行,Mind+ 则没有。

别小看这两行——它就是风扇失效的根源。米思齐生成的程序上传到 Pico 后,风扇会出现全速乱转、该停时不停等失效现象;Mind+ 生成的程序没有这两行,运行完全正常。

原理:引脚"挂挡"后不会自动换挡

Pico 用的 RP2040 芯片,每个引脚像一辆手动挡汽车,同一个引脚可以挂在不同的"挡位"上工作:

挡位功能谁用它
GPIO_FUNC_SIO普通数字输入/输出pinMode()digitalWrite()
GPIO_FUNC_PWM硬件 PWM 输出analogWrite()

第一次调用哪个函数,引脚就挂到哪个挡位,这一步核心库会自动完成。问题在于:挂上挡之后,再想换到另一个挡,核心库不会自动完成切换。

  • 先执行 pinMode(18, OUTPUT),引脚被挂到数字口(SIO 挡)。之后 analogWrite(18, 200) 想输出 PWM 时,引脚还卡在数字口上,PWM 信号根本送不到物理引脚——引脚停在固定电平,相当于一直给 H 桥发送"正转"或"停止"的固定指令,于是风扇全速乱转、该停不停,连 analogWrite(18, 0) 都关不掉它;
  • 反过来也一样:先 analogWrite 挂到 PWM 挡,再用 digitalWrite 就切不回去了,引脚输出的还是 PWM 信号。

米思齐生成的代码正是踩了第一个坑:一上电就用 pinMode 把 GP18、GP19 挂到了数字口,后面 loop() 里的 analogWrite 全部失效。

解决办法

一个引脚全程只用一种写法,不要混用。

风扇的正转、停止、反转全部用 analogWrite 实现:

cpp
void setup() {
    // 这里什么都不用写,尤其不要写 pinMode
}

void loop() {
    analogWrite(18, 200);  // 正转
    analogWrite(19, 0);
    delay(5000);

    analogWrite(18, 0);    // 停止(占空比为 0)
    analogWrite(19, 0);
    delay(2000);

    analogWrite(18, 0);    // 反转
    analogWrite(19, 200);
    delay(5000);
}

记住一条:analogWrite() 不需要先调用 pinMode()。在 Pico 上,先写 pinMode() 反而会把 PWM 废掉。

  • 米思齐用户:上传前把 setup() 里自动生成的 pinMode(18, OUTPUT); pinMode(19, OUTPUT); 两行删掉或注释掉;
  • Mind+ 用户:生成的代码本来就没有这两行,无需处理;
  • MicroPython(Python 模式)用户PWM(Pin(18)) 是显式创建 PWM 对象的写法,不存在这种隐式抢挡的问题。

进阶:确实需要混用时(了解即可)

如果程序逻辑真的需要在同一个引脚上交替使用 PWM 和数字输出,那就得用 gpio_set_function() 手动换挡:

cpp
#include <Arduino.h>

void setup() {
    gpio_set_function(18, GPIO_FUNC_PWM);   // 手动挂到 PWM 挡
    gpio_set_function(19, GPIO_FUNC_PWM);
}

void loop() {
    analogWrite(18, 200);   // 正转
    analogWrite(19, 0);
    delay(3000);

    gpio_set_function(18, GPIO_FUNC_SIO);   // 手动换回数字口挡
    gpio_set_function(19, GPIO_FUNC_SIO);
    digitalWrite(18, LOW);  // 停止
    digitalWrite(19, LOW);
    delay(3000);
}

<Arduino.h> 已经包含了 Pico SDK 的头文件,gpio_set_function() 可以直接用,不用额外 #include

注意analogWrite(pin, 0) 只是把 PWM 占空比调到 0%,引脚还停在 PWM 挡,并没有切回数字口。要彻底切换,必须用 gpio_set_function()

四、快速排查清单

风扇不听话时,对照下表从上往下查:

检查项正常标准
GND 线路万用表红笔接 VCC、黑笔接 GND,显示 3.3V / 5V
GP18 线路测试程序输出高电平,红笔接 GP18、黑笔接 GND,显示 3.3V
GP19 线路测试程序输出高电平,红笔接 GP19、黑笔接 GND,显示 3.3V
程序初始化setup() 里没有 pinMode(18, OUTPUT)pinMode(19, OUTPUT)
调速与停止analogWrite(pin, 0) 实现,不借助 digitalWrite()pinMode()
写法一致性一个引脚全程 analogWrite,或全程 digitalWrite,不混用

不积跬步,无以至千里;万里之行,始于当下。