串口通信与NJH框架
NJH框架
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class BaseSerialPort { 2 3 #region 字段 4 private const int msgMaxLength = 512;//通信协议中最长的指令的字节数目,制定的通信协议最长指令不得超过msgMaxLength 5 private object locker = new object();//在串口接收数据的线程中使用,保证数据接收正确 6 7 private byte[] byteMsgRecv;//使用字节协议时,用于接收数据 8 private string strMsgRecv;//使用字符协议时,用于接收数据 9 10 private AutoResetEvent waiterForRsp = new AutoResetEvent(false);//用于同步用户的Logic线程和串口的DataReceived线程 11 private int usingSource = 0;//保证一条指令的交互完整性,或多条指令组成的模块的交互完整性 12 13 public SerialPort _device { 14 get; private set; 15 } 16 private Encoding encoding;//字符编码 17 #endregion 18 19 #region 构造函数 20 /// <summary> 21 /// 使用字节型通信协议调用此构造函数 22 /// </summary> 23 /// <param name="portName">串口名</param> 24 /// <param name="baudRate">波特率</param> 25 /// <param name="dataBits">数据位</param> 26 /// <param name="stopBits">停止位</param> 27 /// <param name="parity">奇偶校验</param> 28 public BaseSerialPort(string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity) { 29 this._device = new SerialPort(); 30 this._device.PortName = portName; 31 this._device.BaudRate = baudRate; 32 this._device.DataBits = dataBits; 33 this._device.StopBits = stopBits; 34 this._device.Parity = parity; 35 this._device.DataReceived += Device_DataReceived; 36 this.encoding = null; 37 } 38 /// <summary> 39 /// 使用字符串型通信协议调用此构造函数 40 /// </summary> 41 /// <param name="portName">串口名</param> 42 /// <param name="baudRate">波特率</param> 43 /// <param name="dataBits">数据位</param> 44 /// <param name="stopBits">停止位</param> 45 /// <param name="parity">奇偶校验</param> 46 public BaseSerialPort(string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity, Encoding encoding) { 47 this._device = new SerialPort(); 48 this._device.PortName = portName; 49 this._device.BaudRate = baudRate; 50 this._device.DataBits = dataBits; 51 this._device.StopBits = stopBits; 52 this._device.Parity = parity; 53 this._device.DataReceived += Device_DataReceived; 54 this.encoding = encoding; 55 } 56 #endregion 57 58 #region 连接设备 59 public void Connect() { 60 try { 61 if (!this._device.IsOpen) { 62 this._device.Open(); 63 } 64 } 65 catch { 66 throw new Exception(string.Format("端口{0}已被占用或被拒绝访问",this._device.PortName)); 67 } 68 } 69 #endregion 70 71 #region 关闭连接 72 public void Close() { 73 if (this._device.IsOpen) { 74 this._device.Close(); 75 } 76 } 77 #endregion 78 79 #region 接收数据 80 public void Device_DataReceived(object sender, SerialDataReceivedEventArgs e) { 81 lock (locker) { 82 if (_device.BytesToRead > 0) { 83 byte[] buff = new byte[msgMaxLength]; 84 int startIndex = 0; 85 while (_device.BytesToRead > 0) { 86 int count = _device.Read(buff, startIndex, _device.BytesToRead); 87 startIndex += count; 88 Thread.Sleep(10); 89 } 90 91 byte[] msg = new byte[startIndex]; 92 Array.Copy(buff, msg, startIndex); 93 94 if (this.encoding == null) { 95 this.byteMsgRecv = msg; 96 } 97 else { 98 string strMsg = this.encoding.GetString(msg); 99 this.strMsgRecv = strMsg; 100 } 101 waiterForRsp.Set(); 102 } 103 } 104 } 105 #endregion 106 107 #region 发送消息 108 public void SendMsg(byte[] msg) { 109 this.Connect(); 110 this._device.Write(msg, 0, msg.Length); 111 } 112 113 public void SendMsg(string msg) { 114 this.Connect(); 115 byte[] msgToByte = this.encoding.GetBytes(msg); 116 this._device.Write(msgToByte, 0, msgToByte.Length); 117 } 118 #endregion 119 120 #region 原子操作 121 /// <summary> 122 /// 接收设备的响应消息(字节型通信协议) 123 /// </summary> 124 /// <param name="timeOut">等待设备回复的最大时长</param> 125 /// <param name="msg">收到的设备的回复</param> 126 /// <returns></returns> 127 public bool RecvRsp(int timeOut, out byte[] msg) { 128 bool isInTime = waiterForRsp.WaitOne(timeOut); 129 byte[] buff = new byte[byteMsgRecv.Length]; 130 Array.Copy(byteMsgRecv, buff, byteMsgRecv.Length); 131 msg = buff; 132 return isInTime; 133 } 134 /// <summary> 135 /// 接收设备的响应消息(字符型通信协议) 136 /// </summary> 137 /// <param name="timeOut">等待设备回复的最大时长</param> 138 /// <param name="msg">收到的设备的回复</param> 139 /// <returns></returns> 140 public bool RecvRsp(int timeOut, out string msg) { 141 bool isInTime = waiterForRsp.WaitOne(timeOut); 142 msg = strMsgRecv; 143 return isInTime; 144 } 145 146 public void OccupyRoute() { 147 while (Interlocked.Exchange(ref usingSource, 1) == 1) 148 ; 149 } 150 151 public void AbandonRoute() { 152 Interlocked.Exchange(ref usingSource, 0); 153 } 154 #endregion 155 }
使用方法:
1,适用场景
上位机和设备通过协议发送和接收数据交互,从而达到上位机控制设备的目的。
2,使用方法
(1)示例
现在有个需求:上位机软件包括一个点胶机控制器,用来控制点胶机的开始吐胶,停止吐胶,真空压力值等行为。
/// <summary> /// 点胶机控制器 /// </summary> class DispenserControler { //串口 private BaseSerialPort port = new BaseSerialPort("COM1", 9600, 8, StopBits.One, Parity.None,Encoding.ASCII); public void Init() { port.Connect(); } //吐胶 public void TuJiao() { port.OccupyRoute(); port.SendMsg("tu jiao"); string msg; bool res = port.RecvRsp(20000, out msg); if (res == false) { Console.WriteLine("OVER TIME"); } else { Console.WriteLine(msg); } port.AbandonRoute(); } public void ShutDown() { port.Close(); } }
(2)首先创建一个“控制器”类。
(3)创建一个BaseSerialPort实例,作为控制器的通信工具。
(4)根据通信协议是字节型还是字符串型,选用不同的构造函数创建BaseSerialPort实例。
(5)public void OccupyRoute() public void AbandonRoute() 两个函数极为重要;
一.保证一个由若干指令组合而成的交互过程的连贯性
OccupyRoute();
Send();//不需要回复的指令
Send();//需要回复的指令
RecvRsp();//处理回复
AbandonRoute();
二,每条指令的开头和结尾
OccupyRoute();
Send();//需要回复的指令
RecvRsp();//处理回复
AbandonRoute();
含义:发送完指令,处理设备的回复,然后让出 串口接收数据的唯一缓存,允许其他指令和设备交互。
三,RecvRsp()的使用方法
检查返回值,若false,表示规定时间内未收到设备的回复,Rsp超时。
检查返回值,若true,则使用RecvRsp()传出的接收到的数据。