跳到主要内容

继电器与输入

概述

YF3300-ESP32S3 开发板提供了:

  • 继电器输出:1 路继电器(Q1),可用于控制外部设备(如灯光、电机、阀门等)。继电器为常开型,吸合时负载接通,释放时断开。
  • 开关量输入:2 路数字输入(I1、I2),可检测外部开关、传感器等的高低电平状态。

本章节将介绍如何使用 GPIO 接口控制继电器输出和读取开关量输入状态。

所需 NuGet 包

包名说明
nanoFramework.CoreLibrary.NET nanoFramework 核心库
nanoFramework.Runtime.Events事件运行时支持
nanoFramework.System.Device.GpioGPIO 操作库
nanoFramework.Hardware.Esp32ESP32 硬件底层库(引脚映射等)
YFSoft.Hardware.YF3300_ESP32S3本项目的硬件引脚定义库(自研)

硬件引脚映射

YF3300-ESP32S3 主板定义了以下引脚:

功能引脚名称GPIO 编号
继电器输出 Q1Q1IO48
开关量输入 I1I1IO21
开关量输入 I2I2IO47
用户指示灯UserLEDIO39
通信指示灯CommLEDIO40
按键 BOOTBOOTIO0

所有引脚定义均封装在 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}) ");
}
}
}