樹莓派硬件編程——(二)用RPi.GPIO庫獲取信號

說到輸入,咱們能夠簡單的把傳感器分爲數字傳感器模擬傳感器,數字傳感器就是指只有高低電平兩種狀態的傳感器,好比說開關、紅外線傳感器、傾斜傳感器、繼電器等等,他們只有兩種狀態:閉合和斷開,像這種傳感器咱們獲取狀態就很是簡單了,今天咱們也着重討論數字信號的獲取和處理。html

那麼什麼是模擬傳感器呢?那麼就先舉個栗子,咱們說話發出的聲音,聲音是一種連續的量,從發出到結束,能量愈來愈大再逐漸變小,直到結束,聲音還有頻率之分;那麼咱們把這種連續的量,能夠測量出具體的值的量稱之爲模擬量,這種傳感器爲模擬傳感器。咱們生活中常見的模擬傳感器還有溫溼度傳感器、光敏傳感器、壓力傳感器,咱們發現溫溼度是在不斷變化的,而且咱們能夠測量獲得具體的值;亮度和物體重量咱們也均可以測量出來。模擬傳感器值的獲取就會相較麻煩,這個咱們後面再逐一討論。python

1、用槽型光電模塊當開關,控制LED燈的亮滅

對於這裏爲何不用按鍵來控制,實在是按鍵模塊不知去向,只好用同數字傳感器代替;編程

  1. 咱們先來回顧一下LED閃爍的效果是如何實現的:
    import RPi.GPIO as GPIO
    import time
     
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(16,GPIO.OUT)    # 定義LED引腳模式爲輸出
     
    try:
        while True:
            GPIO.output(16,GPIO.HIGH)    # 用output()函數控制引腳的電平狀態
            time.sleep(1)
            GPIO.output(16,GPIO.LOW)
            time.sleep(1)
    except KeyboardInterrupt:
        GPIO.output(16,GPIO.GPIO.LOW)
        GPIO.cleanup()
  2. 那麼,咱們就能夠再這個基礎上添加信號輸入的部分
    import RPi.GPIO as GPIO
    import time
    
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(16,GPIO.OUT)
    GPIO.setup(12,GPIO.IN)    # 定義槽型光電引腳爲輸入模式
    
    try:
        while True:
            if GPIO.input(12):    # 用GPIO.input(引腳)函數來獲取引腳電平狀態
                                  # 若是有信號輸入,那麼就證實有物體通過,則進行處理
                GPIO.output(16,GPIO.HIGH)
            else:
                GPIO.output(16,GPIO.LOW)
    except KeyboardInterrupt:
        GPIO.output(16,GPIO.LOW)
        GPIO.cleanup()

  3. 那麼,若是咱們想槽型光電每觸發一次,更改一次LED燈的狀態,該怎麼處理呢?
    import RPi.GPIO as GPIO
    import time
    
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(16,GPIO.OUT)
    GPIO.setup(12,GPIO.IN)
    
    state = False    # 狀態位
    try:
        while True:
            if GPIO.input(12):            # 檢測到有信號
                state = not state         # 置反原先的狀態位
                GPIO.output(16,state)     # 設置引腳的輸出狀態
                time.sleep(1)             # 這裏的延時只是爲了你們方便看到效果
    except KeyboardInterrupt:
        GPIO.output(16,GPIO.LOW)
        GPIO.cleanup()


    咱們經過程序的運行效果,能夠看到每檢測到一次信號,確實會改變一次LED燈的狀態,可是咱們須要的是「檢測到 - 信號消失」應該纔是一個信號週期,才改變一次狀態,那麼咱們能夠怎麼實現呢?


    函數

    import RPi.GPIO as GPIO
    import time
    
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(16,GPIO.OUT)
    GPIO.setup(12,GPIO.IN)
    
    state = False
    try:
        while True:
            if GPIO.input(12):
            while GPIO.input(12):    # 咱們能夠再這裏加一個while循環,若是仍是檢測到有信號
                pass                 # 那麼就一直執行空語句
            state = not state
            GPIO.output(16,state)
            time.sleep(0.2)          # 這裏延時200毫秒,是爲了使電平穩定,否則可能會形成誤判
    except KeyboardInterrupt:
        GPIO.output(16,GPIO.LOW)
        GPIO.cleanup()

