1. 为什么我选择用 MicroPython 来折腾 RP2040
1.1 一块不到一杯咖啡钱的板子,凭什么值得花时间
Raspberry Pi Pico 这块板子刚出来的时候,我其实没太在意。市面上几十块钱的开发板太多了,STM32 蓝板、ESP32 模块,哪个不是久经沙场的老将。但真正上手之后我才发现,RP2040 这颗芯片配合 MicroPython,对刚接触硬件开发的人来说,友好程度完全不在一个量级上。
先说价格。Pico 官方板子不到三十块,国产兼容板更便宜,十几块钱就能拿到。这个价格意味着你可以一次性买五六块,一块做小车底盘控制,一块做传感器采集,一块专门跑通信协议,烧了也不心疼。我见过太多新手因为一块 STM32 最小系统板烧了之后就畏手畏脚,不敢再动手,这其实特别影响学习进度。
再说 RP2040 这颗芯片本身。双核 Cortex-M0+,主频默认 133MHz,可以超频到 200MHz 以上,264KB 的 SRAM,外设方面有 2 路 UART、2 路 SPI、2 路 I2C、16 路 PWM、3 路 ADC,还有那个非常好用的 PIO(可编程 I/O)。这个配置放在十年前是高端 MCU 的水平,现在被塞进了一块邮票大小的板子里。
但真正让我决定把它作为教学平台的原因,是 MicroPython。你不需要装 Keil、不需要配 ARM 工具链、不需要理解启动文件里那一堆汇编,插上 USB 线,电脑上出现一个 U 盘,把 Python 文件拖进去,代码就跑起来了。这个体验对新手来说太重要了,它把“从零到点亮一颗 LED”的时间从半天压缩到了五分钟。
1.2 MicroPython 到底是个什么东西,和 CPython 有什么区别
很多人第一次听到 MicroPython 会以为是 Python 的某个精简版,这个理解方向对,但不够准确。MicroPython 是一个完全独立的 Python 3 解释器实现,专门为微控制器设计。它和你在电脑上用的 CPython 共享几乎相同的语法,但底层实现完全不同。
CPython 跑在操作系统之上,有虚拟内存、有文件系统、有进程调度。MicroPython 直接跑在裸机上,没有操作系统,内存管理全靠自己。这意味着两件事:第一,你不能在 MicroPython 里用numpy、requests这些依赖操作系统的库;第二,你必须对内存使用有概念,不能随便创建大列表。
但 MicroPython 也做了很多针对硬件的扩展。比如machine模块提供了 Pin、PWM、ADC、I2C、SPI、UART 这些类,time模块提供了sleep_ms、ticks_ms这些微控制器友好的函数。这些在 CPython 里是没有的,因为 CPython 不需要直接操作硬件寄存器。
我经常用这样一个类比来解释:CPython 像是一辆自动挡的家用轿车,你只管踩油门刹车,其他的它帮你处理好了;MicroPython 像是一辆手动挡的越野车,你需要自己换挡、自己判断路况,但它能带你去轿车去不了的地方。
1.3 这套教程适合谁,学完能做什么
这套内容我定位得很清楚:面向完全没有硬件开发经验,但有一点编程基础的人。如果你写过 Python,哪怕只是写过几个脚本处理 Excel,那你的基础就够了。如果你连 Python 都没碰过,也没关系,MicroPython 的语法足够简单,边做边学完全来得及。
学完之后你能做什么?我给你几个具体的场景。你可以做一个温湿度监控器,把数据传到电脑上画成曲线;你可以做一个小车,用 PWM 控制两个电机的转速,用 GPIO 读取红外避障传感器的信号;你可以做一个舵机云台,用 PWM 信号控制舵机角度,配合摄像头做简单的目标追踪。这些项目听起来不复杂,但每一个都涵盖了硬件开发的核心技能:GPIO 输入输出、PWM 生成、ADC 采样、串口通信、定时器中断。
更重要的是,你会建立起一套调试硬件的方法论。硬件开发和纯软件开发最大的区别在于,软件出错你能看到报错信息,硬件出错可能什么反应都没有。学会用万用表量电压、用逻辑分析仪看波形、用串口打印调试信息,这些技能比会写几行代码重要得多。
2. 动手之前,先把开发环境搭明白
2.1 固件烧录:从一块空白板子到能跑 Python
拿到 Pico 的第一件事是烧录 MicroPython 固件。这个过程比 STM32 烧录简单太多,不需要 ST-Link,不需要 J-Link,一根 USB 线就够了。
具体操作步骤是这样的。先按住板子上的 BOOTSEL 按钮不放,然后用 USB 线把 Pico 连接到电脑。注意顺序不能反,必须先按住按钮再插线。连接成功后,电脑上会出现一个名为 RPI-RP2 的 U 盘。这时候松开按钮,把下载好的 MicroPython 固件文件(.uf2 格式)拖进这个 U 盘。拖进去的瞬间,U 盘会自动弹出,板子自动重启,MicroPython 就烧好了。
我第一次做这个操作的时候犯了个错误,先插线再按按钮,结果电脑识别出来的是一个串口设备而不是 U 盘。后来查资料才知道,BOOTSEL 按钮必须在通电之前就按住,芯片上电时会检测这个引脚的状态,如果被拉低就进入固件下载模式。
固件下载地址在 Raspberry Pi 官网上,选择对应 Pico 型号的 MicroPython 固件就行。这里有个细节要注意,Pico 和 Pico W 的固件不一样,Pico W 带无线功能,固件里包含了网络相关的模块。如果你买的是 Pico W 但烧了普通 Pico 的固件,无线功能就用不了。
2.2 Thonny:新手最省心的代码编辑器
烧好固件之后,你需要一个工具来写代码、传文件、看输出。市面上有好几个选择,Thonny、VS Code + Pico-Go 插件、Mu Editor,我都试过。对于刚入门的人来说,我强烈推荐 Thonny。
Thonny 的好处是它把三件事集成在了一起:代码编辑、文件管理、串口终端。你不需要在多个软件之间切换,打开 Thonny 就能完成所有操作。而且它有一个专门为 MicroPython 设计的“MicroPython (Raspberry Pi Pico)”解释器选项,选中之后,Thonny 会自动检测 Pico 的串口,你点运行按钮,代码就直接在板子上执行了。
安装 Thonny 之后,在“工具”菜单里找到“选项”,在“解释器”标签页里选择“MicroPython (Raspberry Pi Pico)”,端口一般会自动识别。如果识别不到,检查一下 USB 线是不是只能充电不能传数据的那种,这种线在市面上很常见,我至少被坑过三次。
连接成功之后,Thonny 底部的 Shell 窗口会出现 MicroPython 的版本信息和>>>提示符。这时候你就可以在 Shell 里直接输入 Python 代码并立即看到结果。比如输入print("hello"),回车之后就会打印出 hello。这个交互式环境对调试特别有用,你可以随时测试某个函数的行为,不用每次都写完整的脚本。
2.3 文件系统:main.py 和 boot.py 的分工
Pico 烧录 MicroPython 之后,内部会有一个小的文件系统,你可以往里面存 Python 文件。这个文件系统在 Thonny 里能看到,在“视图”菜单里勾选“文件”,左侧就会出现两个面板,上面是电脑的文件,下面是 Pico 的文件。
有两个文件名有特殊含义:boot.py和main.py。板子上电或者复位之后,MicroPython 会先执行boot.py,再执行main.py。boot.py一般用来做初始化配置,比如设置 CPU 频率、配置网络连接。main.py是你的主程序,每次上电都会自动运行。
我建议新手把主要代码都写在main.py里,boot.py保持默认不动。因为如果你在boot.py里写了死循环或者阻塞代码,板子会卡在启动阶段,你连 Shell 都进不去,只能重新烧录固件。这个坑我踩过,当时在boot.py里写了一个等待按键的循环,结果板子一上电就卡住,折腾了半小时才反应过来是boot.py的问题。
另外要注意的是,往 Pico 里写文件的时候,如果main.py正在运行,可能会遇到文件被占用的情况。这时候按一下板子上的 RUN 按钮复位,或者拔插 USB 重新连接,就能正常写入了。
3. GPIO 操作:从点亮一颗 LED 开始理解硬件编程
3.1 GPIO 到底是什么,为什么说它是硬件开发的基石
GPIO 这三个字母是 General Purpose Input Output 的缩写,翻译过来就是通用输入输出。你可以把它理解成芯片上的一根引脚,这根引脚可以被程序控制成两种状态:输出高电平或者输出低电平,也可以被配置成输入模式,读取外部信号的高低电平。
听起来很简单,但整个硬件开发的大厦就是建立在这个基础之上的。你控制 LED 亮灭,是 GPIO 输出;你读取按键按下,是 GPIO 输入;你驱动电机转动,底层也是 GPIO 在输出 PWM 波形;你和传感器通信,I2C 和 SPI 本质上也是 GPIO 在按照特定时序翻转电平。
RP2040 一共有 30 个 GPIO 引脚,其中 Pico 板子引出了 26 个。每个引脚都可以独立配置为输入或输出,也可以配置内部上拉或下拉电阻。这里说的上拉和下拉,是指在引脚内部接一个电阻到电源正极或地。上拉就是把引脚默认拉到高电平,下拉就是默认拉到低电平。这个功能在读取按键的时候特别有用,因为按键一端接引脚、一端接地,如果不加上拉电阻,引脚在按键松开时处于悬空状态,读出来的值是不确定的。
3.2 用 MicroPython 控制 LED:三行代码背后的逻辑
在 Pico 上点亮一颗 LED 只需要三行代码。Pico 板子上有一颗板载 LED,连接在 GPIO 25 上。
from machine import Pin led = Pin(25, Pin.OUT) led.value(1)第一行从machine模块导入Pin类。第二行创建一个Pin对象,指定引脚编号 25,模式为输出。第三行把引脚电平设为高,LED 就亮了。把led.value(1)改成led.value(0),LED 就灭了。
这三行代码看起来简单,但每一行都有值得展开的地方。Pin(25, Pin.OUT)这个构造函数做了很多事情:它找到了 GPIO 25 对应的硬件寄存器,配置了引脚方向为输出,关闭了上拉和下拉电阻,设置了驱动能力。这些操作在 STM32 上需要写好几行寄存器配置代码,MicroPython 把它封装成了一个构造函数。
led.value(1)这个调用也不是简单地写一个寄存器。它先读取当前引脚的状态,然后修改对应的位,再写回去。这样做是为了避免影响同一个端口上其他引脚的状态。如果你直接操作寄存器,可能会不小心把其他引脚的状态也改了。
除了value()方法,Pin对象还有on()、off()、toggle()这几个方法。on()等同于value(1),off()等同于value(0),toggle()是翻转当前状态。我平时写代码更喜欢用on()和off(),因为可读性更好,一眼就能看出是在开还是在关。
3.3 按键输入:上拉电阻和消抖的实际处理
读取按键比控制 LED 稍微复杂一点,因为涉及到上拉电阻和按键消抖两个问题。
先看硬件连接。按键的一端接 GPIO 引脚,另一端接地。当按键按下时,引脚被直接拉到地,读出来是低电平。当按键松开时,引脚悬空,读出来的值不确定。所以我们需要在引脚内部启用上拉电阻,让引脚在按键松开时保持高电平。
from machine import Pin import time button = Pin(14, Pin.IN, Pin.PULL_UP) while True: if button.value() == 0: print("按键按下") time.sleep_ms(20) while button.value() == 0: pass print("按键释放")这段代码里,Pin.PULL_UP就是启用内部上拉电阻。button.value() == 0判断按键是否按下,因为按下时引脚接地,读出来是 0。
time.sleep_ms(20)这一行是按键消抖。机械按键在按下和松开的瞬间,触点会产生抖动,电平会在高低之间快速跳变几次,持续时间大概几毫秒到十几毫秒。如果不处理,一次按下可能会被识别成多次。延时 20 毫秒之后再检测,抖动已经结束了,读到的就是稳定的电平。
后面的while button.value() == 0: pass是等待按键释放。如果没有这个循环,按键按住不放的时候,程序会不停地打印“按键按下”。加上这个循环之后,只有松开按键才会打印“按键释放”,逻辑更清晰。
我实测下来,20 毫秒的消抖时间对大多数按键都够用。如果你用的是那种特别便宜的微动开关,可能需要加到 50 毫秒。消抖时间太长会影响响应速度,太短又消不干净,20 到 50 毫秒是比较合适的范围。
3.4 GPIO 的八种工作模式:RP2040 和 STM32 的对比
网上经常有人搜“GPIO 的八种工作模式”,这其实是 STM32 的说法。STM32 的 GPIO 可以配置为浮空输入、上拉输入、下拉输入、模拟输入、开漏输出、推挽输出、开漏复用、推挽复用这八种模式。每种模式对应不同的硬件电路结构,适用于不同的场景。
RP2040 的 GPIO 模式没有 STM32 那么复杂,但核心概念是相通的。RP2040 的每个 GPIO 可以配置为输入或输出,可以启用或禁用上拉和下拉电阻,可以配置驱动强度(2mA、4mA、8mA、12mA),可以配置压摆率(慢速或快速)。虽然没有明确的“八种模式”分类,但通过组合这些配置,可以实现和 STM32 类似的功能。
举个例子,STM32 的推挽输出对应 RP2040 的普通输出模式,驱动能力强,可以同时输出高电平和低电平。STM32 的开漏输出对应 RP2040 的输出模式加上禁用上拉,这种模式下引脚只能拉低,不能拉高,需要外部上拉电阻才能输出高电平。开漏输出在 I2C 总线上很常见,因为多个设备可以共享一根数据线,任何一个设备拉低都能被其他设备检测到。
对于新手来说,不需要一开始就记住所有模式。先把输入和输出两种模式用熟,遇到具体问题再查对应的配置方式。我见过太多人卡在“GPIO 模式如何选择”这个问题上,其实大多数场景下,普通输入加内部上拉、普通推挽输出这两种配置就能解决 90% 的问题。
4. PWM 控制:让电机转起来,让舵机动起来
4.1 PWM 是什么:用方波模拟模拟量
PWM 是 Pulse Width Modulation 的缩写,翻译过来是脉冲宽度调制。它的核心思想很简单:通过快速切换引脚的高低电平,来控制输出功率。如果高电平占整个周期的比例大,平均电压就高;如果低电平占的比例大,平均电压就低。
这里有两个关键参数:频率和占空比。频率是指一秒钟内有多少个周期,单位是赫兹。占空比是指高电平时间占整个周期的百分比。比如频率 1000Hz、占空比 50%,意思是一秒钟有 1000 个周期,每个周期 1 毫秒,其中高电平 0.5 毫秒,低电平 0.5 毫秒。
RP2040 的 PWM 外设一共有 8 个切片,每个切片可以输出两路 PWM 信号,所以总共 16 路 PWM。每路 PWM 的频率和占空比都可以独立设置。频率范围从几赫兹到几十兆赫兹,占空比分辨率默认是 16 位,也就是可以设置 0 到 65535 之间的值。
在 MicroPython 里使用 PWM 很简单:
from machine import Pin, PWM pwm = PWM(Pin(15)) pwm.freq(1000) pwm.duty_u16(32768)PWM(Pin(15))在 GPIO 15 上创建一个 PWM 对象。pwm.freq(1000)设置频率为 1000Hz。pwm.duty_u16(32768)设置占空比为 50%,因为 32768 是 65535 的一半。
4.2 用 PWM 驱动直流电机:频率选择和电路注意事项
用 PWM 控制直流电机转速是最常见的应用之一。电机两端加上 PWM 信号,占空比越高,平均电压越高,转速越快。
但这里有几个坑要注意。首先是频率选择。如果频率太低,比如几十赫兹,电机会发出明显的啸叫声,因为电机线圈在低频 PWM 下会产生机械振动。如果频率太高,比如几十千赫兹,电机驱动电路(比如 MOS 管)的开关损耗会增大,发热严重。我实测下来,对于普通的小型直流电机,1kHz 到 5kHz 是比较合适的范围。对于风扇电机,20kHz 以上更好,因为人耳听不到这个频率,不会有噪音。
其次是驱动能力。RP2040 的 GPIO 引脚最大输出电流只有几毫安,直接驱动电机是不可能的。你需要一个电机驱动模块,比如 L298N、TB6612 或者 DRV8833。这些模块内部有 H 桥电路,可以用小电流的 PWM 信号控制大电流的电机。
还有一个常见问题是 PWM 接 MOS 管发热。如果你用 MOS 管自己搭驱动电路,MOS 管发热严重通常是因为没有完全导通或完全关断。PWM 信号的高电平如果不够高,MOS 管可能工作在线性区,内阻大,发热就严重。解决方法是确保 PWM 信号的高电平电压足够驱动 MOS 管的栅极,或者加一个栅极驱动芯片。
4.3 舵机控制:50Hz 频率和脉宽计算
舵机和直流电机不一样,它需要的不是占空比,而是特定宽度的脉冲。标准舵机要求 50Hz 的 PWM 信号,也就是周期 20 毫秒。在这个周期内,高电平的持续时间决定了舵机的角度。一般来说,0.5 毫秒对应 0 度,1.5 毫秒对应 90 度,2.5 毫秒对应 180 度。
用 MicroPython 控制舵机的时候,需要把占空比换算成脉宽。RP2040 的 PWM 频率设为 50Hz 时,周期是 20 毫秒。16 位分辨率的占空比范围是 0 到 65535,对应 0 到 20 毫秒。所以 1 毫秒对应 65535 除以 20,约等于 3277。
from machine import Pin, PWM import time servo = PWM(Pin(16)) servo.freq(50) def set_angle(angle): pulse_ms = 0.5 + angle / 180 * 2.0 duty = int(pulse_ms / 20 * 65535) servo.duty_u16(duty) set_angle(0) time.sleep(1) set_angle(90) time.sleep(1) set_angle(180)这段代码里,set_angle函数把角度换算成脉宽,再把脉宽换算成占空比。0 度对应 0.5 毫秒,180 度对应 2.5 毫秒,中间线性插值。
实际使用中,不同品牌舵机的脉宽范围可能略有差异。有的舵机 0 度对应 0.5 毫秒,有的对应 0.6 毫秒。如果你发现舵机转到极限位置时发出异响或者抖动,说明脉宽超出了舵机的机械范围,需要调整换算公式里的最小和最大脉宽值。
4.4 PWM 进阶:用 DMA 和 PIO 实现更复杂的波形
当你需要生成更复杂的波形,比如精确控制脉冲数量、生成任意波形、或者同时控制很多路 PWM 时,MicroPython 的 PWM 类就不够用了。这时候可以考虑 RP2040 的两个高级特性:DMA 和 PIO。
DMA 是直接内存访问,它可以在不占用 CPU 的情况下,把内存里的数据搬运到外设寄存器。用 DMA 配合 PWM,可以实现精确的脉冲计数和任意波形生成。比如你要驱动步进电机走固定的步数,用 DMA 发送固定数量的 PWM 脉冲,发完之后自动停止,CPU 完全不用干预。
PIO 是可编程 I/O,RP2040 独有的一种外设。它本质上是一个小型的状态机,可以执行你自己编写的汇编指令,精确控制 GPIO 的时序。PIO 特别适合实现那些标准外设不支持的协议,比如 WS2812 灯带的单总线协议、DHT11 温湿度传感器的单总线协议、或者自定义的同步串行协议。
不过这两个特性对新手来说有点超前,我建议先把基础的 GPIO 和 PWM 用熟,等遇到具体需求的时候再研究 DMA 和 PIO。MicroPython 对 PIO 的支持是通过rp2模块提供的,用法比 C 语言简单很多,但依然需要理解状态机的基本概念。
5. 调试与排错:硬件开发中那些没人告诉你的坑
5.1 串口打印:硬件开发中最重要的调试手段
硬件开发不像软件开发,没有断点调试那么方便。大多数时候,你只能靠串口打印来了解程序运行状态。MicroPython 的print函数默认输出到 USB 串口,在 Thonny 的 Shell 窗口里能看到。
但串口打印也有讲究。首先,不要在中断处理函数里用print,因为print是阻塞操作,在中断里调用会导致时序问题。其次,打印频率不要太高,如果每毫秒打印一次,串口带宽可能不够,数据会丢失。我一般会在关键位置打印,比如函数入口、条件分支、循环计数,而不是每行都打印。
还有一个技巧是用不同的前缀区分打印来源。比如[SENSOR]表示传感器数据,[MOTOR]表示电机状态,[ERROR]表示错误信息。这样在串口输出里一眼就能找到你需要的信息。
5.2 常见问题速查表
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 板子插上电脑没反应 | USB 线只能充电 | 换一根数据线 |
| Thonny 找不到串口 | 驱动未安装或端口被占用 | 检查设备管理器,关闭其他串口工具 |
| LED 不亮 | 引脚编号错误或极性接反 | 用万用表量引脚电压 |
| 按键读取不稳定 | 未启用上拉或未消抖 | 启用内部上拉,加 20ms 延时 |
| 电机不转 | PWM 频率不对或驱动能力不足 | 用示波器看 PWM 波形,检查驱动模块 |
| 舵机抖动 | 脉宽超出范围或电源功率不足 | 调整脉宽范围,单独给舵机供电 |
| 程序跑一会儿就卡死 | 内存不足或死循环 | 减少大列表创建,检查循环条件 |
| PWM 输出无波形 | 引脚不支持 PWM 或配置错误 | 查数据手册确认引脚功能 |
这张表里的问题都是我实际遇到过的。其中“程序跑一会儿就卡死”这个问题最隐蔽,因为 MicroPython 的内存管理不像 CPython 那么完善,频繁创建和销毁大对象会导致内存碎片,最终分配失败。解决方法是尽量复用对象,避免在循环里创建大列表或大字符串。
5.3 电源问题:最容易被忽视的故障源
很多新手遇到问题会怀疑代码,但实际排查下来,十次里有三次是电源问题。Pico 可以通过 USB 供电,也可以用外部电源通过 VSYS 引脚供电。USB 供电的电压是 5V,经过板子上的稳压芯片降到 3.3V 给 RP2040 使用。
如果你接了电机、舵机、显示屏这些耗电设备,USB 口的 500mA 电流可能不够。电流不足会导致电压跌落,板子会随机复位或者外设工作不正常。我建议在大功率项目中,用独立的电源给外设供电,Pico 只负责发控制信号。注意要把外设电源的地和 Pico 的地连在一起,否则控制信号没有参考电平,外设不会响应。
还有一个细节是电源去耦。在电机这种感性负载的电源引脚附近,最好加一个 100nF 的陶瓷电容和一个 10uF 的电解电容,用来吸收电机启停时产生的电压尖峰。没有这两个电容,电机启动的瞬间可能会拉低电源电压,导致 Pico 复位。
5.4 实操心得:从失败中总结出来的经验
第一个心得是关于引脚选择。RP2040 的 GPIO 0 到 GPIO 22 都可以用作普通 IO,但 GPIO 23、24、25 有特殊功能。GPIO 23 控制板载电源管理芯片,GPIO 24 是 VBUS 检测,GPIO 25 是板载 LED。如果你把 GPIO 23 配置成输出并拉低,板子会断电。这个坑我踩过,当时想用 GPIO 23 驱动一个 LED,结果板子直接关机了,还以为烧了。
第二个心得是关于 PWM 引脚分配。RP2040 的 16 路 PWM 来自 8 个切片,每个切片的两路 PWM 共享同一个频率。也就是说,如果你把 GPIO 0 和 GPIO 1 都配置成 PWM,它们必须用相同的频率。如果你需要两个不同频率的 PWM,必须使用不同切片的引脚。具体哪个引脚对应哪个切片,查数据手册的引脚定义表就能看到。
第三个心得是关于代码保存。在 Thonny 里直接点运行按钮,代码是在 RAM 里执行的,断电就没了。要让代码上电自动运行,必须把文件保存到 Pico 的文件系统里,并且命名为main.py。我刚开始用的时候,每次断电后代码就没了,还以为是板子有问题,后来才发现是没保存。
第四个心得是关于 MicroPython 版本。不同版本的 MicroPython 在 API 上可能有细微差异。比如早期版本的duty_u16方法可能不存在,需要用duty方法。如果你照着教程写代码但报错说方法不存在,先检查一下固件版本,去官网下载最新固件重新烧录。
6. 从入门到项目:下一步可以做什么
6.1 三个适合练手的小项目
第一个项目是呼吸灯。用 PWM 控制 LED 的亮度,让亮度从 0 慢慢增加到最大,再从最大慢慢减小到 0,循环往复。这个项目练习的是 PWM 占空比的连续调节和循环控制。代码很简单,一个 for 循环加上duty_u16和sleep_ms就够了。
第二个项目是按键控制电机。按一下按键,电机正转;再按一下,电机反转;再按一下,停止。这个项目练习的是按键输入、状态机和电机驱动的结合。你需要用一个变量记录当前状态,每次按键按下时切换状态,根据状态设置电机的两个控制引脚。
第三个项目是舵机扫描。让舵机从 0 度慢慢转到 180 度,再慢慢转回来,同时用串口打印当前角度。这个项目练习的是舵机控制和串口输出的配合。你可以把角度数据传到电脑上,用 Excel 画成曲线,直观地看到舵机的运动轨迹。
6.2 进阶方向:传感器、通信和实时控制
把基础练熟之后,你可以往三个方向深入。第一个方向是传感器,Pico 支持 ADC、I2C、SPI、UART 这些接口,可以接温湿度传感器、加速度计、气压计、GPS 模块。第二个方向是通信,用 UART 和电脑或其他单片机通信,用 I2C 连接多个设备,用 SPI 驱动显示屏。第三个方向是实时控制,用定时器中断实现精确的周期任务,用 PIO 实现自定义协议,用双核并行处理不同的任务。
RP2040 的双核特性在 MicroPython 里可以通过_thread模块使用。你可以让一个核心跑控制循环,另一个核心跑通信和显示,两个核心通过共享变量交换数据。不过 MicroPython 的线程支持还不完善,共享变量的访问需要加锁,否则会出现竞态条件。这个特性我还在摸索中,等有成熟经验了再单独写一篇分享。
6.3 我个人的一些体会
折腾 Pico 和 MicroPython 这段时间,最大的感受是硬件开发的门槛确实在降低。以前学 STM32,光是配环境就要花好几天,现在用 MicroPython,半小时就能让 LED 闪起来。这种即时反馈对学习动力的保持非常重要。
但门槛降低不意味着可以跳过基础。GPIO 的上拉下拉、PWM 的频率占空比、串口通信的波特率,这些概念在哪个平台上都是一样的。你把 Pico 上的这些概念搞明白了,换到 STM32 或者 ESP32 上,只是换了一套 API 而已,底层逻辑是相通的。
最后分享一个小技巧。如果你在调试的时候不确定某个引脚的状态,可以用Pin对象的value()方法读取当前电平,打印出来看看。这个方法对输入和输出模式都有效,输出模式下读回来的是你设置的值,输入模式下读回来的是外部信号的值。这个技巧帮我排查过好几次接线错误的问题。