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 //////发送消息(byte数组) /// ////// /// publicvoidSend(byte[]buffer,intoffset,intcount) { lock(_mux) { _serialPort.Write(buffer,offset,count); _sendCount+=(count-offset); } } /// ///发送消息(字符串) /// ///字符串编码方式,具体方式见 /// 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();
}
};
}
///
///获取串口接受到的内容
///
///取消息的超时时间
///返回byte数组
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_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();
_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
///
///获取当前计算机的串行端口名的数组
///
///
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();
}
};
}
///
///获取串口接受到的内容
///
///取消息的超时时间
///返回byte数组
publicbyte[]TryMessage(intmillisecondsToTimeout=-1)
{
if(_messageQueue.TryDequeue(outvarmessage))
{
returnmessage;
}
if(_messageWaitHandle.WaitOne(millisecondsToTimeout))
{
if(_messageQueue.TryDequeue(outmessage))
{
returnmessage;
}
}
returndefault;
}
#endregion
#regionSend
///
///发送消息(byte数组)
///
///
///
///
publicvoidSend(byte[]buffer,intoffset,intcount)
{
lock(_mux)
{
_serialPort.Write(buffer,offset,count);
_sendCount+=(count-offset);
}
}
///
///发送消息(字符串)
///
///字符串编码方式,具体方式见
///
publicvoidSend(Encodingencoding,stringmessage)
{
lock(_mux)
{
varbuffer=encoding.GetBytes(message);
_serialPort.Write(buffer,0,buffer.Length);
_sendCount+=buffer.Length;
}
}
#endregion
///
///清空接受/发送总数统计
///
publicvoidClearCount()
{
lock(_mux)
{
_sendCount=0;
_receiveCount=0;
}
}
///
///关闭串口
///
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#串口连接读取和发送内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!