XK3168E 基本资料
分秤体跟仪表两部分,秤体即吊车下挂的部分,它跟仪表通过无线通讯,有效距离>100米,
仪表部分有个串口,可以跟电脑串口连接,采用DB9连线,有2,3,5三根线,2,3两线交叉,5是地线,2,3线一根发数据一根是收数据
将仪表的串口设置成9600波特率,8位无校验,连续模式
当秤体有负重时仪表会每100毫秒发送帧数据,3.5分钟后每1秒发送,2.5小时后停止发送.
串口通信的基本概念
接收数据通过2号线一位一位的发送过来,每个位占用 1/BaudRate 秒时间
波特率:即每秒种信号(电压)的最大改变次数,约等于每秒能发送多少bit
串口采样按16倍波特率的频率进行
开始位:由空闲信号变成逻辑0,长度1bit
数据位:即一桢中数据的位数,有,5,7,8等
校验:即奇偶校验,长度1bit,可以不用
停止位:一桢数据接受完后,空闲信号持续的时间,可以是1,2,1.5等(保持线路逻辑1时电压的时间,等于 1|2|1.5 * 1/波特率 (秒))
数据帧: 开始位(1)+数据(5~8)+校验(0~1)+停止(1~2)
电子秤数据格式
固定为 17个字节,采用ASCII,开始是stx(0x02) 结束是CL(0x13),1-3字节是状态子,4-9字节是重量信息10-15字节是皮重信息
默认时4-9字节的重量信息的保留一位小数点,即在8与9字节间加入.
代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DotNet.ElecScales { using System.IO.Ports; using System.Text; using System.Threading; /// <summary> /// 针对XK3168E /// 厂家:杭州福达电子设置厂 /// 设置成 波特率9600, 8位无校验,连续模式 /// 编写:Wdfrog /// http://www.cnblogs.com/wdfrog/archive/2012/10/25/2738257.html /// </summary> public class ElecScalesReader { #region 属性d定义 /// <summary> /// 属性部分 /// </summary> private List<Byte> _BufferList = new List<byte>(1024 * 10); private byte C_STX = 0x02; //开始位标志 private byte C_END = 0x0D; //结束位标志 private int C_MaxQueueElem = 30;//队列里存放的最多元素个数 private int C_FrameLength = 17; private bool Enabled = false; private Thread WorkThread { get; set; } //公开属性 public Queue<GatherDataInfo> DataQueue { get; private set; } public SerialPort COMPort { get; private set; } public Exception LastError { get; set; } #endregion /// <summary> /// 构造 /// </summary> public ElecScalesReader() { DataQueue = new Queue<GatherDataInfo>(); } private void EnsureCOMPortOpen() { if (COMPort == null) { //配置COMPort COMPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); COMPort.Encoding = Encoding.ASCII; //COMPort.DtrEnable = true; } if (!COMPort.IsOpen) { COMPort.Open(); } } /// <summary> /// 格式-> 状态,读数,原始数据16进制表示,备注 /// 状态-> 1:成功,0:无数据,4:错误 /// </summary> /// <returns></returns> public string GetValue() { string valueTmp = "{0},{1},{2},{3}";//状态{1成功,0无数据,4错误},读数,原始数据16进制表示,备注 try { if (WorkThread == null || WorkThread.IsAlive!=true ) { Launch(); Thread.Sleep(100); } } catch (Exception ex) { LastError = ex; return string.Format(valueTmp, "4", "", "", ex.Message); } GatherDataInfo data = new GatherDataInfo() { Status = 0, AddTime = DateTime.Now, Bytes = null, StrValue = Thread.CurrentThread.ManagedThreadId.ToString() + ":" + this.GetHashCode().ToString(), }; lock (DataQueue) { if (DataQueue.Count > 0) { data = DataQueue.Last(); } } return string.Format(valueTmp, data.Status, data.StrValue, data.Bytes == null ? "" : BitConverter.ToString(data.Bytes.ToArray()), ""); } /// <summary> /// 关闭COM /// </summary> public void Close() { this.Enabled = false; while (COMPort != null && COMPort.IsOpen) Thread.Sleep(10); } /// <summary> /// 启动 /// </summary> private void Launch() { EnsureCOMPortOpen(); Enabled = true; WorkThread = new Thread(DoReceive); WorkThread.IsBackground = true; WorkThread.Priority = ThreadPriority.Highest; WorkThread.Start(); } private void DoReceive() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString()); #region try { byte[] buffer = new byte[COMPort.ReadBufferSize]; while (Enabled) { Thread.Sleep(15); if (COMPort.BytesToRead <= 0) continue; var readLen = COMPort.Read(buffer, 0, COMPort.BytesToRead); Array.Resize<byte>(ref buffer, readLen); _BufferList.AddRange(buffer); Array.Resize(ref buffer, COMPort.ReadBufferSize); #region 数据解析 if (_BufferList.Count <= 0) return; var stxIndex = _BufferList.IndexOf(C_STX); if (stxIndex >= 0) { var endIndex = stxIndex + C_FrameLength - 1; //一个包17个字节 if (_BufferList.Count > endIndex) { byte[] frameBytes = new byte[C_FrameLength]; _BufferList.CopyTo(stxIndex, frameBytes, 0, C_FrameLength); //清空该帧以及该帧前面的全部数据 _BufferList.RemoveRange(0, endIndex); //验证有效性 if (frameBytes[frameBytes.Length - 1] == C_END) { var str = Encoding.ASCII.GetString(frameBytes, 4, 6); var val = (str.Substring(0, 5) + "." + str.Substring(5, 1)).Trim(); //将数据入队列 var data = new GatherDataInfo() { Status = 1, AddTime = DateTime.Now, Bytes = new List<byte>(frameBytes), StrValue = val }; lock (DataQueue) { DataQueue.Enqueue(data); if (DataQueue.Count > C_MaxQueueElem) { DataQueue.Dequeue(); } } } else { Console.WriteLine("无效帧,结尾不是CR符号!"); } Console.WriteLine(BitConverter.ToString(frameBytes)); } } //已经收到17个字节但是还没有找到开始标志的 //则放弃缓存中的数据 if (_BufferList.Count > C_FrameLength && stxIndex < 0) { _BufferList.RemoveRange(0, _BufferList.Count); } #endregion } } catch (Exception ex) { LastError = ex; throw; } finally { if (COMPort != null && COMPort.IsOpen) { COMPort.Close(); } } #endregion } } /// <summary> /// 获取的有效桢信息 /// </summary> public class GatherDataInfo { public DateTime? AddTime { get; set; } /// <summary> /// 字节信息 /// </summary> public List<byte> Bytes { get; set; } /// <summary> /// 转化后的信息 /// </summary> public string StrValue { get; set; } /// <summary> /// 1,有效果 /// 0,无效 /// </summary> public int Status { get; set; } } public class ElecScalesHelper { private static ElecScalesReader _Reader; public static ElecScalesReader Reader { get { if (_Reader == null) { _Reader = new ElecScalesReader(); } return _Reader; } } /// <summary> /// 格式-> 状态,读数,原始数据16进制表示,备注 /// 状态-> 1:成功,0:无数据,4:错误 /// </summary> /// <returns></returns> public static string GetValue() { lock (typeof(ElecScalesHelper)) { return Reader.GetValue(); } } /// <summary> /// 关闭COM /// </summary> public static string Close() { lock (typeof(ElecScalesHelper)) { Reader.Close(); _Reader = null; } return ""; } } }
参考资料
http://cs.nju.edu.cn/yangxc/dcc2003.files/jszc-sub/comif-06.htm
http://www.dotblogs.com.tw/billchung/category/5702.aspx?Show=All
在线工具
http://www.cnblogs.com/wdfrog/archive/2012/10/25/2738257.html