樹莓派---GPIO應(yīng)用詳解

前言

經(jīng)過上一篇文章的解說狡忙,相信大家都應(yīng)該都可以掌握樹莓派的安裝和基本的使用祟绊,然而要玩轉(zhuǎn)樹莓派燎竖,單單靠幾條命令是不夠璃弄。其中,對(duì)于GPIO的操作就是極其重要的构回。這篇文章介紹樹莓派的GPIO夏块,以及用python操作GPIO來實(shí)現(xiàn)一些輸出輸入疏咐。

GPIO基本介紹

GPIO(General Purpose I/O Ports)意思為通用輸入/輸出端口,通俗地說脐供,就是一些引腳浑塞,可以通過它們輸出高低電平或者通過它們讀入引腳的狀態(tài)-是高電平或是低電平。GPIO是個(gè)比較重要的概念患民,用戶可以通過GPIO口和硬件進(jìn)行數(shù)據(jù)交互(如UART)缩举,控制硬件工作(如LED、蜂鳴器等),讀取硬件的工作狀態(tài)信號(hào)(如中斷信號(hào))等匹颤。GPIO口的使用非常廣泛仅孩。掌握了GPIO,差不多相當(dāng)于掌握了操作硬件的能力印蓖。

現(xiàn)在辽慕,我們先看看樹莓派上的GPIO是怎么樣的:

網(wǎng)上找來的

右上角的詳細(xì)圖:


我們重點(diǎn)看第二張?jiān)敿?xì)圖。這張圖上可以看到赦肃,每一個(gè)針腳都有Pin#和NAME字段溅蛉。Pin代表的是該針腳的編號(hào),其中01和02針腳對(duì)應(yīng)第一張圖中GPIO最右邊豎排的兩個(gè)針腳他宛。而NAME代表的是該針腳的BCM名稱船侧,當(dāng)然NAME也可以直接看得出針腳的默認(rèn)功能。比如 3.3v和5v代表著該針腳會(huì)輸出3.3v和5v的電壓厅各,Ground代表著該針腳是接地的镜撩,GPIO0*則是一些待用戶開發(fā)的針腳。每個(gè)針腳都可以使用程序進(jìn)行控制操作队塘。

控制GPIO

想用python來控制GPIO袁梗,最便捷的辦法就是使用一些python類庫,比如樹莓派系統(tǒng)本身集成的RPi.GPIO憔古。本文詳細(xì)介紹如何使用RPi.GPIO來控制GPIO遮怜。

導(dǎo)入RPi.GPIO模塊

可以用下面的代碼導(dǎo)入RPi.GPIO模塊。

import RPi.GPIO as GPIO

引入之后鸿市,就可以使用GPIO模塊的函數(shù)了锯梁。如果你想檢查模塊是否引入成功,也可以這樣寫:

try:
    import RPi.GPIO as GPIO
except RuntimeError:
    print("引入錯(cuò)誤")

針腳編號(hào)

在RPi.GPIO中灸芳,同時(shí)支持樹莓派上的兩種GPIO引腳編號(hào)涝桅。第一種編號(hào)是BOARD編號(hào),這和樹莓派電路板上的物理引腳編號(hào)相對(duì)應(yīng)烙样。使用這種編號(hào)的好處是冯遂,你的硬件將是一直可以使用的,不用擔(dān)心樹莓派的版本問題谒获。因此蛤肌,在電路板升級(jí)后壁却,你不需要重寫連接器或代碼。

第二種編號(hào)是BCM規(guī)則裸准,是更底層的工作方式展东,它和Broadcom的片上系統(tǒng)中信道編號(hào)相對(duì)應(yīng)。在使用一個(gè)引腳時(shí)炒俱,你需要查找信道號(hào)和物理引腳編號(hào)之間的對(duì)應(yīng)規(guī)則盐肃。對(duì)于不同的樹莓派版本,編寫的腳本文件也可能是無法通用的权悟。

你可以使用下列代碼(強(qiáng)制的)指定一種編號(hào)規(guī)則:

GPIO.setmode(GPIO.BOARD)
  # or
GPIO.setmode(GPIO.BCM)

下面代碼將返回被設(shè)置的編號(hào)規(guī)則

mode = GPIO.getmode()

警告

如果RPi.GRIO檢測(cè)到一個(gè)引腳已經(jīng)被設(shè)置成了非默認(rèn)值砸王,那么你將看到一個(gè)警告信息。你可以通過下列代碼禁用警告:

GPIO.setwarnings(False)

引腳設(shè)置

在使用一個(gè)引腳前峦阁,你需要設(shè)置這些引腳作為輸入還是輸出谦铃。配置一個(gè)引腳的代碼如下:

# 將引腳設(shè)置為輸入模式
GPIO.setup(channel, GPIO.IN)

# 將引腳設(shè)置為輸出模式
GPIO.setup(channel, GPIO.OUT)

