串口通信
概述
串口通信是嵌入式系统中最常用的数据传输方式之一。YF3300-ESP32S3 开发板提供了 2 个串口:RS232 和 RS485,用于与外部设备进行数据通信。本章节将介绍如何使用 nanoFramework 进行串口编程。
所需 NuGet 包
在 nanoFramework 项目中使用串口需要引用以下包:
| 包名 | 说明 |
|---|---|
nanoFramework.CoreLibrary | 基础类库(通常自动包含) |
nanoFramework.System.IO.Ports | 串口操作核心库 |
nanoFramework.Hardware.Esp32 | ESP32 硬件配置库(用于引脚映射) |
注意:串口通信不需要引用
nanoFramework.System.Device.Gpio库。引脚映射通过nanoFramework.Hardware.Esp32.Configuration.SetPinFunction()完成,而非 GPIO 控制器。
核心概念
SerialPort 类
SerialPort 是 nanoFramework 中串口操作的核心类,主要属性和方法:
硬件连接说明
YF3300-ESP32S3 开发板提供 2 个串口:
| 串口类型 | 端口名 | TX 引脚 | RX 引脚 | 默认波特率 |
|---|---|---|---|---|
| RS232 | COM2 | GPIO11 | GPIO12 | 9600 |
| RS485 | COM1 | GPIO9 | GPIO10 | 9600 |
串口使用示例
YF3300_ESP32S3.cs
using System;
namespace YFSoft.Hardware.YF3300_ESP32S3
{
public static class CPU
{
public static class Pins
{
public const int GPIO9 = 9;
public const int GPIO10 = 10;
public const int GPIO11 = 11;
public const int GPIO12 = 12;
}
}
public static class Mainboard
{
// RS485 串口定义
public static class RS485
{
public const string PortName = "COM1";
public const int DefaultBaudRate = 9600;
public const int TxPin = CPU.Pins.GPIO9; // UART1 TX (电路图: TX1=IO9)
public const int RxPin = CPU.Pins.GPIO10; // UART1 RX (电路图: RX1=IO10)
}
// RS232 串口定义
public static class RS232
{
public const string PortName = "COM2";
public const int DefaultBaudRate = 9600;
public const int TxPin = CPU.Pins.GPIO11; // UART2 TX (电路图: TX2=IO11)
public const int RxPin = CPU.Pins.GPIO12; // UART2 RX (电路图: RX2=IO12)
}
}
}
Program.cs
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.IO.Ports;
using YFSoft.Hardware.YF3300_ESP32S3;
using nanoFramework.Hardware.Esp32;
namespace SerialPortTest
{
public class Program
{
// 串口实例
private static SerialPort _rs232Port;
private static SerialPort _rs485Port;
public static void Main()
{
Debug.WriteLine("YF3300-ESP32S3 串口测试程序启动");
try
{
// 初始化 RS232
InitializeRS232();
// 初始化 RS485
InitializeRS485();
// 发送打招呼消息
SendGreeting();
// 启动回显线程
StartEchoLoop();
}
catch (Exception ex)
{
Debug.WriteLine($"初始化失败: {ex.Message}");
}
Thread.Sleep(Timeout.Infinite);
}
// 初始化 RS232 串口
private static void InitializeRS232()
{
Debug.WriteLine($"配置 RS232 引脚: Tx={Mainboard.RS232.TxPin}, Rx={Mainboard.RS232.RxPin}");
// 配置 RS232 串口引脚映射 (COM2)
// 在创建 SerialPort 实例之前配置引脚
Configuration.SetPinFunction(Mainboard.RS232.TxPin, DeviceFunction.COM2_TX);
Configuration.SetPinFunction(Mainboard.RS232.RxPin, DeviceFunction.COM2_RX);
Debug.WriteLine("创建 RS232 SerialPort 实例...");
_rs232Port = new SerialPort(
Mainboard.RS232.PortName,
Mainboard.RS232.DefaultBaudRate,
Parity.None,
8,
StopBits.One);
Debug.WriteLine("打开 RS232 串口...");
_rs232Port.Open();
Debug.WriteLine($"RS232 已初始化: {Mainboard.RS232.PortName} @ {Mainboard.RS232.DefaultBaudRate}");
}
// 初始化 RS485 串口
private static void InitializeRS485()
{
Debug.WriteLine($"配置 RS485 引脚: Tx={Mainboard.RS485.TxPin}, Rx={Mainboard.RS485.RxPin}");
// 配置 RS485 串口引脚映射 (COM1)
// 在创建 SerialPort 实例之前配置引脚
Configuration.SetPinFunction(Mainboard.RS485.TxPin, DeviceFunction.COM1_TX);
Configuration.SetPinFunction(Mainboard.RS485.RxPin, DeviceFunction.COM1_RX);
Debug.WriteLine("创建 RS485 SerialPort 实例...");
_rs485Port = new SerialPort(
Mainboard.RS485.PortName,
Mainboard.RS485.DefaultBaudRate,
Parity.None,
8,
StopBits.One);
Debug.WriteLine("打开 RS485 串口...");
_rs485Port.Open();
Debug.WriteLine($"RS485 已初始化: {Mainboard.RS485.PortName} @ {Mainboard.RS485.DefaultBaudRate}");
}
// 发送打招呼消息
private static void SendGreeting()
{
string greeting = "Hello from YF3300-ESP32S3!\r\n";
byte[] data = Encoding.UTF8.GetBytes(greeting);
// RS232 发送
_rs232Port.Write(data, 0, data.Length);
Debug.WriteLine("RS232 打招呼消息已发送");
// RS485 发送
_rs485Port.Write(data, 0, data.Length);
Debug.WriteLine("RS485 打招呼消息已发送");
}
// 启动回显循环
private static void StartEchoLoop()
{
Debug.WriteLine("进入回显模式...");
Debug.WriteLine("RS232 和 RS485 会回显收到的所有数据");
// 启动 RS232 回显线程
new Thread(RS232EchoLoop).Start();
// 启动 RS485 回显线程
new Thread(RS485EchoLoop).Start();
}
// RS232 回显循环
private static void RS232EchoLoop()
{
byte[] buffer = new byte[256];
Debug.WriteLine("RS232 回显线程已启动");
while (true)
{
try
{
int bytesAvailable = _rs232Port.BytesToRead;
if (bytesAvailable > 0)
{
Debug.WriteLine($"RS232 有 {bytesAvailable} 字节待读取");
int bytesRead = _rs232Port.Read(buffer, 0, bytesAvailable);
if (bytesRead > 0)
{
// 回显数据
_rs232Port.Write(buffer, 0, bytesRead);
// 输出调试信息
string received = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Debug.WriteLine($"RS232 收到 {bytesRead} 字节: {received}");
}
}
Thread.Sleep(10);
}
catch (Exception ex)
{
Debug.WriteLine($"RS232 回显错误: {ex.Message}");
}
}
}
// RS485 回显循环
private static void RS485EchoLoop()
{
byte[] buffer = new byte[256];
Debug.WriteLine("RS485 回显线程已启动");
while (true)
{
try
{
int bytesAvailable = _rs485Port.BytesToRead;
if (bytesAvailable > 0)
{
Debug.WriteLine($"RS485 有 {bytesAvailable} 字节待读取");
int bytesRead = _rs485Port.Read(buffer, 0, bytesAvailable);
if (bytesRead > 0)
{
// 回显数据(自动流向控制,无需手动切换)
_rs485Port.Write(buffer, 0, bytesRead);
// 输出调试信息
string received = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Debug.WriteLine($"RS485 收到 {bytesRead} 字节: {received}");
}
}
Thread.Sleep(10);
}
catch (Exception ex)
{
Debug.WriteLine($"RS485 回显错误: {ex.Message}");
}
}
}
}
}