在 C# 中计算 CRC16
Calculating CRC16 in C#
我正在尝试将一个旧代码从 C 移植到 C#,它基本上接收一个字符串和 return它的 CRC16...
C方法如下:
#define CRC_MASK 0x1021 /* x^16 + x^12 + x^5 + x^0 */
UINT16 CRC_Calc (unsigned char *pbData, int iLength)
{
UINT16 wData, wCRC = 0;
int i;
for ( ;iLength > 0; iLength--, pbData++) {
wData = (UINT16) (((UINT16) *pbData) << 8);
for (i = 0; i < 8; i++, wData <<= 1) {
if ((wCRC ^ wData) & 0x8000)
wCRC = (UINT16) ((wCRC << 1) ^ CRC_MASK);
else
wCRC <<= 1;
}
}
return wCRC;
}
我移植的C#代码是这样的:
private static ushort Calc(byte[] data)
{
ushort wData, wCRC = 0;
for (int i = 0; i < data.Length; i++)
{
wData = Convert.ToUInt16(data[i] << 8);
for (int j = 0; j < 8; j++, wData <<= 1)
{
var a = (wCRC ^ wData) & 0x8000;
if ( a != 0)
{
var c = (wCRC << 1) ^ 0x1021;
wCRC = Convert.ToUInt16(c);
}
else
{
wCRC <<= 1;
}
}
}
return wCRC;
}
测试字符串是 "OPN"... 它必须 return 一个 uint,它是 (ofc) 2 字节 A8 A9 并且 #CRC_MASK 是该计算的多项式。我确实在这里和网络上找到了几个 CRC16 示例,但其中 none 实现了这个结果,因为此 CRC 计算必须与我们连接的设备相匹配。
哪里错了?非常感谢任何帮助。
谢谢!最好的问候
古腾堡
更新
根据@rcgldr 的回答,我整理了以下示例:
_serial = new SerialPort("COM6", 19200, Parity.None, 8, StopBits.One);
_serial.Open();
_serial.Encoding = Encoding.GetEncoding(1252);
_serial.DataReceived += Serial_DataReceived;
var msg = "OPN";
var data = Encoding.GetEncoding(1252).GetBytes(msg);
var crc = BitConverter.GetBytes(Calc(data));
var msb = crc[0].ToString("X");
var lsb = crc[1].ToString("X");
//The following line must be something like: \x16OPN\x17\xA8\xA9
var cmd = string.Format(@"{0}{1}{2}\x{3}\x{4}", SYN, msg, ETB, msb, lsb);
//var cmd = "\x16OPN\x17\xA8\xA9";
_serial.Write(cmd);
cmd 变量的值是我要发送到设备的值。如果您查看注释的 cmd 值,这是一个有效的字符串。 CRC16 的 2 个字节位于最后两个参数(msb 和 lsb)中。因此,在此处的示例中,msb 必须是“\xA8”并且 lsb 必须是“\xA9”才能使命令工作(设备上的 CRC16 匹配)。
有什么线索吗?
再次感谢。
更新 2
对于那些遇到相同情况的人,您是否需要使用 \x 格式化字符串,这就是我为使其正常工作所做的工作:
protected string ToMessage(string data)
{
var msg = data + ETB;
var crc = CRC16.Compute(msg);
var fullMsg = string.Format(@"{0}{1}{2:X}{3:X}", SYN, msg, crc[0], crc[1]);
return fullMsg;
}
这 return 给我的是我需要的完整信息,包括上面的 \x。 SYN变量是'\x16',ETB是'\x17'
谢谢大家的帮助!
古腾堡
这里的问题是包含 ETB (\x17) 的消息有 4 个字节长(前导同步字节不用于 CRC):"OPN\x17" == {'O' , 'P', 'N', 0x17},这导致将 {0xA8, 0xA9} 的 CRC 附加到消息中。所以CRC函数是正确的,但是原始测试数据不包括第4个字节,即0x17。
这是一个工作示例(至少在 VS2015 express 中)。
private static ushort Calc(byte[] data)
{
ushort wCRC = 0;
for (int i = 0; i < data.Length; i++)
{
wCRC ^= (ushort)(data[i] << 8);
for (int j = 0; j < 8; j++)
{
if ((wCRC & 0x8000) != 0)
wCRC = (ushort)((wCRC << 1) ^ 0x1021);
else
wCRC <<= 1;
}
}
return wCRC;
}
我正在尝试将一个旧代码从 C 移植到 C#,它基本上接收一个字符串和 return它的 CRC16...
C方法如下:
#define CRC_MASK 0x1021 /* x^16 + x^12 + x^5 + x^0 */
UINT16 CRC_Calc (unsigned char *pbData, int iLength)
{
UINT16 wData, wCRC = 0;
int i;
for ( ;iLength > 0; iLength--, pbData++) {
wData = (UINT16) (((UINT16) *pbData) << 8);
for (i = 0; i < 8; i++, wData <<= 1) {
if ((wCRC ^ wData) & 0x8000)
wCRC = (UINT16) ((wCRC << 1) ^ CRC_MASK);
else
wCRC <<= 1;
}
}
return wCRC;
}
我移植的C#代码是这样的:
private static ushort Calc(byte[] data)
{
ushort wData, wCRC = 0;
for (int i = 0; i < data.Length; i++)
{
wData = Convert.ToUInt16(data[i] << 8);
for (int j = 0; j < 8; j++, wData <<= 1)
{
var a = (wCRC ^ wData) & 0x8000;
if ( a != 0)
{
var c = (wCRC << 1) ^ 0x1021;
wCRC = Convert.ToUInt16(c);
}
else
{
wCRC <<= 1;
}
}
}
return wCRC;
}
测试字符串是 "OPN"... 它必须 return 一个 uint,它是 (ofc) 2 字节 A8 A9 并且 #CRC_MASK 是该计算的多项式。我确实在这里和网络上找到了几个 CRC16 示例,但其中 none 实现了这个结果,因为此 CRC 计算必须与我们连接的设备相匹配。
哪里错了?非常感谢任何帮助。
谢谢!最好的问候
古腾堡
更新
根据@rcgldr 的回答,我整理了以下示例:
_serial = new SerialPort("COM6", 19200, Parity.None, 8, StopBits.One);
_serial.Open();
_serial.Encoding = Encoding.GetEncoding(1252);
_serial.DataReceived += Serial_DataReceived;
var msg = "OPN";
var data = Encoding.GetEncoding(1252).GetBytes(msg);
var crc = BitConverter.GetBytes(Calc(data));
var msb = crc[0].ToString("X");
var lsb = crc[1].ToString("X");
//The following line must be something like: \x16OPN\x17\xA8\xA9
var cmd = string.Format(@"{0}{1}{2}\x{3}\x{4}", SYN, msg, ETB, msb, lsb);
//var cmd = "\x16OPN\x17\xA8\xA9";
_serial.Write(cmd);
cmd 变量的值是我要发送到设备的值。如果您查看注释的 cmd 值,这是一个有效的字符串。 CRC16 的 2 个字节位于最后两个参数(msb 和 lsb)中。因此,在此处的示例中,msb 必须是“\xA8”并且 lsb 必须是“\xA9”才能使命令工作(设备上的 CRC16 匹配)。
有什么线索吗?
再次感谢。
更新 2 对于那些遇到相同情况的人,您是否需要使用 \x 格式化字符串,这就是我为使其正常工作所做的工作:
protected string ToMessage(string data)
{
var msg = data + ETB;
var crc = CRC16.Compute(msg);
var fullMsg = string.Format(@"{0}{1}{2:X}{3:X}", SYN, msg, crc[0], crc[1]);
return fullMsg;
}
这 return 给我的是我需要的完整信息,包括上面的 \x。 SYN变量是'\x16',ETB是'\x17'
谢谢大家的帮助!
古腾堡
这里的问题是包含 ETB (\x17) 的消息有 4 个字节长(前导同步字节不用于 CRC):"OPN\x17" == {'O' , 'P', 'N', 0x17},这导致将 {0xA8, 0xA9} 的 CRC 附加到消息中。所以CRC函数是正确的,但是原始测试数据不包括第4个字节,即0x17。
这是一个工作示例(至少在 VS2015 express 中)。
private static ushort Calc(byte[] data)
{
ushort wCRC = 0;
for (int i = 0; i < data.Length; i++)
{
wCRC ^= (ushort)(data[i] << 8);
for (int j = 0; j < 8; j++)
{
if ((wCRC & 0x8000) != 0)
wCRC = (ushort)((wCRC << 1) ^ 0x1021);
else
wCRC <<= 1;
}
}
return wCRC;
}