# 為輸出的引腳設(shè)置默認(rèn)值
GPIO.setup(channel, GPIO.OUT, initial=GPIO.HIGH)

釋放

一般來說,程序到達(dá)最后都需要釋放資源榔昔,這個(gè)好習(xí)慣可以避免偶然損壞樹莓派驹闰。釋放腳本中的使用的引腳:

GPIO.cleanup()

注意,GPIO.cleanup()只會(huì)釋放掉腳本中使用的GPIO引腳撒会,并會(huì)清除設(shè)置的引腳編號(hào)規(guī)則嘹朗。

輸出

要想點(diǎn)亮一個(gè)LED燈,或者驅(qū)動(dòng)某個(gè)設(shè)備诵肛,都需要給電流和電壓他們骡显,這個(gè)步驟也很簡單,設(shè)置引腳的輸出狀態(tài)就可以了曾掂,代碼如下:

GPIO.output(channel, state)

狀態(tài)可以設(shè)置為0 / GPIO.LOW / False / 1 / GPIO.HIGH / True。如果編碼規(guī)則為壁顶,GPIO.BOARD珠洗,那么channel就是對(duì)應(yīng)引腳的數(shù)字。

如果想一次性設(shè)置多個(gè)引腳若专,可使用下面的代碼:

chan_list = [11,12]
GPIO.output(chan_list, GPIO.LOW)
GPIO.output(chan_list, (GPIO.HIGH, GPIO.LOW))   

你還可以使用Input()函數(shù)讀取一個(gè)輸出引腳的狀態(tài)并將其作為輸出值许蓖,例如:

GPIO.output(12, not GPIO.input(12))

讀取

我們也常常需要讀取引腳的輸入狀態(tài),獲取引腳輸入狀態(tài)如下代碼:

GPIO.input(channel)

低電平返回0 / GPIO.LOW / False调衰,高電平返回1 / GPIO.HIGH / True膊爪。

如果輸入引腳處于懸空狀態(tài),引腳的值將是漂動(dòng)的嚎莉。換句話說米酬,讀取到的值是未知的,因?yàn)樗]有被連接到任何的信號(hào)上趋箩,直到按下一個(gè)按鈕或開關(guān)赃额。由于干擾的影響加派,輸入的值可能會(huì)反復(fù)的變化。
使用如下代碼可以解決問題:

GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_UP)
  # or
GPIO.setup(channel, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

需要注意的是跳芳,上面的讀取代碼只是獲取當(dāng)前一瞬間的引腳輸入信號(hào)芍锦。

如果需要實(shí)時(shí)監(jiān)控引腳的狀態(tài)變化,可以有兩種辦法飞盆。最簡單原始的方式是每隔一段時(shí)間檢查輸入的信號(hào)值娄琉,這種方式被稱為輪詢。如果你的程序讀取的時(shí)機(jī)錯(cuò)誤吓歇,則很可能會(huì)丟失輸入信號(hào)孽水。輪詢是在循環(huán)中執(zhí)行的,這種方式比較占用處理器資源照瘾。另一種響應(yīng)GPIO輸入的方式是使用中斷(邊緣檢測(cè))匈棘,這里的邊緣是指信號(hào)從高到低的變換(下降沿)或從低到高的變換(上升沿)。

輪詢方式

while GPIO.input(channel) == GPIO.LOW:
    time.sleep(0.01)  # wait 10 ms to give CPU chance to do other things

邊緣檢測(cè)

邊緣是指信號(hào)狀態(tài)的改變析命,從低到高(上升沿)或從高到低(下降沿)主卫。通常情況下,我們更關(guān)心于輸入狀態(tài)的該邊而不是輸入信號(hào)的值鹃愤。這種狀態(tài)的該邊被稱為事件簇搅。
先介紹兩個(gè)函數(shù):

  • wait_for_edge() 函數(shù)。
    wait_for_edge()被用于阻止程序的繼續(xù)執(zhí)行软吐,直到檢測(cè)到一個(gè)邊沿瘩将。也就是說,上文中等待按鈕按下的實(shí)例可以改寫為:
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() 函數(shù)
    該函數(shù)對(duì)一個(gè)引腳進(jìn)行監(jiān)聽凹耙,一旦引腳輸入狀態(tài)發(fā)生了改變姿现,調(diào)用event_detected()函數(shù)會(huì)返回true,如下代碼:
GPIO.add_event_detect(channel, GPIO.RISING)  # add rising edge detection on a channel
do_something()
// 下面的代碼放在一個(gè)線程循環(huán)執(zhí)行肖抱。
if GPIO.event_detected(channel):
    print('Button pressed')

上面的代碼需要自己新建一個(gè)線程去循環(huán)檢測(cè)event_detected()的值备典,還算是比較麻煩的。

不過可采用另一種辦法輕松檢測(cè)狀態(tài)意述,這種方式是直接傳入一個(gè)回調(diào)函數(shù):

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)

