前面大概讲了说到串口类型的特性、优缺点外,当下看看怎么通过C#代码,如何建立串口通讯。以下是 C# 串口通信简单实现,有基础配置、数据收发、错误处理及完整代码示例
一、实现方案
1. 基础实现步骤
-
添加引用
在项目中添加对System.IO.Ports
的引用(默认已包含在.NET Framework中,.NET Core/5+需手动添加)。
如打开/关闭端口、读写数据、配置参数等 -
配置串口参数
using System.IO.Ports; SerialPort serialPort = new SerialPort { PortName = "COM3", // 串口号(根据设备实际端口修改) BaudRate = 9600, // 波特率(需与设备一致) Parity = Parity.None, // 校验位(None/Even/Odd/Mark/Space) DataBits = 8, // 数据位(5~8) StopBits = StopBits.One, // 停止位(One/Two/OnePointFive) Handshake = Handshake.None, // 流控制(None/XOnXOff/RequestToSend等) ReadTimeout = 500, // 读取超时(毫秒,-1为无限) WriteTimeout = 500 // 写入超时(毫秒) };
-
打开串口
try { serialPort.Open(); Console.WriteLine("串口已打开"); } catch (Exception ex) { Console.WriteLine($"打开串口失败: {ex.Message}"); }
-
发送数据
// 发送字符串(自动转换为字节) serialPort.WriteLine("Hello SerialPort!"); // 发送字节数组 byte[] data = { 0x01, 0x02, 0x03 }; serialPort.Write(data, 0, data.Length);
-
接收数据(事件驱动)
serialPort.DataReceived += (sender, e) => { // 在单独线程中触发,需注意线程安全 try { string receivedData = serialPort.ReadExisting(); Console.WriteLine($"接收到数据: {receivedData}"); } catch (TimeoutException) { Console.WriteLine("读取超时"); } };
-
关闭串口
if (serialPort.IsOpen) { serialPort.Close(); Console.WriteLine("串口已关闭"); }
2. 异步操作优化
-
异步读写
使用异步方法避免阻塞主线程(适用于UI应用如WinForms/WPF):await serialPort.BaseStream.WriteAsync(data, 0, data.Length); await serialPort.BaseStream.FlushAsync();
-
异步流(.NET 6+)
使用ReadAsync
和WriteAsync
结合异步流处理大数据:public async Task ReadAsync() { var buffer = new byte[1024]; while (serialPort.IsOpen) { int bytesRead = await serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead > 0) { // 处理接收到的数据 } } }
二、注意事项
-
端口冲突
- 确保串口未被其他程序占用(如设备管理器或后台服务)。
- 使用
SerialPort.GetPortNames()
获取可用端口列表。
-
参数一致性
- 波特率、数据位、停止位、校验位必须与设备完全一致,否则数据可能损坏。
-
线程安全
DataReceived
事件在后台线程触发,更新UI时需使用Invoke
或Dispatcher
:// 在WPF中 Application.Current.Dispatcher.Invoke(() => { textBox.Text += receivedData; });
-
超时处理
- 设置合理的
ReadTimeout
和WriteTimeout
,避免无限等待。
- 设置合理的
-
异常处理
- 捕获
UnauthorizedAccessException
(端口被占用)、IOException
(端口不存在或损坏)等异常。
- 捕获
-
资源释放
- 使用
using
语句确保SerialPort
正确关闭:using (SerialPort sp = new SerialPort("COM3", 9600)) { // 配置并打开端口 } // 自动关闭端口
- 使用
三、扩展技术
1. 跨平台与现代化框架
- .NET MAUI/Blazor
在跨平台应用中使用System.IO.Ports
(需依赖平台特定库,如Linux的/dev/ttyACM0
)。 - .NET Core/5+的异步流
利用IAsyncEnumerable<T>
实现流式数据处理:public async IAsyncEnumerable<byte[]> ReadStream() { while (serialPort.IsOpen) { byte[] buffer = new byte[1024]; int bytesRead = await serialPort.BaseStream.ReadAsync(buffer); if (bytesRead > 0) { yield return buffer; } } }
2. 协议与数据处理
- 协议解析框架
使用开源库如Modbus.NET
或NModbus
处理工业协议(如Modbus RTU/ASCII)。 - 数据校验与加密
在应用层添加CRC校验(如BitConverter
计算CRC16)或AES加密。
3. 与物联网集成
- 边缘计算与云平台
将串口数据通过MQTT或HTTP协议发送到云平台(如Azure IoT Hub、AWS IoT)。 - 设备网关
将串口设备数据桥接到其他协议(如WebSocket、gRPC)。
4. 自动化与监控
- 日志与监控
使用Serilog
或NLog
记录通讯日志,结合Prometheus/Grafana监控串口状态。 - 单元测试与模拟
使用Moq
模拟SerialPort
类,编写单元测试验证业务逻辑。
5. 低功耗与实时性优化
- DMA传输(硬件级)
在嵌入式设备中利用DMA(直接内存访问)提升传输效率。 - 硬件流控制(RTS/CTS)
启用硬件流控制(Handshake.RequestToSendXOnXOff
)减少数据丢失。
四、代码示例
以下是一个完整的串口通讯示例(控制台应用):
using System;
using System.IO.Ports;
class Program
{
static SerialPort serialPort;
static void Main()
{
serialPort = new SerialPort("COM3", 9600);
serialPort.DataReceived += SerialPort_DataReceived;
try
{
serialPort.Open();
Console.WriteLine("串口已打开,输入内容发送(输入exit退出)");
while (true)
{
string input = Console.ReadLine();
if (input.ToLower() == "exit")
{
break;
}
serialPort.WriteLine(input);
}
}
finally
{
serialPort.Close();
}
}
private static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = serialPort.ReadExisting();
Console.WriteLine($"接收数据: {data}");
}
}
五、总结
- 核心要点:正确配置参数、处理线程安全、异常和资源管理。
- 未来方向:结合云平台、边缘计算、协议解析库以及自动化测试工具,提升系统可靠性与扩展性。
- 注意事项:始终确保串口参数与设备匹配,避免硬件冲突。