树莓派gpio接口及编程方法
树莓派现在越来越火,网上树莓派的资料也越来越多。树莓派源自英国,国外嵌入式开源领域具有良好的分享精神,树莓派各种集成库也层出不穷,下面详细介绍一下树莓派gpio接口及编程方法。
GPIO基本介绍
GPIO(GeneralPurposeI/OPorts)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。GPIO是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。掌握了GPIO,差不多相当于掌握了操作硬件的能力。
图上可以看到,每一个针脚都有Pin#和NAME字段。Pin代表的是该针脚的编号,其中01和02针脚对应第一张图中GPIO最右边竖排的两个针脚。而NAME代表的是该针脚的BCM名称,当然NAME也可以直接看得出针脚的默认功能。比如3.3v和5v代表着该针脚会输出3.3v和5v的电压,Ground代表着该针脚是接地的,GPIO0*则是一些待用户开发的针脚。每个针脚都可以使用程序进行控制操作。
GPIO的编号
操作树莓派的GPIO端口,GPIO的编号便存在三种不同的方式
1、插座编号方式
编号侧重P1插座侧,从上到下,从左到右。正如图的Header一栏。
2、BCM2835编号方式
编号侧重CPU寄存器,根据BCM2835的GPIO寄存器编号。正如图BCMGPIO一栏。
3、wiringPi编号方式
编号侧重实现逻辑,把扩展GPIO端口从0开始编号,这种编号方便编程。正如图WiringPi一栏。
GPIO代码编写
1、直接在树莓派中编写
树莓派中有几款代码(文本)编辑软件,可以在树莓派中编写代码。
2、在windows中编写
可以在windows中编写代码,如果是Python脚本可通过FTP软件传输到树莓派中,然后使用SSH客户端登录到树莓派中执行Python脚本。如果是C语言文件,除了使用FTP传输之外还需要在树莓派上编译才可以生成可执行文件(相当于windows上的exe文件)。
3、在其他linux中编写
与windows中编写代码不同,在其他linux发行版(例如ubuntu)中可安装交叉工具链,交叉编译C文件生成在树莓派的可执行文件,那么使用FTP传输可执行文件至树莓派即可。其实方式1和方式3是几乎相同的。
控制GPIO编程
想用python来控制GPIO,最便捷的办法就是使用一些python类库,比如树莓派系统本身集成的RPi.GPIO。本文详细介绍如何使用RPi.GPIO来控制GPIO。
导入RPi.GPIO模块
可以用下面的代码导入RPi.GPIO模块。
importRPi.GPIOasGPIO
引入之后,就可以使用GPIO模块的函数了。如果你想检查模块是否引入成功,也可以这样写:
try:
importRPi.GPIOasGPIO
exceptRuntimeError:
print(“引入错误”)
针脚编号
在RPi.GPIO中,同时支持树莓派上的两种GPIO引脚编号。第一种编号是BOARD编号,这和树莓派电路板上的物理引脚编号相对应。使用这种编号的好处是,你的硬件将是一直可以使用的,不用担心树莓派的版本问题。因此,在电路板升级后,你不需要重写连接器或代码。
第二种编号是BCM规则,是更底层的工作方式,它和Broadcom的片上系统中信道编号相对应。在使用一个引脚时,你需要查找信道号和物理引脚编号之间的对应规则。对于不同的树莓派版本,编写的脚本文件也可能是无法通用的。
你可以使用下列代码(强制的)指定一种编号规则:
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,notGPIO.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输入的方式是使用中断(边缘检测),这里的边缘是指信号从高到低的变换(下降沿)或从低到高的变换(上升沿)。
轮询方式
whileGPIO.input(channel)==GPIO.LOW:
time.sleep(0.01)#wait10mstogiveCPUchancetodootherthings
边缘检测
边缘是指信号状态的改变,从低到高(上升沿)或从高到低(下降沿)。通常情况下,我们更关心于输入状态的该边而不是输入信号的值。这种状态的该边被称为事件。先介绍两个函数:
wait_for_edge()函数。wait_for_edge()被用于阻止程序的继续执行,直到检测到一个边沿。也就是说,上文中等待按钮按下的实例可以改写为:channel=GPIO.wait_for_edge(channel,GPIO_RISING,timeout=5000)
ifchannelisNone:
print(‘Timeoutoccurred’)
else:
print(‘Edgedetectedonchannel’,channel)
add_event_detect()函数该函数对一个引脚进行监听,一旦引脚输入状态发生了改变,调用event_detected()函数会返回true,如下代码:GPIO.add_event_detect(channel,GPIO.RISING)#addrisingedgedetectiononachannel
do_something()
//下面的代码放在一个线程循环执行。
ifGPIO.event_detected(channel):
print(‘Buttonpressed’)
上面的代码需要自己新建一个线程去循环检测event_detected()的值,还算是比较麻烦的。
不过可采用另一种办法轻松检测状态,这种方式是直接传入一个回调函数:
defmy_callback(channel):
print(‘Thisisaedgeeventcallbackfunction!’)
print(‘Edgedetectedonchannel%s’%channel)
print(‘Thisisruninadifferentthreadtoyourmainprogram’)
GPIO.add_event_detect(channel,GPIO.RISING,callback=my_callback)
如果你想设置多个回调函数,可以这样:
defmy_callback_one(channel):
print(‘Callbackone’)
defmy_callback_two(channel):
print(‘Callbacktwo’)
GPIO.add_event_detect(channel,GPIO.RISING)
GPIO.add_event_callback(channel,my_callback_one)
GPIO.add_event_callback(channel,my_callback_two)
注意:回调触发时,并不会同时执行回调函数,而是根据设置的顺序调用它们。
综合例子:点亮LED灯
上面说明了一大堆函数库的用法,那么现在就应该来个简单的实验了。这个实验很简单,点亮一个LED灯。
编写代码之前,首先你需要将led灯的针脚通过杜邦线连接到树莓派的引脚上,比如你可以连接到11号引脚。
新建一个main.py文件,写入如下代码:
importRPi.GPIOasGPIO//引入函数库
importtime
RPi.GPIO.setmode(GPIO.BOARD)//设置引脚编号规则
RPi.GPIO.setup(11,RPi.GPIO.OUT)//将11号引脚设置成输出模式
whileTrue
GPIO.output(channel,1)//将引脚的状态设置为高电平,此时LED亮了
time.sleep(1)//程序休眠1秒钟,让LED亮1秒
GPIO.output(channel,0)//将引脚状态设置为低电平,此时LED灭了
time.sleep(1)//程序休眠1秒钟,让LED灭1秒
GPIO.cleanup()//程序的最后别忘记清除所有资源
保存,并退出文件。执行python3main.py,即可观看效果。Ctrl+C可以关闭程序。
此外,不妨也试试其它的函数吧,增强印象。
使用PWM
这个python函数库还支持PWM模式的输出,我们可以利用PWM来制作呼吸灯效果。详情看代码:
importtime
importRPi.GPIOasGPIO//引入库
GPIO.setmode(GPIO.BOARD)//设置编号方式
GPIO.setup(12,GPIO.OUT)//设置12号引脚为输出模式
p=GPIO.PWM(12,50)//将12号引脚初始化为PWM实例,频率为50Hz
p.start(0)//开始脉宽调制,参数范围为:(0.0《=dc《=100.0)
try:
while1:
fordcinrange(0,101,5):
p.ChangeDutyCycle(dc)//修改占空比参数范围为:(0.0《=dc《=100.0)
time.sleep(0.1)
fordcinrange(100,-1,-5):
p.ChangeDutyCycle(dc)
time.sleep(0.1)
exceptKeyboardInterrupt:
pass
p.stop()//停止输出PWM波
GPIO.cleanup()//清除
结语
在文中,主要讲解了GPIO的概念,以及如何使用python操作GPIO。但是想要充分了解树莓派gpio编程,还需要自己实际动手操作以及分析总结。