1、串口鏈接的打開與關閉數組
串口,即COM口,在.NET中使用 SerialPort 類進行操做。串口開啓與關閉,是涉及慢速硬件的IO操做,頻繁打開或關閉會影響總體處理速度,甚至致使打開或關閉串口失敗。非特殊狀況,串口一次性打開後,在退出程序時關閉串口便可。在打開串口前,能夠設置一些經常使用的參數。經常使用的參數以下:緩存
(1)串口的接受/發送超時時間:ReadTimeout/WriteTimeout。ide
(2) 串口的接受/發送緩存區大小:ReadBufferSize/WriteBufferSize。工具
具體代碼以下:測試
1 // Open Com 2 _serialPort = new SerialPort(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();
須要注意的是超出緩衝區的部分會被直接丟棄。所以,若是須要使用串口傳送大文件,那接收方和發送方都須要將各自的緩衝區域設置的足夠大,以便可以一次性存儲下大文件的二進制數組。若條件限制,緩衝區域不能設置過大,那就須要在發送大文件的時候按照發送緩衝區大小分包去發送,接收方按順序把該數組組合起來造成接受文件的二進制數組。編碼
2、串口發送spa
串口發送支持二進制發送與文本發送,須要注意的是文本發送時,須要知道轉換的規則,通常經常使用的是ASCII、UTF七、UTF-八、UNICODE、UTF32。具體代碼以下:線程
1 #region Send 2 /// <summary> 3 /// 發送消息(byte數組) 4 /// </summary> 5 /// <param name="buffer"></param> 6 /// <param name="offset"></param> 7 /// <param name="count"></param> 8 public void Send(byte[] buffer, int offset, int count) 9 { 10 lock (_mux) 11 { 12 _serialPort.Write(buffer, offset, count); 13 _sendCount += (count - offset); 14 } 15 } 16 17 /// <summary> 18 /// 發送消息(字符串) 19 /// </summary> 20 /// <param name="encoding">字符串編碼方式,具體方式見<see cref="Encoding"/></param> 21 /// <param name="message"></param> 22 public void Send(Encoding encoding , string message) 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
3、串口接受code
串口接受須要注意,消息接受與消息處理要代碼分離。不能把流程處理的代碼放入信息接受處,由於消息處理或多或少會有耗時,這會形成當發送方發送過快時,接受方的接受緩衝區會緩存多條消息。咱們能夠把接受到的消息放入隊列中,而後在外部線程中,嘗試去拿出該條消息進行消費。採用 「生產-消費」模式。具體代碼以下:blog
1 #region Receive 2 private void PushMessage() 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 /// <summary> 20 /// 獲取串口接受到的內容 21 /// </summary> 22 /// <param name="millisecondsToTimeout">取消息的超時時間</param> 23 /// <returns>返回byte數組</returns> 24 public byte[] TryMessage(int millisecondsToTimeout = -1) 25 { 26 if (_messageQueue.TryDequeue(out var message)) 27 { 28 return message; 29 } 30 31 if (_messageWaitHandle.WaitOne(millisecondsToTimeout)) 32 { 33 if (_messageQueue.TryDequeue(out message)) 34 { 35 return message; 36 } 37 } 38 return default; 39 } 40 #endregion
4、完整代碼與測試結果
串口工具類的完整代碼以下:
1 using System; 2 using System.Collections.Concurrent; 3 using System.Collections.Generic; 4 using System.IO.Ports; 5 using System.Linq; 6 using System.Runtime.Serialization; 7 using System.Text; 8 using System.Threading; 9 using System.Threading.Tasks; 10 11 namespace SerialportDemo 12 { 13 public class SSerialPort 14 { 15 private SerialPort _serialPort; 16 private readonly ConcurrentQueue<byte[]> _messageQueue; 17 private readonly EventWaitHandle _messageWaitHandle; 18 private int _receiveCount, _sendCount; 19 private readonly object _mux; 20 public int ReceiveCount 21 { 22 get => _receiveCount; 23 } 24 public int SendCount 25 { 26 get => _sendCount; 27 } 28 public SSerialPort(string com, int baud ) 29 { 30 // initialized 31 _mux=new object(); 32 _receiveCount = 0; 33 _sendCount = 0; 34 _messageQueue = new ConcurrentQueue<byte[]>(); 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, int baud) 45 { 46 // Open Com 47 _serialPort = new SerialPort(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 /// <summary> 68 /// 獲取當前計算機的串行端口名的數組 69 /// </summary> 70 /// <returns></returns> 71 public static string[] GetPortNames() 72 { 73 return SerialPort.GetPortNames(); 74 } 75 #endregion 76 77 #region Receive 78 private void PushMessage() 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 /// <summary> 96 /// 獲取串口接受到的內容 97 /// </summary> 98 /// <param name="millisecondsToTimeout">取消息的超時時間</param> 99 /// <returns>返回byte數組</returns> 100 public byte[] TryMessage(int millisecondsToTimeout = -1) 101 { 102 if (_messageQueue.TryDequeue(out var message)) 103 { 104 return message; 105 } 106 107 if (_messageWaitHandle.WaitOne(millisecondsToTimeout)) 108 { 109 if (_messageQueue.TryDequeue(out message)) 110 { 111 return message; 112 } 113 } 114 return default; 115 } 116 #endregion 117 118 119 #region Send 120 /// <summary> 121 /// 發送消息(byte數組) 122 /// </summary> 123 /// <param name="buffer"></param> 124 /// <param name="offset"></param> 125 /// <param name="count"></param> 126 public void Send(byte[] buffer, int offset, int count) 127 { 128 lock (_mux) 129 { 130 _serialPort.Write(buffer, offset, count); 131 _sendCount += (count - offset); 132 } 133 } 134 135 /// <summary> 136 /// 發送消息(字符串) 137 /// </summary> 138 /// <param name="encoding">字符串編碼方式,具體方式見<see cref="Encoding"/></param> 139 /// <param name="message"></param> 140 public void Send(Encoding encoding , string message) 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 /// <summary> 152 /// 清空接受/發送總數統計 153 /// </summary> 154 public void ClearCount() 155 { 156 lock (_mux) 157 { 158 _sendCount = 0; 159 _receiveCount = 0; 160 } 161 } 162 163 /// <summary> 164 /// 關閉串口 165 /// </summary> 166 public void Close() 167 { 168 _serialPort.Close(); 169 } 170 } 171 }
測試代碼以下:
1 class Program 2 { 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 }
使用串口工具測試以下,對於串口的接受如絲般順滑。當咱們在消息中增長測試延時後,就會發現當串口工具繼續快速發送一段時間後關閉發送,發現使用隊列後,依然沒有丟失一條來自發送方的消息。