C#串口连接的读取和发送详解
作者:Dwaynerbing 发布时间:2022-05-11 10:04:47
一、串口连接的打开与关闭
串口,即COM口,在.NET中使用 SerialPort 类进行操作。串口开启与关闭,是涉及慢速硬件的IO操作,频繁打开或关闭会影响整体处理速度,甚至导致打开或关闭串口失败。非特殊情况,串口一次性打开后,在退出程序时关闭串口即可。在打开串口前,可以设置一些常用的参数。常用的参数如下:
(1)串口的接受/发送超时时间:ReadTimeout/WriteTimeout。
(2) 串口的接受/发送缓存区大小:ReadBufferSize/WriteBufferSize。
具体代码如下:
// Open Com
_serialPort = new SerialPort(com, baud);
if (_serialPort.IsOpen) _serialPort.Close();
// Set the read / write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
// Set read / write buffer Size,the default of value is 1MB
_serialPort.ReadBufferSize = 1024 * 1024;
_serialPort.WriteBufferSize = 1024 * 1024;
_serialPort.Open();
// Discard Buffer
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
需要注意的是超出缓冲区的部分会被直接丢弃。因此,如果需要使用串口传送大文件,那接收方和发送方都需要将各自的缓冲区域设置的足够大,以便能够一次性存储下大文件的二进制数组。若条件限制,缓冲区域不能设置过大,那就需要在发送大文件的时候按照发送缓冲区大小分包去发送,接收方按顺序把该数组组合起来形成接受文件的二进制数组。
二、串口发送
SerialPort 类发送支持二进制发送与文本发送,需要注意的是文本发送时,需要知道转换的规则,一般常用的是ASCII、UTF7、UTF-8、UNICODE、UTF32。具体代码如下:
#region Send
/// <summary>
/// 发送消息(byte数组)
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
public void Send(byte[] buffer, int offset, int count)
{
lock (_mux)
{
_serialPort.Write(buffer, offset, count);
_sendCount += (count - offset);
}
}
/// <summary>
/// 发送消息(字符串)
/// </summary>
/// <param name="encoding">字符串编码方式,具体方式见<see cref="Encoding"/></param>
/// <param name="message"></param>
public void Send(Encoding encoding , string message)
{
lock (_mux)
{
var buffer = encoding.GetBytes(message);
_serialPort.Write(buffer, 0, buffer.Length);
_sendCount += buffer.Length;
}
}
#endregion
三、串口接受
串口接受需要注意,消息接受与消息处理要代码分离。不能把流程处理的代码放入信息接受处,因为消息处理或多或少会有耗时,这会造成当发送方发送过快时,接受方的接受缓冲区会缓存多条消息。我们可以把接受到的消息放入队列中,然后在外部线程中,尝试去拿出该条消息进行消费。采用 “生产-消费”模式。具体代码如下:
#region Receive
private void PushMessage()
{
_serialPort.DataReceived += (sender, e) =>
{
lock (_mux)
{
if (_serialPort.IsOpen == false) return;
int length = _serialPort.BytesToRead;
byte[] buffer = new byte[length];
_serialPort.Read(buffer, 0, length);
_receiveCount += length;
_messageQueue.Enqueue(buffer);
_messageWaitHandle.Set();
}
};
}
/// <summary>
/// 获取串口接受到的内容
/// </summary>
/// <param name="millisecondsToTimeout">取消息的超时时间</param>
/// <returns>返回byte数组</returns>
public byte[] TryMessage(int millisecondsToTimeout = -1)
{
if (_messageQueue.TryDequeue(out var message))
{
return message;
}
if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
{
if (_messageQueue.TryDequeue(out message))
{
return message;
}
}
return default;
}
#endregion
四、完整代码与测试结果
串口工具类的完整代码如下:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SerialportDemo
{
public class SSerialPort
{
private SerialPort _serialPort;
private readonly ConcurrentQueue<byte[]> _messageQueue;
private readonly EventWaitHandle _messageWaitHandle;
private int _receiveCount, _sendCount;
private readonly object _mux;
public int ReceiveCount
{
get => _receiveCount;
}
public int SendCount
{
get => _sendCount;
}
public SSerialPort(string com, int baud )
{
// initialized
_mux=new object();
_receiveCount = 0;
_sendCount = 0;
_messageQueue = new ConcurrentQueue<byte[]>();
_messageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
// Open Com
OpenCom(com.ToUpper(),baud);
// Receive byte
PushMessage();
}
private void OpenCom(string com, int baud)
{
// Open Com
_serialPort = new SerialPort(com, baud);
if (_serialPort.IsOpen) _serialPort.Close();
// Set the read / write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
// Set read / write buffer Size,the default of value is 1MB
_serialPort.ReadBufferSize = 1024 * 1024;
_serialPort.WriteBufferSize = 1024 * 1024;
_serialPort.Open();
// Discard Buffer
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
}
#region Static
/// <summary>
/// 获取当前计算机的串行端口名的数组
/// </summary>
/// <returns></returns>
public static string[] GetPortNames()
{
return SerialPort.GetPortNames();
}
#endregion
#region Receive
private void PushMessage()
{
_serialPort.DataReceived += (sender, e) =>
{
lock (_mux)
{
if (_serialPort.IsOpen == false) return;
int length = _serialPort.BytesToRead;
byte[] buffer = new byte[length];
_serialPort.Read(buffer, 0, length);
_receiveCount += length;
_messageQueue.Enqueue(buffer);
_messageWaitHandle.Set();
}
};
}
/// <summary>
/// 获取串口接受到的内容
/// </summary>
/// <param name="millisecondsToTimeout">取消息的超时时间</param>
/// <returns>返回byte数组</returns>
public byte[] TryMessage(int millisecondsToTimeout = -1)
{
if (_messageQueue.TryDequeue(out var message))
{
return message;
}
if (_messageWaitHandle.WaitOne(millisecondsToTimeout))
{
if (_messageQueue.TryDequeue(out message))
{
return message;
}
}
return default;
}
#endregion
#region Send
/// <summary>
/// 发送消息(byte数组)
/// </summary>
/// <param name="buffer"></param>
/// <param name="offset"></param>
/// <param name="count"></param>
public void Send(byte[] buffer, int offset, int count)
{
lock (_mux)
{
_serialPort.Write(buffer, offset, count);
_sendCount += (count - offset);
}
}
/// <summary>
/// 发送消息(字符串)
/// </summary>
/// <param name="encoding">字符串编码方式,具体方式见<see cref="Encoding"/></param>
/// <param name="message"></param>
public void Send(Encoding encoding , string message)
{
lock (_mux)
{
var buffer = encoding.GetBytes(message);
_serialPort.Write(buffer, 0, buffer.Length);
_sendCount += buffer.Length;
}
}
#endregion
/// <summary>
/// 清空接受/发送总数统计
/// </summary>
public void ClearCount()
{
lock (_mux)
{
_sendCount = 0;
_receiveCount = 0;
}
}
/// <summary>
/// 关闭串口
/// </summary>
public void Close()
{
_serialPort.Close();
}
}
}
测试代码如下:
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"该计算机可使用的串口列表:{string.Join(",", SSerialPort.GetPortNames())}");
Console.Write("请输入需要打开的串口:");
string port = Console.ReadLine();
SSerialPort com = new SSerialPort(port, 57600);
Console.WriteLine($"串口 {port} 打开成功...");
Console.Write("请输入需要打开的串口发送的消息:");
string text = Console.ReadLine();
while (true)
{
com.Send(Encoding.Default, text);
Console.WriteLine($"总共发送 {com.SendCount}");
var message = com.TryMessage();
if (message != null)
{
Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")} {Encoding.Default.GetString(message)}");
//// TEST:从添加延时可以测试到,接受消息和处理消息必须分不同线程处理。因为对于消息的处理或多或少都需要耗时,这样容易造成消息处理不及时。而添加到队列后,我们可以随时取出处理
//System.Threading.Thread.Sleep(100*1);
}
Console.WriteLine($"总共接受 {com.ReceiveCount}");
}
Console.ReadKey();
}
}
使用串口工具测试如下,对于串口的接受如丝般顺滑。当我们在消息中增加测试延时后,就会发现当串口工具继续快速发送一段时间后关闭发送,发现使用队列后,依然没有丢失一条来自发送方的消息。
总结
来源:https://www.cnblogs.com/dongweian/p/14232446.html
猜你喜欢
- 背景实际开发中,常常需要将比较复杂的 JSON 字符串转换为对应的 Java 对象。这里记录下解决方案。如下所示,是入侵事件检测得到的 JS
- 前言首先我们初始化一个最简单的容器,用这个容器研究初始化的流程。下面就是一个再简单不过的IoC容器了,该容器包含了一个名为beanA的bea
- 以下总结是2016/3/23在做一个网站时遇到的一个功能模块,现在将总结从为知笔记上搬家到CSDN,与大家共享,欢迎指正。0.准备工作 0.
- 在项目开发中,经常碰到map转实体对象或者对象转map的场景,工作中,很多时候我们可能比较喜欢使用第三方jar包的API对他们进行转化,而且
- 本文实例为大家分享了flutter日期时间选择器的具体代码,供大家参考,具体内容如下1 日期选择器 //设置默认显示的日期为当前 DateT
- JPA设置表名和实体名,表字段与实体字段的对应首先 你的jpaProperties配置项中要有<prop key="hibe
- 1. json数据类型类型描述Number数字型String字符串型Boolean布尔型Array数组Object对象null空值(1)js
- 前言最近因为同事bean配置的问题导致生产环境往错误的redis实例写入大量的数据,差点搞挂redis。经过快速的问题定位,发现是同事新增一
- RestTemplate设计是为了Spring更好的请求并解析Restful风格的接口返回值而设计的,通过这个类可以在请求接口时直接解析对应
- idea pom文件图标不对今天遇到一个奇怪的现象,如下图原先pom的图标应该是有个m的,现在直接变成了xml的文件了。右边的Maven P
- 前言本文主要介绍其具体的实现思路(视频仅有代码输入,并无过程介绍等),同时,在原本实现的基础上,进行了多处修改和优化,具体参见下面的内容。优
- 请停止代码注释“干净的代码应该像写好的散文一样” - Robert C. Martin不良代码的通病就是有很多注释。这是凌乱的源代码最明显的
- SLF4J是一个日志框架抽象层,底下绑定具体的日志框架,比如说Log4J,Logback,Java Logging API等。SLF4J也有
- 1、volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)&nb
- Java中可以使用关键字synchronized进行线程同步控制,实现关键资源顺序访问,避免由于多线程并发执行导致的数据不一致性等问题。sy
- 本文实例为大家分享了PropertyDescriptor反射调用set和get方法,供大家参考,具体内容如下第一段:package com.
- Logback日志基础配置logback日志配置有很多介绍,但是有几个非常基础的,容易忽略的。下面是最简单的一个配置,注意加粗的描述<
- 一、电子邮件详解假设自己的电子邮件是me@163.com,对方的邮件是you@163.com我们编写好文件填写好对方文件,点击发送,这些电子
- MyBatis的注解实现复杂映射开发实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解
- Lua是目前国内使用最多的热更语言,基于Lua的热更框架也非常多,最近学习了一下ToLua的热更框架,主要使用的问题在于C#和Lua之间的互