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

什麼是 GPIO

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


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

簡單的講,每一個 GPIO 引腳都有兩種模式:輸出模式(OUTPUT)和輸入模式(INPUT)。輸出模式類似於一個電源,Raspberry Pi 可以控制這個電源是否向外供電,比如打開外部的 LED 小燈,當然最有用的還是向外部設備發送信號。輸入模式相當於電源的陰極,還是以 LED 小燈爲例,只不過這次 LED 小燈的陽極接着外部電源,一個電路只有產生電壓差時纔會有電流,因此要想讓小燈亮需要讓電流流入 Raspberry Pi 中。和輸出模式相反,輸入模式是接收外部設備發來的信號。

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

相關類(Class)

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

GPIO 操作主要依賴於兩個類: GpioControllerGpioPin 。這兩個類位於 System.Devices.Gpio 名稱空間下。

GpioController

// GpioController 即 GPIO 控制器
// GPIO 引腳依靠 GpioController 初始化
public class GpioController : IDisposable
{
    // 構造函數
    /* PinNumberingScheme 即引腳編號方案,是一個枚舉類型,包含 Board 和 Gpio 兩個值。 
     * 可以看上方的 Raspberry Pi 引腳圖,以 GPIO 17 爲例,如果實例化時選 Gpio ,那麼打開引腳時需要填寫 17。
     * 如果實例化時選 Board ,那麼打開引腳時需要填寫右側灰色方框內的值,即 11 。
    */
    public GpioController(PinNumberingScheme numbering = PinNumberingScheme.Gpio);
    // 第二個構造函數中的 GpioDriver 應該是用於擴展的,一般還是用 Raspberry Pi 默認的 GPIO 驅動。
    public GpioController(GpioDriver driver, PinNumberingScheme numbering = PinNumberingScheme.Gpio);

    // 屬性
    // 獲取已打開的所有 GPIO 引腳
    public IEnumerable<GpioPin> OpenPins { get; }

    // 方法
    // 打開 GPIO 引腳,pinNumber 需要填寫和 PinNumberingScheme 相對應的值。
    public GpioPin OpenPin(int pinNumber);
    // 關閉 GPIO 引腳
    public void ClosePin(int pinNumber);
    public void ClosePin(GpioPin pin);
    // 判斷某個引腳是否打開
    // 注意:引腳連續打開會拋出異常
    public bool IsPinOpen(int pinNumber); 
}

GpioPin

// GpioPin 表示單個的引腳實體
// 需要通過 GpioController.OpenPin() 獲取
public class GpioPin : IDisposable
{
    // 屬性
    // 一個去抖時間,即在此時間間隔引腳電平變化,不觸發 ValueChanged 事件
    public TimeSpan DebounceTimeout { get; set; }

    // 事件
    // 引腳電平變化時觸發
    public event EventHandler<PinValueChangedEventArgs> ValueChanged;

    // 方法
    // 讀取當前引腳電平
    public PinValue Read();
    // 向引腳寫入指定電平
    public void Write(PinValue value);
}

人體紅外傳感器實驗

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

人體紅外傳感器是基於周圍區域的紅外熱來檢測運動的,也稱被動紅外傳感器(Passive Infra-Red, PIR)。

這裏使用的是 HC-SR501 。當傳感器檢測到人體時,LED 小燈亮,當傳感器未檢測到人體時,LED 小燈滅。

傳感器圖像


HC-SR501

硬件

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

電路

HC-SR501

  • VCC - 5V
  • GND - GND
  • OUT - GPIO 17

LED

  • VCC & 220 Ω resistor - GPIO 27
  • GND - GND

代碼

  1. 打開 Visual Studio ,新建一個 .NET Core 控制檯應用程序,項目名稱爲“PIR”。
  2. 引入 System.Devices.Gpio NuGet 包。
  3. 新建類 HCSR501,替換如下代碼(此處略有精簡,只爲必要的代碼,不包含自定義事件,詳細可查看提供的示例):

    public class HCSR501 : IDisposable
    {
        private GpioPin sensor;
        private readonly int pinOut;
    
        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="pin">OUT Pin</param>
        public HCSR501(int pin)
        {
            pinOut = pin;
        }
    
        /// <summary>
        /// 初始化
        /// </summary>
        public void Initialize()
        {
            // 實例化 GpioController
            GpioController controller = new GpioController(PinNumberingScheme.Gpio);
            // 打開引腳,設置模式爲輸入模式
            sensor = controller.OpenPin(pinOut, PinMode.Input);
        }
    
        /// <summary>
        /// 讀取
        /// </summary>
        /// <returns>是否檢測到人體</returns>
        public bool Read()
        {
            // 當電平爲高時,認爲檢測到人體
            if (sensor.Read() == PinValue.High)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
  4. Program.cs 中,將主函數代碼替換如下:

    static void Main(string[] args)
    {
        // get the GPIO controller
        // 獲取 GPIO 控制器
        GpioController controller = new GpioController(PinNumberingScheme.Gpio);
        // open PIN 27 for led
        // 爲 led 打開引腳 27
        GpioPin led = controller.OpenPin(27, PinMode.Output);
    
        // initialize PIR sensor
        // 初始化傳感器
        HCSR501 sensor = new HCSR501(17);
        sensor.Initialize();
    
        // loop
        // 循環
        while (true)
        {
            if (sensor.Read() == true)
            {
                // turn the led on when the sensor detected infrared heat
                // 當傳感器檢測到熱量時打開 led
                led.Write(PinValue.High);
                Console.WriteLine("Detected! Turn the LED on.");
            }
            else
            {
                // turn the led off when the sensor undetected infrared heat
                // 當傳感器未檢測到熱量時關閉 led
                led.Write(PinValue.Low);
                Console.WriteLine("Undetected! Turn the LED off.");
            }
    
            // wait for a second
            // 等待 1s
            Thread.Sleep(1000);
        }
    }
  5. 發佈、拷貝、更改權限、運行

效果圖

  如何改進?

剔除主函數循環,嘗試在自定義事件中進行檢測,即 GpioPin 的 ValueChanged 事件。


  備註

下一篇文章將談談 IIC 總線的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章