从应用角度学习FreeModbus
一、前言
FreeModbus本身没有什么说明文档。网上已经有了一些介绍,有些是内核分析,很有深度;有一些是应用笔记,实际应用时可能会用到。作为一个新入,我并不想了解那么多。我只想按步就班地知道到底要怎样能够很快地用起来。收集了一些资料,花了一些时间才终于基本搞清楚了一个大概。下面我从新人应用的角度来谈谈,参考甚至抄摘了前人的文档。入手主要是FreeModbus-V1.5自带的AVR平台下的DEMO项目,适当考虑FREERTOS下的特点。
二、FreeModbus简介
(本节摘抄自“百度百科”的词条“FreeModbus”)
FreeMODBUS一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另外一层是网络层。
FreeMODBUS 提供了RTU/ASCII 传输模式及TCP协议支持。FreeModbus遵循BSD许可证,这意味着用户可以将FreeModbus应用于商业环境中。目前版本FreeModbus-V1.5提供如下的功能支持:
表1 FreeModbus-V1.5功能支持
代码 Master Slave MB_RTU MB_ASCII MB_TCP 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0B 0x0C 0x0F 0x10 0x11 0x14 0x15 0x16 0x17 0x18 0x2B 0x2B/0x0D 主机 从机 RTU模式 ASCII模式 TCP模式 读线圈 读离散输入 读保持寄存器 读输入寄存器 写单个线圈 写单个寄存器 读异常状态 诊断 获取事件计数器 获取事件记录 写多个线圈 写多个寄存器 报告从机ID 读文件记录 写文件记录 屏蔽写寄存器 读/写多个寄存器 写FIFO 封装接口传输 CANopen参考请求与应答 描述 是否支持 否 是 是 是 是 是 是 是 是 是 是 否 否 否 否 是 是 是 否 否 否 是 否 否 否 协议与文档不一致 备注 注意!!!!我批注 0x2B/0x0E
读设备身份表示 否 三、准备工作
下载FreeModbus-V1.5.0.zip,解压到当前文件夹。在当前文件夹下会生成“freemodbus-v1.5.0”目录。
如果选择了解压到“freemodbus-v1.5.0\\”,则会形成两级“freemodbus-v1.5.0”,可以不管,也可用复制粘贴的方法删除一级。将此目录(或两级目录)改为“freemodbus_v1.5.0”,否则后面用AVRSTUDIO4.12编译时会出错。
下载ATMEL公司的
AVRSTUDIO4.12并安装。再下载
AVR的GCC编译器
avr-toolchain-installer-3.3.0.710-win32.win32.x86.exe并安装,它会集成到AVRSTUDIO4.12中。
关于freemodbus和STUDIO工程,后面会结合freemodbus文件目录结构进一步介绍。继续准备。 下载并安装虚拟串口VSPD。
设置串口3与4互连(后面PROTEUS里面的硬件,串口设置为3;ModScan32主机软件,串口设置为4),如图所示:
下载并安装ModScan32包,绿色版里直接运行ModScan32.exe就行了。它可以当作ModBus主机,也可以查看收发的数据,起监测作用。连接设置:菜单-->connection-->connect,出现下面界面,按图设置:
注意:串口是用的N,8,1的模式,没有校验位,原例子程序是需要有even偶校验位,后面会改原代码。因为总是模拟通不过,换这个就通过了,具体原因没有去找。
再点“rotocol selection”,如下图设置:
报文设置: 菜单-->setup-->data define,然后如下图所示:
按上述连接并设置后,结果应出现类似下面的显示界面(如有不对,需设置数据格式等):
上面是正常取得寄存器地址1000~1004的数据时的情况(从机正常返回)。现在没有PROTEUS仿真,肯定是没有正常返回的,应是下面的界面:
上面两幅图中,返回数据的寄存器是按31000的格式,因为这是3X系输入寄存器。用功能码04。从报文的角度,不用理会,仍认为是寄存器1000。下面会重点分析地址1000输入寄存器的细节。
为了监控串口4的进出数据,可以设置:菜单-->setup-->display options-->show traffic,此时显示下面界面(注意,选择用HEX显示,也是在setup菜单下进行选择):
有黑色背景的是从串口4返回的(需有从机应答),没有背景的是本程序通过串口4 发出去的。 发出去的一帧报文是0a 04 03 e7 00 04 40 c1,共8个字节。0a--device id 10,即从机地址,可以设置为1~247;04--功能码04,为读INPUT REGISTER;03 e7--从机输入寄存器起始地址,即十进字999;00 04--寄存器个数为4个字(功能码04是针对字寄存器);40 c1--CRC校验值。这里不辞辛苦将报文写出来,不是为了介绍报文,报文格式参见MODBUS标准。请关注寄存器起始地址,报文里寄存器的起始地址是999,比上述界面上的Address:1000少1。也即,这里主机的寄存器起始地址1000,可能的最小值是1,而报文的寄存器地址,可能的最小值是0。提前说一下,FreeModbus里需要我们编写的几个回读函数,其寄存器地址,也是从1开始的,报文里的地址需减1。
言归正传,接着做准备工作。现在请下载并安装PROTEUS仿真软件。我这里的版本是7.8SP2。打开ISIS画原理图,如图所示:
注意,P1串口接口元件接收发送脚与单片机串口脚的连接关系,很多人搞不明白。从串口接口来看,其接收端和发送端是从具有这个串口的设备本身的角度来看的,比如,计算机具有的串口,其RXD脚是指计算机的接收脚。在这里,P1的RXD脚就是指这个从机的接收脚了,所以其意义是与单片机的RXD是一致的,于是它们连接在一起。
虚拟终端V1,提供了一个在进行PROTEUS仿真时,可以直接与串口接口P1相连的设备。其相当于
是一个的显示和输入终端,接收和发送脚是针对它自己而言的。其RXD接收脚可以接P1的RXD,意思是监控从机接收的数据;也可以接P1的TXD,意思是监控单片机发出去的数据。如果要从V1敲入报文,将V1当作主机,也是可以的,就不需要ModScan32程序,但是会比较麻烦,此时应将V1的发送脚TXD接P1的RXD脚。这个V1终端在这里不是必须的,这里主要用来监控单片机通过串口3(在P1里设置)接收和发送的数据。注意,应在PROTEUS运行时,点菜单-->调试-->Verture Terminal V1,显示出端口界面,在界面上右键再选中“Hex Display Mode”,否则,非显示字符是显示不出来的。用AVRSTUDIO联调是一样的设置。V1的属性设置如下:
单片机的设置如下:
注意两点:单片机型号和频率应和AVRSTUDIO项目中的设置一样;“Program File”是从机程序,这里是DEMO编译输出,可以是.HEX也可以是.COF等。
P1的设置如下:
四、FreeModbus
文件结构
......
|- 主目录,注意下划线 |- 例子目录| |- 基于AT91SAM7X_ROWLEY平台例子,未展开 | |- 基于ATSAM3S平台例子,未展开 | |- 基于ATSAM3S_FREERTOS平台例子,未展开 | |- 基于AVR平台例子 | | |- AVR硬件平台下与移植有关的目录 | | | |-mbcrc.c √ 适用于AVR和GNC的CRC程序,可减少RAM。注1 | | | |-port.h √ 头文件| | | |-portevent.c √ 事件相关。注2。 | | | |-portserial.c √ 串口相关。
| | | |-porttimer.c √ 定时器相关。
| | |-demo.c √ 本文所主要分析的例程,main()所在。 | | |-excoils.c AVR下另一个例程,其main()所在。
| | |-makefile 本demo例程在GNC下的编译配置文件,用STUDIO时不用。 | | |-README.txt FREEMODBUS 0.4 AVR EXAMPLE说明文档。 | |- BARE平台例子,未展开。 | |-<......> 其它平台例子,未展开。 |- 一些文本文档,未展开。|- FreeModbus的主要目录。 | |- ascii模式有用的目录。 | | |-mbascii.c | | |-mbascii.h | |-| | |-mbfunccoils.c | | |-mbfuncdiag.c | | |-mbfuncdisk.c | | |-mbfuncholding.c | | |-mbfuncinput.c | | |-mbfuncother.c | | |-mbutils.c | |-| | |-mb.h √ | | |-mbconfig.h √ | | |-mbframe.h √ | | |-mbfunc.h √ | | |-mbport.h √ | | |-mbproto.h √
| | |-mbutils.h √
| |- rtu模式有用的目录 | | |-mbcrc.c 注1 | | |-mbcrc.h √ | | |-mbrtu.c √| | |-mbrtu.h √
| |- tcp通讯下有用的目录| | |-mbtcp.c | | |-mbtcp.h
| |-mb.c √ mb.c文件中定义了一系列的宏定义、函数指针及全局变量, | 并使用优先编译指令预编译一些程序代码。
|- |-...注1:在...\\freemodbus_v1.5.0\\demo\\avr\\port目录和...\\freemodbus_v1.5.0\\modbus\\rtu目录中都有mbcrc.c,两者并不一样,虽然都可以使用,但后者是通用的,前者是专门针对AVR单片机优化过的,可以大大减少RAM占用量。
注2:将报文接收到响应,分成若干状态,状态改变看作事件。 本文以AVR平台下的demo.c为例子来进行。
五、FreeModbus对硬件的需求
在移植之前,先要简单考察一下自己的硬件跑不跑得动。 (以下摘抄自“百度百科”的词条“FreeModbus”)
FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。
一个异步串行接口,能够支持接收缓冲区满和发送缓存区空中断。 一个能够产生RTU传输所需要的t3.5字符超时定时器的时钟。
对于软件部分,仅仅需要一个简单的事件队列。在使用操作系统的处理器上,可通过单独定义一个任
务完成Modbus时间的查询。小点的微控制器往往不允许使用操作系统,在那种情况下,可以使用一个全局变量来实现该事件队列(Atmel AVR 移植使用这种方式实现)。
实际的存储器需求决定于所使用的Modbus模块的多少。下表列出了所支持的功能编译后所需要的存储器。ARM是使用GNUARM编译器3.4.4使用-O1选项得到的。AVR项数值是使用WinAVR编译器3.4.5使用-Os选项编译得到的。
表2 FreeModbus对硬件的需求
Module Modbus RTU (Required) Modbus ASCII (Optional) Modbus Functions [1] Modbus Core (Required) Porting Layer (Required [2]) Totals ARM Code 1132Byte 1612Byte 1180Byte 924Byte 1756Byte 7304Byte ARM RAM AVR Code (static) 272Byte 28Byte 34Byte 180Byte 16Byte 530Byte 1456Byte 1222Byte 1602Byte 608Byte 704Byte 5592Byte AVR RAM (static) 266Byte 16Byte 34Byte 75Byte 7Byte 398Byte [1] 实际大小决定于可支持的Modbus功能码的多少。功能码可以在头文件mbconfig.h中进行配置。 [2] 决定于硬件。
以上摘抄自“百度百科”的词条“FreeModbus”。
AVR单片机MEGA168显然在硬件上是满足的。实际应用时,因为不可能只是一个modbus单独的实现,还要各其它的软硬件功能结合起来,所以还需考虑两点:
1、中断(主要是定时中断),与其它中断的优先级关系,以及各应用对开关中断的控制的时间点。特别是基于RTOS应用时。
2、ROM/RAM空间的需求,从AVR下的DEMO来看,用AVRSTUDIO4.12并安装GCC3.3.0.710,用-Os选项,实际所用ROM为5942byte,所用RAM为925byte(使用...\\freemodbus_v1.5.0\\modbus\\rtu目录下的mbcrc.c)。如下所示: AVR Memory Usage ----------------
Device: atmega168
Program: 5942 bytes (36.3% Full) (.text + .data + .bootloader)
Data: 925 bytes (90.3% Full) (.data + .bss + .noinit)
Build succeeded with 3 Warnings...
改成使用...\\freemodbus_v1.5.0\\demo\\avr\\port目录下的mbcrc.c(其它不变),最大变化是RAM用量大大减少,与上表基本相符,如下所示: AVR Memory Usage ----------------
Device: atmega168
Program: 5946 bytes (36.3% Full) (.text + .data + .bootloader)
Data: 413 bytes (40.3% Full) (.data + .bss + .noinit)
Build succeeded with 3 Warnings...
ROM、RAM减少可以通过配置mbconfig.h减少功能来实现,但没有特别去试验。 有一个改变可以很明显地减少RAM的占用,那就是mbrtu.c中的宏定义:
#define MB_SER_PDU_SIZE_MAX 256
其中,256可以改少一些,如设为56。它决定了串口缓冲区的大小(仍位于mbrtu.c中): volatile UCHAR ucRTUBuf[MB_SER_PDU_SIZE_MAX];
说明: 它也是采用ASCii模式时的串口缓冲区。在mbascii.c中有如下语句:、 /* We reuse the Modbus RTU buffer because only one buffer is needed and the * RTU buffer is bigger. */
extern volatile UCHAR ucRTUBuf[];
static volatile UCHAR *ucASCIIBuf = ucRTUBuf;
比如,#define MB_SER_PDU_SIZE_MAX 256=>56,则RAM减少200,只有213了。
---------------- Device: atmega168
Program: 5944 bytes (36.3% Full) (.text + .data + .bootloader)
Data: 213 bytes (20.8% Full) (.data + .bss + .noinit)
Build succeeded with 3 Warnings...
六、程序架构