张高兴的 .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 小灯,当然最有用的还是向外部设备发送信号。和输出模式相反,输入模式是接收外部设备发来的信号。其中还包含两种特殊的输入模式:上拉输入(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
张高兴的 .NET Core IoT 入门指南:(二)GPIO 的使用的更多相关文章
- 张高兴的 .NET Core IoT 入门指南:(一)环境配置、Blink、部署
如何在 Raspberry Pi 的 Raspbian 上构建使用 GPIO 引脚的 IoT 程序?你可能会回答使用 C++ 或 Python 去访问 Raspberry Pi 的引脚.现在,C# 程 ...
- 张高兴的 .NET Core IoT 入门指南:(四)使用 SPI 进行通信
什么是 SPI 和上一篇文章的 I2C 总线一样,SPI(Serial Peripheral Interface,串行外设接口)也是设备与设备间通信方式的一种.SPI 是一种全双工(数据可以两个方向同 ...
- 张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信
什么是 I2C 总线 I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种.它是一种串行通信总线,由飞利浦公司在1980年代为了让主板.嵌入式系统或手机 ...
- 张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出
什么是 PWM 在解释 PWM 之前首先来了解一下电路中信号的概念,其中包括模拟信号和数字信号.模拟信号是一种连续的信号,与连续函数类似,在图形上表现为一条不间断的连续曲线.数字信号为只能取有限个数值 ...
- 张高兴的 .NET Core IoT 入门指南:(五)串口通信入门
在开始之前,首先要说明的是串口通信所用到的 SerialPort 类并不包含在 System.Device.Gpio NuGet 包中,而是在 System.IO.Ports NuGet 包中.之所以 ...
- 张高兴的 .NET IoT 入门指南:(七)制作一个气象站
距离上一篇<张高兴的 .NET Core IoT 入门指南>系列博客的发布已经过去 2 年的时间了,2 年的时间 .NET 版本发生了巨大的变化,.NET Core 也已不复存在,因此本系 ...
- 张高兴的 .NET IoT 入门指南:(八)基于 GPS 的 NTP 时间同步服务器
时间究竟是什么?这既可以是一个哲学问题,也可以是一个物理问题.古人对太阳进行观测,利用太阳的投影发明了日晷,定义了最初的时间.随着科技的发展,天文观测的精度也越来越准确,人们发现地球的自转并不是完全一 ...
- require.js入门指南(二)
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- kotlin 语言入门指南(二)--代码风格
语言风格 这里整理了 kotlin 惯用的代码风格,如果你有喜爱的代码风格,可以在 github 上给 kotlin 提 pull request . 创建DTOs(POJSs/POCOs) 文件: ...
随机推荐
- C#取整函数Math.Round、Math.Ceiling和Math.Floor 【非原创,用来收藏,分享】
1.Math.Round:四舍六入五取偶 引用内容 Math.Round(0.0) //0Math.Round(0.1) //0Math.Round(0.2) //0Math.Round(0.3) / ...
- JWT操作(.net)
1.JWT定义 JWT(Json Web Token)是一种用于双方之间传递安全信息的简洁的.URL安全的表述性声明规范.JWT作为一个开放的标准( RFC 7519 ),定义了一种简洁的,自包含的方 ...
- 【C#设计模式-抽象工厂模式】
一.抽象工厂模式的定义: 为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类. 二.抽象工厂模式的结构: 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态.抽象工厂模 ...
- Java - TreeMap源码解析 + 红黑树
Java提高篇(二七)-----TreeMap TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap ...
- 洛谷P4069 [SDOI2016]游戏(李超线段树)
题意 题目链接 Sol 这题细节好多啊qwq..稍不留神写出一个小bug就要调1h+.. 思路就不多说了,把询问区间拆成两段就是李超线段树板子题了. 关于dis的问题可以直接维护. // luogu- ...
- cf97D. Robot in Basement(模拟 bitset)
题意 题目链接 Sol 接下来我的实现方式和论文里不太一样 然后用bitset优化,上下走分别对应着右移/左移m位,左右走对应着右移/左移1位 我们可以直接预处理出能走的格子和不能走的格子,每次走的时 ...
- js-ES6学习笔记-module(1)
1.在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种.前者用于服务器,后者用于浏览器.ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可 ...
- short、int、long、float、double区别
一.基本数据类型的特点,位数,最大值和最小值.1.基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 (-2的15此方 ...
- tomcat报错this web application instance has been stopped already问题
上网搜了下,大部分的报错原因:重启时候 之前的tomcat未正常关闭 ,导致在重启时候 报了这个问题.mac下解决: ps -ef|grep tomcat 找到在进行的tomcat ,kill -9 ...
- AWS CSAA -- 04 AWS Object Storage and CDN - S3 Glacier and CloudFront(一)
013 S3 - 101 014 Create an S3 Bucket - Lab