转自:http://blog.sina.com.cn/s/blog_5d4d58a90100rkxf.html
基于串口的Modbus软件开发
----------------------八股--------------------
1、已经有好多个测试软件了,但是没有开源。如modbusscan 7.0(ModScan32.exe),Modbus Poll version, 4.3.1, Modbustester.exe,ModLink,ModbusSimulator
2、协议比较老了,但是很管用。
----------------------八股----qin yufei blog---------
----------------------N步走----qin yufei blog--------
第一步:图书馆借N本VC串口编程的书
第二步:上网下N个源代码
第三步:上网下个虚拟串口的程序。(http://www.sudt.com/cn/sn/download.htm)
第四步:学习Modbus协议
主从方式,主站初始化传输。从站根据主设备查询提供的数据作出反应。
主站查询格式:站(或广播)地址、功能代码、要发送的数据、错误检测域。
从站回应格式:确认要行动的域、返回的数据、错误检测域。如果在消息接收过程中发生错误,或从站不能执行其命令,从站将建立错误消息并把它作为回应发送出去。
从站地址是0...247,0为广播地址
ASCII模式
: | 地址 | 功能代码 | 数据数量 | 数据1 | ... | 数据n | LRC高字节 | LRC低字节 | 回车 | 换行 |
代码系统:
十六进制,ASCII字符0...9,A...F
消息中的每个ASCII字符都是一个十六进制字符组成
每个字节的位:
1个起始位
7个数据位,最小的有效位先发送
1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)
错误检测域
LRC(纵向冗长检测)
使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。
其它域可以使用的传输字符是十六进制的0...9,A...F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。
消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。
RTU模式
地址 | 功能代码 | 数据数量 | 数据1 | ... | 数据n | CRC低字节 | CRC高字节 | | |
代码系统:
8位二进制,十六进制数0...9,A...F
消息中的每个8位域都是一个两个十六进制字符组成
每个字节的位:
1个起始位
8个数据位,最小的有效位先发送
1个奇偶校验位,无校验则无
1个停止位(有校验时),2个Bit(无校验时)
错误检测域
CRC(循环冗长检测)
深入理解ModBus功能码
Modbus主要功能码
功能码 | 名称 | 注释 | |
01 | Read Coil Status | (线圈状态0x)(读PLC的开出状态) | bit 读位 |
02 | Read Input Status | (输入状态1x) (读PLC的开入状态) | bit 读位,只读DI |
03 | Read Holding Register | (保持寄存器4x HR) (读模出状态) | 读整形、状态字、浮点型、字符型,与16对应 |
04 | Read Input Register | (输入寄存器3x AR) (读PLC模入状态) | 读整形、状态字、浮点型 只读AI |
05 | Write Single Coil | (强制单路开出,给PLC写数据) | 写单个位 |
06 | Write Single Register | (强制单路模出,给PLC写数据) | 写单个整形、状态字、浮点型、字符型,写HR4x的地址区 |
15 | Write Multiple Coil | (强制多路开出,给PLC写数据) | 写多个位 |
16(0x10H) | Write Multiple Register | (强制多路模出,给PLC写数据) | 写多个整形、状态字、浮点型、字符型 |
15和16可能是用于一次写一串数据的,不允许单个写。如时间的世纪、年、月、日、时、分、秒要一次写下去
设备和Modbus 地址范围对应表 | ||||
设备地址 | Modbus地址 | 描述 | 功能 | R/W |
1...10000* | address - 1 | Coils (outputs) | 0 | Read/Write |
10001...20000* | address - 10001 | Discrete Inputs | 01 | Read |
40001...50000* | address - 40001 | Holding Registers | 03 | Read/Write |
30001...40000* | address - 30001 | Input Registers | 04 | Read |
*最大值与设备相关
注:设备地址是从1开始的,所以Modbus写入地址要在设备地址上减1。
协议格式
功能码:01 (线圈状态0x)(读开出状态)
从站地址 | 功能码 | H 地址 | L 地址 | H Coils | L Coils | CRC | |
04 | 01 | 00 | 0A | 00 | 0D | DD | 98 |
主站
从站地址 | 功能码 | 字节数 | Coils 7..10 | Coils 27..20 | CRC | |
| 01 | 02 | 0A | 11 | 50 | B3 |
从站
功能码:02 (输入状态1x) (读开入状态)
从站地址 | 功能码 | H 地址 | L 地址 | H Input | L Input | CRC | |
04 | 02 | 00 | 0A | 00 | 0D | 99 | 98 |
主站
从站地址 | 功能码 | 字节数 | Input 7..10 | Input 27..20 | CRC | |
| 02 | 02 | 0A | 11 | 14 | B3 |
从站
功能码:03 (保持寄存器4x HR) (读模出状态)
从站地址 | 功能码 | H 地址 | L 地址 | H 数据 | L 数据 | CRC | |
01 | 03 | 00 | 00 | 00 | 02 | C4 | 0B |
主站
从站地址 | 功能码 | 字节数 | H数据 | L数据 | H数据 | L数据 | CRC | |
| 03 | 04 | 00 | 06 | 00 | 05 | DA | 31 |
从站
功能码:04 (输入寄存器3x AR) (读模入状态)
从站地址 | 功能码 | H 地址 | L 地址 | H 数据 | L 数据 | CRC | |
01 | 04 | 00 | 00 | 00 | 02 | 71 | CB |
主站
从站地址 | 功能码 | 字节数 | H数据 | L数据 | H数据 | L数据 | CRC | |
| 04 | 04 | 00 | 06 | 00 | 05 | DB | 86 |
从站
功能码:05 强制单路开出,给PLC写数据
从站地址 | 功能码 | H 地址 | L 地址 | H 数据 | L 数据 | CRC | |
11 | 05 | 00 | AC | FF | 00 | 4E | 8B |
主站
注:写0xFF00表示ON,写0x0000表示OFF
从站地址 | 功能码 | H 地址 | L 地址 | H数据 | L数据 | CRC | |
| 05 | 00 | AC | FF | 00 | 4E | 8B |
从站
注:返回帧与主站相同
功能码:06 强制单路模出,给PLC写数据
从站地址 | 功能码 | H 地址 | L 地址 | H 数据 | L 数据 | CRC | |
11 | 06 | 00 | 01 | 00 | 03 | 9A | 9B |
主站
从站地址 | 功能码 | H 地址 | L 地址 | H数据 | L数据 | CRC | |
| 06 | 00 | 01 | 00 | 03 | 9A | 9B |
从站
注:返回帧与主站相同
功能码:15 强制多路开出,给PLC写数据
帧中位 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
位置 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
本例位置 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | - | - | - | - | - | - | 29 | 28 |
从站地址 | 功能码 | H 地址 | L 地址 | H线圈数量 | L线圈数量 | 字节数 | H数据 | L数据 | CRC | |
11 | 0F | 00 | 13 | 00 | 0A | 02 | CD | 01 | BF | 0B |
主站
从站地址 | 功能码 | H 地址 | L 地址 | H线圈数量 | L线圈数量 | CRC | |
| 05 | 00 | 13 | 00 | 0A | 4E | 8B |
从站
注:变量写的从站反馈没有数据
功能码:16 强制多路模出到保持寄存器,给PLC写数据
例:给17站从40002HR开始的2个寄存器,数据为000A和0102,MB地址:40002-1=40001=MB1
从站地址 | 功能码 | H地址 | L地址 | H模入数量 | L模入数量 | 字节数 | H | L | H | L | CRC | |
11 | 10 | 00 | 01 | 00 | 02 | 04 | 00 | 0A | 01 | 02 | C6 | F0 |
主站
从站地址 | 功能码 | H 地址 | L 地址 | H线圈数量 | L线圈数量 | CRC | |
| 10 | 00 | 01 | 00 | 02 | 12 | 98 |
从站
注:变量写的从站反馈没有数据
秦批:Modbus的从站反馈数据没有数据帧的编号(不像TCP协议那样),所以不会显示这个反馈数据是主站的哪次请求的。若主站一次发送N个数据,从站而没有及时反馈,这样就乱了,主站就不知道从站发过来的数据是哪次命令的。
单变量与多变量:读可以读单读多,命令不分读几个。但是写要区分写一个还是写多个,所以写命令有写单线圈和写多线圈。
Modbus所有功能码
功能码 | 名称 | 作用 |
01 | 读取线圈状态 | 取得一组逻辑线圈的当前状态(ON/OFF) |
02 | 读取输入状态 | 取得一组开关输入的当前状态(ON/OFF) |
03 | 读取保持寄存器 | 在一个或多个保持寄存器中取得当前的二进制值 |
04 | 读取输入寄存器 | 在一个或多个输入寄存器中取得当前的二进制值 |
05 | 强置单线圈 | 强置一个逻辑线圈的通断状态 |
06 | 预置单寄存器 | 把具体二进值装入一个保持寄存器 |
07 | 读取异常状态 | 取得8个内部线圈的通断状态,这8个线圈的地址由控制器决定,用户逻辑可以将这些线圈定义,以说明从机状态,短报文适宜于迅速读取状态 |
08 | 回送诊断校验 | 把诊断校验报文送从机,以对通信处理进行评鉴 |
09 | 编程(只用于484) | 使主机模拟编程器作用,修改PC从机逻辑 |
10 | 控询(只用于484) | 可使主机与一台正在执行长程序任务从机通信,探询该从机是否已完成其操作任务,仅在含有功能码9的报文发送后,本功能码才发送 |
11 | 读取事件计数 | 可使主机发出单询问,并随即判定操作是否成功,尤其是该命令或其他应答产生通信错误时 |
12 | 读取通信事件记录 | 可是主机检索每台从机的ModBus事务处理通信事件记录。如果某项事务处理完成,记录会给出有关错误 |
13 | 编程(184/384 484 584) | 可使主机模拟编程器功能修改PC从机逻辑 |
14 | 探询(184/384 484 584) | 可使主机与正在执行任务的从机通信,定期控询该从机是否已完成其程序操作,仅在含有功能13的报文发送后,本功能码才得发送 |
15 | 强置多线圈 | 强置一串连续逻辑线圈的通断 |
16 | 预置多寄存器 | 把具体的二进制值装入一串连续的保持寄存器 |
17 | 报告从机标识 | 可使主机判断编址从机的类型及该从机运行指示灯的状态 |
18 | (884和MICRO 84) | 可使主机模拟编程功能,修改PC状态逻辑 |
19 | 重置通信链路 | 发生非可修改错误后,是从机复位于已知状态,可重置顺序字节 |
20 | 读取通用参数(584L) | 显示扩展存储器文件中的数据信息 |
21 | 写入通用参数(584L) | 把通用参数写入扩展存储文件,或修改之 |
22~64 | 保留作扩展功能备用 | |
65~72 | 保留以备用户功能所用 | 留作用户功能的扩展编码 |
73~119 | 非法功能 | |
120~127 | 保留 | 留作内部作用 |
128~255 | 保留 | 用于异常应答 |
ModBus功能码与数据类型对应表
代码 | 功能 | 数据类型 |
01 | 读 | 位 |
02 | 读 | 位 |
03 | 读 | 整型、字符型、状态字、浮点型 |
04 | 读 | 整型、状态字、浮点型 |
05 | 写 | 位 |
06 | 写 | 整型、字符型、状态字、浮点型 |
08 | N/A | 重复“回路反馈”信息 |
15 | 写 | 位 |
16 | 写 | 整型、字符型、状态字、浮点型 |
17 | 读 | 字符型 |
Modbus变量地址
映射地址 | 功能 | 地址类型 | 存取方式 | 描述 |
0xxxx | 01,05,15 | Coil | 读写 | |
1xxxx | 02 | 离散输入 | 只读 | |
2xxxx | 03,04,06,16 | 浮点寄存器 | 读写 | 两个连续16位寄存器表示一个浮点数(ieee754格式32位) |
3xxxx | 04 | 输入寄存器 | 只读 | 每个寄存器表示一个16位无符号整数(0~65535)同上 |
4xxxx | 03,06,16 | 保持寄存器 | 读写 | |
5xxxx | 03,04,06,16 | ascii字符 | 读写 | 每个寄存器表示两个ascii字符 |
FAQ0:ABB组态软件有功能块读线圈-1,读线圈-8,读寄存器-1,读寄存器-8的原因是什么?
因为Modbus功能码的读操作可以指定数据长度。如果从站的地址连续,则可以用一次读八个变量的操作。如果不连续,可以一次读一个。读八个的效率要高一些。
FAQ1 :MB地址的问题:MB经常用30001,40001这样的地址,WHY?给个理由。
ANS1 :地址以3开头,如IFIX的mb1中地址用30001、3001、300001表示的是功能码4的操作,即读输入寄存器。同理, 地址以4开头表示的是功能码3的操作,即对指的是对输出寄存器/内部寄存器进行的操作。虽然Modbus有两个字节表示地址0xFFFF,共64K,但是一般用不了那么多的。
例:高安屯垃圾电厂DCS Modbus通讯点表
DCS-TAG_№ | DESCRIPTION | Type | Data | Addr. |
XA 0FG10-1 | ACTIVATED CARBON STORAGE SILO VENTING FILTER FAN FAULT | DI | BOOL | 00407 |
HC #RC10-O | No.# INLET DAMPER FOR NID SYSTEM A IN SERVICE COMMAND | DO | BOOL | 10001 |
ATO #FG03-S | No.# SET POINT SO2 EMISSION | AO | INT | 30002 |
AI #RC01 | No.# REACTOR INLET GAS SO2 ANALYZER | AI | INT | 40001 |
注:地址以2开头表示浮点数,以0,1,3,4开头表示整数。
FAQ2 : Modbus功能码的名称来源是什么?
ANS2: 因为Modbus主要用于与PLC通讯,所以Modbus的地址空间命名也服从PLC的方式。我一起在弄DCS,没玩过PLC。所以没明白这个道理之前,根本搞不明白Modbus的功能码要那么叫,经过两天多的研究研究研究,终于大彻大悟了。
功能码 | 名称 | 注释 | |
01 | Read Coil Status | 读PLC的开出状态,可能是内部量 | 可读可写 |
02 | Read Input Status | 读PLC的开入DI点状态 | 只读DI,通道来 |
03 | Read Holding Register | 读PLC内部模拟量 | |
04 | Read Input Register | 读PLC的模入AI状态 | 只读AI,通道来 |
05 | Write Single Coil | 给PLC写开关量数据 | 写单个位 |
06 | Write Single Register | 给PLC写模拟量数据 | 写模拟量 |
15 | Write Multiple Coil | 给PLC写多个开关量数据 | 写多个位 |
16(0x10H) | Write Multiple Register | 给PLC写多个模拟量数据 | 写多个模拟量 |
列出PLC的线圈与寄存器的分配如下,以三菱FX2n介绍:
一般 | 500点 | M0到M499 | |
锁定 | 2572点 | M384至M3071 | |
特殊 | 256点 | M8000至8255 | |
状态继电器 | 一般 | 490点 | S0至S499 |
锁定 | 400点 | S500至S899 | |
初始 | 10点 | S0至S9 | |
信号报警器 | 100点 | S900至S999 | |
数据寄存器(D) | 一般 | 200点 | D0至D199 |
锁定 | 7800点 | D200至D7999 | |
文件寄存器 | 7000点 | D1000至D7999通过14块500程式步的参数设置类型:16位数据存储寄存器 | |
特殊 | 256点 | 从D8000至D8255 | |
变址 | 16点 | V0至V7和Z0至Z7 |
松下FP1—C40寄存器I/O配置表
名称 | 符号 | 编号(地址) | 功能说明 | |
外部 输入/ 输出 继电 器 | X(位) | X0~X12F (主机X0~X17) | 输入继电器 总点数208点,主机24点,用来存储外部输入信号 | |
WX(字) | WX0~WX12(13个字) | |||
Y(位) | Y0~Y12F(主机Y0~YF) | 输出继电器 总点数208点,主机16点,用来存储程序运行结果并输出 | ||
WY(字) | WY0~WY12(13个字) | |||
内 部 继 电 器 | R(位) | R0~R62F | 通用内部继电器 只能在PLC内部供用户编程使用,不能用于输出 | |
WR(字) | WR0~WR62 | |||
R(位) | R9000~R903F | 特殊内部继电器 每个继电器均具有特殊用途,用 户只能使用其接点,不能用程序 控制其状态,不能用于输出 | ||
数据 寄存器 | DT(字) | DT0~DT1659(1,660字) | 通用数据寄存器 用来存储PLC内处理的数据 | |
DT9000~DT9069(70字) | 特殊数据寄存器 具有特殊用途的数据寄存器,不能存储用户数据 |