You are on page 1of 2

2004 年第 11 期                福  建  电  脑    63

Modbus 通信协议中 CRC 校验的快速 C 语言算法


孟开元
( 西安石油大学 计算机学院 陕西 西安 710065)

【摘  要】 本文主要讨论了 Modbus 通信协议的 R T U 帧格式中常用的错误校验方法 , 即循环冗余校验法 ( CR C) 。


提出了 Modbus 协议反转 CR C 校验的方法 , 推导了反转 CR C 校验快速计算表格 , 并用 C 语言实现了基于快速查
表算法的循环冗余校验程序 。
【关键词】 校验算法 , 循环冗余校验 , 生成多项式 , 查表法

1  Modbus 协议应用简介 间 ,它被使用在网络中 ( 显示为 T1 - T2 - T3 - T4 ,如表 2 ) 。然


Modicon 可编程控制器 ,既可以在它们之间 ,也可以和各种 后被发送的是第一个域设备地址 。联网的设备不断地监控网
网络上其他的设备间进行数据传输 。所支持的网络包括 Mod2 络总线 ,包括在‘默认’
间隔期间 。当第一个域 ( 地址域) 被接收
bus 和 Modbus Plus 工业网络 ,还有标准网络 ,如 MAP 和 Et her2 到时 ,每台设备译解它去查找该地址的设备 。在最后一个传送
net 。网络通过控制器的接入线或者网络适配器 ,和 Modicon 可 的字符后面 ,一个类似的至少 3. 5 字符时间间隔标志者消息的
利用的出口等相连 。连接关系图见图 1 。 结束 。一个新的消息在这个间隔之后开始 。整个消息帧必须
作为一连续的数据流被传送 。如果超过 1. 5 字符时间的默认
间隔在帧结束前发生 ,接收设备删除不完全的消息并且假设下
一个字节将是一条新消息的地址域 。一个典型的消息帧如表 2
所示 。
表 2  R TU 消息帧
开始标志 地址域 功能域 数据域 CRC 校验域 结束标志
T1 - T2 - T3 - T4 8 比特 8 比特 n 3 8 比特 16 比特 T1 - T2 - T3 - T4
2. 3 地址域的处理
信息框架的地址领域包括 2 个字符的 ASCII 或 8 个比特的
R TU ,有效可使用的设备地址是从 1 到 247 ( 十进制 ) 单个的可
使用设备被设置在从 1 到 247 。一个主地址通过信息地址领域
安排从属地址 。当从属地址发送响应是 ,它设置自己的地址在
响应领域地址中 ,让主地址知道从地址正在工作 。
地址 0 被用在通信地址中 ,这个通信地址所有的从地址都
认识 。当 Modbus 协议被用在高级的网络上时 ,通信不能进行 ,
也不能用别的方法代替 。例如 ,Modbus 升级版能用一个被全球
图 1  Modbus 协议应用框图
共享的数据库 。这个数据库能被任意更改 。
全部的 Modicon 控制器应用 Modbus 数据通信协议 。这个
2. 4 功能域的设置
协议定义了一种信息结构标准 , 不管信息在何种网络上传输 ,
信息框架的功能代码域包括两个字符的 ASCII 或八个字
控制器都可以识别和应用这个结构 。它描述了控制器通常接
节的 R TU 。有效代码范围从 1 到 255 ( 十进制) 。当然 ,一些代
入其他设备的进程 , 它如何应答别的设备的请求 , 如何可以检
码能适用所有的 Modicon 控制器 ,但是一些代码只可用于某些
查和显示错误 。它为信息域的页面格式和内容建立了一个标
模式 ,并且还有一些被保留供以后使用 。各信息传送各子设备
准的格式 。
时 ,功能代码域告诉子设备所要进行哪种运行类别 。例如 , 读
为了分析信息 ,Modbus 协议提供了为 Modicon 控制器所应
一组不连续线圈或输入端的开/ 关状态 ; 读一组寄存器的内容 ;
用的内部标准 。在 Modbus 网络上传输期间 , 协议决定每个控
写指定线圈或寄存器 ; 或者允许装载 ,记录 ,或在子设备中验证
制器如何知道它的设备地址 , 如何识别信息地址 ; 决定用来操
程序 。当子设备反应给主设备时 ,要用代码域功能显示其他的
作的类型 ; 和提取所有的数据或包含在信息中的其他信息 。
正常反应或一些错误发生 ( 称为异常反应 ) 。作为正常响应 ,子
2  Modbus 协议中数据帧格式
设备简单应答原来的功能代码 。作为异常反应 ,子设备返回一
2. 1 ASCII 帧格式
个代码 ,这个代码等价于原先的功能代码 ( 在字节中最有效的
在 ASCII 模式中 , 消息开始于一个‘ : ’字符 ( ASCII 码为
设置) 。除了对例外响应的功能代码修改外 , 子设备在响应信
3A) ,结束于一个‘回车/ 换行’对 ( CR/ L F ) ( ASCII 码为 0D 和
息数据域里设置一个唯一的代码 。这样告诉主设备发生了哪
0A) 。联网的设备不断地对‘ : ’ 字符监控网络总线 。当一个‘ : ’
类错误 ,和错误的原因 。
被接收到时 ,每个设备译解它去查找该地址的设备 。一个典型
3  RTU 错误校验域的内容
的消息帧如表 1 。
3. 1 CRC 校验域的次序
表 1  ASCII 消息帧格式
当 R TU 模式用在字符格式中时 ,错误校验域包含一个 16
开始标志 地址域 功能域 数据域 L RC 校验域 结束标志
比特位 ,相当两个 8 位字节的校验值 。校验值是对信息内容计
1 字符‘ : ’ 2 字符 2 字符 n 字符 2 字符 2 字符 CRL F 算的 CRC 结果 。CRC 域被附加在信息最后 , 作为信息最后的
2. 2 RTU 帧格式 域 。CRC 域内低字节被附加在先 , 跟在后面的是高字节 , CRC
在 R TU 模式中 ,消息开始于一个默认的至少 3. 5 字符时 高字节被设置为信息的最后 。
间的间隔 。这是最容易被实现的在波特率中作为一多字符时 3. 2 选择 CRC 生成器多项式

