張高興的 .NET Core IoT 入門指南:(二)GPIO 的使用

什麼是 GPIO

GPIO 是 General Purpose Input Output 的縮寫,即「通用輸入輸出」。 Raspberry Pi 有兩列 GPIO 引腳, Raspberry Pi 經過這兩行引腳進行一些硬件上的擴展,與傳感器進行交互等等。css


Raspberry Pi B+/2B/3B/3B+/Zero 引腳圖git

簡單的講,每個 GPIO 引腳都有兩種模式:輸出模式(OUTPUT)和輸入模式(INPUT)。輸出模式相似於一個電源,Raspberry Pi 能夠控制這個電源是否向外供電,好比打開外部的 LED 小燈,固然最有用的仍是向外部設備發送信號。和輸出模式相反,輸入模式是接收外部設備發來的信號。其中還包含兩種特殊的輸入模式:上拉輸入(INPUT_PULLUP)和下拉輸入(INPUT_PULLDOWN)。上拉輸入就是內部的上拉電阻接 VCC ,將該引腳設置爲高電平,下拉輸入則相反。程序員

GPIO 一般採用標準邏輯電平,即高電平和低電平,用二進制 0 和 1 表示。在這兩值中間還有閾值電平,即高電平和低電平之間的界限。Arduino 會將 -0.5 ~ 1.5 V 讀取爲低電平,3 ~ 5.5 V 讀取爲高電平, Raspberry Pi 未查到相關資料。GPIO 還可用於中斷請求,即設置 GPIO 爲輸入模式,值達到相應的要求時進行中斷。github

相關類

此處默認各位是面向對象的程序員,具備必定的 C# 基礎,這裏只介紹本人認爲經常使用的方法,介紹將以代碼註釋的形式體現。docker

GPIO 操做主要依賴於 GpioController 類 。這個類位於 System.Device.Gpio 名稱空間下。c#

GpioController

// GpioController 即 GPIO 控制器
// GPIO 引腳依靠 GpioController 初始化
public class GpioController : IGpioController, IDisposable
{
    // 構造函數
    public GpioController();
    // PinNumberingScheme 即引腳編號方案,是一個枚舉類型,包含 Board 和 Logical 兩個值。 
    // 能夠看上方的 Raspberry Pi 引腳圖,以 GPIO 17 爲例,若是實例化時選 Logical ,那麼打開引腳時須要填寫 17。
    // 若是實例化時選 Board ,那麼打開引腳時須要填寫右側灰色方框內的值,即 11 。
    public GpioController(PinNumberingScheme numbering);
    // GpioDriver 用於指定要使用的 GPIO 驅動,好比 libgpiod 或 sysfs
    public GpioController(PinNumberingScheme numberingScheme, GpioDriver driver);


    // 方法
    // 打開 GPIO 引腳
    // pinNumber 須要填寫和 PinNumberingScheme 相對應的值。
    // PinMode 是設置 GPIO 的模式,如輸入、輸出、上拉、下拉
    public void OpenPin(int pinNumber, PinMode mode);
    // 關閉 GPIO 引腳
    public void ClosePin(int pinNumber);
    // 判斷某個引腳是否打開
    // 注意:引腳連續打開會拋出異常
    public bool IsPinOpen(int pinNumber); 

    // 讀取指定引腳的值
    public PinValue Read(int pinNumber);
    // 向指定的引腳寫入值
    public void Write(int pinNumber, PinValue value);

    // 爲指定引腳的值改變時註冊回調(即上文中提到的 GPIO 中斷)
    // PinEventTypes 是值改變的類型,包括上升沿(Rising,0->1)和降低沿(Falling,1->0),注意當設置爲 None 時不會觸發
    // PinChangeEventHandler 爲回調事件
    public void RegisterCallbackForPinValueChangedEvent(int pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback);
    // 爲指定引腳的值改變時註銷回調
    public void UnregisterCallbackForPinValueChangedEvent(int pinNumber, PinChangeEventHandler callback);
}

人體紅外傳感器實驗

人體紅外傳感器是基於周圍區域的紅外熱來檢測運動的,也稱被動紅外傳感器(Passive Infra-Red, PIR)。這裏使用的是 HC-SR501 。當傳感器檢測到人體時,LED 小燈亮,當傳感器未檢測到人體時,LED 小燈滅。函數

傳感器圖像


HC-SR501ui

硬件需求

名稱 數量
HC-SR501 x1
LED 小燈 x1
220 Ω 電阻 x1
杜邦線 若干

電路

HC-SR501spa

  • VCC - 5V
  • GND - GND
  • OUT - GPIO 17 (Pin 11)

LEDcode

  • VCC & 220 Ω resistor - GPIO 27 (Pin 14)
  • GND - GND

使用 Docker 運行示例

示例地址:https://github.com/ZhangGaoxing/dotnet-core-iot-demo/tree/master/src/Hcsr501

docker build -t pir-sample -f Dockerfile .
docker run --rm -it --device /dev/gpiomem pir-sample

代碼

  1. 打開 Visual Studio ,新建一個 .NET Core 控制檯應用程序,項目名稱爲「PIR」。
  2. 引入 System.Device.Gpio NuGet 包。
  3. 新建類 Hcsr501,替換以下代碼:

    public class Hcsr501 : IDisposable
    {
        private GpioController _controller;
        private readonly int _outPin;
    
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="pin">OUT Pin</param>
        public HCSR501(int outPin, PinNumberingScheme pinNumberingScheme = PinNumberingScheme.Logical)
        {
            _outPin = outPin;
    
            _controller = new GpioController(pinNumberingScheme);
            _controller.OpenPin(outPin, PinMode.Input);
        }
    
        /// <summary>
        /// 是否檢測到人體
        /// </summary>
        public bool IsMotionDetected => _controller.Read(_outPin) == PinValue.High;
    
        /// <summary>
        /// Cleanup
        /// </summary>
        public void Dispose()
        {
            _controller?.Dispose();
            _controller = null;
        }
    }
  4. Program.cs 中,將主函數代碼替換以下:

    static void Main(string[] args)
    {
        // HC-SR501 OUT Pin
        int hcsr501Pin = 17;
        // LED Pin
        int ledPin = 27;
    
        // 獲取 GPIO 控制器
        using GpioController ledController = new GpioController(PinNumberingScheme.Logical);
        // 初始化 PIR 傳感器
        using Hcsr501 sensor = new Hcsr501(hcsr501Pin, PinNumberingScheme.Logical);
        // 打開 LED 引腳
        ledController.OpenPin(ledPin, PinMode.Output);
    
        while (true)
        {
            // 檢測到了人體
            if (sensor.IsMotionDetected == true)
            {
                ledController.Write(ledPin, PinValue.High);
                Console.WriteLine("Detected! Turn the LED on.");
            }
            else
            {
                ledController.Write(ledPin, PinValue.Low);
                Console.WriteLine("Undetected! Turn the LED off.");
            }
    
            Thread.Sleep(1000);
        }
    }
  5. 發佈、拷貝、更改權限、運行

效果圖

  如何改進?

剔除主函數循環,嘗試使用 RegisterCallbackForPinValueChangedEvent() 註冊一個回調進行檢測。

供參考

  1. General-purpose input/output - Wikipedia:https://en.wikipedia.org/wiki/General-purpose_input/output
  2. GPIO - Raspberry Pi Documentation:https://www.raspberrypi.org/documentation/usage/gpio/
  3. GPIO source code:https://github.com/dotnet/iot/tree/master/src/System.Device.Gpio/System/Device/Gpio
相關文章
相關標籤/搜索