打印

关于modbus的小错误!

[复制链接]
2866|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
bl20020825|  楼主 | 2007-8-19 17:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我看了网上modbus的资料很多,也很全,但都是一个模板!现在我在做modbus,看了协议,主要对modbus的CRC校验研究了一下,觉得网上的资料有”误导“新手的“嫌疑”,我将我的疑惑写在这,如果是我的错的话,请大家出来指点!
网上对modbus协议中的CRC校验的描述如下:
生成CRC-16校验字节的步骤如下: 

①装如一个16位寄存器,所有数位均为1。 

②该16位寄存器的高位字节与开始8位字节进行“异或”运算。运算结果放入这个16位寄存器。 

③把这个16寄存器向右移一位。 

④若向右(标记位)移出的数位是1,则生成多项式1010000000000001和这个寄存器进行“异或”运算;若向右移出的数位是0,则返回③。 

⑤重复③和④,直至移出8位。 

⑥另外8位与该十六位寄存器进行“异或”运算。 

⑦重复③~⑥,直至该报文所有字节均与16位寄存器进行“异或”运算,并移位8次。 

⑧这个16位寄存器的内容即2字节CRC错误校验,被加到报文的最高有效位。

我的意见:
看了,整体上是对的,但是第②步就有点问题!应该是8bit数据跟16位寄存器的低位字节进行异或才对!如果是跟高位字节进行异或的话,那根据CRC协议,它应该是进行左移来判断是否进行异或多项式!
协议的英文原文是如下:
A procedure for generating a CRC is:
1, Load a 16–bit register with FFFF hex (all 1’s). Call this the CRC register.
2. Exclusive OR the first 8–bit byte of the message with the low–order byte of the 16–bit CRC register, putting the result in the CRC register.
3. Shift the CRC register one bit to the right (toward the LSB), zero–filling the MSB. Extract and examine the LSB.
4. (If the LSB was 0): Repeat Step 3 (another shift).
(If the LSB was 1): Exclusive OR the CRC register with the polynomial
value A001 hex (1010 0000 0000 0001).
5. Repeat Steps 3 and 4 until 8 shifts have been performed. When this is done, a complete 8–bit byte will have been processed.
6. Repeat Steps 2 through 5 for the next 8–bit byte of the message.
Continue doing this until all bytes have been processed.
7. The final contents of the CRC register is the CRC value.
8. When the CRC is placed into the message, its upper and lower bytes
must be swapped as described below.
大家可以自己看看英文原文和CRC校验定义,如果是我的理解有误忘大家指出来!谢谢浏览,请多多回帖!

相关帖子

沙发
chunyang| | 2007-8-19 18:25 | 只看该作者

看我写的老帖

里面有正确的CRC生成方法,亦可用于Modbus。不过CRC的计算方法很多,数学上的步骤不是唯一的,需要验证或推导后才好说对错。

使用特权

评论回复
板凳
bl20020825|  楼主 | 2007-8-19 19:58 | 只看该作者

我的疑问是否存在呢??不应该跟高字节相异或

但请问,我的疑问是否对呢??第②步的应该是8bit数据跟16位寄存器的低位字节进行异或才对?

使用特权

评论回复
地板
bl20020825|  楼主 | 2007-8-19 20:07 | 只看该作者

chunyang先生出来说说啊!

在modbus里,我个人从英文原版来说,应该是来的字节数据应该跟16位的寄存器直接异或,即跟低字节异或!但我用程序写就出现问题了,就是用资料提供的查表法跟我用一位一位移动然后异或的方法计算出来的值不一样啊!
就比如字节数据是1,用查表法算出来的CRC值是32384,而如果是按着上面(跟低位异或)的方法来运行的话,算出来的是32894!不知到这是何原因???

使用特权

评论回复
5
chunyang| | 2007-8-19 20:17 | 只看该作者

用我写的方法去做就行,然后对比结果

你有兴趣也可以在纸上推演验证一番,你不会让我来替你做吧。严格说,运算高位或运算低位都行,但过程不同,结果相同。当然,你找到的算法是否正确,我不能在未推演的前提下判断,推演的工作你该自己进行。

使用特权

评论回复
6
bl20020825|  楼主 | 2007-8-19 20:32 | 只看该作者

这样我将我的程序贴出来!

