chenbin 4 жил өмнө
parent
commit
cd439d4615

+ 4 - 14
README.md

@@ -1,14 +1,14 @@
 # small modbus
 
-
-
-基于了libmodbus重构,可以实现主机和从机大部分功能,可以多实例
+一个支持主机从机,modbus rtu,modbus tcp的多实例modbus库。
 
 将modbus功能和操作系统数据端口分离,分为:
 
 modbus核心(rtu、tcp),
 
-modbus端口(rtthread device、rtthread sal socket、linux devfs 、linux socket、win32 device 、win32socket)
+modbus端口(rtthread device、rtthread sal socket、linux devfs 、linux socket、win32 device 、win32socket)基于libmodbus的前后端思想,可以实现在rtthread,win32,linux多个平台运行。
+
+[modbus数据协议]: modbus.md
 
 
 
@@ -49,8 +49,6 @@ git clone 下载源码,将 src目录的中所有.c文件和 inc目录添加到
 #include "small_modbus_port_win32.h"
 #endif
 
-#define SMALL_MODBUS_CRC_BYTE_SWAP 0
-
 #endif /* __SMALL_MODBUS_PORT_H__ */
 ```
 
@@ -60,14 +58,6 @@ git clone 下载源码,将 src目录的中所有.c文件和 inc目录添加到
 
 
 
-
-
-
-
-
-
-
-
 ## 通用函数:
 
 ```c

BIN
docs/MB-TCP-Security-v21_2018-07-24.pdf


BIN
docs/PI_MBUS_300.pdf


+ 268 - 104
modbus.md

@@ -1,74 +1,147 @@
-# modbus协议
+# modbus.org的协议规范
 
-##### modbus rtu 、modbus tcp、modbus ascii区别
+**modbus协议规范: https://modbus.org/specs.php**  
 
-ModBus协议是应用层报文传输协议(OSI模型第7层),它定义了一个与通信层无关的协议数据单元(PDU),即PDU=功能码+数据域。
-ModBus协议能够应用在不同类型的总线或网络。对应不同的总线或网络,Modbus协议引入一些附加域映射成应用数据单元(ADU),即ADU=附加域+PDU。目前,Modbus有下列三种通信方式:
+**原文翻译:**
 
-modbus rtu : 数据是按byte传输,一般通过串口RS232/RS422/RS485传输、也可以通过tcp/udp等其他介质
+MODBUS 是一种应用层消息协议,位于 OSI 模型的第 7 层。它在不同类型的总线或网络上连接的设备之间提供客户端/服务器通信
 
-modbus tcp : 数据是按byte传输,一般通过tcp/udp传输
+作为事实上的工业串行标准,自 1979 年以来,MODBUS 继续支持数百万自动化设备进行通信。今天,对 MODBUS 简单而优雅的结构的支持继续增长。Internet 社区可以在 TCP/IP 堆栈上保留的系统端口 502 上访问 MODBUS
 
-modbus ascii : 数据是按十六进制字符表示,传输介质与rtu一样
+MODBUS 是一种请求/应答协议,提供由功能代码指定的服务。MODBUS 功能代码是 MODBUS 请求/回复 PDU 的元素。该协议规范文档描述了在 MODBUS 事务框架内使用的功能代码
 
+[**MODBUS 协议规范**](https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf)
 
+本文档为 Modbus 应用协议 V1.1b3。它描述了在 MODBUS 事务框架内使用的功能代码。
 
