跳到主要内容

GPIO基础

概述

GPIO(通用输入输出)是嵌入式开发中最基础也是最重要的外设之一。YF3300-ESP32S3 开发板提供了丰富的 GPIO 资源,本章节将介绍如何使用 nanoFramework 进行 GPIO 编程,包括 LED 控制和按钮输入。

所需 NuGet 包

在 nanoFramework 项目中使用 GPIO 需要引用以下包:

包名说明
nanoFramework.CoreLibrary基础类库(通常自动包含)
nanoFramework.System.Device.Gpio核心 GPIO 操作库
nanoFramework.Runtime.Events事件运行时支持(下载 GPIO 核心库时自动包含)

核心概念

GpioController

GpioController 是 nanoFramework 中 GPIO 操作的核心类,负责管理所有 GPIO 引脚的初始化、读写和释放。推荐使用 using 语句确保资源正确释放:

using (var gpio = new GpioController())
{
// 使用 GPIO 控制器进行引脚操作
}

PinMode(引脚模式)

nanoFramework 的 System.Device.Gpio 模块支持 8 种 GPIO 模式,分为输入模式和输出模式两大类:

输入模式(3种)

模式说明适用场景
PinMode.Input浮空输入(高阻态)外部已接上拉/下拉电阻的输入场景
PinMode.InputPullUp内部上拉输入按钮、开关,未按下时为高电平
PinMode.InputPullDown内部下拉输入按钮、开关,未按下时为低电平

输出模式(5种)

模式说明适用场景
PinMode.Output推挽输出LED、继电器等需要较强驱动能力的场景
PinMode.OutputOpenDrain开漏输出I2C 总线、漏极开路通信协议
PinMode.OutputOpenDrainPullUp开漏+内部上拉需要上拉电阻的开漏总线
PinMode.OutputOpenSource开极输出(推挽互补)需要低侧驱动的场景
PinMode.OutputOpenSourcePullDown开极+内部下拉需要下拉电阻的推挽总线

关于模拟输入(ADC):nanoFramework 并未省略 ADC 功能,而是将其独立为 System.Device.Adc 模块。System.Device.Gpio 专门处理数字信号(开关量),而模拟信号读取需要使用 System.Device.Adc 模块。

PinValue(引脚值)

说明电压范围(典型)
PinValue.High高电平接近 VCC(3.3V)
PinValue.Low低电平接近 GND(0V)

PinEventTypes(中断事件类型)

用于检测引脚电平变化的中断事件:

事件类型说明
PinEventTypes.None无事件
PinEventTypes.Rising上升沿(低→高)
PinEventTypes.Falling下降沿(高→低)

注意:.NET nanoFramework 官方定义的 PinEventTypes 枚举中没有提供“双边沿触发 (Both)”选项。但双边沿触发可通过 Rising | PinEventTypes.Falling 位运算实现。

综合示例:LED 与按钮控制

硬件连接说明

LED

YF3300-ESP32S3 开发板板载 2 个 LED,均采用低电平点亮方式:

LED 名称颜色GPIO 引脚功能说明
通信指示灯黄色GPIO40网络状态指示(CommLED)
用户指示灯绿色GPIO39用户自定义(UserLED)

按钮

YF3300-ESP32S3 开发板板载 1 个 BOOT 按钮,采用上拉输入方式:

按钮名称GPIO 引脚连接方式功能说明
BOOT 按钮GPIO0上拉输入系统启动/配网触发

注意:未按下时引脚为高电平,按下时为低电平。

代码示例

Led.cs

using System;
using System.Device.Gpio;
using System.Threading;

namespace GPIOTest.Drivers
{
public class Led: IDisposable
{
private readonly GpioPin _pin; // GPIO引脚
private readonly bool _activeLow; // 是否低电平点亮(true:低电平点亮,false:高电平点亮)
private Timer _blinkTimer; // 闪烁定时器
private bool _disposed; // 是否已释放资源
private int _onDurationMs = 500; // 亮灯时间(毫秒)
private int _offDurationMs = 500; // 熄灯时间(毫秒)

public int PinNumber => _pin.PinNumber; // 获取LED引脚编号
public int OnDurationMs { get => _onDurationMs; set => _onDurationMs = value; } // 亮灯时间
public int OffDurationMs { get => _offDurationMs; set => _offDurationMs = value; } // 熄灯时间

// 构造函数,初始化LED实例
public Led(GpioController controller,int pinNumber,bool activeLow = true)
{
_activeLow = activeLow;
_pin = controller.OpenPin(pinNumber, PinMode.Output);
Off();
}

// 打开LED灯
public void On()
{
_pin.Write(_activeLow ? PinValue.Low : PinValue.High);
}

// 关闭LED灯
public void Off()
{
_pin.Write(_activeLow ? PinValue.High : PinValue.Low);
}

// 切换LED灯状态
public void ToggleLED()
{
_pin.Write(_pin.Read() == PinValue.High ? PinValue.Low : PinValue.High);
}

// 闪烁LED灯(使用默认时间)
public void BlinkLED()
{
BlinkLED(_onDurationMs, _offDurationMs);
}

// 闪烁LED灯(指定亮/灭时间)
public void BlinkLED(int onMs, int offMs)
{
StopBlink();
bool isOn = false;
_blinkTimer = new Timer(_ =>
{
if (isOn)
{
Off();
if (_blinkTimer != null) _blinkTimer.Change(offMs, Timeout.Infinite);
}
else
{
On();
if (_blinkTimer != null) _blinkTimer.Change(onMs, Timeout.Infinite);
}
isOn = !isOn;
}, null, 0, Timeout.Infinite); // 立即开始闪烁
}

// 停止闪烁LED灯
public void StopBlink()
{
if (_blinkTimer != null)
{
_blinkTimer.Dispose();
_blinkTimer = null;
}
}

// 释放资源
public void Dispose()
{
if (!_disposed)
{
StopBlink();
if(_pin != null)
{
_pin.Dispose();
}
_disposed = true;
}
}
}
}

