时间:2021-05-20
串口,即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(); } }使用串口工具测试如下,对于串口的接受如丝般顺滑。当我们在消息中增加测试延时后,就会发现当串口工具继续快速发送一段时间后关闭发送,发现使用队列后,依然没有丢失一条来自发送方的消息。
到此这篇关于C#串口连接的读取和发送的文章就介绍到这了,更多相关C#串口连接读取和发送内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
创建C#串口通信程序之命名空间System.IO.Ports命名空间中最重用的是SerialPort类。创建C#串口通信程序之创建SerialPort对象通过创
python3读取串口数据demo最近在写一个demo,zigbee串口连接树莓派,树莓派使用串口通信接受zigbee穿过来得值。其中我是用的树莓派是3代B+,
本文实例讲述了C#读取或设置ScrollLock状态的方法。分享给大家供大家参考。具体如下:C#读取或者设置ScrollLock状态,允许滚动状态读取或者设置u
本文实例讲述了C#逐行读取文件的方法。分享给大家供大家参考。具体如下:这里使用C#逐行读取文件,对于大文件的读取非常有用。StreamReadersr=newS
C#动态创建button按钮的方法实例详解C#编程中经常需要动态创建,本文主要介绍C#动态创建button按钮的方法,涉及C#按钮属性动态设置的相关技巧,以供借