-### modbus PDU
+[**MODBUS 安全协议**](https://modbus.org/docs/MB-TCP-Security-v21_2018-07-24.pdf)
 
-| 功能码        | 数据区域 |
-| ------------- | -------- |
-| function code | data     |
+Modbus 安全协议通过将传输层安全 (TLS) 与传统 Modbus 协议相结合来提供保护。TLS 封装 Modbus 数据包以提供身份验证和消息完整性保护。新协议还利用 X.509v3 数字证书对服务器和客户端进行身份验证。新的 Modbus 安全协议使用端口 802。
 
-### 
+[**仅适用于传统应用的串行线路上的 MODBUS**](https://modbus.org/docs/PI_MBUS_300.pdf)
 
-### modbus rtu
+这是 1996 年过时的 Modbus 规范。仅用于解决遗留问题。请不要将它用于新的实现。
 
-| 从站地址 | 功能码 | 数据区               | CRC校验   |
-| -------- | ------ | -------------------- | --------- |
-| 1byte    | 1byte  | 0-252byte            | 2byte     |
-| 0x01     | 0x01   | 0x00 0x00  0x00 0x01 | 0xFD 0xCA |
-| 0x01     | 0x01   | 0x01 0x00            | 0x51 0x88 |
-| 0x01     | 0x03   | 0x00 0x00  0x00 0x01 | 0x84 0x0A |
-| 0x01     | 0x03   | 0x02 0x00  0x00      | 0xB8 0x44 |
+[**Modbus 串行线路协议和实施指南 V1.02**](https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf)请将此用于新的 MODBUS 实施。
 
 
 
-### modbus tcp
 
-| 报文头                              | 功能码 | 数据区              |
-| ----------------------------------- | ------ | ------------------- |
-| 7byte                               | 1byte  | 0-252byte           |
-| 0x00 0x00 0x00 0x00 0x00 0x06  0x01 | 0x01   | 0x00 0x00 0x00 0x01 |
-| 0x00 0x01 0x00 0x00 0x00 0x04 0x01  | 0x01   | 0x01 0x00           |
-| 0x00 0x02 0x00 0x00 0x00 0x06 0x01  | 0x02   | 0x00 0x00 0x00 0x01 |
-| 0x00 0x02 0x00 0x00 0x00 0x04 0x01  | 0x02   | 0x01 0x00           |
-| 0x00 0x03 0x00 0x00 0x00 0x06 0x01  | 0x03   | 0x00 0x00 0x00 0x01 |
-| 0x00 0x03 0x00 0x00 0x00 0x05 0x01  | 0x03   | 0x02 0x00 0x00      |
 
+# modbus rtu tcp ascii 的区别
 
+ModBus协议是应用层报文传输协议(OSI模型第7层),它定义了一个与通信层无关的
 
-### modbus ascii
+协议数据单元(PDU),即  **PDU=功能码+数据域**。
 
-| 报文头  | 从站地址      | 功能码        | 数据区    | LRC校验 | 报文尾          |
-| ------- | ------------- | ------------- | --------- | ------- | --------------- |
-| 1byte   | 2byte         | 2byte         | 0-252byte | 2byte   | 2byte           |
-| :(0x3A) | 01(0x30,0x31) | 01(0x30,0x31) | ...       |         | \n\r(0x0D,0x0A) |
 
 
+ModBus协议能够应用在不同类型的总线或网络。
+
+对应不同的总线或网络,Modbus协议引入一些附加域映射成
+
+应用数据单元(ADU),即   **ADU=附加域+PDU**。
+
+
+
+| modbus应用层      | modbus rtu             | modbus tcp                  | modbus ascii           |
+| ----------------- | ---------------------- | --------------------------- | ---------------------- |
+| modbus协议栈      | master主机 slave从机   | master主机 slave从机        | master主机 slave从机   |
+| 协议数据单元(PDU) | 功能码+数据域          | 功能码+数据域               | 功能码+数据域          |
+| 应用数据单元(ADU) | 地址码+PDU+CRC校验码   | MBAP+PDU                    | 地址码+PDU+CRC校验码   |
+|                   |                        |                             |                        |
+| 表示层            | 无                     | tcp/ip                      | 无                     |
+| 会话层            | 无                     | tcp/ip                      | 无                     |
+| 传输层            | 无                     | tcp/ip                      | 无                     |
+| 网络层            | 无                     | 三层交换机/路由器/tcp/ip    | 无                     |
+| 数据链路层        | modbus 串行协议 (串口) | 二层交换机/以太网/WLAN/网卡 | modbus 串行协议 (串口) |
+| 物理链路层        | RS232/RS485/RS422      | 中继器/集线器/双绞线        | RS232/RS485/RS422      |
+|                   |                        |                             |                        |
+
+
+
+modbus rtu 的 **地址码**:
+
+| 0        | 1 - 247            | 248-255  |
+| -------- | ------------------ | -------- |
+| 广播地址 | 从机地址可用的地址 | 保留地址 |
+|          |                    |          |
+
+
+
+modbus rtu 的 **CRC校验码**: 使用crc16 rtu算法
+
+| 名称             | 长度    | 描述           |
+| ---------------- | ------- | -------------- |
+| CRC16  LOW BYTE  | 1 Bytes | CRC16低8位字节 |
+| CRC16  HIGH BYTE | 1 Bytes | CRC16高8位字节 |
+|                  |         |                |
+
+
+
+
+modbus tcp 的 **MBAP**:  modbus应用协议头(MODBUS Application Protocol header  ),下表表示具体格式
+
+| 名称                   | 长度    | 描述            | Client(master)主机   | Server(slave)从机 |
+| ---------------------- | ------- | --------------- | ---------------------- | ------------------- |
+| Transaction Identifier | 2 Bytes | 请求的ID        | 初始化为0,每次请求加1 | 保持跟主机请求一致  |
+| Protocol Identifier    | 2 Bytes | 0表示modbus协议 | 初始化为0              | 保持跟主机请求一致  |
+| Length                 | 2 Bytes | 表示PDU部分长度 |                        | 保持跟主机请求一致  |
+| Unit Identifier        | 1 Byte  |                 |                        | 保持跟主机请求一致  |
+|                        |         |                 |                        |                     |
+
+
+
+
+
+
+# modbus 线圈和寄存器
+
+**线圈寄存器区别:**
+
+|                  | 保持线圈      | 输入线圈     | 保持寄存器        | 输入寄存器      |
+| ---------------- | ------------- | ------------ | ----------------- | --------------- |
+|                  | HOLDING_COILS | INPUTS_COILS | HOLDING_REGISTERS | INPUT_REGISTERS |
+| 可表示值范围     | 开关量 0/1    | 开关量 0/1   | 模拟量 0-65535    | 模拟量 0-65535  |
+| 读取功能码       | 0x01          | 0x02         | 0x03              | 0x04            |
+| 写功能码         | 0x05 / 0x0F   | 只读,不可写 | 0x06 / 0x10       | 只读,不可写    |
+| 单次可读最大数量 | 2000          | 2000         | 125               | 125             |
+| 单次可写最大数量 | 1 / 1968      | 0            | 1 / 123           | 0               |
+|                  |               |              |                   |                 |
+
+**保持线圈:**
+
+一个单位只有1bit,只有1,0两个状态,可读可写.一般用来表示可控制的数字量,比如继电器,灯,电源开关等等。
+
+**输入线圈:**
+
+一个单位只有1bit,只有1,0两个状态,只能读.一般用来表示输入的数字量,开关,数字量传感器等等。
+
+**保持寄存器:**
+
+一个单位只有16bit,可以表示0-65535,可读可写. 一般用来表示可控制的模拟量,DAC模拟量输出设备 等等。
+
+**输入寄存器:**
+
+一个单位只有16bit,可以表示0-65535,只能读. 一般用来表示输入的模拟量,ADC采集值,模拟量传感器的ADC值等等。
 
-### 线圈/寄存器:
 
-##### 线圈 coil :  保持线圈、输入线圈   
 
-一个单位只有1bit,只有1,0两个状态。保持线圈可读可写,输入线圈只能读。
+可多个单位的寄存器组合成一个值。比如:
 
-##### 寄存器register: 保持寄存器、输入寄存器  
+2个寄存器组合起来数据宽度是32bit可以表示一个int型的值,也可以表示一个单精度浮点型float的值。
 
- 一个单位只有16bit,可以表示0-4095。保持寄存器可读可写,输入寄存器只能读。
+4个寄存器组合起来数据宽度是64bit可以表示一个双精度浮点型double的值
 
 
 
-### 常用功能码:
+
+
+
+
+# modbus 读写流程
+
+​    **常用功能码:**
 
 | 功能码 | 名称                                      | 可操作最大数量 |
 | ------ | ----------------------------------------- | -------------- |
@@ -85,121 +158,186 @@ modbus ascii : 数据是按十六进制字符表示,传输介质与rtu一样
 
 
 
-# modbus流程
 
-​    
 
-##### 主机(master)查询 poll
 
-(主机) ---查询--->  (从机) ---应答--->  (主机)
 
-   
+### master请求  读线圈 0x01 0x02
+
+| 名称     | 长度    | 描述         |
+| -------- | ------- | ------------ |
+| 功能码   | 1 Bytes | 0x01 或 0x02 |
+| 线圈地址 | 2 Bytes | 高字节在前   |
+| 线圈个数 | 2 Bytes | 高字节在前   |
+
+### slave应答  读线圈 0x01 0x02
+
+| 名称     | 长度    | 描述                                                  |
+| -------- | ------- | ----------------------------------------------------- |
+| 功能码   | 1 Bytes | 0x01 或 0x02                                          |
+| 数据长度 | 1 Bytes | 数据值等于 =  (线圈个数/ 8) + ((线圈个数% 8) ? 1 : 0) |
+| 线圈数据 | n Bytes | 1个字节表示8个线圈状态                                |
+
+### 
+
+
+
+
+
+
+
+### master请求  读寄存器 0x03 0x04
+
+| 名称       | 长度    | 描述         |
+| ---------- | ------- | ------------ |
+| 功能码     | 1 Bytes | 0x03 或 0x04 |
+| 寄存器地址 | 2 Bytes | 高字节在前   |
+| 寄存器个数 | 2 Bytes | 高字节在前   |
+
+### slave应答  读寄存器 0x03 0x04
+
+| 名称       | 长度    | 描述                                 |
+| ---------- | ------- | ------------------------------------ |
+| 功能码     | 1 Bytes | 0x03 或 0x04                         |
+| 数据长度   | 1 Bytes | 数据值等于 =  (寄存器个数×2)         |
+| 寄存器数据 | n Bytes | 高字节在前,2个字节表示1个寄存器状态 |
+
+### 
+
+
+
+
+
+### master请求  写单个线圈 0x05
+
+| 名称     | 长度    | 描述                   |
+| -------- | ------- | ---------------------- |
+| 功能码   | 1 Bytes | 0x05                   |
+| 线圈地址 | 2 Bytes | 高字节在前             |
+| 线圈状态 | 2 Bytes | 0x0000表示0,其他表示1 |
 
-##### 从机(slave)等待查询 wait 
+### slave应答  写单个线圈 0x05
 
-(从机) ---等待主机查询--->  处理数据  ---应答--->  (主机)
+| 名称     | 长度    | 描述                       |
+| -------- | ------- | -------------------------- |
+| 功能码   | 1 Bytes | 0x05                       |
+| 线圈地址 | 2 Bytes | 高字节在前,跟请求保持一致 |
+| 线圈状态 | 2 Bytes | 高字节在前,跟请求保持一致 |
 
-​    
+### 
 
-查询: 读线圈、读寄存器、写线圈、写寄存器、读从机地址、读错误码
 
-应答: 正常应答、错误应答
 
-   
 
 
+### master请求  写多个线圈 0x0F
 
-### 0x01、0x02 读线圈 PDU格式
+| 名称     | 长度    | 描述                                                  |
+| -------- | ------- | ----------------------------------------------------- |
+| 功能码   | 1 Bytes | 0x0F                                                  |
+| 线圈地址 | 2 Bytes | 高字节在前                                            |
+| 线圈个数 | 2 Bytes | 高字节在前                                            |
+| 数据长度 | 1 Bytes | 数据值等于 =  (线圈个数/ 8) + ((线圈个数% 8) ? 1 : 0) |
+| 线圈数据 | n Bytes | 1个字节表示8个线圈状态                                |
 
-| 功能码 | 起始线圈地址     | 线圈个数         |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
+### slave应答  写多个线圈 0x0F
 
-##### 应答 PDU格式
+| 名称     | 长度    | 描述                       |
+| -------- | ------- | -------------------------- |
+| 功能码   | 1 Bytes | 0x0F                       |
+| 线圈地址 | 2 Bytes | 高字节在前,跟请求保持一致 |
+| 线圈个数 | 2 Bytes | 高字节在前,跟请求保持一致 |
 
-| 功能码 | 数据长度 | 数据(1bit表示一个线圈) |
-| ------ | -------- | ------------------------ |
-| 1byte  | 1byte    | 数据长度                 |
+### 
 
-   
 
 
 
-### 0x03、0x04 读寄存器 PDU格式
 
-| 功能码 | 起始寄存器地址   | 寄存器个数       |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
 
-##### 应答 PDU格式
 
-| 功能码 | 数据长度 | 数据(16bit表示一个寄存器) |
-| ------ | -------- | --------------------------- |
-| 1byte  | 1byte    | 数据长度                    |
+### master请求  写单个寄存器 0x06
 
-   
+| 名称       | 长度    | 描述       |
+| ---------- | ------- | ---------- |
+| 功能码     | 1 Bytes | 0x06       |
+| 寄存器地址 | 2 Bytes | 高字节在前 |
+| 寄存器状态 | 2 Bytes | 高字节在前 |
 
+### slave应答  写单个寄存器 0x06
 
+| 名称       | 长度    | 描述                       |
+| ---------- | ------- | -------------------------- |
+| 功能码     | 1 Bytes | 0x06                       |
+| 寄存器地址 | 2 Bytes | 高字节在前,跟请求保持一致 |
+| 寄存器状态 | 2 Bytes | 高字节在前,跟请求保持一致 |
 
-### 0x05写单个线圈 PDU格式
+### 
 
-| 功能码 | 起始线圈地址     | 状态数据             |
-| ------ | ---------------- | -------------------- |
-| 1byte  | 2byte 高字节在前 | 2byte( 高字节有效) |
 
-##### 应答 PDU格式
 
-| 功能码 | 起始线圈地址     | 状态数据              |
-| ------ | ---------------- | --------------------- |
-| 1byte  | 2byte 高字节在前 | 2byte ( 高字节有效) |
 
 
+### master请求  写多个寄存器 0x10
 
-### 0x0F 写多个线圈 PDU格式
+| 名称       | 长度    | 描述                                 |
+| ---------- | ------- | ------------------------------------ |
+| 功能码     | 1 Bytes | 0x10                                 |
+| 寄存器地址 | 2 Bytes | 高字节在前                           |
+| 寄存器个数 | 2 Bytes | 高字节在前                           |
+| 数据长度   | 1 Bytes | 数据值等于 =  (寄存器个数X2)         |
+| 寄存器数据 | n Bytes | 高字节在前,2个字节表示1个寄存器状态 |
 
-| 功能码 | 起始线圈地址     | 线圈个数         | 数据长度 | 数据(一个线圈占1bit) |
-| ------ | ---------------- | ---------------- | -------- | -------------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 | 1byte    | 数据长度             |
+### slave应答  写多个线圈 0x0F
 
-##### 应答 PDU格式
+| 名称       | 长度    | 描述                       |
+| ---------- | ------- | -------------------------- |
+| 功能码     | 1 Bytes | 0x10                       |
+| 寄存器地址 | 2 Bytes | 高字节在前,跟请求保持一致 |
+| 寄存器个数 | 2 Bytes | 高字节在前,跟请求保持一致 |
 
-| 功能码 | 起始线圈地址     | 线圈个数         |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
+### 
 
 
 
-   
 
-### 0x06 写单个寄存器 PDU格式
 
-| 功能码 | 起始寄存器地址   | 数据             |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
 
-##### 应答 PDU格式
 
-| 功能码 | 起始寄存器地址   | 数据             |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
+### modbus rtu格式样例
 
+| 从站地址 | 功能码 | 数据区               | CRC校验   |
+| -------- | ------ | -------------------- | --------- |
+| 1byte    | 1byte  | 0-252byte            | 2byte     |
+| 0x01     | 0x01   | 0x00 0x00  0x00 0x01 | 0xFD 0xCA |
+| 0x01     | 0x01   | 0x01 0x00            | 0x51 0x88 |
+| 0x01     | 0x03   | 0x00 0x00  0x00 0x01 | 0x84 0x0A |
+| 0x01     | 0x03   | 0x02 0x00  0x00      | 0xB8 0x44 |
 
 
-### 0x10 写多个寄存器 PDU格式
 
-| 功能码 | 起始寄存器地址   | 寄存器个数       | 数据长度 | 数据(一个寄存器占16bit) |
-| ------ | ---------------- | ---------------- | -------- | ----------------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 | 1byte    | 数据长度                |
+### modbus tcp格式样例
 
-##### 应答 PDU格式
+| 报文头                              | 功能码 | 数据区              |
+| ----------------------------------- | ------ | ------------------- |
+| 7byte                               | 1byte  | 0-252byte           |
+| 0x00 0x00 0x00 0x00 0x00 0x06  0x01 | 0x01   | 0x00 0x00 0x00 0x01 |
+| 0x00 0x01 0x00 0x00 0x00 0x04 0x01  | 0x01   | 0x01 0x00           |
+| 0x00 0x02 0x00 0x00 0x00 0x06 0x01  | 0x02   | 0x00 0x00 0x00 0x01 |
+| 0x00 0x02 0x00 0x00 0x00 0x04 0x01  | 0x02   | 0x01 0x00           |
+| 0x00 0x03 0x00 0x00 0x00 0x06 0x01  | 0x03   | 0x00 0x00 0x00 0x01 |
+| 0x00 0x03 0x00 0x00 0x00 0x05 0x01  | 0x03   | 0x02 0x00 0x00      |
 
-| 功能码 | 起始寄存器地址   | 寄存器个数       |
-| ------ | ---------------- | ---------------- |
-| 1byte  | 2byte 高字节在前 | 2byte 高字节在前 |
 
 
+### modbus ascii格式样例
+
+| 报文头  | 从站地址      | 功能码        | 数据区    | LRC校验 | 报文尾          |
+| ------- | ------------- | ------------- | --------- | ------- | --------------- |
+| 1byte   | 2byte         | 2byte         | 0-252byte | 2byte   | 2byte           |
+| :(0x3A) | 01(0x30,0x31) | 01(0x30,0x31) | ...       |         | \n\r(0x0D,0x0A) |
+
 
-   
 
 
 
@@ -207,7 +345,33 @@ modbus ascii : 数据是按十六进制字符表示,传输介质与rtu一样
 
 
 
+# CRC16 A001 算法
 
+```c
+uint16_t modbus_crc16(uint8_t *buffer, uint16_t buffer_length)
+{
+	uint16_t CRC= 0XFFFF;
+	uint16_t CRC_count = 0;
+	uint16_t i = 0;
+	for(CRC_count=0;CRC_count< buffer_length ; CRC_count++)
+	{
+		CRC= CRC^ *(buffer+CRC_count);
+		for(i=0;i<8;i++)
+		{
+			if(CRC&1)
+			{
+				CRC>>=1;
+				CRC^=0xA001;
+			}
+			else
+			{
+				CRC>>=1;
+			}
+		}
+	}
+	return CRC;
+}
+```