Button.cs

using System;
using System.Device.Gpio;
using System.Threading;

namespace GPIOTest.Drivers
{
// 按钮回调委托
public delegate void ButtonCallback(int pinNumber, bool isPressed);

public class Button : IDisposable
{
private readonly GpioPin _pin; // GPIO引脚
private readonly bool _activeLow; // 是否低电平有效(true:按下低电平,false:释放高电平)
private bool _disposed; // 是否已释放资源
private Timer _initTimer; // 初始化延迟定时器
private ButtonCallback _callback; // 按钮状态变化回调

public int PinNumber => _pin.PinNumber; // 获取按钮引脚编号

public bool IsPressed => _activeLow ? _pin.Read() == PinValue.Low
: _pin.Read() == PinValue.High; // 获取按钮当前状态

// 构造函数,初始化按钮实例
public Button(GpioController controller, int pinNumber, ButtonCallback callback, bool activeLow = true, PinMode pinMode = PinMode.InputPullUp)
{
_activeLow = activeLow;
_callback = callback;
_pin = controller.OpenPin(pinNumber, pinMode);

// 延迟订阅事件,等待引脚状态稳定
_initTimer = new Timer(InitCallback, null, 100, Timeout.Infinite);
}

// 延迟初始化回调
private void InitCallback(object state)
{
if (_disposed) return;
_pin.ValueChanged += OnPinValueChanged;
_initTimer.Dispose();
_initTimer = null;
}

// GPIO引脚值变化事件处理
private void OnPinValueChanged(object sender, PinValueChangedEventArgs e)
{
if (_disposed) return;

// 低电平有效:Falling=按下,Rising=释放;高电平有效:相反
bool isPressed = _activeLow ? (e.ChangeType == PinEventTypes.Falling)
: (e.ChangeType == PinEventTypes.Rising);

_callback?.Invoke(PinNumber, isPressed);
}

// 释放资源
public void Dispose()
{
if (_disposed) return;
_disposed = true;

if (_initTimer != null)
{
_initTimer.Dispose();
}

_pin.ValueChanged -= OnPinValueChanged;
_pin.Dispose();
}
}
}

Program.cs

using System;
using System.Device.Gpio;
using System.Diagnostics;
using System.Threading;
using GPIOTest.Drivers;

namespace GPIOTest
{
public class Program
{
// 硬件引脚定义(参考YF3300_ESP32S3硬件配置)
private const int YellowLEDPin = 40; // 黄色LED - 网络状态指示
private const int GreenLEDPin = 39; // 绿色LED - 配网状态指示
private const int ButtonPin = 0; // 按钮引脚

public static void Main()
{
Debug.WriteLine("=== LED双闪测试开始 ===");

using (var gpio = new GpioController())
{
// 初始化黄色LED(快闪:亮500ms,熄灭1500ms)
var yellowLed = new Led(gpio, YellowLEDPin, activeLow: true);
Debug.WriteLine($"黄色LED (GPIO{YellowLEDPin}) - 快闪模式");
yellowLed.BlinkLED(onMs: 500, offMs: 1500);

// 初始化绿色LED(慢闪:亮200ms,灭200ms)
var greenLed = new Led(gpio, GreenLEDPin, activeLow: true);
Debug.WriteLine($"绿色LED (GPIO{GreenLEDPin}) - 慢闪模式");
greenLed.BlinkLED(onMs: 200, offMs: 200);

// 初始化按钮,控制LED开关
var button = new Button(gpio, ButtonPin, OnButtonChanged);
Debug.WriteLine($"按钮 (GPIO{ButtonPin}) - 就绪");

Thread.Sleep(Timeout.Infinite);
}
}

// 按钮状态变化回调
private static void OnButtonChanged(int pinNumber, bool isPressed)
{
if (isPressed)
{
Debug.WriteLine($"按钮按下 - 引脚{pinNumber}");
}
else
{
Debug.WriteLine($"按钮释放 - 引脚{pinNumber}");
}
}
}
}