© 1994-2007 China Academic Journal Electronic Publishing House. All rights reserved. http://www.cnki.net
64 福  建  电  脑                2004 年第 11 期

有两种通行的 16 位 CRC 多项式 。第一种是由 CCIT T 规 / 3 高 8 位 CRC 校验表 3 /


定的 。经由信息段内各字符连接而成的信息位串对应信息多 unsigned char CRCHighByte Table[ 256 ] = {
    0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,
项式的系数 ,信息多项式共有 n - 16 项 ,即从 Xn - 1 到 X16 ( n = 信     0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,
息块或信息段总位数) 。这个多项式要生成多项式作模 2 除法     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 ,
运算 。校验位串对应于上述除法最终得到的余数多项式的 X15
    0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,
到 X0 项的系数 。这个多项式简称为 CRC - CCIT T : X16 + X12 +     0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,
X5 + 1 。最先由 IBM 用在第一个软盘控制器 ( Model 3770 ) 上 ,     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 ,
很快便成为了微计算机软盘控制器的标准 。它也用在 IBM 有     0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,
名的同步协议 HDL C/ SDL C ( 高级数据连接控制/ 同步数据连接     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 ,
另一种广为使用的 CRC 生成多项式是 CRC - 16 : X16 + X15     0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,
2     0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,
+ X + 1 。这种多项式尽管在捕获错误时不如 CRC - CCIT T 有     0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,
效 ,但它仍然得以流行 。其原因是它有着长久的历史 ( 在 IBM     0x00 ,0xc1 ,0x81 ,0x40 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x01 ,0xc0 ,0x80 ,0x41 ,0x00 ,0xc1 ,0x81 ,0x40 ,

的 B YSYNC —二 进 制 同 步 通 信 协 议 —中 使 用 ) 。Modbus 中 };
CRC 校验算法就是采用的就是 CRC - 16 生成多项式 。
4  Modbus 中 CRC 校验快速 C 语言程序的实现 / 3 低 8 位 CRC 校验表 3 /
4. 1 Modbus 通信协议 CRC 算法 char CRCLowByte Table[ 256 ] = {
0x00 ,0xc0 ,0xc1 ,0x01 ,0xc3 ,0x03 ,0x02 ,0xc2 ,0xc6 ,0x06 ,0x07 ,0xc7 ,0x05 ,0xc5 ,0xc4 ,0x04 ,
Modbus 中生成 CRC 的步骤为 :     0xcc ,0x0c ,0x0d ,0xcd ,0x0f ,0xcf ,0xce ,0x0e ,0x0a ,0xca ,0xcb ,0x0b ,0xc9 ,0x09 ,0x08 ,0xc8 ,
1) 预置一个 16 位寄存器为十六进制数 FFFF ( 全为 1 ) ,该     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 ,
寄存器称为 CRC 寄存器 。     0xf0 ,0x30 ,0x31 ,0xf1 ,0x33 ,0xf3 ,0xf2 ,0x32 ,0x36 ,0xf6 ,0xf7 ,0x37 ,0xf5 ,0x35 ,0x34 ,0xf4 ,
2) 把第一个 8 位数据与 16 位 CRC 寄存器的低 8 位相异     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 ,
或 ,将结果放于 CRC 寄存器中 ;     0xe4 ,0x24 ,0x25 ,0xe5 ,0x27 ,0xe7 ,0xe6 ,0x26 ,0x22 ,0xe2 ,0xe3 ,0x23 ,0xe1 ,0x21 ,0x20 ,0xe0 ,
3 ) 把寄存器的内容右移一位 ( 向最低位 L SB 方向) ,用 0 填     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 ,
补最高位 MSB ,并检查最低位 ;     0x78 ,0xb8 ,0xb9 ,0x79 ,0xbb ,0x7b ,0x7a ,0xba ,0xbe ,0x7e ,0x7f ,0xbf ,0x7d ,0xbd ,0xbc ,0x7c ,
4) 如果最低位 L SB 为 0 ,重复步骤 3 ( 再次移位) ;     0xb4 ,0x74 ,0x75 ,0xb5 ,0x77 ,0xb7 ,0xb6 ,0x76 ,0x72 ,0xb2 ,0xb3 ,0x73 ,0xb1 ,0x71 ,0x70 ,0xb0 ,

