Csharp/C#教程:C#串口连接的读取和发送详解分享

一、串口连接的打开与关闭

串口,即COM口,在.NET中使用SerialPort类进行操作。串口开启与关闭,是涉及慢速硬件的IO操作,频繁打开或关闭会影响整体处理速度,甚至导致打开或关闭串口失败。非特殊情况,串口一次性打开后,在退出程序时关闭串口即可。在打开串口前,可以设置一些常用的参数。常用的参数如下:

 (1)串口的接受/发送超时时间:ReadTimeout/WriteTimeout。

  (2) 串口的接受/发送缓存区大小:ReadBufferSize/WriteBufferSize。

具体代码如下:

//OpenCom _serialPort=newSerialPort(com,baud); if(_serialPort.IsOpen)_serialPort.Close(); //Settheread/writetimeouts _serialPort.ReadTimeout=500; _serialPort.WriteTimeout=500; //Setread/writebufferSize,thedefaultofvalueis1MB _serialPort.ReadBufferSize=1024*1024; _serialPort.WriteBufferSize=1024*1024; _serialPort.Open(); //DiscardBuffer _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer();

   需要注意的是超出缓冲区的部分会被直接丢弃。因此,如果需要使用串口传送大文件,那接收方和发送方都需要将各自的缓冲区域设置的足够大,以便能够一次性存储下大文件的二进制数组。若条件限制,缓冲区域不能设置过大,那就需要在发送大文件的时候按照发送缓冲区大小分包去发送,接收方按顺序把该数组组合起来形成接受文件的二进制数组。

二、串口发送

SerialPort类发送支持二进制发送与文本发送,需要注意的是文本发送时,需要知道转换的规则,一般常用的是ASCII、UTF7、UTF-8、UNICODE、UTF32。具体代码如下:

#regionSend ///<summary> ///发送消息(byte数组) ///</summary> ///<paramname="buffer"></param> ///<paramname="offset"></param> ///<paramname="count"></param> publicvoidSend(byte[]buffer,intoffset,intcount) { lock(_mux) { _serialPort.Write(buffer,offset,count); _sendCount+=(count-offset); } } ///<summary> ///发送消息(字符串) ///</summary> ///<paramname="encoding">字符串编码方式,具体方式见<seecref="Encoding"/></param> ///<paramname="message"></param> publicvoidSend(Encodingencoding,stringmessage) { lock(_mux) { varbuffer=encoding.GetBytes(message); _serialPort.Write(buffer,0,buffer.Length); _sendCount+=buffer.Length; } } #endregion 三、串口接受

串口接受需要注意,消息接受与消息处理要代码分离。不能把流程处理的代码放入信息接受处,因为消息处理或多或少会有耗时,这会造成当发送方发送过快时,接受方的接受缓冲区会缓存多条消息。我们可以把接受到的消息放入队列中,然后在外部线程中,尝试去拿出该条消息进行消费。采用“生产-消费”模式。具体代码如下:

#regionReceive privatevoidPushMessage() { _serialPort.DataReceived+=(sender,e)=> { lock(_mux) { if(_serialPort.IsOpen==false)return; intlength=_serialPort.BytesToRead; byte[]buffer=newbyte[length]; _serialPort.Read(buffer,0,length); _receiveCount+=length; _messageQueue.Enqueue(buffer); _messageWaitHandle.Set(); } }; } ///<summary> ///获取串口接受到的内容 ///</summary> ///<paramname="millisecondsToTimeout">取消息的超时时间</param> ///<returns>返回byte数组</returns> publicbyte[]TryMessage(intmillisecondsToTimeout=-1) { if(_messageQueue.TryDequeue(outvarmessage)) { returnmessage; } if(_messageWaitHandle.WaitOne(millisecondsToTimeout)) { if(_messageQueue.TryDequeue(outmessage)) { returnmessage; } } returndefault; } #endregion 四、完整代码与测试结果

串口工具类的完整代码如下:

usingSystem; usingSystem.Collections.Concurrent; usingSystem.Collections.Generic; usingSystem.IO.Ports; usingSystem.Linq; usingSystem.Runtime.Serialization; usingSystem.Text; usingSystem.Threading; usingSystem.Threading.Tasks; namespaceSerialportDemo { publicclassSSerialPort { privateSerialPort_serialPort; privatereadonlyConcurrentQueue<byte[]>_messageQueue; privatereadonlyEventWaitHandle_messageWaitHandle; privateint_receiveCount,_sendCount; privatereadonlyobject_mux; publicintReceiveCount { get=>_receiveCount; } publicintSendCount { get=>_sendCount; } publicSSerialPort(stringcom,intbaud) { //initialized _mux=newobject(); _receiveCount=0; _sendCount=0; _messageQueue=newConcurrentQueue<byte[]>(); _messageWaitHandle=newEventWaitHandle(false,EventResetMode.AutoReset); //OpenCom OpenCom(com.ToUpper(),baud); //Receivebyte PushMessage(); } privatevoidOpenCom(stringcom,intbaud) { //OpenCom _serialPort=newSerialPort(com,baud); if(_serialPort.IsOpen)_serialPort.Close(); //Settheread/writetimeouts _serialPort.ReadTimeout=500; _serialPort.WriteTimeout=500; //Setread/writebufferSize,thedefaultofvalueis1MB _serialPort.ReadBufferSize=1024*1024; _serialPort.WriteBufferSize=1024*1024; _serialPort.Open(); //DiscardBuffer _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); } #regionStatic ///<summary> ///获取当前计算机的串行端口名的数组 ///</summary> ///<returns></returns> publicstaticstring[]GetPortNames() { returnSerialPort.GetPortNames(); } #endregion #regionReceive privatevoidPushMessage() { _serialPort.DataReceived+=(sender,e)=> { lock(_mux) { if(_serialPort.IsOpen==false)return; intlength=_serialPort.BytesToRead; byte[]buffer=newbyte[length]; _serialPort.Read(buffer,0,length); _receiveCount+=length; _messageQueue.Enqueue(buffer); _messageWaitHandle.Set(); } }; } ///<summary> ///获取串口接受到的内容 ///</summary> ///<paramname="millisecondsToTimeout">取消息的超时时间</param> ///<returns>返回byte数组</returns> publicbyte[]TryMessage(intmillisecondsToTimeout=-1) { if(_messageQueue.TryDequeue(outvarmessage)) { returnmessage; } if(_messageWaitHandle.WaitOne(millisecondsToTimeout)) { if(_messageQueue.TryDequeue(outmessage)) { returnmessage; } } returndefault; } #endregion #regionSend ///<summary> ///发送消息(byte数组) ///</summary> ///<paramname="buffer"></param> ///<paramname="offset"></param> ///<paramname="count"></param> publicvoidSend(byte[]buffer,intoffset,intcount) { lock(_mux) { _serialPort.Write(buffer,offset,count); _sendCount+=(count-offset); } } ///<summary> ///发送消息(字符串) ///</summary> ///<paramname="encoding">字符串编码方式,具体方式见<seecref="Encoding"/></param> ///<paramname="message"></param> publicvoidSend(Encodingencoding,stringmessage) { lock(_mux) { varbuffer=encoding.GetBytes(message); _serialPort.Write(buffer,0,buffer.Length); _sendCount+=buffer.Length; } } #endregion ///<summary> ///清空接受/发送总数统计 ///</summary> publicvoidClearCount() { lock(_mux) { _sendCount=0; _receiveCount=0; } } ///<summary> ///关闭串口 ///</summary> publicvoidClose() { _serialPort.Close(); } } }

测试代码如下:

classProgram { staticvoidMain(string[]args) { Console.WriteLine($"该计算机可使用的串口列表:{string.Join(",",SSerialPort.GetPortNames())}"); Console.Write("请输入需要打开的串口:"); stringport=Console.ReadLine(); SSerialPortcom=newSSerialPort(port,57600); Console.WriteLine($"串口{port}打开成功..."); Console.Write("请输入需要打开的串口发送的消息:"); stringtext=Console.ReadLine(); while(true) { com.Send(Encoding.Default,text); Console.WriteLine($"总共发送{com.SendCount}"); varmessage=com.TryMessage(); if(message!=null) { Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ssfff")}{Encoding.Default.GetString(message)}"); ////TEST:从添加延时可以测试到,接受消息和处理消息必须分不同线程处理。因为对于消息的处理或多或少都需要耗时,这样容易造成消息处理不及时。而添加到队列后,我们可以随时取出处理 //System.Threading.Thread.Sleep(100*1); } Console.WriteLine($"总共接受{com.ReceiveCount}"); } Console.ReadKey(); } }

使用串口工具测试如下,对于串口的接受如丝般顺滑。当我们在消息中增加测试延时后,就会发现当串口工具继续快速发送一段时间后关闭发送,发现使用队列后,依然没有丢失一条来自发送方的消息。

上述就是C#学习教程:C#串口连接的读取和发送详解分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/903576.html

(0)
上一篇 2021年10月21日
下一篇 2021年10月21日

精彩推荐