我有一个通过RS232 COM与计算机通信的终端。我给出的协议说我必须发送一定的字节组合和CRC 16 IBM最终发送的数据计算
我还获得了一个可以测试的C编写应用程序,该应用程序使用发送数据和接收数据写入日志。在该日志中,我看到是否向终端发送此字符串hexString = "02 00 04 a0 00 01 01 03"
。我还必须发送CRC16 IBM数据结果06 35。
我已经设法以某种方式将作为示例给出的C方法转换为C#。但是我的结果远远不是我所知道的必须接受的结果。
我测试过从日志中发送数据,一切都很好。我的计算错了。我在这里做错了吗?
这是我的代码:
CRC课程:
public enum Crc16Mode : ushort
{
ARINC_NORMAL = 0XA02B, ARINC_REVERSED = 0xD405, ARINC_REVERSED_RECIPROCAL = 0XD015,
CCITT_NORMAL = 0X1021, CCITT_REVERSED = 0X8408, CCITT_REVERSED_RECIPROCAL = 0X8810,
CDMA2000_NORMAL = 0XC867, CDMA2000_REVERSED = 0XE613, CDMA2000_REVERSED_RECIPROCAL = 0XE433,
DECT_NORMAL = 0X0589, DECT_REVERSED = 0X91A0, DECT_REVERSED_RECIPROCAL = 0X82C4,
T10_DIF_NORMAL = 0X8BB7, T10_DIF_REVERSED = 0XEDD1, T10_DIF_REVERSED_RECIPROCAL = 0XC5DB,
DNP_NORMAL = 0X3D65, DNP_REVERSED = 0XA6BC, DNP_REVERSED_RECIPROCAL = 0X9EB2,
IBM_NORMAL = 0X8005, IBM_REVERSED = 0XA001, IBM_REVERSED_RECIPROCAL = 0XC002,
OPENSAFETY_A_NORMAL = 0X5935, OPENSAFETY_A_REVERSED = 0XAC9A, OPENSAFETY_A_REVERSED_RECIPROCAL = 0XAC9A,
OPENSAFETY_B_NORMAL = 0X755B, OPENSAFETY_B_REVERSED = 0XDDAE, OPENSAFETY_B_REVERSED_RECIPROCAL = 0XBAAD,
PROFIBUS_NORMAL = 0X1DCF, PROFIBUS_REVERSED = 0XF3B8, PROFIBUS_REVERSED_RECIPROCAL = 0X8EE7
}
public class Crc16
{
readonly ushort[] table = new ushort[256];
public ushort ComputeChecksum(params byte[] bytes)
{
ushort crc = 0;
for (int i = 0; i < bytes.Length; ++i)
{
byte index = (byte)(crc ^ bytes[i]);
crc = (ushort)((crc >> 8) ^ table[index]);
}
return crc;
}
public byte[] ComputeChecksumBytes(params byte[] bytes)
{
ushort crc = ComputeChecksum(bytes);
return BitConverter.GetBytes(crc);
}
public Crc16(Crc16Mode mode)
{
ushort polynomial = (ushort)mode;
ushort value;
ushort temp;
for (ushort i = 0; i < table.Length; ++i)
{
value = 0;
temp = i;
for (byte j = 0; j < 8; ++j)
{
if (((value ^ temp) & 0x0001) != 0)
{
value = (ushort)((value >> 1) ^ polynomial);
}
else
{
value >>= 1;
}
temp >>= 1;
}
table[i] = value;
}
}
}
处理收到的字节的方法:
public ushort CalculateCRC(byte[] data)
{
Crc16 crcCalc = new Crc16(Crc16Mode.IBM_NORMAL);
ushort crc = crcCalc.ComputeChecksum(data);
return crc;
}
在此方法中,您可以从枚举中选择多项式。
主要在课程类中:
static void Main(string [] args) { 尝试 { Metode m = new Metode();
string hexString = "02 00 04 a0 00 01 01 03";
byte[] bytes = m.HexStringToByteArray(hexString);
ushort crc = m.CalculateCRC(bytes);
string hexResult;
int myInt = crc;
hexResult = myInt.ToString("X");
//Console.WriteLine(crc.ToString());
Console.WriteLine(hexResult);
Console.ReadLine();
}
catch (Exception ex)
{
Metode m = new Metode();
m.writeError(ex.Message);
}
}
从hexstring转换为byte array:
public byte[] HexStringToByteArray(string hexString)
{
hexString = hexString.Replace(" ", "");
return Enumerable.Range(0, hexString.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
.ToArray();
}
从字节数组转换为十六进制字符串:
public string ByteArrayToHexString(byte[] byteArray)
{
return BitConverter.ToString(byteArray);
}
我在这里做错了什么?
更新:
感谢@MarkAdler,我设法翻译了计算。我直到很晚才注意到的事实是CRC计算应该是在线发送到终端的DATA,而不是整个消息!
所以hexString实际上应该是“a0 00 01 01”,没有STX / length / ETX的数据。
以下是C#中此特定CRC16微积分的代码:
public ushort CalculateCRC(byte[] data, int len)
{
int crc = 0, i = 0;
while (len-- != 0)
{
crc ^= data[i++] << 8;
for (int k = 0; k < 8; k++)
crc = ((crc & 0x8000) != 0) ? (crc << 1) ^ 0x8005 : (crc << 1);
}
return (ushort)(crc & 0xffff);
}
答案 0 :(得分:2)
您需要提供有关您尝试实施的规范的更多信息。但是我可以马上告诉你使用了错误的多项式。 CRC例程向右移位,这意味着多项式应该是位反转的。因此,IBM_NORMAL
无法正确。
虽然IBM_REVERSED
是适合右移的多项式,但可能是也可能不是您需要满足规范的多项式。此外,还可能存在或者离开CRC例程所需的异或。
更新
链接文档提供了计算CRC的实际代码。你为什么不看那个?在不查看文档中的内容的情况下在interwebs上查找随机代码来计算CRC并不太可能让您走得太远。它没有。
记录的代码将CRC左移,与您在问题中发布的代码相反。你需要左转。多项式为0x8005
。没有最终的异或,并且初始CRC值为零。
以下是文档中代码的简化版本,用C语言编写(此代码避免了文档中代码中内置的小端假设):
#include <stddef.h>
typedef unsigned char byte;
typedef unsigned short ushort;
ushort crc16ecr(byte data[], int len) {
ushort crc = 0;
for (int i = 0; i < len; i++) {
crc ^= (ushort)(data[i]) << 8;
for (int k = 0; k < 8; k++)
crc = crc & 0x8000 ? (crc << 1) ^ 0x8005 : crc << 1;
}
return crc;
}
根据文档,CRC是根据标签,len和数据计算的,您的消息为a0 00 01 01
。不是全部。 (彻底阅读文档始终是一个很好的第一步。)通过文档中的CRC代码运行它,你得到0x0635
。该文档说,这是首先传输最重要的字节,所以0x06 0x35
。