c#串口缓存字节数_C# 串口连接的读取与发送

本文介绍了C#中使用SerialPort类进行串口通信的操作,包括设置串口的打开与关闭、读写超时、缓存大小。强调了缓存大小对大文件传输的影响,并提供了串口发送二进制和文本数据的方法。同时,文章展示了如何处理串口接收,通过队列实现“生产-消费”模式确保消息的完整接收,即使在接收处理较慢时也不会丢失消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

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

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

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

具体代码如下:

1 //Open Com

2 _serialPort = newSerialPort(com, baud);3 if(_serialPort.IsOpen) _serialPort.Close();4

5 //Set the read / write timeouts

6 _serialPort.ReadTimeout = 500;7 _serialPort.WriteTimeout = 500;8

9 //Set read / write buffer Size,the default of value is 1MB

10 _serialPort.ReadBufferSize = 1024 * 1024;11 _serialPort.WriteBufferSize = 1024 * 1024;12

13 _serialPort.Open();14

15 //Discard Buffer

16 _serialPort.DiscardInBuffer();17 _serialPort.DiscardOutBuffer();

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

二、串口发送

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

1 #region Send

2 ///

3 ///发送消息(byte数组)4 ///

5 ///

6 ///

7 ///

8 public void Send(byte[] buffer, int offset, intcount)9 {10 lock(_mux)11 {12 _serialPort.Write(buffer, offset, count);13 _sendCount += (count -offset);14 }15 }16

17 ///

18 ///发送消息(字符串)19 ///

20 /// 字符串编码方式,具体方式见

21 ///

22 public void Send(Encoding encoding , stringmessage)23 {24 lock(_mux)25 {26 var buffer =encoding.GetBytes(message);27 _serialPort.Write(buffer, 0, buffer.Length);28 _sendCount +=buffer.Length;29 }30 }31 #endregion

三、串口接受

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

1 #region Receive

2 private voidPushMessage()3 {4 _serialPort.DataReceived += (sender, e) =>

5 {6 lock(_mux)7 {8 if (_serialPort.IsOpen == false) return;9 int length =_serialPort.BytesToRead;10 byte[] buffer = new byte[length];11 _serialPort.Read(buffer, 0, length);12 _receiveCount +=length;13 _messageQueue.Enqueue(buffer);14 _messageWaitHandle.Set();15 }16 };17 }18

19 ///

20 ///获取串口接受到的内容21 ///

22 /// 取消息的超时时间

23 /// 返回byte数组

24 public byte[] TryMessage(int millisecondsToTimeout = -1)25 {26 if (_messageQueue.TryDequeue(out varmessage))27 {28 returnmessage;29 }30

31 if(_messageWaitHandle.WaitOne(millisecondsToTimeout))32 {33 if (_messageQueue.TryDequeue(outmessage))34 {35 returnmessage;36 }37 }38 return default;39 }40 #endregion

四、完整代码与测试结果

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

1 usingSystem;2 usingSystem.Collections.Concurrent;3 usingSystem.Collections.Generic;4 usingSystem.IO.Ports;5 usingSystem.Linq;6 usingSystem.Runtime.Serialization;7 usingSystem.Text;8 usingSystem.Threading;9 usingSystem.Threading.Tasks;10

