一、RPI.GPIO模块
GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。GPIO是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。掌握了GPIO,差不多相当于掌握了操作硬件的能力。
二、安装RPI.GPIO
# 更换镜像源
pi@raspberrypi:~ $ sudo sed -i 's#://raspbian.raspberrypi.org#s://mirrors.ustc.edu.cn/raspbian#g' /etc/apt/sources.list
pi@raspberrypi:~ $ sudo sed -i 's#://archive.raspberrypi.org/debian#s://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian#g' /etc/apt/sources.list.d/raspi.list
# 更新软件包
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install python3-rpi.gpio
三、针脚编号
第一种编号是BOARD编号,这和树莓派电路板上的物理引脚编号相对应。使用这种编号的好处是,你的硬件将是一直可以使用的,不用担心树莓派的版本问题。因此,在电路板升级后,你不需要重写连接器或代码。
第二种编号是BCM规则,是更底层的工作方式,它和Broadcom的片上系统中信道编号相对应。在使用一个引脚时,你需要查找信道号和物理引脚编号之间的对应规则。对于不同的树莓派版本,编写的脚本文件也可能是无法通用的。
对于GPIO号接口的12引脚GPIO信号18.如果你使用GPIO.BCM模式,你可以使用数字18使用它,但如果你使用GPIO.BOARD模式,你需要使用数字12使用它。
带GPIO的为BCM编号。
指定一种编号规则:
GPIO.setmode(GPIO.BOARD)
# or
GPIO.setmode(GPIO.BCM)
# 返回被设置的编号规则
mode = GPIO.getmode()
四、警告
如果RPi.GRIO检测到一个引脚已经被设置成了非默认值,那么你将看到一个警告信息。你可以通过下列代码禁用警告:
GPIO.setwarnings(False)
四、引脚设置
在使用一个引脚前,你需要设置这些引脚作为输入还是输出。配置一个引脚的代码如下:
# 将引脚设置为输入模式
GPIO.setup(channel, GPIO.IN)
# 将引脚设置为输出模式
GPIO.setup(channel, GPIO.OUT)
# 为输出的引脚设置默认值
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)
五、释放
一般来说,程序到达最后都需要释放资源,这个好习惯可以避免偶然损坏树莓派。释放脚本中的使用的引脚:
GPIO.cleanup()
注意,GPIO.cleanup()只会释放掉脚本中使用的GPIO引脚,并会清除设置的引脚编号规则。
六、输出
要想点亮一个LED灯,或者驱动某个设备,都需要给电流和电压他们,这个步骤也很简单,设置引脚的输出状态就可以了,代码如下:
GPIO.output(channel, state)
状态可以设置为0 / GPIO.LOW / False / 1 / GPIO.HIGH / True。如果编码规则为,GPIO.BOARD,那么channel就是对应引脚的数字。
如果想一次性设置多个引脚,可使用下面的代码:
chan_list = [11,12]
GPIO.output(chan_list, GPIO.LOW)
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW))
你还可以使用Input()函数读取一个输出引脚的状态并将其作为输出值,例如:
GPIO.output(12, not GPIO.input(12))
七、输入
我们也常常需要读取引脚的输入状态,获取引脚输入状态如下代码:
GPIO.input(channel)
低电平返回0 / GPIO.LOW / False,高电平返回1 / GPIO.HIGH / True。
如果输入引脚处于悬空状态,引脚的值将是漂动的。换句话说,读取到的值是未知的,因为它并没有被连接到任何的信号上,直到按下一个按钮或开关。由于干扰的影响,输入的值可能会反复的变化。
使用如下代码可以解决问题:
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
需要注意的是,上面的读取代码只是获取当前一瞬间的引脚输入信号。
如果需要实时监控引脚的状态变化,可以有两种办法。最简单原始的方式是每隔一段时间检查输入的信号值,这种方式被称为轮询。如果你的程序读取的时机错误,则很可能会丢失输入信号。轮询是在循环中执行的,这种方式比较占用处理器资源。另一种响应GPIO输入的方式是使用中断(边缘检测),这里的边缘是指信号从高到低的变换(下降沿)或从低到高的变换(上升沿)。
八、轮询方式
while GPIO.input(channel) == GPIO.LOW:
time.sleep(0.01) # 等10毫秒给CPU一个信号
九、边缘检测
边缘是指信号状态的改变,从低到高(上升沿)或从高到低(下降沿)。通常情况下,我们更关心于输入状态的该边而不是输入信号的值。这种状态的该边被称为事件。
先介绍两个函数:
wait_for_edge() 函数。
wait_for_edge()被用于阻止程序的继续执行,直到检测到一个边沿。也就是说,上文中等待按钮按下的实例可以改写为:
channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
if channel is None:
print('Timeout occurred')
else:
print('Edge detected on channel', channel)
add_event_detect() 函数
该函数对一个引脚进行监听,一旦引脚输入状态发生了改变,调用event_detected()函数会返回true,如下代码:
do_something()
// 下面的代码放在一个线程循环执行。
if GPIO.event_detected(channel):
print('Button pressed')
上面的代码需要自己新建一个线程去循环检测event_detected()的值,还算是比较麻烦的。
不过可采用另一种办法轻松检测状态,这种方式是直接传入一个回调函数:
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)
如果你想设置多个回调函数,可以这样:
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)
注意:回调触发时,并不会同时执行回调函数,而是根据设置的顺序调用它们。