GPIO 基础控制:从点亮一颗 LED 开始

各位同学,欢迎来到第二章。这一章我们不讲虚的,直接上手操作。你想想看,嵌入式开发里最基础、最直观的反馈是什么?就是让一个灯亮起来。今天我们就用树莓派的 GPIO,把这件事彻底搞明白。

我个人习惯,学任何新平台,第一件事就是控制 GPIO。这就像学编程先写 "Hello World" 一样,是建立信心的第一步。而且,GPIO 控制背后涉及到的寄存器操作、电平逻辑,是所有外设驱动的基础。搞懂了它,后面电机、传感器、通信协议,你都会觉得顺理成章。

RPi.GPIO 库:你的 GPIO 操作瑞士军刀

树莓派上操作 GPIO,最常用的就是 RPi.GPIO 这个 Python 库。它把底层的寄存器操作封装成了几个简单的函数。你不需要去翻 BCM2835 的数据手册,也能把引脚玩得飞起。

但注意,封装归封装,底层原理你得懂。我在项目中遇到过好几次,有人因为不理解 GPIO 的电气特性,直接把 5V 信号怼到 3.3V 的引脚上,结果就是——冒烟。嗯,所以咱们既要会用库,也要知道它在背后干了什么。

核心函数一览

RPi.GPIO 库的核心函数就那么几个,我列个表,你一眼就能看明白:

函数 作用 我的经验
setmode() 设置引脚编号模式 我习惯用 BCM,因为和芯片引脚对应,查手册方便
setup() 配置引脚方向(输入/输出) 别忘了设初始电平,否则引脚可能处于不确定状态
output() 输出高/低电平 这就是让 LED 亮灭的关键
input() 读取引脚电平 读取按键、传感器信号就靠它

setmode():先定规矩

树莓派的 GPIO 引脚有两种编号方式:BOARD 和 BCM。

  • BOARD:按物理引脚位置编号,从 1 到 40。好处是,你拿着板子数引脚就行,不用记芯片型号。
  • BCM:按博通芯片的 GPIO 编号,比如 GPIO17、GPIO18。好处是,和芯片数据手册一致,写驱动时更直观。

我个人习惯用 BCM。为什么?因为我在做项目时,经常需要对照芯片手册查复用功能。用 BCM 编号,一眼就能找到对应的寄存器。当然,如果你只是搭个简单的电路,用 BOARD 也没问题。关键是——统一。别在一个脚本里混用,否则你会疯掉。

重要提醒: setmode() 必须在所有 GPIO 操作之前调用。而且,一个脚本里只能调用一次。如果你不小心调了两次,程序会报错。我曾经在调试时犯过这个错,找了半天才发现是重复调用了 setmode()。

setup():给引脚定方向

每个 GPIO 引脚,要么是输入,要么是输出。你不能让它同时干两件事。这就好比一个人,不能同时当司机和乘客。

setup(channel, direction, initial) 这个函数,就是用来干这个的。

  • channel:引脚编号,根据你设定的模式来。
  • direction:GPIO.OUT 或 GPIO.IN。
  • initial:可选参数,设置初始电平。比如 initial=GPIO.HIGH。

这里有个坑,我必须要说。如果你把引脚设为输出,但没设初始电平,那这个引脚在程序启动时可能处于一个不确定的状态。有时候是高,有时候是低,完全看运气。所以,我建议你每次 setup() 时,都明确指定 initial 参数。养成好习惯,能省去很多排查时间。

output() 与 input():真正的读写操作

这两个函数,就是 GPIO 的核心操作了。

  • output(channel, state):让引脚输出高电平(GPIO.HIGH)或低电平(GPIO.LOW)。
  • input(channel):读取引脚当前的电平状态,返回 GPIO.HIGH 或 GPIO.LOW。

你可能会问:高电平和低电平到底是多少伏?

树莓派的 GPIO 是 3.3V 逻辑电平。也就是说,GPIO.HIGH 对应 3.3V,GPIO.LOW 对应 0V。注意,不是 5V!如果你需要驱动 5V 的设备,必须加电平转换电路。我曾经见过有人直接把 5V 的舵机信号线接到 GPIO 上,结果树莓派的 GPIO 引脚直接烧了。嗯,血的教训。

GPIO 高低电平控制原理

说白了,GPIO 输出高低电平,就是控制引脚内部的 MOSFET 开关,让它连接到 VCC(3.3V)或者 GND(0V)。

我画了一张图,帮你理解这个过程:

GPIO 输出高低电平原理 树莓派 SoC 内部上拉 输出驱动器 GPIO 引脚 LED 输出 HIGH:引脚连接到 3.3V;输出 LOW:引脚连接到 GND

