3.4差错检验
1.ASCII 模式
在 ASCII 模式中,报文包含一个错误校验字段。该字段由两个字符组成,其值基于对全部报文内容执行的纵向冗余校验(Longitudinal Redundancy Check, LRC)计算的结果而来,计算对象不包括起始的冒号(;)和回车换行符号(CRLF)。
2.RTU模式
在 RTU 模式中,报文同样包含一个错误校验字段。与 ASCII 模式不同的是,该字段由 16 个比特位共两个字节组成。其值基于对全部报文内容执行的循环冗
余校验(Cyclical Redundancy Check,CRC)计算的结果而来,计算对象包括校验域之前的所有字节。
3.4.1 LRC校验
(1)对消息帧中的全部字节相加(不包括起始";“和结束符"CR-LF”),并把结果送入 8 位数据区,舍弃进位。
(2)由 0xFF(即全 1)减去最终的数据值,产生 1的补码(即二进制反码)。
(3)加"1"产生二进制补码。
3.4.2 CRC校验
(1)预置一个值为 0xFFFF 的 16 位寄存器,此寄存器为 CRC寄存器。
(2)把第 1 个 8 位二进制数据(即通信消息帧的第 1 个字节)与 16 位的 CRC寄存器的相异或,异或的结果仍存放于该 CRC 寄存器中。
(3)把 CRC寄存器的内容右移一位,用 0 填补最高位,并检测移出位是 0 还是1。
(4)如果移出位为零,则重复步骤(3)(再次右移一位);如果移出位为 1,则CRC 寄存器与0xA001 进行异或。
(5)重复步骤(3)和(4),直到右移 8 次,这样整个 8 位数据全部进行了处理。(6)重复步骤(2)~(5),进行通信消息帧下一个字节的处理。
(7)将该通信消息帧所有字节按上述步骤计算完成后,得到的 16 位 CRC 寄存器的高、低字节进行交换。即发送时首先添加低位字节,然后添加高位字节。
(8)最后得到的 CRC 寄存器内容即为 CRC 校验码。
第四章 功能码
简而言之,Modbus 功能码占用一个字节,取值范围是 1~127 。之所以 127以上不能使用,是因为 Modbus 规定当出现异常时,功能码士0x80(十进制 128)代表异常状态,因此 129(1+128)~255(127+128)的取值代表异常码。
Modbus 标准协议中规定了有 3 类 Modbus 功能码,分别是∶
1.公共功能码
(1)被明确定义的功能码;
(2)保证唯一性;
(3)由 Modbus 协会确认,并提供公开的文档;
(4)可进行一致性测试;
(5)包括协议定义的功能码和保留将来使用的功能码。
2.用户自定义功能码
(1)有两个用户自定义功能码区域,分别是65~72 和 100~110;
(2)用户自定义,不保证唯一性。
3.保留功能码
保留功能码是因为历史遗留原因,某些公司的传统产品上现行使用的功能码不作为公共使用。
功能码可分为位操作和字操作两类。位操作的最小单位为一位(bit),字操作的最小单位为两个字节。
● 位操作指令;读线圈状态功能码 01,读(离散)输入状态功能码 02,写单个线圈功能码 06 和写多个线圈功能码 15。
●字操作指令∶读保持寄存器功能码 03,读输入寄存器功能码 04,写单个保持寄存器功能码 06,写多个保持寄存器功能码 16。
4.1 01 (0x01)读线圈应用方法
读线圈01 (0x01) |
||
请求 |
||
功能码 |
1字节 | 0x01 |
起始地址 | 2字节 | 0x0000至0xFFFF |
线圈数量 | 2字节 | 1至2000(0x7D0) |
响应 | ||
功能码 | 1字节 | 0x01 |
字节计数 | 1字节 | N |
线圈状态 | n字节 | n = N或N + 1 |
N = 寄存器数量/8,余数不等0,那么N = N + 1 | ||
错误 | ||
功能码 | 1字节 | 功能码 + 0x80 |
异常码 | 1字节 | 01或02或03或04 |
4.2 03 (0x03)读保持寄存器应用方法
请求 |
||
功能码 |
1个字节 | 0x03 |
起始地址 | 2个字节 | 0x0000 to 0xFFFF |
寄存器数量 | 2个字节 | 1 to 125 (0x7D) |
响应 | ||
功能码 |
1个字节 | 0x03 |
字节数 | 1个字节 | 2 x N* |
寄存器值 | N* x 2个字节 | |
错误 | ||
差错码 | 1个字节 | 0x83 |
异常码 | 1个字节 | 01或02或03或04 |
4.3 06 (0x06)写单个寄存器应用方法
4.4 15 (0x0F) 写多个线圈应用方法
4.5 16 (0x10) 写多个寄存器应用方法
第五章 libmobus库
libmodbus 是一个免费的跨平台的支持 RTU和 TCP 的 Modbus 库,遵循LGPLv2.1+ 协议。libmodbus 支持 Linux、Mac OS X、FreeBSD、QNX 和
Windows 等操作系统。libmodbus 可以向符合 Modbus 协议的设备发送和接收数据,并支持通过串口或者 TCP 网络进行连接。
libmodbus库与应用程序的基本关系。
第六章 libmodbus源码解析
第七章 RTU Slave开发
RTU-Slave的代码流程:
1.初始化并生成modbus_t 结构体;
2.设置从机端的ID;
3.起点调试模式;
4.建立modbus连接;
5.modbus_mapping_new()初始化寄存器,返回一个modbus_mapping_t 指针;
6.调用modbus_receive()函数判断串口的接收数据,负责接收和分析;
7.调用modbus_reply()函数,对接收到的请求指示发送响应(回复);
8.释放modbus_mapping_t 结构体;
9.关闭modbus连接;
10.释放modbus_t 结构体。
(1) 初始化
ctx = modbus_new_rtu("COM4", 9600, 'N', 8, 1); //创建一个RTU类型的容器,即创建一个COM口为C0M4 波特率9600bit/s
MODBUS_API modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
设置从机地址
/* Define the slave number */ int modbus_set_slave(modbus_t *ctx, int slave) { if (ctx == NULL) { errno = EINVAL; return -1; } return ctx->backend->set_slave(ctx, slave); } int modbus_get_slave(modbus_t *ctx) { if (ctx == NULL) { errno = EINVAL; return -1; } return ctx->slave; }
(2) 建立连接
int modbus_connect(modbus_t *ctx) { if (ctx == NULL) { errno = EINVAL; return -1; } return ctx->backend->connect(ctx); }
(3)申请内存块用作4种寄存器数据存放
mb_mapping = modbus_mapping_new(500, 500, 500, 500);
(4)循环查询和响应
for (;;) { //MODBUS_TCP_MAX_ADU_LENGTH,RTU帧格式最大数据字符数 uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH]; int rc; rc = modbus_receive(ctx, query); if (rc >= 0) { /*rc is the query size*/ modbus_reply(ctx, query, rc, mb_mapping); } else { //connection closed by the client or error printf("Connection Closed\n"); } }
(5)释放结构体,关闭连接
modbus_mapping_free(mb_mapping); modbus_close(ctx); modbus_free(ctx);
/