如果最低位 L SB 为 1 , CRC 寄存器与多项式码 A001 Hex     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 ,
( 1010 0000 0000 0001) 进行异或 ;     0x88 ,0x48 ,0x49 ,0x89 ,0x4b ,0x8b ,0x8a ,0x4a ,0x4e ,0x8e ,0x8f ,0x4f ,0x8d ,0x4d ,0x4c ,0x8c ,
5) 重复步骤 3 和 4 ,直到右移 8 次 ,这样整个 8 位数据全部     0x44 ,0x84 ,0x85 ,0x45 ,0x87 ,0x47 ,0x46 ,0x86 ,0x82 ,0x42 ,0x43 ,0x83 ,0x41 ,0x81 ,0x80 ,0x40 ,

进行了处理 ; };
6 ) 重复步骤 2 到 5 ,进行下一个 8 位数据的处理 ; 直到所有 4. 3 CRC 源程序的实现
的字节被处理 。 以下是用 Visual C + + 语言实现的基于查表算法的源程
7) 最后得到的 CRC 寄存器内容即为 CRC 码 。 序。
必须注意 ,当 CRC 寄存器值被置入消息时 ,它的高位和低 两个参数的功能 :
位字节必须交换 。当 16 位 CRC ( 两个 8 位字节) 在消息中被传 unsigned char 3 MessageBuffer : 包含二进制数据的消息缓
送 ,低位字节将先被传送 ,随后传送高位字节 。 冲区的指针 ,被用于产生 CRC 校验码 。
4. 2 CRC 表的生成过程 unsigned short DataLengt h :在消息缓冲区的字节的数量 。
如果每次参与 CRC 计算的信息为一个字节 ,该信息字节加 CRC 生成函数功能返回 CRC 校验值 , 其类型为无符号短
到 16 位的累加器中去时 ,只有累加器的高 8 位或低 8 位与信息 整型 。
字节相异或 ,异或的结果记为组合值 , 那么累加器中的新值等
unsigned CModbusDlg: :CRC16(unsigned char 3 MessageBuffer , unsigned short DataLength)
于组合值加上 ( 按模 2 异或 ) 累加器中未改变的那一半即为新
{
的 CRC 值 。组合值只有 256 种可能 ,因此可利用硬件模拟算法   BYTE CRCHighByte = 0xFF ;     / 3 CRC 高位校验变量 3 /
先算好它们的 CRC 值预先填入一张表中 ,该表的每一单元对应   BYTE CRCLowByte = 0xFF ;      / 3 CRC 低位校验变量 3 /
相对值的 CRC 。这样就可以通过查表法来计算 CRC 值 , 以便   unsigned into index ;          / 3 CRC 校验值表格表格索引 3 /
大大提高 CRC 运算的速度 。
  while (DataLength - - )        / 3 循环参与 CRC 校验的消息缓冲区的字节 3 /
Modbus 通信协议中 CRC 值表格采用 16 位 CRC16 多项式
  {
( X16 + X15 + X2 + 1) 的反转 CRC 多项式 。反转多项式是指在数     index = CRCHighByte ^ 3 MessageBuffer + + ;     / 3 计算 CRC 3 /
据通讯时 ,信息字节先传送或接收低位字节 , 如重新排位则影     CRCHighByte = CRCLowByte ^ CRCHighByteTable[index] ;
响 CRC 计算速度 ,故设反转多项式 。X15 + X2 + 1 多项式码为     CRCLowByte = CRCLowByteTable[index] ;
  }
100A Hex ( 0001 0000 0000 1010) , 故反转多项式码码为 A001
  return (CRCHighByte < < 8 | CRCLowByte) ;
Hex ( 1010 0000 0000 0001) 。
}

 参考文献
[ 1 ] [ 美 ] Joe Campbell. 串行通信编程指南 . 北京 : 北京科海培训中心 ,1990.
[ 2 ] 刘涛 . CRC 差错校验法在 PC 机与 8031 单片机串行通讯中的应用 . 现代电子技术 ,2002 , (4) :21 - 22.
[ 3 ] Modbus Protocol. Modicon , Inc. , Industrial Automation Systems , J une 1996.

© 1994-2007 China Academic Journal Electronic Publishing House. All rights reserved. http://www.cnki.net

You might also like