继电器与输入
概述
YF3300-ESP32S3 开发板提供了:
- 继电器输出:1 路继电器(Q1),可用于控制外部设备(如灯光、电机、阀门等)。继电器为常开型,吸合时负载接通,释放时断开。
- 开关量输入:2 路数字输入(I1、I2),可检测外部开关、传感器等的高低电平状态。
本章节将介绍如何使用 GPIO 接口控制继电器输出和读取开关量输入状态。
所需 NuGet 包
| 包名 | 说明 |
|---|---|
nanoFramework.CoreLibrary | .NET nanoFramework 核心库 |
nanoFramework.Runtime.Events | 事件运行时支持 |
nanoFramework.System.Device.Gpio | GPIO 操作库 |
nanoFramework.Hardware.Esp32 | ESP32 硬件底层库(引脚映射等) |
YFSoft.Hardware.YF3300_ESP32S3 | 本项目的硬件引脚定义库(自研) |
硬件引脚映射
YF3300-ESP32S3 主板定义了以下引脚:
| 功能 | 引脚名称 | GPIO 编号 |
|---|---|---|
| 继电器输出 Q1 | Q1 | IO48 |
| 开关量输入 I1 | I1 | IO21 |
| 开关量输入 I2 | I2 | IO47 |
| 用户指示灯 | UserLED | IO39 |
| 通信指示灯 | CommLED | IO40 |
| 按键 BOOT | BOOT | IO0 |
所有引脚定义均封装在
YFSoft.Hardware.YF3300_ESP32S3.Mainboard.Pins类中,推荐使用常量而非直接写 GPIO 编号。
核心概念
继电器输出
- 吸合:GPIO 输出高电平(High),继电器线圈得电,触点闭合。
- 释放:GPIO 输出低电平(Low),继电器断电,触点断开。
开关量输入
- 开关量输入检测外部电平:当外部开关接通到 GND 时,引脚为低电平(Low);断开时为高电平(High,需外部上拉或内部上拉)。
- 本示例中,输入引脚未启用内部上拉,需外部电路提供确定电平(如开关到 GND,并外接上拉电阻)。读数为
PinValue.High表示未触发,PinValue.Low表示触发。
代码实现
YF3300_ESP32S3.cs
using System;
using System.Diagnostics;
using System.Threading;
using System.Device.Gpio;
using nanoFramework.Hardware.Esp32;
using YFSoft.Hardware.YF3300_ESP32S3;
namespace RelayDI2Test
{
public class Program
{
// GPIO控制器
private static GpioController _gpio;
// 继电器输出引脚 - IO48
private static GpioPin _Q1;
// 继电器状态跟踪变量
private static bool _relayOn = false;
// 开关量输入引脚
private static GpioPin _I1; // 开关量1 - IO21
private static GpioPin _I2; // 开关量2 - IO47
public static void Main()
{
Debug.WriteLine("=== YF3300-ESP32S3 继电器输出与开关量输入测试 ===");
try
{
// 初始化硬件
InitializeHardware();
// 主循环
MainLoop();
}
catch (Exception ex)
{
Debug.WriteLine($"错误: {ex.Message}");
}
Thread.Sleep(Timeout.Infinite);
}
/// <summary>
/// 初始化硬件引脚
/// </summary>
private static void InitializeHardware()
{
Debug.WriteLine("\n初始化硬件...");
// 创建GPIO控制器
_gpio = new GpioController();
// 初始化继电器输出 - IO48
Debug.WriteLine($"继电器Q1: IO{Mainboard.Pins.Q1} 配置为输出");
_Q1 = _gpio.OpenPin(Mainboard.Pins.Q1);
_Q1.SetPinMode(PinMode.Output);
_relayOn = false;
_Q1.Write(PinValue.Low); // 默认关闭
Debug.WriteLine("继电器初始状态: 释放");
// 初始化开关量输入 - IO21(禁用内部上拉)
Debug.WriteLine($"开关量I1: IO{Mainboard.Pins.I1} 配置为输入(禁用上拉)");
_I1 = _gpio.OpenPin(Mainboard.Pins.I1);
_I1.SetPinMode(PinMode.Input);
// 初始化开关量输入 - IO47(禁用内部上拉)
Debug.WriteLine($"开关量I2: IO{Mainboard.Pins.I2} 配置为输入(禁用上拉)");
_I2 = _gpio.OpenPin(Mainboard.Pins.I2);
_I2.SetPinMode(PinMode.Input);
Debug.WriteLine("硬件初始化完成!");
}
/// <summary>
/// 主循环
/// </summary>
private static void MainLoop()
{
Debug.WriteLine("\n=== 开始测试 ===");
Debug.WriteLine("开关量状态实时监测");
Debug.WriteLine("------------------------");
int tickCount = 0;
while (true)
{
tickCount++;
// tickCount > 0
if (tickCount > 0 && tickCount % 60 == 0)
{
ToggleRelay();
}
// 读取开关量状态
ReadDigitalInputs();
Thread.Sleep(500);
}
}
/// <summary>
/// 切换继电器状态
/// </summary>
private static void ToggleRelay()
{
// 反转状态
_relayOn = !_relayOn;
// 写入新状态
PinValue value = _relayOn ? PinValue.High : PinValue.Low;
_Q1.Write(value);
string status = _relayOn ? "吸合" : "释放";
Debug.WriteLine($"继电器Q1: {status}");
}
/// <summary>
/// 读取开关量输入
/// </summary>
private static void ReadDigitalInputs()
{
// 读取I1状态和实际电平
PinValue i1Value = _I1.Read();
bool i1Triggered = i1Value == PinValue.Low;
// 读取I2状态和实际电平
PinValue i2Value = _I2.Read();
bool i2Triggered = i2Value == PinValue.Low;
Debug.WriteLine($"I1: {(i1Triggered ? "触发" : "未触发")} (电平: {i1Value}) | I2: {(i2Triggered ? "触发" : "未触发")} (电平: {i2Value}) ");
}
}
}
Program.cs
using System;
using System.Diagnostics;
using System.Threading;
using System.Device.Gpio;
using nanoFramework.Hardware.Esp32;
using YFSoft.Hardware.YF3300_ESP32S3;
namespace RelayDI2Test
{
public class Program
{
// GPIO控制器
private static GpioController _gpio;
// 继电器输出引脚 - IO48
private static GpioPin _Q1;
// 继电器状态跟踪变量
private static bool _relayOn = false;
// 开关量输入引脚
private static GpioPin _I1; // 开关量1 - IO21
private static GpioPin _I2; // 开关量2 - IO47
public static void Main()
{
Debug.WriteLine("=== YF3300-ESP32S3 继电器输出与开关量输入测试 ===");
try
{
// 初始化硬件
InitializeHardware();
// 主循环
MainLoop();
}
catch (Exception ex)
{
Debug.WriteLine($"错误: {ex.Message}");
}
Thread.Sleep(Timeout.Infinite);
}
/// <summary>
/// 初始化硬件引脚
/// </summary>
private static void InitializeHardware()
{
Debug.WriteLine("\n初始化硬件...");
// 创建GPIO控制器
_gpio = new GpioController();
// 初始化继电器输出 - IO48
Debug.WriteLine($"继电器Q1: IO{Mainboard.Pins.Q1} 配置为输出");
_Q1 = _gpio.OpenPin(Mainboard.Pins.Q1);
_Q1.SetPinMode(PinMode.Output);
_relayOn = false;
_Q1.Write(PinValue.Low); // 默认关闭
Debug.WriteLine("继电器初始状态: 释放");
// 初始化开关量输入 - IO21(禁用内部上拉)
Debug.WriteLine($"开关量I1: IO{Mainboard.Pins.I1} 配置为输入(禁用上拉)");
_I1 = _gpio.OpenPin(Mainboard.Pins.I1);
_I1.SetPinMode(PinMode.Input);
// 初始化开关量输入 - IO47(禁用内部上拉)
Debug.WriteLine($"开关量I2: IO{Mainboard.Pins.I2} 配置为输入(禁用上拉)");
_I2 = _gpio.OpenPin(Mainboard.Pins.I2);
_I2.SetPinMode(PinMode.Input);
Debug.WriteLine("硬件初始化完成!");
}
/// <summary>
/// 主循环
/// </summary>
private static void MainLoop()
{
Debug.WriteLine("\n=== 开始测试 ===");
Debug.WriteLine("继电器每5秒切换一次");
Debug.WriteLine("开关量状态实时监测");
Debug.WriteLine("------------------------");
int tickCount = 0;
while (true)
{
tickCount++;
// 每5秒切换继电器(每500ms计数一次,10次=5秒)
// tickCount > 0 确保第一次切换从第5秒开始
if (tickCount > 0 && tickCount % 60 == 0)
{
ToggleRelay();
}
// 读取开关量状态
ReadDigitalInputs();
Thread.Sleep(500);
}
}
/// <summary>
/// 切换继电器状态
/// </summary>
private static void ToggleRelay()
{
// 反转状态
_relayOn = !_relayOn;
// 写入新状态
PinValue value = _relayOn ? PinValue.High : PinValue.Low;
_Q1.Write(value);
string status = _relayOn ? "吸合" : "释放";
Debug.WriteLine($"继电器Q1: {status}");
}
/// <summary>
/// 读取开关量输入
/// </summary>
private static void ReadDigitalInputs()
{
// 读取I1状态和实际电平
PinValue i1Value = _I1.Read();
bool i1Triggered = i1Value == PinValue.Low;
// 读取I2状态和实际电平
PinValue i2Value = _I2.Read();
bool i2Triggered = i2Value == PinValue.Low;
Debug.WriteLine($"I1: {(i1Triggered ? "触发" : "未触发")} (电平: {i1Value}) | I2: {(i2Triggered ? "触发" : "未触发")} (电平: {i2Value}) ");
}
}
}