C# SerialPort的 DataReceived事件,可能是存在问题,使用时,数据丢失,造成数据不完整。
解决方法思路:
使用独立线程读取数据,把串口缓冲区的数据,读取到程序中。抛开DataReceived事件。
使用其它线程获取“串口中读取的数据”再进行解析。
客户程序由被动触发,改为主动获取。
相关变量、对象
C# Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/// <summary> /// 保持读取开关 /// </summary> bool _keepReading; /// <summary> /// 检测频率【检测等待时间,毫秒】【按行读取,可以不用】 /// </summary> int _jcpl = 1 ; /// <summary> /// 串口 /// </summary> SerialPort _sp = new SerialPort(); /// <summary> /// 字符串队列【.NET Framework 4.0以上】 /// </summary> ConcurrentQueue< string > _cq = new ConcurrentQueue< string >(); /// <summary> /// 字节数据队列 /// </summary> ConcurrentQueue< byte []> _cqBZ = new ConcurrentQueue< byte []>(); |
串口读取方法【ViewCommStatus,自定义方法,串口读取状态输出】
C# Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/// <summary> /// 串口读取方法 /// </summary> void SerialPortRead() { while (_keepReading) { if (_jcpl > 0 ) //按行读取可以省略 { Thread.Sleep(_jcpl); } if (_sp == null ) { ViewCommStatus( false , "串口对象未实例化!" ); Thread.Sleep( 3000 ); //3秒后重新检测 continue ; } if (!_sp.IsOpen) { ViewCommStatus( false , "串口未打开" ); Thread.Sleep( 3000 ); continue ; } try { #region 按行读取 string buffer = _sp.ReadLine(); if (! string .IsNullOrEmpty(buffer)) //可以不加判断,允许添加null值,数据解析时,再做判断。 { _cq.Enqueue(buffer); } #endregion //#region 字节读取 //byte[] readBuffer = new byte[_sp.ReadBufferSize + 1]; //int count = _sp.Read(readBuffer, 0, _sp.ReadBufferSize); //if (count != 0) //{ // _cqBZ.Enqueue(readBuffer); //} //#endregion ViewCommStatus( true , "串口通信正常" ); } catch (TimeoutException) //注意超时时间的定义 { ViewCommStatus( false , "串口读取超时!" ); } catch (Exception ex) //排除隐患后可以去掉。 { ViewCommStatus( false , "串口读取异常:" + ex.Message); } } } |
数据解析方法
C# Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/// <summary> /// 数据解析 /// </summary> void DataAnalysis() { while ( true ) { Thread.Sleep( 10 ); if (_cq.IsEmpty) { continue ; } #region 按行读取 for ( int i = 0 ; i < _cq.Count; i++) { string strTmp = "" ; _cq.TryDequeue( out strTmp); //解析strTmp //解析strTmp //解析strTmp } #endregion #region 按字节读取 //StringBuilder sb = new StringBuilder(); //for (int i = 0; i < _cq.Count; i++) //{ // byte[] bt = null; // _cqBZ.TryDequeue(out bt); // string strTmp = System.Text.Encoding.ASCII.GetString(bt); // sb.Append(strTmp); //} ////解析sb ////解析sb ////解析sb #endregion } } |
调用示例
C# Code
1
2 3 4 5 6 7 8 9 10 11 |
void
StartRead() { _keepReading = true ; Thread trdRead = new Thread(SerialPortRead); trdRead.Start(); Thread trdDataAnalysis = new Thread(DataAnalysis); trdDataAnalysis.Start(); } |
SerialPort的参数配置和资源释放,就先不写了。这个一般情况下都会。