树莓派---GPIO应用详解

前言

经过上一篇文章的解说,相信大家都应该都可以掌握树莓派的安装和基本的使用,然而要玩转树莓派,单单靠几条命令是不够。其中,对于GPIO的操作就是极其重要的。这篇文章介绍树莓派的GPIO,以及用python操作GPIO来实现一些输出输入。

GPIO基本介绍

GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。GPIO是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。掌握了GPIO,差不多相当于掌握了操作硬件的能力。

现在,我们先看看树莓派上的GPIO是怎么样的:

网上找来的

右上角的详细图:


我们重点看第二张详细图。这张图上可以看到,每一个针脚都有Pin#和NAME字段。Pin代表的是该针脚的编号,其中01和02针脚对应第一张图中GPIO最右边竖排的两个针脚。而NAME代表的是该针脚的BCM名称,当然NAME也可以直接看得出针脚的默认功能。比如 3.3v和5v代表着该针脚会输出3.3v和5v的电压,Ground代表着该针脚是接地的,GPIO0*则是一些待用户开发的针脚。每个针脚都可以使用程序进行控制操作。

控制GPIO

想用python来控制GPIO,最便捷的办法就是使用一些python类库,比如树莓派系统本身集成的RPi.GPIO。本文详细介绍如何使用RPi.GPIO来控制GPIO。

导入RPi.GPIO模块

可以用下面的代码导入RPi.GPIO模块。

import RPi.GPIO as GPIO

引入之后,就可以使用GPIO模块的函数了。如果你想检查模块是否引入成功,也可以这样写:

try:
    import RPi.GPIO as GPIO
except RuntimeError:
    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, 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)  # wait 10 ms to give CPU chance to do other things

边缘检测

边缘是指信号状态的改变,从低到高(上升沿)或从高到低(下降沿)。通常情况下,我们更关心于输入状态的该边而不是输入信号的值。这种状态的该边被称为事件。
先介绍两个函数:

  • 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,如下代码:
GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
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)

注意:回调触发时,并不会同时执行回调函数,而是根据设置的顺序调用它们。

综合例子:点亮LED灯

好了,上面说明了一大堆函数库的用法,那么现在就应该来个简单的实验了。这个实验很简单,点亮一个LED灯。

  • 编写代码之前,首先你需要将led灯的针脚通过杜邦线连接到树莓派的引脚上,比如你可以连接到11号引脚。
  • 新建一个main.py文件,写入如下代码:
import RPi.GPIO as GPIO  //引入函数库
import time

RPi.GPIO.setmode(GPIO.BOARD)  //设置引脚编号规则
RPi.GPIO.setup(11, RPi.GPIO.OUT)    //将11号引脚设置成输出模式

while True
    GPIO.output(channel, 1)   //将引脚的状态设置为高电平,此时LED亮了
    time.sleep(1)   //程序休眠1秒钟,让LED亮1秒
    GPIO.output(channel, 0)   //将引脚状态设置为低电平,此时LED灭了
    time.sleep(1)   //程序休眠1秒钟,让LED灭1秒

GPIO.cleanup()    //程序的最后别忘记清除所有资源
  • 保存,并退出文件。执行python3 main.py,即可观看效果。Ctrl+C可以关闭程序。
  • 此外,不妨也试试其它的函数吧,增强印象。

使用PWM

这个python函数库还支持PWM模式的输出,我们可以利用PWM来制作呼吸灯效果。详情看代码:

import time
import RPi.GPIO as GPIO   //引入库
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:
    while 1:
        for dc in range(0, 101, 5):
            p.ChangeDutyCycle(dc)   //修改占空比 参数范围为: (0.0 <= dc <= 100.0)
            time.sleep(0.1)
        for dc in range(100, -1, -5):
            p.ChangeDutyCycle(dc)
            time.sleep(0.1)
except KeyboardInterrupt:
    pass
p.stop()    //停止输出PWM波
GPIO.cleanup()    //清除

结语

在文中,主要讲解了GPIO的概念,以及如何使用python操作GPIO。如果有条件,建议大家多动动手,你会收获不少满足感。我也是初学者,如果你有任何问题,大家一起探讨学习。
本文参考文档:https://sourceforge.net/p/raspberry-gpio-python/wiki/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容