2、什麼是上拉電阻和下拉電阻

咱們先來看一下下面的現象(只是將槽型光電換成了普通按鍵,程序、引腳都沒有更改):spa

很神奇,我並無按按鍵,爲何它一直處於觸發狀態,並且不是一直保持高電平或者低電平,否則LED燈就不會一直閃爍了;.net

這是由於此時按鍵引腳是處於浮空狀態的,什麼意思,就是它的狀態不肯定,受周圍電平的干擾,多是高電平也多是低電平,那麼咱們就須要添加一個上拉電阻(使引腳默認爲高電平)或者一個下拉電阻(使引腳默認爲低電平)來保證引腳狀態的穩定,否則就會出現上面的現象了。設計

那麼,這裏問題來了,以前的槽型光電爲何是正常的呢?這是由於,槽型光電模塊在硬件電路上就已經添加了下拉電阻,使其默認是低電平狀態,因此咱們在編程的時候能夠不設置,可以保證電平穩定;可是咱們如今佈置的電路可沒有設計下拉電阻,因此如今引腳的狀態是不肯定的。code

注意:不是全部的傳感器都會設計上拉/下拉電阻的,因此咱們在編程的時候最好進行一步設置,再有就是,不是全部的傳感器都是下拉電阻的,這個跟硬件的電路設計有關,因此咱們能夠注意一下傳感器上面究竟是高電平觸發仍是低電平觸發。htm

