【问题标题】:Simple serial comm. & Calculating CRC16 using C简单的串行通讯。 & 使用 C 计算 CRC16
【发布时间】:2020-09-26 22:59:14
【问题描述】:

我有一个传感器,我希望从中获取一些数据。我想问它日期或要求它提供数据。

传感器使用RS-232; 9600 8N1 进行通讯。

通信包由headerpayloadCRC组成。该手册为您想要做的任何事情提供了标头和有效负载。每个通信包的组成如下:

<SOH> header <STX> payload <ETB> CRC16 <ETX>

  • <SOH>: 0x01
  • <STX>:0x02
  • <ETB>: 0x17
  • <ETX>:0X03

手册给出了一个例子,如果你想询问日期,它告诉你header是0x31,payload是0x41。

因此发送传感器的命令是:\x01\x31\x02\x41\x17\CRC16\x03

现在作为一个例子,手册为你计算CRC16,在ASCII中是A0D5。 CRC16需要小端传输。

所以现在完整的命令是: \x01\x31\x02\x41\x17\x44\x35\x41\x30\x03

该手册不提供任何其他 CRC16 计算,它希望用户这样做很好:)


来自手册:每个数据包都由一个 16 位 CRC 验证,该 CRC 以十六进制 ASCII 编码(四个字符)传输。 CRC 由 header+payload 连接计算得到

WORD CRC16_Compute( BYTE *pBuffer, WORD length )
{
  BYTE i;
  BOOL bs;
  WORD crc=0;

  while( length-- )
  {
    crc ^= *pBuffer++;
    for( i = 0; i < 8; i++ )
    {
       bs = crc & 1;
       crc >>= 1;
       if( bs )
       {
         crc ^= 0xA001;
       }
    }
  }
return crc;
}

那是 C 语言中的 CRC 计算器,我不太懂,但这个代码 sn-p 是他们提供的全部,没有上下文。


在 ASCII 中,他们使用 1+A (0x31+0x41) 来获得 2 字节,A0D5。谁能给我解释一下 CRC 代码在做什么,谢谢!

【问题讨论】:

  • 这是一个纯粹的编程问题。该函数接收数据头和有效负载并计算 CRC16 校验和,这是 CRC16 的许多不同变体之一。你甚至研究过关于 CRC 的维基百科吗? “\x44\x35\x41\x30”是“D5A0”。 "\x01\x31\x02\x41\x17" 被输入 CRC 码。
  • 首先阅读关于 CRC 的维基百科文章,然后就您不了解的部分提出一个重点问题。您可以参加 CRC 的整个课程。
  • 当然,我理解CRC和按位运算符的前提。我在编程上卡住了——主要是 for 循环和 i++
  • 一个好消息;如果创建一个实时应用程序,你从来没有检查过 CRC。

标签: c serial-port crc


【解决方案1】:
#include <stdio.h>
typedef unsigned char BYTE;
typedef unsigned int BOOL;
typedef unsigned int WORD;
WORD CRC16_Compute( BYTE *pBuffer, WORD length )
{
  BYTE i;
  BOOL bs;
  WORD crc=0;

  while( length-- )
  {
    crc ^= *pBuffer++;
    for( i = 0; i < 8; i++ )
    {
       bs = crc & 1;
       crc >>= 1;
       if( bs )
       {
         crc ^= 0xA001;
       }
    }
  }
    return crc;
}
int main ( void )
{
    unsigned char data[2];
    unsigned char sdata[9];
    unsigned int x;
    unsigned int z;
    unsigned int i;

    data[0]=0x31;
    data[1]=0x41;
    x = CRC16_Compute(data,2);
    x&=0xFFFF;
    printf("0x%X\n",x);
    z=0;
    sdata[z++]=0x01;
    sdata[z++]=data[0];
    sdata[z++]=0x02;
    sdata[z++]=data[1];
    sdata[z++]=0x17;
    sdata[z  ]=((x>> 4)&0xF)+0x30; if(sdata[4]>0x39) sdata[4]+=7; z++;
    sdata[z  ]=((x>> 0)&0xF)+0x30; if(sdata[5]>0x39) sdata[5]+=7; z++;
    sdata[z  ]=((x>>12)&0xF)+0x30; if(sdata[6]>0x39) sdata[6]+=7; z++;
    sdata[z  ]=((x>> 8)&0xF)+0x30; if(sdata[7]>0x39) sdata[7]+=7; z++;
    sdata[z++]=0x03;
    for(i=0;i<z;i++) printf("%02X ",sdata[i]); printf("\n");
    return(0);
}

运行它

gcc so.c -o so
./so 
0xA0D5
01 31 02 41 17 44 35 41 30 03 

那个正确答案怎么样....

您需要知道的一切都在您的问题中,按照它所说的去做。几分钟的粗鲁拼凑。

CRC 是由 header+payload 连接计算得到的

    data[0]=0x31;
    data[1]=0x41;

即标头和有效负载,它根据提供的 CRC 码给出正确答案。

然后你用其他项目构建数据包。如果你谷歌 ASCII 表,你可以看到 'D' 'A' '0' '5' 的值,并且可以弄清楚如何从 0xD 到 0x44 和 0xA 到 0x41 但首先查看 0x0 到 0x30 和 0x5 到 0x35, 0-9 很简单,但 0x0A 给出 0x3A 但必须是 0x41,所以你调整。

所以代码的工作原理是基于所描述的 cmets 所描述的,我不知道这个传感器,他们这样做的方式似乎很愚蠢,但对他们提供一个示例和 crc16 上的详细信息有好处,因为有多个标准包括初始值在内的变化,因此再次为他们节省大量时间试图弄清楚......

【讨论】:

  • 在这里割喉,嗯?谢谢,非常感谢您花时间制定回复。
  • 更多关于老前辈的评论“有多种标准变体,包括初始值”。除了知道所使用的 CRC 多项式之外,为了获得一致的结果,您还需要指定 1) CRC 寄存器的初始值,2) 在附加到消息之前是否应该对 CRC 进行补充 (XOR'd),3 ) 将 CRC 字节附加到消息的顺序(小端与大端),以及 4) 每个字节中的位是否被反映,有些硬件会,有些则不会。
  • 此文本可能会提供一些见解:"A Painless Guide to CRC Error Detection Algorithms"
猜你喜欢
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 2015-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多