快速排名程序,招聘seo网站推广,超级外链工具 增加外链中,南京中建乡旅建设投资有限公司网站索引 一、SerialPort串口通信二、使用SerialPort1.创建SerialPort对象#xff0c;进行基本配置2.写入串口数据①.写入串口数据的方法②.封装数据 3.读取串口数据①.读取串口数据的方法②.解析数据 4.读取串口数据的时机①.DataReceived事件②.多线程接收数据 5.粘包问题处理 一… 索引 一、SerialPort串口通信二、使用SerialPort1.创建SerialPort对象进行基本配置2.写入串口数据①.写入串口数据的方法②.封装数据 3.读取串口数据①.读取串口数据的方法②.解析数据 4.读取串口数据的时机①.DataReceived事件②.多线程接收数据 5.粘包问题处理 一、SerialPort串口通信
C#中的SerialPort类是.NET框架提供的一个用于串口通信的强大工具主要用于实现计算机与外部设备如传感器、嵌入式设备、PLC等之间的数据交换。
二、使用SerialPort
1.创建SerialPort对象进行基本配置
使用SerialPort时直接创建一个SerialPort对象即可其基本配置项包括 PortName串口号如COM1。 BaudRate波特率如9600或115200。 DataBits数据位通常为7或8。 StopBits停止位通常为1、1.5或2。 Parity奇偶校验位如None无校验、Odd奇校验或Even偶校验。 代码如下 /// summary/// 串口号/// /summary[Label(串口号)] public string portName;/// summary/// 波特率/// /summary[Label(波特率)] public int baudRate;/// summary/// 数据位/// /summary[Label(数据位)] public int dataBits;/// summary/// 停止位/// /summary[Label(停止位)] public StopBits stopBits;/// summary/// 奇偶校验位/// /summary[Label(奇偶校验位)] public Parity parity;private SerialPort _serialPort;protected override void Awake(){base.Awake();_serialPort new SerialPort();_serialPort.PortName portName;_serialPort.BaudRate baudRate;_serialPort.DataBits dataBits;_serialPort.StopBits stopBits;_serialPort.Parity parity;try{_serialPort.Open();}catch (Exception e){Log.Error(e.Message);}}2.写入串口数据
①.写入串口数据的方法
写入串口数据的方法非常简单如下 //将数据封装为字节数组可以直接强转字符串也可以按16进制处理等byte[] bytes EncapsulatePackage(data);_serialPort.Write(bytes, 0, bytes.Length);②.封装数据
如何封装数据取决于数据交换的协议比如直接强转字符串 /// summary/// 单个数据包结束符换行符/// /summaryprivate const byte EndSign 10;/// summary/// 封装数据包这里要求所有字符必须为ASCII码也即是一个字符只占一个字节/// /summary/// param namedata原始数据/param/// returns数据包/returnsprivate byte[] EncapsulatePackage(string data){byte[] bytes new byte[data.Length 1];for (int i 0; i data.Length; i){bytes[i] (byte)data[i];}//在每个数据包后面补充【结束符】表明此数据包结束bytes[bytes.Length - 1] EndSign;return bytes;}这里以简单协议进行讲解每个数据包以【换行符】代表结束符。
3.读取串口数据
①.读取串口数据的方法
读取串口数据的方法非常简单如下 //建立数据缓冲区大小为串口中可读取数据大小_serialPort.BytesToReadbyte[] bytes new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析数据包string data AnalyzePackage(bytes);②.解析数据
如何解析数据取决于数据交换的协议比如直接强转字符串 /// summary/// 解析数据包/// /summary/// param namebytes数据包/param/// returns数据/returnsprivate string AnalyzePackage(byte[] bytes){return Encoding.Default.GetString(bytes);}4.读取串口数据的时机
那么对如上的简单的写入、读取串口数据有了基本的了解之后接下来便是相对不那么简单的部分了。
首先写入串口数据的时机由我们说了算何时调用便何时写入这点毋容置疑。
但是读取串口数据的时机该是何时参考下面两种方式。
①.DataReceived事件
SerialPort的DataReceived事件当对象对应的串口接收到了数据时便会触发用他来读取数据简直不要太丝滑 protected override void Awake(){base.Awake();//......_serialPort.DataReceived OnDataReceived;//......}private void OnDataReceived(object sender, SerialDataReceivedEventArgs e){if (_serialPort.IsOpen _serialPort.BytesToRead 0){//建立数据缓冲区大小为串口中可读取数据大小_serialPort.BytesToReadbyte[] bytes new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析数据包string data AnalyzePackage(bytes);}}但是这里必须得有一个但是只要在Unity中用过SerialPort的都会知道DataReceived这玩意他不起作用主要原因如下别怀疑就是问的AI 1.Unity引擎对System.IO.Ports命名空间的支持有限尤其是对DataReceived事件的支持存在缺陷。在Unity中DataReceived事件通常不会像在常规C#项目中那样正常触发这主要是因为Unity的运行环境与普通.NET应用有所不同特别是在事件处理机制上存在差异。 2.DataReceived事件需要在一个独立的线程中监听串口数据但在Unity中主线程用于游戏逻辑和渲染和串口数据接收线程之间的同步机制存在问题。因此DataReceived事件可能无法被正确触发。 3.Unity的Update和FixedUpdate等事件函数运行在主线程中而串口数据接收通常需要多线程支持。当串口操作在主线程中执行时可能会因为线程冲突导致DataReceived事件无法触发。 那么我们不得不考虑更换其他方案了。
②.多线程接收数据
老样子像处理Socket通信那样多线程永远是最强的利器 private Thread _receiveThread;protected override void Awake(){base.Awake();//新建一个线程启动数据接收方法ReceivedData_receiveThread new Thread(new ThreadStart(ReceivedData));_receiveThread.Start();}protected override void OnDestroy(){base.OnDestroy();_receiveThread.Abort();_receiveThread null;}/// summary/// 从串口接收数据/// /summaryprivate void ReceivedData(){while (true){if (_serialPort.IsOpen _serialPort.BytesToRead 0){try{//建立数据缓冲区大小为串口中可读取数据大小_serialPort.BytesToReadbyte[] bytes new byte[_serialPort.BytesToRead];_serialPort.Read(bytes, 0, bytes.Length);//解析数据包string data AnalyzePackage(bytes);//......}catch (Exception e){Log.Error(e.Message);}}}}5.粘包问题处理
当然到此时我们并不能高枕无忧还有另一个在数据交换领域普遍存在的问题需要我们解决那就是数据的粘包问题。
粘包问题是指多个数据包在接收端被错误地合并成一个数据包导致接收方无法正确区分每个数据包的边界。这通常发生在连续发送多个数据包时接收端来不及解析或缓冲区管理不当的情况下。
就像Socket通信一样发送方发出的数据是A03568但接收方可能会分多次接收到比如2次才接收完那就可能是A03、568接收方只是一个机器不是人自然不知道这2个包该连起来合成一个包那么后续的处理自然就乱套了。
这就是通信协议存在的必要了以我们的简单通信协议为例每个数据包以【换行符】代表结束符在读取数据的方法中进行防粘包处理 private byte[] _buffer new byte[16];private Listbyte _receiveBuffer new Listbyte();/// summary/// 从串口接收数据/// /summaryprivate void ReceivedData(){while (true){if (_serialPort.IsOpen _serialPort.BytesToRead 0){try{//读取一次数据最大不超过缓冲区大小int count _serialPort.Read(_buffer, 0, Mathf.Min(_buffer.Length, _serialPort.BytesToRead));for (int i 0; i count; i){//如果为结束符则代表一个数据包接收完成解析该包if (_buffer[i] EndSign){string data AnalyzePackage(_receiveBuffer.ToArray());//清空缓冲区_receiveBuffer.Clear();//处理数据HandlerData(data);Log.Info($接收串口数据{data});}//否则加入数据缓冲区else{_receiveBuffer.Add(_buffer[i]);}}}catch (Exception e){Log.Error(e.Message);}}}}代码很简单相信处理过字节流的人应该都能看懂事实上在串口通信中如上的简单通信协议已能胜任大多数情况。