OK,接下來咱們就須要經過編程,來肯定引腳的狀態了:blog

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(12,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
# 咱們能夠經過 pull_up_down 設置上拉/下拉電阻,對應的就是GPIO.PUD_UP/GPIO.PUD_DOWN

state = False
try:
    while True:
        if GPIO.input(12):
            while GPIO.input(12):
                pass
            state = not state
            GPIO.output(16,state)
            time.sleep(0.2)
except KeyboardInterrupt:
    GPIO.output(16,GPIO.LOW)
    GPIO.cleanup()

3、樹莓派引腳輸入的檢測方式:輪詢和中斷

  1. 什麼是輪詢:
    咱們上面檢測按鍵的方式就是輪詢,就是說樹莓派在不斷的檢測按鍵是否有輸入,這是一種比較初等的檢測方式,它對資源的消耗會比較大,且當樹莓派在執行其餘任務是,可能會錯過按鍵的檢測;
  2. 什麼是中斷:
    舉個栗子,咱們週末在家,做業作完了正在看電視,這時候媽媽說家裏醬油沒有了,讓你幫忙去買醬油,這時候你就須要暫停看電視這件事,先去把醬油買了以後,再回來看電視。中斷其實就是這個道理,我不阻止你作其餘事情,你在作其餘事情的時候可能會被優先級更高的事情打斷,那麼就要先把優先級更高的事情作完,再回來作以前沒有完成的事情,這就是中斷。
    中斷的好處就是,不會佔用不少資源(能夠忽略不計),且當觸發中斷時,能夠第一時間來處理中斷,不會形成錯過事件觸發。

  3. 什麼是邊沿檢測:
    以前咱們講過,對於咱們的硬件來說,它們就只有兩種狀態:高電平和低電平,其實這個電平是一個相對狀態;什麼意思,就是說低電平不必定是0V,咱們通常規定低於一個閾值,那麼此時就是低電平狀態,高於一個閾值就是高電平狀態;那麼在電平變化的過程當中,確定會有一個上升或者降低的趨勢,那麼這個趨勢咱們稱之爲上升沿或者降低沿。
    這裏咱們就能夠知道了,邊沿檢測就是檢測這個變化趨勢是怎麼樣的,好比說咱們按下按鍵,原本是低電平的,如今要變成高電平了,這時候就處於一個上升的趨勢,那麼這就是上升沿,當樹莓派檢測到這個電平變化以後呢,就能夠對其進行處理了。

4、wait_for_edge()函數的使用

wait_for_edge()函數的做用,咱們來打個比方:它就像一個霸王,它在執行的時候,必定要獲得他想要的東西(檢測到邊沿變化),否則的話它就一直等在這裏,不讓後面的語句運行,直到檢測到邊沿變化。

那麼,它存在的意義是什麼呢?它佔用資源的時間不多,基本能夠忽略不計。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(12,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

state = False
try:
    while True:
        GPIO.wait_for_edge(12, GPIO.RISING)
        # GPIO.RISING    上升沿檢測
        # GPIO.FALLING   降低沿檢測
        # GPIO.BOTH      二者均可以,也就是說檢測到邊沿變化
        state = not state
        GPIO.output(16,state)
        time.sleep(0.2)
except KeyboardInterrupt:
    GPIO.output(16,GPIO.LOW)
    GPIO.cleanup()

可是咱們總不可能就讓樹莓派執行一個任務吧,咱們能夠給他添加響應時間,在響應時間內接受響應,超過響應時間就容許你作接下來的任務:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(12,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

state = False
try:
    while True:
        channel = GPIO.wait_for_edge(12, GPIO.RISING,timeout=5000)
        # 用 timeout 設置響應時間
        if channel is None:
            print('Timeout',channel)
        else:
            state = not state
            GPIO.output(16,state)
            print(channel)
            time.sleep(0.2)
        print('lalala')
except KeyboardInterrupt:
    GPIO.output(16,GPIO.LOW)
    GPIO.cleanup()

咱們發現,當咱們沒有在響應時間內按下按鍵,那麼wait_for_edge()返回的值是None,若是觸發了,返回的則是引腳編號;
可是這裏須要注意的一點是,這裏並無使用中斷,也就是說其餘任務在執行的時候,按下按鍵是沒有任何反應的,否則咱們經過在後面加一個五秒延時,在延時的時候,按鍵是沒有任何反應的;

5、用add_event_detect()添加中斷檢測

其實咱們能夠這樣理解,樹莓派有一個小本本,記錄了當什麼中斷觸發的時候,該處理什麼事情,這時候咱們就能夠在這個小本本上面添加咱們的需求,當引腳12檢測到邊沿變化的時候,就要對這個事件進行處理。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT)
GPIO.setup(12,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)

state = False
def comm(chn):
    global state
    state = not state
    GPIO.output(16,state)
    time.sleep(0.2)    # 這個語句的做用是防止按鍵電平不穩定,形成屢次觸發
                       # 能夠在add_event_detect()中添加bouncetime=200來代替

try:
    GPIO.add_event_detect(12,GPIO.RISING,callback=comm)
    # GPIO.add_event_detect(12,GPIO.RISING,callback=comm,bouncetime=200)
    # 若是在後面咱們不須要事件響應了,能夠用GPIO.remove_event_detect(引腳)在事件列表中刪除
    while True:
        print('lalala')
        time.sleep(5)
except KeyboardInterrupt:
    GPIO.output(16,GPIO.LOW)
    GPIO.cleanup()

咱們分析一下,上面這段程序都作了些什麼事情:

  1. 首先,咱們將按鍵按下去以後應該作的事情定義了一個函數comm(chn)
    注意:此時的函數後面會被其餘函數調用,因此這個函數咱們稱之爲回調函數,且這個函數會有一個默認的參數:chn,即引腳號,咱們這麼定義就好,不歸咱們使用
  2. 而後咱們用 GPIO.add_event_detect()函數添加了一箇中斷事件
    其參數就是:引腳號、邊沿變化模式和回調函數
    注意:添加事件咱們不可以放在循環中,重複添加就會報錯的

  3. 最後,循環主體咱們能夠作其餘任務,當事件觸發的時候,會停下當前的事情,把事件的回調函數作完以後再回來作以前沒有完成的任務。

那麼,一個事件可不能夠對應多個回調函數呢?
答案是能夠的,可是它不是同時執行多個回調函數的,他會按照你的添加事件列表的順序去執行;

發佈了104 篇原創文章 · 獲贊 50 · 訪問量 21萬+
相關文章
相關標籤/搜索