如果你想設(shè)置多個(gè)回調(diào)函數(shù)提佣,可以這樣:

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)

注意:回調(diào)觸發(fā)時(shí),并不會(huì)同時(shí)執(zhí)行回調(diào)函數(shù)荤崇,而是根據(jù)設(shè)置的順序調(diào)用它們拌屏。

綜合例子:點(diǎn)亮LED燈

好了,上面說明了一大堆函數(shù)庫的用法术荤,那么現(xiàn)在就應(yīng)該來個(gè)簡單的實(shí)驗(yàn)了倚喂。這個(gè)實(shí)驗(yàn)很簡單,點(diǎn)亮一個(gè)LED燈喜每。

  • 編寫代碼之前务唐,首先你需要將led燈的針腳通過杜邦線連接到樹莓派的引腳上雳攘,比如你可以連接到11號(hào)引腳。
  • 新建一個(gè)main.py文件枫笛,寫入如下代碼:
import RPi.GPIO as GPIO  //引入函數(shù)庫
import time

RPi.GPIO.setmode(GPIO.BOARD)  //設(shè)置引腳編號(hào)規(guī)則
RPi.GPIO.setup(11, RPi.GPIO.OUT)    //將11號(hào)引腳設(shè)置成輸出模式

while True
    GPIO.output(channel, 1)   //將引腳的狀態(tài)設(shè)置為高電平吨灭,此時(shí)LED亮了
    time.sleep(1)   //程序休眠1秒鐘,讓LED亮1秒
    GPIO.output(channel, 0)   //將引腳狀態(tài)設(shè)置為低電平刑巧,此時(shí)LED滅了
    time.sleep(1)   //程序休眠1秒鐘喧兄,讓LED滅1秒

GPIO.cleanup()    //程序的最后別忘記清除所有資源
  • 保存,并退出文件啊楚。執(zhí)行python3 main.py吠冤,即可觀看效果。Ctrl+C可以關(guān)閉程序恭理。
  • 此外拯辙,不妨也試試其它的函數(shù)吧,增強(qiáng)印象颜价。

使用PWM

這個(gè)python函數(shù)庫還支持PWM模式的輸出涯保,我們可以利用PWM來制作呼吸燈效果。詳情看代碼:

import time
import RPi.GPIO as GPIO   //引入庫
GPIO.setmode(GPIO.BOARD)  //設(shè)置編號(hào)方式
GPIO.setup(12, GPIO.OUT)  //設(shè)置12號(hào)引腳為輸出模式

p = GPIO.PWM(12, 50)  //將12號(hào)引腳初始化為PWM實(shí)例 周伦,頻率為50Hz
p.start(0)    //開始脈寬調(diào)制夕春,參數(shù)范圍為: (0.0 <= dc <= 100.0)
try:
    while 1:
        for dc in range(0, 101, 5):
            p.ChangeDutyCycle(dc)   //修改占空比 參數(shù)范圍為: (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()    //清除

結(jié)語

在文中,主要講解了GPIO的概念专挪,以及如何使用python操作GPIO及志。如果有條件,建議大家多動(dòng)動(dòng)手寨腔,你會(huì)收獲不少滿足感速侈。我也是初學(xué)者,如果你有任何問題迫卢,大家一起探討學(xué)習(xí)锌畸。
本文參考文檔:https://sourceforge.net/p/raspberry-gpio-python/wiki/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市靖避,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌比默,老刑警劉巖幻捏,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異命咐,居然都是意外死亡篡九,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門醋奠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榛臼,“玉大人伊佃,你說我怎么就攤上這事洞豁∶杏椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵邑飒,是天一觀的道長金刁。 經(jīng)常有香客問我帅涂,道長,這世上最難降的妖魔是什么尤蛮? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任媳友,我火速辦了婚禮,結(jié)果婚禮上产捞,老公的妹妹穿的比我還像新娘醇锚。我一直安慰自己,他們只是感情好坯临,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布焊唬。 她就那樣靜靜地躺著,像睡著了一般尿扯。 火紅的嫁衣襯著肌膚如雪求晶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天衷笋,我揣著相機(jī)與錄音芳杏,去河邊找鬼。 笑死辟宗,一個(gè)胖子當(dāng)著我的面吹牛爵赵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泊脐,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼空幻,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了容客?” 一聲冷哼從身側(cè)響起秕铛,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缩挑,沒想到半個(gè)月后但两,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡供置,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年谨湘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡紧阔,死狀恐怖坊罢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情擅耽,我是刑警寧澤活孩,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站秫筏,受9級(jí)特大地震影響诱鞠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜这敬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一航夺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧崔涂,春花似錦阳掐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蝙茶,卻和暖如春艺骂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背隆夯。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工钳恕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹄衷。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓忧额,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愧口。 傳聞我的和親對(duì)象是個(gè)殘疾皇子睦番,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容