CRC16校验算法
private void crc16(byte[] data) { int[] temdata = new int[data.Length + 2]; int xda, xdapoly; int i, j, xdabit; xda = 0xFFFF; xdapoly = 0xA001; for (i = 0; i < data.Length - 2; i++) { xda ^= data[i]; for (j = 0; j < 8; j++) { xdabit = (int)(xda & 0x01); xda >>= 1; if (xdabit == 1) xda ^= xdapoly; } } data[data.Length - 2] = (byte)(xda & 0xFF); data[data.Length - 1] = (byte)(xda >> 8); }检查CRC
private bool checkCRC(byte[] data) { int len = data.Length; byte[] crc = new byte[data.Length]; Array.Copy(data, crc, data.Length - 2); crc16(crc); return (crc[len - 1] == data[len - 1]) && (crc[len - 2] == data[len - 2]); }示例
/// <summary> /// 0x03 功能码 读多路寄存器输入 /// </summary> /// <param name="addr_reg">要读的起始寄存器地址,比如 12</param> /// <param name="len">要读几个寄存器(LEN 要小于128)</param> /// <returns>读回来的无符号16位整数数组,长度 = len</returns> public UInt16[] ReadRegister(UInt16 addr_reg, UInt16 len) { try { // 8 个字节的分配:地址1 + 功能码1 + 地址2 + 个数2 + CRC校验2 byte[] bytesSend = new byte[8]; // 创建一个长度 = len 的结果数组 用于函数返回 UInt16[] data = new UInt16[len]; // 填"从站地址",要跟哪台仪器通讯 bytesSend[0] = 1; // "功能码" 0x03,"读保持寄存器" bytesSend[1] = 0x03; unchecked { //读取的地址 //地址高8位 bytesSend[2] = (byte)(addr_reg >> 8); //地址低8位 bytesSend[3] = (byte)addr_reg; //读取的数量 //读取的高8位 bytesSend[4] = (byte)(len >> 8); //读取的低8位 bytesSend[5] = (byte)len; } // 调用 CRC16 校验算法,它会"直接修改"bytesSend 数组 crc16(bytesSend); // 通过串口把这封信发出去,然后等着收回答复 byte[] rx_bytes = mComPort.QueryBytes(bytesSend, len * 2 + 5, 50); // 检查收回来的数据的 CRC 校验码对不对 if (!checkCRC(rx_bytes)) { // 校验不通过,说明数据在传输过程中出错了(干扰、丢包等) throw new Exception("crc error"); } // 创建一个长度 = len × 2 的字节数组,专门装"纯数据部分" // 因为每个寄存器是 2 个字节,len 个寄存器就是 len*2 个字节 byte[] des = new byte[len * 2]; // 从收到的 rx_bytes 里,把纯数据抠出来放进 des Array.Copy(rx_bytes, 3, des, 0, des.Length); // 循环 len 次,把每 2 个字节拼成 1 个 UInt16 数字 for (int i = 0; i < len; i++) { // 高8位 + 低8位 → | 拼接 把它们粘回一个完整的 16 位数字 data[i] = (UInt16)(des[2 * i] << 8 | des[2 * i + 1]); } return data; } catch (Exception) { throw; } }人脸模组实战
/// <summary> /// 通用自定义命令发送方法 /// </summary> /// <param name="Model">消息 ID</param> /// <param name="Readdata">具体数据</param> /// <returns></returns> public string SendDataRead(byte Model, byte[] Readdata) { int DataSize = 0; if (Readdata != null) { DataSize = Readdata.Length; } byte[] sendData = new byte[DataSize + 6]; sendData[0] = 0xEF; sendData[1] = 0xAA; sendData[2] = Model; int jsonSize = DataSize; sendData[3] = (byte)((jsonSize >> 8) & 0xFF); sendData[4] = (byte)(jsonSize & 0xFF); if (DataSize > 0) { Array.Copy(Readdata, 0, sendData, 5, DataSize); } sendData[sendData.Length - 1] = XOR(sendData.Skip(2).ToArray());//除去 EF AA 的XOR校验 return SendData(sendData); } /// <summary> /// 通用自定义命令接收方法 /// </summary> /// <param name="Readdata">缓存区</param> /// <param name="validLen">缓存区有效的数据长度</param> /// <returns></returns> public string ReadDataRead(byte[] Readdata,ref int validLen) { if (Readdata == null || validLen <= 0) { addLOG?.Invoke("缓存区为空"); return "FAIL"; } int DataSyncWord = -1; for (int i = 0; i < validLen-1; i++) { if (Readdata[i]==0xEF&& Readdata[i+1]== 0xAA) { DataSyncWord = i; break; } } if (DataSyncWord == -1) { // 保留末尾可能是帧头首字节的那个 0xEF,其余丢弃因为前面的数据都是无效的 bool keep = Readdata[validLen - 1] == 0xEF; if (keep) Readdata[0] = 0xEF; validLen = keep ? 1 : 0; addLOG?.Invoke("未找到数据帧头"); return "FAIL"; } // 清除帧头之前的无效数据 if (DataSyncWord > 0) { Array.Copy(Readdata, DataSyncWord, Readdata, 0, validLen - DataSyncWord); validLen -= DataSyncWord; // 同步扣减有效长度 DataSyncWord = 0; } //清除帧头之前的无效数据后,重新计算长度 半包判断 if (validLen < 6) { addLOG?.Invoke("数据帧不完整"); return "WAIT"; } //获取数据长度 int length = (Readdata[3] << 8) | Readdata[4]; int frameLen = 6 + length; //完整信息的长度 //检查是否为完整数据帧 if (validLen < frameLen) { addLOG?.Invoke("数据帧不完整"); return "WAIT"; } //完整信息的长度 = 帧头(2) + Model(1) + Size(2) + Data(length) + XOR(1) byte[] data = new byte[frameLen]; //提取完整信息 Array.Copy(Readdata, 0, data, 0, frameLen); //检查XOR校验 if (!CheckXOR(data.Skip(2).ToArray())) { addLOG?.Invoke("XOR 校验失败,丢弃该帧"); Array.Copy(Readdata, 1, Readdata, 0, validLen - 1); validLen -= 1; return "FAIL"; } // 删除已提取的完整信息:前移 + 尾部清零 + 更新有效长度 int remain = validLen - frameLen; if (remain > 0) { Array.Copy(Readdata, frameLen, Readdata, 0, remain); } Array.Clear(Readdata, remain, frameLen); // 清残留,否则下次把垃圾当数据 validLen = remain; sendmodel?.Invoke(data); return "PASS"; } bool CheckXOR(byte[] data) { byte[] bytes= new byte[data.Length - 1]; Array.Copy(data, 0, bytes, 0, data.Length - 1); byte xor = XOR(bytes); return xor == data[data.Length - 1]; }