我将我的程序贴出来!还有你的贴子真是好多,找不到哪个才是我需要的!
还有个问题,敬请回答,就是我的疑问是不是对的啊!从英文资料来说,它的CRC形成步骤应该是跟16位寄存器的低字节相异或而不是高字节,这样才会进行右移判断是否异或!!???
我的代码,都是对数据d=1 ,进行CRC编码的!
首先是查表法:这跟modbus guide文档有的:
/* CRC 高位字节值表 */ 
unsigned char auchCRCHi[] = { 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 
} ; 
/* CRC低位字节值表*/ 
unsigned char auchCRCLo[] = { 
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 
0x43, 0x83, 0x41, 0x81, 0x80, 0x40 
} ;
unsigned char uchCRCHi = 0xFF ; /* 高CRC字节初始化 */ 
unsigned char uchCRCLo = 0xFF ; /* 低CRC 字节初始化 */ 
unsigned char d;
Uint16 e;
Uint32 uIndex ; /* CRC循环中的索引 */ 
main()
{    
d = 1;
uIndex = uchCRCHi ^ d ; /* 计算CRC */ 
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ; 
uchCRCLo = auchCRCLo[uIndex] ; 
e=(uchCRCHi << 8 | uchCRCLo) ;
}

下面就是根据我对modbus的crc生成步骤的理解,来实现CRC的:也是对d=1进行crc编码
Uint16 c= 0xffff,e=0;
unsigned char d,f;
main()
{
    d = 1!
    e = c^(unsigned int)d;//跟低位字节进行异或运算
    for(f=0;f<8;f++)
    {
        if(e&1)
        {
            e >>= 1;
            e ^= 0xa001;
        }
        else e >>= 1;
        
    }
}

如果是照你的理解的话,跟高位运算只要改一下e = c^(unsigned int)d;就可以拉,但运行的结果,三个代码都是不一样的,这是为何啊???敬请chunyang前辈解答一下!!!

使用特权

评论回复
7
chunyang| | 2007-8-19 21:40 | 只看该作者

“我的理解”你根本就没有体会到

针对高位或针对低位的算法过程是不同的,不是改一下就行,先好好理解运算过程吧。

使用特权

评论回复
8
bl20020825|  楼主 | 2007-8-19 23:26 | 只看该作者

我对于你的回答,不是很理解!

我个人认为我的理解没有错!我看了你的blog里面的关于modbus的协议!你这样辛苦的翻译,我深表敬佩!但我也要指出的是,你的中文协议对CRC校验翻译有点问题!我要说明的一点,我对CRC校验还是理解的,虽然没您理解得透彻!在CRC校验实现过程中针对高位和低位的操作其实只是数据模2除法的变通,机制是一样的!如果是CRC进行的是向低位移出的话,必然要将字节数据异或放在低位字节!一般来说,CRC校验码的生成,都是采用高位移出,因为这样是标准模2运算!但其实低位移出,跟模2运算机制也一样,效果一样!虽然刚开始引入低位运算只是为了数据流在传输过程中反向了(高位和低位对调了),其实不管反不反向,对数据的低位操作生成的CRC编码,检错率是一样的,只要通信双方采用一样的移位算法就可以拉!看一下modbus的英文协议就可以知道,modbus里采用的就是低位移出算法!如果我的知识有限的话,请chun先生多多指点!还有我上面贴出来的程序,两个都是正确的!第一个查表法,刚开始时,我理解出错,其实uchCRCLo是最后crc校验码的高字节

使用特权

评论回复
9
chunyang| | 2007-8-20 00:44 | 只看该作者

一堆感叹号

如果正确,结果将一样,我给出的算法步骤应是比较简单的。至于你找到的算法和你的理解,我前面已经讲过,我不能在未推演的前提下判断正误,而推演的工作你该自己进行。

使用特权

评论回复
10
erhui_cn| | 2007-8-20 13:33 | 只看该作者

re:

使用特权

评论回复
11
erhui_cn| | 2007-8-20 13:36 | 只看该作者

re:

你先用网上的查表法测试一组数,得出结果,然后与你那些移法得出的结果比较,哪个对就是哪个移法对吧

说到底,你知道移来移去的意思吗?

使用特权

评论回复
12
bl20020825|  楼主 | 2007-8-20 18:32 | 只看该作者

知道移位的意思啊!

只要你理解了CRC的定义得话,掌握模2除法的精髓,结果应该是一样的!其实我发这个帖是为了让网上的资料清楚点,只是想更正一下,有益于我们新手的了理解!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

57

帖子

1

粉丝