当你调用 output(17, GPIO.HIGH) 时,树莓派内部的寄存器会被设置,让 GPIO17 这个引脚内部的 PMOS 导通,把引脚拉到 3.3V。电流从引脚流出,经过 LED 和限流电阻,流回 GND。LED 就亮了。

当你调用 output(17, GPIO.LOW) 时,NMOS 导通,引脚被拉到 GND。LED 两端没有电压差,自然就灭了。

小提示: 树莓派的 GPIO 输出电流有限,每个引脚最大只能提供 16mA。驱动 LED 没问题,但驱动电机、继电器这类大电流设备,必须加驱动电路(比如 ULN2003、L298N 或 MOSFET)。

实战:点亮一颗 LED

理论说完了,咱们来点实际的。下面这个例子,就是让 LED 亮 2 秒,然后灭 2 秒,循环 5 次。

import RPi.GPIO as GPIO
import time

# 设置引脚编号模式
GPIO.setmode(GPIO.BCM)

# 定义 LED 引脚
LED_PIN = 17

# 设置引脚为输出,初始电平为低
GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.LOW)

print("LED 开始闪烁...")

# 循环 5 次
for i in range(5):
    GPIO.output(LED_PIN, GPIO.HIGH)  # 点亮
    time.sleep(2)                     # 保持 2 秒
    GPIO.output(LED_PIN, GPIO.LOW)   # 熄灭
    time.sleep(2)                     # 保持 2 秒
    print(f"第 {i+1} 次闪烁完成")

# 清理 GPIO 资源
GPIO.cleanup()
print("程序结束,GPIO 已清理")

这段代码很简单,但有几个细节我要强调:

  • GPIO.cleanup():程序结束时一定要调用。它会释放 GPIO 资源,把引脚恢复到默认状态。否则,下次运行程序时,引脚可能还是上次的状态,导致奇怪的问题。
  • 限流电阻:LED 不能直接接在 GPIO 引脚和 GND 之间。必须串联一个 220Ω ~ 1kΩ 的电阻,否则电流过大,会烧坏 LED 甚至 GPIO 引脚。
  • 接线:LED 的正极(长脚)接 GPIO 引脚,负极(短脚)通过电阻接 GND。别接反了。

警告: 千万不要把 GPIO 引脚直接接到 5V 电源上!树莓派的 GPIO 是 3.3V 逻辑,耐压只有 3.3V。接到 5V 上,芯片会立即损坏。我曾经在项目中因为误操作烧过一块树莓派,那感觉,就像看着自己的钱包被烧了一样心疼。

GPIO 输入:读取按键状态

输出搞定了,咱们再看看输入。读取按键状态,是 GPIO 输入的经典应用。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

BUTTON_PIN = 18
LED_PIN = 17

# 设置按键引脚为输入,启用内部上拉电阻
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.LOW)

print("按下按键,LED 会亮起")

try:
    while True:
        # 读取按键状态
        button_state = GPIO.input(BUTTON_PIN)
        
        # 按键按下时,引脚为低电平(因为上拉电阻)
        if button_state == GPIO.LOW:
            GPIO.output(LED_PIN, GPIO.HIGH)
            print("按键按下,LED 亮")
        else:
            GPIO.output(LED_PIN, GPIO.LOW)
            
        time.sleep(0.1)  # 防抖

except KeyboardInterrupt:
    print("程序被用户中断")

finally:
    GPIO.cleanup()

这里有个关键点:上拉电阻。当按键没有按下时,GPIO 引脚处于悬空状态,电平不确定。启用内部上拉电阻后,引脚被拉到 3.3V,读取为高电平。按键按下时,引脚被拉到 GND,读取为低电平。

你可能会问:为什么不用下拉电阻?当然也可以。但树莓派内部只有上拉电阻,没有下拉。所以,用上拉是更简单的方案。

避坑指南: 我曾经在读取按键时,没有加防抖处理。结果每次按下按键,程序会误判为多次触发。后来加了一个 10ms 的延时,问题就解决了。嗯,这就是所谓的「按键抖动」,是硬件设计中的常见问题。

本章小结

这一章,我们搞定了 GPIO 最核心的操作:输出和输入。你学会了:

  • 用 setmode() 选择引脚编号模式
  • 用 setup() 配置引脚方向
  • 用 output() 控制 LED 亮灭
  • 用 input() 读取按键状态
  • 理解了 GPIO 高低电平的硬件原理

这些知识,是后面所有驱动开发的基础。不管是电机控制,还是 UART 通信,本质上都是在操作 GPIO。只不过,通信协议把简单的电平变化,组合成了有意义的时序。

好了,拿起你的树莓派,动手试试吧。让那个小小的 LED 亮起来,你会感受到嵌入式开发的乐趣。