4、GPIO基础与传感器驱动:树莓派GPIO引脚图、PIR传感器工作原理、使用RPi.GPIO读取传感器状态、中断触发模式

好,咱们正式开始动手了。这一章,我会带你从树莓派的“神经末梢”——GPIO引脚讲起,然后接入一个最常用的传感器:PIR人体红外传感器。说白了,就是让树莓派学会“看”到有人经过。

我个人习惯,每次做嵌入式项目,第一步永远是先搞清楚引脚定义。别小看这个,我在项目里见过太多人因为接错线烧了板子。嗯,咱们先把这个基础打牢。

树莓派GPIO引脚图:别接错了,会烧板子

树莓派的GPIO(通用输入输出)引脚,是它和外部世界沟通的桥梁。以树莓派4B为例,它有一个40针的排针。但注意,不是所有针脚都能当GPIO用,有些是电源、有些是地线。

我建议你手边常备一张引脚图。你想想看,如果VCC(3.3V或5V)和GND接反了,或者把3.3V的设备接到了5V上,芯片瞬间就冒烟了。我曾经在调试一个红外传感器时,就因为看错了引脚编号,把信号线接到了5V上,结果传感器直接报废。从那以后,我每次接线前都会用万用表再确认一遍。

这里给你一个常用的引脚功能速查表(以BCM编号为准,这是代码里用的编号):

物理引脚 BCM编号 功能
1 - 3.3V电源
2 - 5V电源
3 2 (SDA) I2C数据线
5 3 (SCL) I2C时钟线
6 - GND
7 4 GPIO 4
11 17 GPIO 17
12 18 GPIO 18
13 27 GPIO 27
14 - GND
警告: 树莓派的GPIO引脚是3.3V逻辑电平。千万不要直接把5V设备接到GPIO引脚上,否则会烧坏CPU。如果必须用5V传感器,记得加电平转换模块。

PIR传感器工作原理:它怎么知道有人来了?

PIR(Passive Infrared,被动红外)传感器,说白了就是检测人体发出的红外线。人体温度大约37度,会持续向外辐射红外线。PIR传感器内部有两个热释电元件,当人移动时,两个元件接收到的红外线强度发生变化,传感器就会输出一个高电平信号。

这里有个坑:PIR传感器检测的是“移动”,不是“静止”。如果你站在它面前一动不动,它过几秒就会认为你消失了。我在做智能家居项目时,客户投诉说“人在厕所里灯却灭了”,其实就是这个原因。后来我加了一个延时逻辑,检测到人后保持10分钟输出,才解决了问题。

常见的PIR模块(比如HC-SR501)有三个引脚:

  • VCC:接5V(注意,这里是5V,不是3.3V)
  • GND:接地
  • OUT:信号输出,接树莓派的GPIO

模块背面通常有两个可调电位器:一个调灵敏度(检测距离),一个调延时(输出保持时间)。我建议你先把灵敏度调到中等,延时调到最短,方便调试。

使用RPi.GPIO读取传感器状态:让树莓派“看见”

好,现在开始写代码。Python的RPi.GPIO库是操作GPIO的标准工具。先安装一下:

sudo apt-get update
sudo apt-get install python3-rpi.gpio

然后,咱们写一个最简单的程序,读取PIR传感器的状态:

import RPi.GPIO as GPIO
import time

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

# 定义PIR传感器连接的GPIO引脚
PIR_PIN = 17

# 设置引脚为输入模式
GPIO.setup(PIR_PIN, GPIO.IN)

print("PIR传感器测试开始...")

try:
    while True:
        # 读取传感器状态
        sensor_state = GPIO.input(PIR_PIN)
        
        if sensor_state == GPIO.HIGH:
            print("检测到移动!")
        else:
            print("一切安静...")
        
        # 每0.5秒读取一次
        time.sleep(0.5)

except KeyboardInterrupt:
    print("程序退出")
    GPIO.cleanup()

这段代码的逻辑很简单:循环读取引脚电平,高电平表示有人,低电平表示没人。但这里有个问题——你想想看,如果一直用while True轮询,CPU占用率会很高,而且响应不及时。这就是为什么我们需要中断。

中断触发模式:让CPU别傻等

中断,说白了就是让传感器主动“喊”CPU,而不是CPU一直去“问”。树莓派的GPIO支持三种中断触发模式:

  • 上升沿触发:从低电平变高电平时触发(比如有人进入检测区域)
  • 下降沿触发:从高电平变低电平时触发(比如人离开检测区域)
  • 双边沿触发:两种变化都触发

我个人习惯用上升沿触发来检测“有人进入”,用下降沿触发来检测“人离开”。这样代码更高效,CPU也不用一直空转。

来看一个使用中断的示例:

import RPi.GPIO as GPIO
import time

PIR_PIN = 17

GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_PIN, GPIO.IN)

# 定义回调函数
def motion_detected(channel):
    print(f"引脚 {channel} 检测到移动!")

def motion_ended(channel):
    print(f"引脚 {channel} 移动结束!")

# 添加中断检测
# 上升沿触发:检测到人
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, 
                      callback=motion_detected, 
                      bouncetime=300)

# 下降沿触发:人离开
GPIO.add_event_detect(PIR_PIN, GPIO.FALLING, 
                      callback=motion_ended, 
                      bouncetime=300)

print("中断模式已启动,等待事件...")

try:
    # 主程序可以干别的事
    while True:
        time.sleep(1)
        print("主程序正在运行其他任务...")

except KeyboardInterrupt:
    print("程序退出")
    GPIO.cleanup()
关键点: bouncetime=300 这个参数很重要。PIR传感器在输出高电平时,可能会因为信号抖动而产生多次触发。设置300毫秒的防抖时间,可以避免误触发。我曾经没加这个参数,结果传感器每触发一次,回调函数被调用了七八次,日志刷得飞起。
小技巧: 如果你需要同时检测上升沿和下降沿,可以用 GPIO.BOTH 模式,然后在回调函数里通过 GPIO.input(PIR_PIN) 判断当前电平,就知道是进入还是离开了。

嗯,到这里,你已经掌握了GPIO的基础操作和PIR传感器的驱动方法。下一章,咱们会把这些知识整合起来,做一个完整的报警系统。到时候,你会看到中断模式在实战中的真正威力。