目录
- 树莓派笔记(三) 使用 RPi.GPIO 模块
-
- RPi.GPIO
- 引脚简介
-
- 引脚编号
- 引脚图
- 引脚设置
-
- 指定引脚编号系统
- 配置通道
- 释放引脚
- 输出
- pwm
- 输入
-
- 上拉/下拉电阻
- 轮询输入
- 中断和边检检测
- 线程回调
- 开关防抖
树莓派笔记(三) 使用 RPi.GPIO 模块
RPi.GPIO
RPI.GPIO是python的一个模块,树莓派官方系统默认已经安装
使用python控制GPIO需要导入RPI.GPIO模块
- 导入模块
#导入模块并检查它是否成功:
import RPi.GPIO as GPIO
try:
import RPi.GPIO as GPIO
except RuntimeError :
print("导入RPi.GPIO时出错,可能是权限问题")
引脚简介
引脚编号
RPi.GPIO中使用的IO引脚编号有两种方法。
- BOARD编号系统。如下图中物理接口。使用此编号系统的优点是,无论树莓派的版本如何,您的硬件将始终可以工作。您无需重新连接连接器或更改代码。
- BCM编号系统。不同版本的树莓派不一样可能要重新修改代码,这是一种较低级别的工作方式-指Broadcom SOC上的通道号。您必须始终使用哪个通道号到达树莓派板上哪个引脚的图表。您的脚本可能会在树莓派板的修订版之间中断。
引脚功能
如下图,功能名一栏写名了树莓派引脚的功能
主要有如下分类
- 电源引脚
- 5v、3.3v :为输出5v、3.3v电源
- 0v /GND :即负极,或接地级
- GPIO引脚
- 通用输入输出引脚,可编程控制高低电平
- 其他功能引脚
- i2c --> SDA,SCL
- SPI —> MOSI,MISO,SCLK 等等
查询引脚编号
若忘了引脚编号,又找不到图,你可以在树莓派linux终端中输入命令查询引脚编号,如下
gpio readall
引脚图
引脚设置
指定引脚编号系统
上面提到,树莓派有多个编号系统,在编程之前需要指定一个编号系统。指定后,即使用该编号进行编程,若使用其他编号会导致错误
GPIO.setmode(GPIO.BOARD) #指定为BOARD编号
# or
GPIO.setmode(GPIO.BCM)#指定为BCM编号
若要检测已经使用了什么编号可使用
mode = GPIO.getmode()
mode为GPIO.BOARD,GPIO.BCM、None,其意义显而易见
配置通道
GPIO引脚是通用输入输出引脚,其是可以作为输入或输出所用
因此使用之前,你需要告诉系统,该引脚你需要作为输入还是输出
设置为输入模式
GPIO.setup(引脚,GPIO.IN)
还可以设置上拉电阻(具体用处后面会介绍)
GPIO.setup(引脚,GPIO.IN,pull_up_down=GPIO.PUD_UP)
下拉电阻
GPIO.setup(引脚,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
设置为输出模式
GPIO.setup(引脚,GPIO.OUT)
设置为输出并初始为为HIGH或LOW
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
可以同时 设置多个引脚
chan_list = [ 11 ,12 ]
#你可以用元组代替,即:
#chan_list =(11,12)
GPIO.setup(chan_list, GPIO.OUT)
注意 引脚的编号为你上面指定的编号系统 ️️️
释放引脚
程序结束不释放引脚是一个很危险的行为️️️️️️
假如执行程序时 你设置引脚输出为高电平,而程序结束时你未释放引脚它将保持这一状态,一旦意外接触到该引脚与GND将会短路,烧毁你的树莓派
释放引脚:
GPIO.cleanup()
注意,同时GPIO.cleanup()也会清除正在使用的引脚编号系统。
输出
根据上文,进行输出操作前,应有
import RPi.GPIO as GPIO
try:
import RPi.GPIO as GPIO
except RuntimeError :
print("导入RPi.GPIO时出错,可能是权限问题")
GPIO.setmode(GPIO.BCM)#指定为BCM编号
GPIO.setup(17,GPIO.OUT)
设置 GPIO 针脚的输出状态:
GPIO.output(channel, state)
state可以是0 / GPIO.LOW / False --- 低电平
或者 1 / GPIO.HIGH / True --- 高电平
输出切换,高变低,低变高
GPIO.output(channel, not GPIO.input(channel))
pwm
脉宽调制(PWM)是指用微处理器的数字输出来对模拟电路进行控制,是一种对模拟信号电平进行数字编码的方法
创建一个 PWM 实例:
p = GPIO.PWM(channel, frequency)
启用 PWM:
p.start(dc) # dc 代表占空比(范围:0.0 <= dc >= 100.0)
更改频率:
p.ChangeFrequency(freq) # freq 为设置的新频率,单位为 Hz
更改占空比:
p.ChangeDutyCycle(dc) # 范围:0.0 <= dc >= 100.0
停止 PWM:
p.stop()
示例
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 50) # 通道为 12 频率为 50Hz
p.start(0)
try:
while 1:
for dc in range(0, 101, 5):
p.ChangeDutyCycle(dc)
time.sleep(0.1)
for dc in range(100, -1, -5):
p.ChangeDutyCycle(dc)
time.sleep(0.1)
except KeyboardInterrupt:
pass
p.stop()
GPIO.cleanup()
输入
输入要比输出复杂一些
上拉/下拉电阻
如果输入引脚没有接其他器件(或者开关关闭),那么这个引脚将处于浮动的状态,即可能高电平可能低电平,或者在两者之间切换。这时候读取引脚的值没有意义。所以我们需要上拉/下拉电阻,使引脚状态确定
- 物理方法
将一个 10K 的电阻连接在输入通道与 3.3V(上拉)
或 输入通道与0V之间(下拉) - 程序方法
上拉电阻
GPIO.setup(引脚,GPIO.IN,pull_up_down=GPIO.PUD_UP)
下拉电阻
GPIO.setup(引脚,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
轮询输入
这是最简易的一种方式,在某个时间点检查输入值。这即是所谓的“轮询 ”,而且如果您的程序在错误的时间里进行了读取,可能会错过某个输入值。在循环中运用轮询,有可能使处理器资源紧张。
示例
if GPIO.input(channel):
print('Input was HIGH')
else:
print('Input was LOW')
循环中等待按钮被按下后进行轮询
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01) # 为 CPU 留出 10 毫秒,供其处理其它事物
中断和边检检测
用这种方法输入不会因为cpu在忙其他事而错过输入,且占用 CPU 资源很少
边缘就是是从 HIGH 到 LOW 的过度(下降临界值falling edge)或从 LOW 到 HIGH 的过度(上升临界值rising edge)
检测到边缘时执行线程回调函数
-
wait_for_edge(channel, state) 函数
用于在检测到边缘之前阻止程序的运行。
上面的示例中,等待按钮被按下的语句可以改写为:
GPIO.wait_for_edge(channel, GPIO.RISING)如果您只想等待一段时间,则可以使用timeout参数:
#上升沿等待最多5秒(超时以毫秒为单位)
pin= GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if pin is None:
print('Timeout occurred')
else:
print('Edge detected on pin', pin)
-
event_detected(channel, state) 函数
函数被设计用于循环中有其它东西时使用,但不同于轮询的是,它不会错过当 CPU 忙于处理其它事物时输入状态的改变。这在类似使用 Pygame 或 PyQt 时主循环实时监听和响应 GUI 的事件是很有用的。
GPIO.add_event_detect(channel, GPIO.RISING) # 在通道上添加上升临界值检测
do_something()
if GPIO.event_detected(channel):
print('Button pressed')
state可以为GPIO.RISING、GPIO.FALLING、GPIO.BOTH
线程回调
RPi.GPIO 在第二条线程中执行回调函数。这意味着回调函数可以同您的主程序同时运行,并且可以立即对边缘进行响应。例如:
def my_callback(channel):
print('这是一个边缘事件回调函数!')
print('在通道 %s 上进行边缘检测'%channel)
print('该程序与您的主程序运行在不同的进程中')
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback) # 在通道上添加上升临界值检测
... 其它程序代码 ...
如果您需要多个回调函数:
def my_callback_one(channel):
print('回调 1')
def my_callback_two(channel):
print('回调 2')
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)
注意,在该示例中,回调函数为顺序运行而不是同时运行。这是因为当前只有一个进程供回调使用,而回调的运行顺序是依据它们被定义的顺序。
开关防抖
每次按钮按下时,回调操作被调用不止一次。这种现象被称作“开关抖动 ”。这里有两种方法解决开关抖动问题:
- 物理方法 将一个 0.1uF 的电容连接到开关上。
- 软件方法防止抖动
使用软件方式抖动,可以在您指定的回调函数中添加 bouncetime= 参数。
抖动时间需要使用毫秒为单位进行书写。例如:
在通道上添加上升临界值检测,忽略由于开关抖动引起的小于 200ms 的边缘操作
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
或者
GPIO.add_event_callback(channel, my_callback, bouncetime=200)
remove_event_detect()
如果你不希望你的程序检测边缘事件,可以将它停止:
GPIO.remove_event_detect(channel)