第4章:GPIO编程入门——RPi.GPIO库安装、引脚模式设置、数字输入输出、PWM脉宽调制

好,咱们进入正题。这一章讲的是GPIO编程,说白了就是让树莓派的那些引脚听你使唤。我刚开始接触树莓派时,觉得GPIO这东西挺神秘的——几个金属针脚,怎么就能控制整个智能家居系统呢?后来做项目多了才发现,其实原理很简单,就是高低电平的切换。

4.1 RPi.GPIO库的安装

在树莓派上操作GPIO,最常用的库就是RPi.GPIO。它是个Python库,直接控制树莓派的硬件引脚。安装方式很简单,但有个坑我得先提醒你。

注意: 树莓派系统通常预装了RPi.GPIO,但版本可能比较老。我建议你手动更新一下,避免遇到一些奇怪的bug。

安装命令如下:

# 更新系统包列表
sudo apt-get update

# 安装RPi.GPIO库
sudo apt-get install python3-rpi.gpio

# 或者用pip安装(推荐)
sudo pip3 install RPi.GPIO

我个人习惯用pip安装,因为版本控制更灵活。安装完成后,在Python交互环境里试一下:

import RPi.GPIO as GPIO
print(GPIO.VERSION)

如果输出版本号,说明安装成功了。嗯,这里要注意,如果你用的是树莓派5,RPi.GPIO可能不兼容,得用gpiozero或者librpi-gpio。我在一次项目迁移时就吃过这个亏,折腾了半天才发现是库不兼容。

4.2 引脚模式设置

树莓派的引脚编号有两种方式:BOARD模式和BCM模式。这俩有啥区别?

模式 说明 示例
BOARD 按物理引脚位置编号(1-40) 物理引脚11
BCM 按Broadcom芯片的GPIO编号 GPIO17

我建议新手用BOARD模式,因为物理引脚好数,不容易接错线。但如果你要参考网上很多现成的电路图,它们通常用BCM模式。你自己选吧,我个人习惯用BCM,因为代码移植方便。

import RPi.GPIO as GPIO

# 设置引脚模式
GPIO.setmode(GPIO.BCM)   # 使用BCM编号
# 或者
GPIO.setmode(GPIO.BOARD) # 使用物理引脚编号
小技巧: 设置模式后,可以用GPIO.getmode()查看当前模式。我曾经在调试时忘了自己设的哪种模式,结果查了半天。

4.3 数字输出——点亮一盏灯

数字输出,说白了就是让引脚输出高电平(3.3V)或低电平(0V)。最常见的例子就是控制LED灯。

先看代码:

import RPi.GPIO as GPIO
import time

# 设置引脚模式
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)  # 关闭警告

# 设置引脚为输出模式
LED_PIN = 17
GPIO.setup(LED_PIN, GPIO.OUT)

# 点亮LED
GPIO.output(LED_PIN, GPIO.HIGH)
time.sleep(2)

# 熄灭LED
GPIO.output(LED_PIN, GPIO.LOW)

# 清理GPIO资源
GPIO.cleanup()

这里有个关键点:GPIO.setup()用来设置引脚方向。输出模式就是让树莓派控制外部设备。我刚开始做智能家居时,用这个功能控制继电器开关,实现了灯光自动控制。说白了,就是高低电平的切换。

警告: 树莓派的GPIO输出电流很小(约16mA),不能直接驱动大功率设备。如果你想控制电机或大功率灯,必须通过继电器或驱动芯片。我曾经有个学生直接接了电机,结果烧了GPIO引脚,教训深刻。

4.4 数字输入——读取按钮状态

数字输入就是读取外部信号,比如按钮按下、传感器触发。这比输出稍微复杂一点,因为涉及到上拉/下拉电阻的问题。

看代码:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)

# 设置引脚为输入模式,启用内部上拉电阻
BUTTON_PIN = 18
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

try:
    while True:
        # 读取引脚状态
        if GPIO.input(BUTTON_PIN) == GPIO.LOW:
            print("按钮被按下")
        else:
            print("按钮未按下")
        time.sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()

为什么会用到上拉电阻?你想想看,当按钮没按下时,引脚处于悬空状态,电平不确定,可能会误触发。上拉电阻把引脚拉到高电平,按下按钮时接地变成低电平。这样信号就稳定了。

我在做门磁传感器项目时,就遇到过悬空引脚导致误报的问题。后来加了内部上拉电阻,问题就解决了。说白了,就是给引脚一个确定的初始状态。

4.5 PWM脉宽调制——模拟输出

PWM(Pulse Width Modulation)是一种模拟信号技术。树莓派的GPIO只能输出数字信号(0或1),但通过PWM,你可以控制LED的亮度、电机的转速。

原理很简单:快速切换高低电平,通过改变高电平占整个周期的比例(占空比),来模拟不同的电压值。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)

# 创建PWM对象,频率1000Hz
pwm = GPIO.PWM(17, 1000)
pwm.start(0)  # 初始占空比0%

try:
    while True:
        # 逐渐增加亮度
        for duty in range(0, 101, 5):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.1)
        # 逐渐降低亮度
        for duty in range(100, -1, -5):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.1)
except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

这里GPIO.PWM(17, 1000)创建了一个频率为1000Hz的PWM信号。频率越高,控制越平滑,但也不能太高,否则树莓派处理不过来。

经验之谈: 控制LED亮度时,频率1000Hz就够了。控制电机时,频率可以低一些(50-100Hz)。我在做智能窗帘项目时,用PWM控制步进电机,频率设到500Hz,效果很好。

4.6 综合示例:呼吸灯

把上面的知识串起来,做一个呼吸灯效果。LED从暗到亮,再从亮到暗,循环往复。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)

pwm = GPIO.PWM(17, 1000)
pwm.start(0)

try:
    while True:
        # 渐亮
        for duty in range(0, 101, 1):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.02)
        # 渐暗
        for duty in range(100, -1, -1):
            pwm.ChangeDutyCycle(duty)
            time.sleep(0.02)
except KeyboardInterrupt:
    pwm.stop()
    GPIO.cleanup()

这个例子虽然简单,但包含了GPIO编程的核心:模式设置、数字输出、PWM控制。你想想看,智能家居里的灯光调光、风扇调速,本质上都是这个原理。

4.7 避坑指南

最后,分享几个我踩过的坑:

  • 忘记清理GPIO: 程序退出后,GPIO引脚可能保持上次的状态。一定要在程序结束时调用GPIO.cleanup(),否则下次运行会报错。
  • 引脚复用冲突: 有些引脚有特殊功能(比如I2C、SPI),如果你把它们当普通GPIO用,可能会影响其他功能。我建议用之前查一下树莓派的引脚功能图。
  • 电流限制: 树莓派每个GPIO引脚最大输出电流16mA,所有引脚总电流不能超过50mA。超过这个限制,轻则重启,重则烧毁。
  • 电平不匹配: 树莓派是3.3V逻辑电平,如果接5V设备,需要电平转换芯片。我曾经直接接了5V传感器,结果树莓派直接重启了。

好了,这一章的内容就到这里。GPIO编程是智能家居系统的基础,掌握了这些,你就能控制各种传感器和执行器了。下一章,我们会把这些知识用到实际项目中,做一个完整的传感器联动系统。