什么是 GPIO
GPIO 是 General Purpose Input Output 的縮寫,即“通用輸入輸出”。 Raspberry Pi 有兩列 GPIO 引腳, Raspberry Pi 通過這兩行引腳進行一些硬件上的擴展,與傳感器進行交互等等。
Raspberry Pi B+/2B/3B/3B+/Zero 引腳圖
簡單的講,每一個 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 為輸入模式,值達到相應的要求時進行中斷。
相關類
此處默認各位是面向對象的程序員,具有一定的 C# 基礎,這里只介紹本人認為常用的方法,介紹將以代碼注釋的形式體現。
GPIO 操作主要依賴於 GpioController
類 。這個類位於 System.Device.Gpio 名稱空間下。
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-SR501
硬件需求
名稱 | 數量 |
---|---|
HC-SR501 | x1 |
LED 小燈 | x1 |
220 Ω 電阻 | x1 |
杜邦線 | 若干 |
電路
HC-SR501
- VCC - 5V
- GND - GND
- OUT - GPIO 17 (Pin 11)
LED
- 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
代碼
-
打開 Visual Studio ,新建一個 .NET Core 控制台應用程序,項目名稱為“PIR”。
-
引入 System.Device.Gpio NuGet 包。
-
新建類 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; } }
-
在 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); } }
-
發布、拷貝、更改權限、運行
效果圖
如何改進?
剔除主函數循環,嘗試使用 RegisterCallbackForPinValueChangedEvent() 注冊一個回調進行檢測。
供參考
- General-purpose input/output - Wikipedia:https://en.wikipedia.org/wiki/General-purpose_input/output
- GPIO - Raspberry Pi Documentation:https://www.raspberrypi.org/documentation/usage/gpio/
- GPIO source code:https://github.com/dotnet/iot/tree/master/src/System.Device.Gpio/System/Device/Gpio