11 namespaceSerialportDemo12 {13 public classSSerialPort14 {15 privateSerialPort _serialPort;16 private readonly ConcurrentQueue_messageQueue;17 private readonlyEventWaitHandle _messageWaitHandle;18 private int_receiveCount, _sendCount;19 private readonly object_mux;20 public intReceiveCount21 {22 get =>_receiveCount;23 }24 public intSendCount25 {26 get =>_sendCount;27 }28 public SSerialPort(string com, intbaud )29 {30 //initialized

31 _mux=new object();32 _receiveCount = 0;33 _sendCount = 0;34 _messageQueue = new ConcurrentQueue();35 _messageWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);36

37 //Open Com

38 OpenCom(com.ToUpper(),baud);39

40 //Receive byte

41 PushMessage();42 }43

44 private void OpenCom(string com, intbaud)45 {46 //Open Com

47 _serialPort = newSerialPort(com, baud);48 if(_serialPort.IsOpen) _serialPort.Close();49

50 //Set the read / write timeouts

51 _serialPort.ReadTimeout = 500;52 _serialPort.WriteTimeout = 500;53

54 //Set read / write buffer Size,the default of value is 1MB

55 _serialPort.ReadBufferSize = 1024 * 1024;56 _serialPort.WriteBufferSize = 1024 * 1024;57

58 _serialPort.Open();59

60 //Discard Buffer

61 _serialPort.DiscardInBuffer();62 _serialPort.DiscardOutBuffer();63 }64

65

66 #region Static

67 ///

68 ///获取当前计算机的串行端口名的数组69 ///

70 ///

71 public static string[] GetPortNames()72 {73 returnSerialPort.GetPortNames();74 }75 #endregion

76

77 #region Receive

78 private voidPushMessage()79 {80 _serialPort.DataReceived += (sender, e) =>

81 {82 lock(_mux)83 {84 if (_serialPort.IsOpen == false) return;85 int length =_serialPort.BytesToRead;86 byte[] buffer = new byte[length];87 _serialPort.Read(buffer, 0, length);88 _receiveCount +=length;89 _messageQueue.Enqueue(buffer);90 _messageWaitHandle.Set();91 }92 };93 }94

95 ///

96 ///获取串口接受到的内容97 ///

98 /// 取消息的超时时间

99 /// 返回byte数组

100 public byte[] TryMessage(int millisecondsToTimeout = -1)101 {102 if (_messageQueue.TryDequeue(out varmessage))103 {104 returnmessage;105 }106

107 if(_messageWaitHandle.WaitOne(millisecondsToTimeout))108 {109 if (_messageQueue.TryDequeue(outmessage))110 {111 returnmessage;112 }113 }114 return default;115 }116 #endregion

117

118

119 #region Send

120 ///

121 ///发送消息(byte数组)122 ///

123 ///

124 ///

125 ///

126 public void Send(byte[] buffer, int offset, intcount)127 {128 lock(_mux)129 {130 _serialPort.Write(buffer, offset, count);131 _sendCount += (count -offset);132 }133 }134

135 ///

136 ///发送消息(字符串)137 ///

138 /// 字符串编码方式,具体方式见

139 ///

140 public void Send(Encoding encoding , stringmessage)141 {142 lock(_mux)143 {144 var buffer =encoding.GetBytes(message);145 _serialPort.Write(buffer, 0, buffer.Length);146 _sendCount +=buffer.Length;147 }148 }149 #endregion

150

151 ///

152 ///清空接受/发送总数统计153 ///

154 public voidClearCount()155 {156 lock(_mux)157 {158 _sendCount = 0;159 _receiveCount = 0;160 }161 }162

163 ///

164 ///关闭串口165 ///

166 public voidClose()167 {168 _serialPort.Close();169 }170 }171 }

View Code

测试代码如下:

1 classProgram2 {3 static void Main(string[] args)4 {5 Console.WriteLine($"该计算机可使用的串口列表:{string.Join(",", SSerialPort.GetPortNames())}");6

7 Console.Write("请输入需要打开的串口:");8 string port =Console.ReadLine();9 SSerialPort com = new SSerialPort(port, 57600);10 Console.WriteLine($"串口 {port} 打开成功...");11

12 Console.Write("请输入需要打开的串口发送的消息:");13 string text =Console.ReadLine();14

15 while (true)16 {17 com.Send(Encoding.Default, text);18 Console.WriteLine($"总共发送 {com.SendCount}");19 var message =com.TryMessage();20 if (message != null)21 {22 Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss fff")} {Encoding.Default.GetString(message)}");23

24 TEST:从添加延时可以测试到,接受消息和处理消息必须分不同线程处理。因为对于消息的处理或多或少都需要耗时,这样容易造成消息处理不及时。而添加到队列后,我们可以随时取出处理

25 //System.Threading.Thread.Sleep(100*1);

26 }27 Console.WriteLine($"总共接受 {com.ReceiveCount}");28 }29

30

31 Console.ReadKey();32 }33 }

View Code

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

public partial class Form1 : Form { public Form1() { InitializeComponent(); } SerialPort port1 = new SerialPort(); string InputData = String.Empty; delegate void SetTextCallback(string text); private void Port_Select() {//获取机器中的串口地址 string[] ports = SerialPort.GetPortNames(); foreach (string port in ports) { comboBox1.Items.Add(port); } } private void Form1_Load_1(object sender, EventArgs e) { Port_Select(); this.comboBox1.SelectedIndex = 0; this.comboBox2.SelectedIndex = 0; } private void button1_Click(object sender, EventArgs e) { if (button1.Text == "关闭串口") //当要关闭串口的时候 { port1.DiscardOutBuffer(); port1.DiscardInBuffer(); port1.Close(); button1.Text = "打开串口"; label3.Text = "串口当前状况:未打开"; comboBox1.Enabled = true; comboBox2.Enabled = true; } else if (button1.Text == "打开串口") //当要打开串口的时候 { try { port1.PortName = comboBox1.SelectedItem.ToString(); port1.BaudRate = Convert.ToInt32(comboBox2.SelectedItem); port1.DataBits = 8; port1.RtsEnable = true; port1.Open(); port1.DiscardOutBuffer(); port1.DiscardInBuffer(); button1.Text = "关闭串口"; comboBox1.Enabled = false; comboBox2.Enabled = false; label3.Text = "串口:" + comboBox1.SelectedItem.ToString() + " 波特率:" + comboBox2.SelectedItem.ToString() + " 数据位:8 "; } catch { button1.Text = "打开串口"; label3.Text = "串口:" + comboBox1.SelectedItem.ToString() + "打开失败"; MessageBox.Show("该串口无法打开"); } } } 资源中部分代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值