树莓派瞎玩~7~RPi.GPIOのWIKI文档
本文翻译的是树莓派中RPi.GPIO模块的WIKI,每个章节分别对应下表页面。
Title | Last Update By | Last Updated |
---|---|---|
Home | Ben Croston (croston) | 2014-10-11 |
BasicUsage | Ben Croston (croston) | 2016-01-01 |
Inputs | Ben Croston (croston) | 2016-02-09 |
Outputs | Ben Croston (croston) | 2014-11-11 |
PWM | Ben Croston (croston) | 2013-12-21 |
Checking function of GPIO channels | Ben Croston (croston) | 2015-02-18 |
在开始使用RPi.GPIO
模块时,这里的实例是值得阅读的。
安装说明见这里。
BCM2835型号片上系统和树莓派的详细技术参数见这里。
可以用以下语句导入RPi.GPIO
模块
import RPi.GPIO as GPIO
这样,你可以在脚本中后面的部分使用GPIO
来指代RPi.GPIO
模块。
同时,你可以检查是否模块是否导入成功:
try:
import RPi.GPIO as GPIO
except RuntimeError:
print("Error importing RPi.GPIO! This is probably because you need superuser privileges. You can achieve this by using 'sudo' to run your script")
在RPi.GPIO中,同时支持树莓派上的两种GPIO引脚编号。第一种编号是BOARD编号,这和树莓派电路板上的物理引脚编号相对应。使用这种编号的好处是,你的硬件将是一直可以使用的,不用担心树莓派的版本问题。因此,在电路板升级后,你不需要重写连接器或代码。
第二种编号是BCM规则,是更底层的工作方式,它和Broadcom的片上系统中信道编号相对应。在使用一个引脚时,你需要查找信道号和物理引脚编号之间的对应规则。对于不同的树莓派版本,编写的脚本文件也可能是无法通用的。
你可以使用下列代码(强制的)指定一种编号规则:
GPIO.setmode(GPIO.BOARD)
# or
GPIO.setmode(GPIO.BCM)
下面代码将返回被设置的编号规则
mode = GPIO.getmode()
所有可能的返回模式包括GPIO.BOARD
,GPIO.BCM
,None
。
由于在树莓派上,可能有多个脚本或电路控制着GPIO模块。如果RPi.GRIO检测到一个引脚已经被设置成了非默认值,那么你将看到一个警告信息。你可以通过下列代码禁用警告:
GPIO.setwarnings(False)
在使用一个引脚前,你需要设置这些引脚作为输入还是输出。配置一个引脚为输入的代码如下:
GPIO.setup(channel, GPIO.IN)
(此处的channel
取决与上文设置的编号规则(BOARD/BCM))
有关输入引脚的更多信息见这里.
配置一个引脚为输出的代码如下:
GPIO.setup(channel, GPIO.OUT)
(此处的channel
取决与上文设置的编号规则(BOARD/BCM))
你也可以为输出的引脚同时指定初始值。
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
你可以使用一次函数调用初始化多个引脚(要求0.5.8以上版本)。例如:
chan_list = [11,12] # add as many channels as you want!
# you can tuples instead i.e.:
# chan_list = (11,12)
GPIO.setup(chan_list, GPIO.OUT)
读取一个GPIO引脚值的代码如下:
GPIO.input(channel)
(此处的channel
取决与上文设置的编号规则(BOARD/BCM))。低电平返回0
/ GPIO.LOW
/ False
,高电平返回1
/ GPIO.HIGH
/ True
。
设置一个GPIO引脚的输出状态,代码如下:
GPIO.output(channel, state)
(此处的channel
取决与上文设置的编号规则(BOARD/BCM))。状态可以设置为0
/ GPIO.LOW
/ False
/ 1
/ GPIO.HIGH
/ True
。
你可以使用一次函数调用设置多个引脚的输出值(要求0.5.8以上版本)。例如:
chan_list = [11,12] # also works with tuples
GPIO.output(chan_list, GPIO.LOW) # sets all to GPIO.LOW
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW)) # sets first HIGH and second LOW
在程序的最后,释放掉使用的资源是一个好的习惯。在使用RPi.GPIO模块中,也是这样。将用到的所有的输入都设置回没有上拉下拉电阻的输入,可以避免因为短接引脚而偶然的损坏树莓派。注意,GPIO.cleanup()
只会释放掉脚本中使用的GPIO引脚,并会清除设置的引脚编号规则。
你可以使用下列代码释放脚本中使用的引脚:
GPIO.cleanup()
如果在退出程序时,不想将脚本中使用的所有引脚都释放,而是保留其中的一部分。你可以释放一个或一组引脚。
GPIO.cleanup(channel)
GPIO.cleanup( (channel1, channel2) )
GPIO.cleanup( [channel1, channel2] )
获取关于树莓派的信息:
GPIO.RPI_INFO
获取树莓派版本的信息:
GPIO.RPI_INFO['P1_REVISION']
GPIO.RPI_REVISION (deprecated)
获取RPi.GPIO的版本:
GPIO.VERSION
你可以通过多种方式获得GPIO引脚的值。最简单原始的方式是每隔一段时间检查输入的信号值,这种方式被称为轮询。如果你的程序读取的时机错误,则很可能会丢失输入信号。轮询是在循环中执行的,这种方式比较占用处理器资源。另一种响应GPIO输入的方式是使用中断(边缘检测),这里的边缘是指信号从高到低的变换(下降沿)或从低到高的变换(上升沿)。
如果输入引脚处于悬空状态,引脚的值将是漂动的。换句话说,读取到的值是未知的,因为它并没有被连接到任何的信号上,直到按下一个按钮或开关。由于干扰的影响,输入的值可能会反复的变化。
我们可以通过硬件或软件两种方式为引脚连接一个上拉电阻或下拉电阻,这样,就会为输入引脚设置一个默认值,从而解决这个抖动问题。你可以使用硬件方式,将10K的电阻连接到输入引脚和3.3V电源之间(称为上拉电阻),或连接在输入引脚和0V的底线之间(成为下拉电阻)。也可以通过RPi.GPIO模块配置Broadcom的片上系统,实现为引脚连接一个上拉或下拉电阻。
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
(此处的channel
取决与上文设置的编号规则(BOARD/BCM))
你可以在某一时刻获取输入信号的值:
if GPIO.input(channel):
print('Input was HIGH')
else:
print('Input was LOW')
下面代码给出了使用轮询的方式,来等待按钮按下:
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01) # wait 10 ms to give CPU chance to do other things
(此处假设按下按钮将使得输入信号从低电平转换为高电平)
边缘是指信号状态的改变,从低到高(上升沿)或从高到低(下降沿)。通常情况下,我们更关心于输入状态的该边而不是输入信号的值。这种状态的该边被称为事件。
当你的程序正在忙于其他事情时,下面提供了几种方式使你的程序可以捕获到按钮的按下事件:
wait_for_edge()
被用于阻止程序的继续执行,直到检测到一个边沿。也就是说,上文中等待按钮按下的实例可以改写为:
GPIO.wait_for_edge(channel, GPIO.RISING)
你可以检测这些边沿,GPIO.RISING
/ GPIO.FALLING
/ GPIO.BOTH
。利用中断的好处是,它占用的CPU资源是可以忽略不计的,因此CPU有更多的资源可以做其他的事情。
如果你只是想在一个确定时间内等待边沿,则可以设置超时参数:
# wait for up to 5 seconds for a rising edge (timeout is in milliseconds)
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
print('Timeout occurred')
else:
print('Edge detected on channel', channel)
event_detected()
函数可以在循环中和其他代码一起使用,和轮询方式不同的是,在CPU忙于其他事情时,并不会错过输入状态的改变。在使用Pygame或PyQt时,通常有一个主循环来监听和及时的响应GUI事件,此时该函数是很有用的。
GPIO.add_event_detect(channel, GPIO.RISING) # add rising edge detection on a channel
do_something()
if GPIO.event_detected(channel):
print('Button pressed')
你可以检测这些边沿,GPIO.RISING
/ GPIO.FALLING
/ GPIO.BOTH
。
RPi.GPIO为回调函数另外开启一个线程。这意味这回调函数可以和你的主程序同时运行,及时的对边沿做出响应。例如:
def my_callback(channel):
print('This is a edge event callback function!')
print('Edge detected on channel %s'%channel)
print('This is run in a different thread to your main program')
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback) # add rising edge detection on a channel
...the rest of your program...
如果你想设置多个回调函数,可以这样:
def my_callback_one(channel):
print('Callback one')
def my_callback_two(channel):
print('Callback two')
GPIO.add_event_detect(channel, GPIO.RISING)
GPIO.add_event_callback(channel, my_callback_one)
GPIO.add_event_callback(channel, my_callback_two)
注意,这种情况下,回调函数将按照他们被定义的顺序的执行,不会同时执行,因为这里只有一个线程用于执行回调函数。(小白注:是按照add的顺序还是定义的顺序?原文:This is because there is only one thread used for callbacks, in which every callback is run, in the order in which they have been defined.)
你可能注意到当你按下一次按钮时,回调函数可能会被调用多次。产生这个结果的原因被成为开关抖动。下面有几种方式可以应对开关抖动:
在指定回调函数时,你可以加入bouncetime=
参数来使用软件去抖,回跳时间的单位是毫秒。例如
# add rising edge detection on a channel, ignoring further edges for 200ms for switch bounce handling
GPIO.add_event_detect(channel, GPIO.RISING, callback=my_callback, bouncetime=200)
或
GPIO.add_event_callback(channel, my_callback, bouncetime=200)
出于某些原因,当你的程序不在需要检测边沿事件时,可以使用下面的代码停止检测:
GPIO.remove_event_detect(channel)
1. 使用RPi.GPIO
设置引脚为输出(正如这里描述的)
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
2. 将输出值设置为高电平:
GPIO.output(12, GPIO.HIGH)
# or
GPIO.output(12, 1)
# or
GPIO.output(12, True)
3. 将输出值设置为低电平:
GPIO.output(12, GPIO.LOW)
# or
GPIO.output(12, 0)
# or
GPIO.output(12, False)
4. 同时设置多个输出引脚的值:
chan_list = (11,12)
GPIO.output(chan_list, GPIO.LOW) # all LOW
GPIO.output(chan_list, (GPIO.HIGH,GPIO.LOW)) # first LOW, second HIGH
5. 在程序的最后释放资源:
GPIO.cleanup()
注意,你可以使用Input()
函数读取一个输出引脚的状态并将其作为输出值,例如:
GPIO.output(12, not GPIO.input(12))
创建一个PWM实例:
p = GPIO.PWM(channel, frequency)
开始脉宽调制:
p.start(dc) # where dc is the duty cycle (0.0 <= dc <= 100.0)
更改调制频率:
p.ChangeFrequency(freq) # where freq is the new frequency in Hz
更改占空比:
p.ChangeDutyCycle(dc) # where 0.0 <= dc <= 100.0
停止输出PWM波:
p.stop()
注意,当实例变量p
超出作用域时,也会停止输出PWM波。
下面的例子将会使LED灯以两秒的速度闪烁:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 0.5)
p.start(1)
input('Press return to stop:') # use raw_input for Python 2
p.stop()
GPIO.cleanup()
下面的例子使一个LED逐渐的变亮和变暗:
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
p = GPIO.PWM(12, 50) # channel=12 frequency=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()
返回一个GPIO信道的功能。
例如:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
func = GPIO.gpio_function(pin)
函数将返回下列值中的一个
GPIO.IN
/ GPIO.OUT
/ GPIO.SPI
/ GPIO.I2C
/ GPIO.HARD_PWM
/ GPIO.SERIAL
/ GPIO.UNKNOWN