1 / 13
文档名称:

FreeModbus学习笔记.pdf

格式:pdf   大小:459KB   页数:13页
下载后只包含 1 个 PDF 格式的文档,没有任何的图纸或源代码,查看文件列表

如果您已付费下载过本站文档,您可以点这里二次下载

分享

预览

FreeModbus学习笔记.pdf

上传人:梅花书斋 2023/2/20 文件大小:459 KB

下载得到文件列表

FreeModbus学习笔记.pdf

文档介绍

文档介绍:该【FreeModbus学习笔记 】是由【梅花书斋】上传分享,文档一共【13】页,该文档可以免费在线阅读,需要了解更多关于【FreeModbus学习笔记 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。:.
学****必备欢迎下载
FreeModbus学****笔记
一、FreeModbus简介
FreeMODBUS一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费
(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协
议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另
外一层是网络层。
FreeMODBUS提供了RTU/ASCII传输模式及TCP协议支持。FreeModbus遵循BSD
许可证,这意味着用户可以将FreeModbus应用于商业环境中。目前版本FreeModbus-
提供如下的功能支持:
表1FreeModbus-
代码描述是否支持备注
Master主机否
Slave从机是
MB_RTURTU模式是
MB_ASCIIASCII模式是
MB_TCPTCP模式是
0x01读线圈是
0x02读离散输入是
0x03读保持寄存器是
0x04读输入寄存器是
0x05写单个线圈是
0x06写单个寄存器是
0x07读异常状态否
0x08诊断否
0x0B获取事件计数器否
0x0C获取事件记录否
0x0F写多个线圈是
0x10写多个寄存器是
0x11报告从机ID是协议与文档不一致
0x14读文件记录否
0x15写文件记录否
0x16屏蔽写寄存器否
0x17读/写多个寄存器是
0x18写FIFO否
0x2B封装接口传输否
0x2B/0x0DCANopen参考请求与应答否
0x2B/0x0E读设备身份表示否
二、FreeModbus对硬件的需求:.
学****必备欢迎下载
FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够
容纳modbus数据帧的RAM的微控制器都足够了。
一个异步串行接口,能够支持接收缓冲区满和发送缓存区空中断。
。
对于软件部分,仅仅需要一个简单的事件队列。在使用操作系统的处理器上,可通过单
独定义一个任务完成Modbus时间的查询。小点的微控制器往往不允许使用操作系统,在那
种情况下,可以使用一个全局变量来实现该事件队列(AtmelAVR移植使用这种方式实现)。
实际的存储器需求决定于所使用的Modbus模块的多少。下表列出了所支持的功能编译
后所需要的存储器。-O1选项得到的。AVR项数值
-Os选项编译得到的。
表2FreeModbus对硬件的需求
ARMRAMAVRRAM
ModuleARMCodeAVRCode
(static)(static)
ModbusRTU(Required)1132Byte272Byte1456Byte266Byte
ModbusASCII(Optional)1612Byte28Byte1222Byte16Byte
ModbusFunctions[1]1180Byte34Byte1602Byte34Byte
ModbusCore(Required)924Byte180Byte608Byte75Byte
PortingLayer(Required[2])1756Byte16Byte704Byte7Byte
Totals7304Byte530Byte5592Byte398Byte
[1]实际大小决定于可支持的Modbus功能码的多少。
行配置。
[2]决定于硬件。
三、FreeModbus的移植
1、物理层接口文件的修改
在物理层,用户只需完成串行口及超时定时器的配置即可。具体应修改接口文件

:
1)voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable)
此函数的功能为设置串口状态。有两个参数:xRxEnable及xTxEnable。当xRxEnable
为真时,应使能串口接收及接收中断。在RS485通讯系统中,还要注意将RS485接口芯片
设为接收使能状态;当xTxEnable为真时,应使能串口发送及发送中断。在RS485通讯系
统中,还要注意将RS485接口芯片设为发送使能状态。
2)voidvMBPortClose(void)
此函数的功能是关闭Modbus通讯端口,具体的,应在此函数中关闭通讯端口的发送使
能及接收使能。
3)BOOLxMBPortSerialInit(UCHARucPORT,ULONGulBaudRate,UCHARucDataBits,
eMBParityeParity)
此函数的功能是初始化串行通讯端口。有四个参数:ucPORT、ulBaudRate、ucDataBits
及eParity。参数ucPORT可以忽略;参数ulBaudRate是通讯端口的波特率,应根据此数值
设置所使用硬件端口的波特率;参数ucDataBits为通讯时所使用的数据位宽,注意,若使用
RTU模式,则有ucDataBits=8,若使用ASCII模式,则有ucDataBits=7,应根据此参数设置
所使用硬件端口的数据位宽;eParity为校验方式,eParity=MB_PAR_NONE为无校验,此时:.
学****必备欢迎下载
硬件端口应设置为无校验方式及两个停止位,eParity=MB_PAR_ODD为奇校验,此时硬件
端口应设置为奇校验方式及一个停止位,eParity=MB_PAR_EVEN为偶校验,此时硬件端口
应设置为偶校验方式及一个停止位。函数返回值务必为TRUE。
4)BOOLxMBPortSerialPutByte(CHARucByte)
此函数的功能为通讯端口发送一字节数据。参数为:ucByte,待发送的数据。应在此函
数中编写发送一字节数据的函数。注意,由于使用的是中断发送,故只需将数据放到发送寄
存器即可。函数返回值务必为TRUE。
5)BOOLxMBPortSerialGetByte(CHAR*pucByte)
此函数的功能为通讯端口接收一字节数据。参数为:*pucByte,接收到的数据。应在
此函数中编写接收的函数。注意,由于使用的是中断接收,故只需将接收寄存器的值放到*
pucByte即可。函数返回值务必为TRUE。
6)voidprvvUARTTxReadyISR(void)
发送中断函数。此函数无需修改。只需在用户的发送中断函数中调用此函数即可,同时,
用户应在调用此函数后,清除发送中断标志位。
7)voidprvvUARTRxISR(void)
发送中断函数。此函数无需修改。只需在用户的接收中断函数中调用此函数即可,同时,
用户应在调用此函数后,清除接收中断标志位。
:
1)BOOLxMBPortTimersInit(USHORTusTim1Timerout50us)
此函数的功能为初始化超时定时器。参数为:usTim1Timerout50us,50us的个数。用户
应根据所使用的硬件初始化超时定时器,使之能产生中断时间为usTim1Timerout50us*50us
的中断。函数返回值务必为TRUE。
2)voidvMBPortTimersEnable()
此函数的功能为使能超时定时器。用户需在此函数中清除中断标志位、清零定时器计数
值,并重新使能定时器中断。
3)voidvMBPortTimersDisable()
此函数的功能为关闭超时定时器。用户需在此函数中清零定时器计数值,并关闭定时器
中断。
4)voidTIMERExpiredISR(void)
定时器中断函数。此函数无需修改。只需在用户的定时器中断中调用此函数即可,同时,
用户应在调用此函数后清除中断标志位。
2、应用层回函数的修改
在应用层,用户需要定义所需要使用的寄存器,并修改对应的回函数。回函数有如下几
个:
1)eMBErrorCodeeMBRegInputCB(UCHAR*pucRegBuffer,USHORTusAddress,USHORT
usNRegs)
输入寄存器回函数。*pucRegBuffer为要添加到协议中的数据,usAddress为输入寄存
器地址,usNRegs为要读取寄存器的个数。用户应根据要访问的寄存器地址usAddress将相
应输入寄存器的值按顺序添加到pucRegBuffer中。
2)eMBErrorCodeeMBRegHoldingCB(UCHAR*pucRegBuffer,USHORTusAddress,
USHORTusNRegs,eMBRegisterModeeMode)
保持寄存器回函数。*pucRegBuffer为要协议中的数据,usAddress为输入寄存器地址,
usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,
MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输
入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址
usAddress放到相应保持寄存器中。
3)eMBErrorCodeeMBRegCoilsCB(UCHAR*pucRegBuffer,USHORTusAddress,USHORT:.
学****必备欢迎下载
usNCoils,eMBRegisterModeeMode)
读写线圈回函数。*pucRegBuffer为要添加到协议中的数据,usAddress为线圈地址,
usNCoils为要访问线圈的个数,eMode为访问类型(MB_REG_READ为读线圈状态,
MB_REG_WRITE为写线圈)。用户应根据要访问的线圈地址usAddress将相应线圈的值按
顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的线圈地址usAddress放到相应
线圈中。
4)eMBErrorCodeeMBRegDiscreteCB(UCHAR*pucRegBuffer,USHORTusAddress,
USHORTusNDiscrete)
读离散线圈回函数。*pucRegBuffer为要添加到协议中的数据,usAddress为线圈地址,
usNDiscrete为要访问线圈的个数。用户应根据要访问的线圈地址usAddress将相应线圈的值
按顺序添加到pucRegBuffer中。
3、应用层初始化及协议访问
用户只需在主函数中调用协议初始化代码,及消息处理函数即可。需用户调用的函数有
如下几个:
1)eMBErrorCodeeMBInit(eMBModeeMode,UCHARucSlaveAddress,UCHARucPort,
ULONGulBaudRate,eMBParityeParity)
协议初始化函数。eMode为所要使用的模式,用户可选MB_RTU(RTU模式)、MB_ASCII
(ASCII模式)或MB_TCP(TCP模式);ucSlaveAddress为从机地址,用户根据需要,取
值为1~247(0为广播地址,248~255协议保留);ulBaudRate为通信波特率,用户根据需要
选用,但务必使主机能支持此波特率;eParity为校验方式,用户根据需要选用,但务必使
主机能支持此校验方式。
2)eMBErrorCodeeMBSetSlaveID(UCHARucSlaveID,BOOLxIsRunning,UCHARconst
*pucAdditional,USHORTusAdditionalLen)
从机ID设置函数。注意,ID表示的是设备的类型,不同于ucSlaveAddress(从机地址)。
对同一通讯系统中,可以有相同的ucSlaveID,但不可以有相同的ucSlaveAddress。ucSlaveID
为一字节的设备ID号;xIsRunning为设备的运行状态,0xFF为运行,0x00为停止;*
pucAdditional为设备的附加描述,根据需要添加;usAdditionalLen为附加描述的长度(按字
节计算)。此函数不是必须调用的。但当一个Modbus通讯系统中有不同种设备时,应调用
此函数添加对应设备的描述。
3)eMBErrorCodeeMBPoll(void)
轮询事件查询处理函数。用户需在主循环中调用此函数。对于使用操作系统的程序,应
单独创建一个任务,使操作系统能周期调用此函数。
四、FreeModbus初始化及运行流程
FreeModbus是基于消息队列的协议。协议通过检测相应的消息来完成对应功能。协议
栈的初始化及运行流程如下:
1)首先调用eMBErrorCodeeMBInit(eMBModeeMode,UCHARucSlaveAddress,UCHAR
ucPort,ULONGulBaudRate,eMBParityeParity)完成物理层设备的初始化,主要包括:
BOOLxMBPortSerialInit(UCHARucPORT,ULONGulBaudRate,UCHARucDataBits,
eMBParityeParity)串口初始化,设定波特率、数据位数、校验方式;BOOL
xMBPortTimersInit(USHORTusTim1Timerout50us)定时器初始化,设定T35定时所需要
的定时器常数。
2)调用(此处非必需)eMBErrorCodeeMBSetSlaveID(UCHARucSlaveID,BOOL
xIsRunning,UCHARconst*pucAdditional,USHORTusAdditionalLen)指定设备ID。
3)调用eMBErrorCodeeMBEnable(void)使能协议栈,主要包括:staticpvMBFrameStart
pvMBFrameStartCur(函数指针)协议栈开始,将eRcvState设为STATE_RX_INIT状态,
调用voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable)使能接收,调用void
vMBPortTimersEnable()使能超时定时器。:.
学****必备欢迎下载
4)在3中使能了超时定时器,故经过T35时间后,发生第一次超时中断,在中断中,向协
议栈发送消息EV_READY(Startupfinished),并调用voidvMBPortTimersDisable()关
闭超时定时器,同时将eRcvState设为STATE_RX_IDLE。此时,协议栈可以接收串口
数据。注意,此处首先启用一次超时定时器是因为初始化完成时,串口有可能已经有数
据,因为无法判断第一个数据是请求的开始,故等待T35,接收下一帧请求。
5)此时,主函数调用eMBErrorCodeeMBPoll(void)检测事件。
6)若发生串口接收中断,且eRcvState为STATE_RX_IDLE(4中已将eRcvState设为
STATE_RX_IDLE),则向接收缓存中存入接收到的字符,同时将eRcvState设为
STATE_RX_RCV状态,并清零超时定时器。在下一个数据来到时,不断将数据存入接
收缓存,并清零超时定时器。
7)如果没有接收完成,则不可能发生超时中断。发生超时中断,说明T35时间内未收到新
的串口数据,根据Modbus协议的规定,这指示着一帧请求数据接收完成。在中断中,
向协议栈发送消息EV_FRAME_RECEIVED(Framereceived),等待协议栈处理此消息。
8)主函数调用eMBErrorCodeeMBPoll(void)检测到事件EV_FRAME_RECEIVED后,调
用staticpeMBFrameReceivepeMBFrameReceiveCur简单判断请求帧数据,并向协议栈发
送消息EV_EXECUTE(Executefunction)。
9)主函数调用eMBErrorCodeeMBPoll(void)检测到事件EV_EXECUTE后,根据相应的请
求代码查找处理该功能的函数指针来处理该功能。若不是广播消息,则调用static
peMBFrameSendpeMBFrameSendCur发送回复消息,在此函数中,只把要回复的数据复
制到了串口缓存中,同时将eSndState设为STATE_TX_XMIT(Transmitterisintransfer
state),并通过调用voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable)使能
发送中断。注意,发送中断使能后,由于串口发送寄存器本来就是空的,故在使能后将
进入发送中断中。
10)发送中断中,且eSndState为STATE_TX_XMIT(9中已将eSndState设为
STATE_TX_XMIT),则将串口缓存中的数据发送出去,同时不断对发送字符个数统计,
当发送完成后,向协议栈发送消息EV_FRAME_SENT(Framesent)。
11)主函数调用eMBErrorCodeeMBPoll(void)检测到事件EV_FRAME_SENT后,不处理此
消息。
12)当串口接收到数据后,协议栈将重复6-11处理消息。
五、一些理解

宏定义与变量
、全局变量与功能函数。所包含的宏定义与
全局变量定义如下:
/*-----------------------Defines------------------------------------------*/
#defineMB_SER_PDU_SIZE_MIN4/*!<MinimumsizeofaModbusRTUframe.*/
#defineMB_SER_PDU_SIZE_MAX256/*!<MaximumsizeofaModbusRTUframe.*/
#defineMB_SER_PDU_SIZE_CRC2/*!<SizeofCRCfieldinPDU.*/
#defineMB_SER_PDU_ADDR_OFF0/*!<OffsetofslaveaddressinSer-PDU.*/
#defineMB_SER_PDU_PDU_OFF1/*!<OffsetofModbus-PDUinSer-PDU.*/
/*-----------------------Typedefinitions---------------------------------*/
typedefenum
{
STATE_RX_INIT,/*!<Receiverisininitialstate.*/:.
学****必备欢迎下载
STATE_RX_IDLE,/*!<Receiverisinidlestate.*/
STATE_RX_RCV,/*!<Frameisbeeingreceived.*/
STATE_RX_ERROR/*!<Iftheframeisinvalid.*/
}eMBRcvState;
typedefenum
{
STATE_TX_IDLE,/*!<Transmitterisinidlestate.*/
STATE_TX_XMIT/*!<Transmitterisintransferstate.*/
}eMBSndState;
/*-----------------------Staticvariables---------------------------------*/
staticvolatileeMBSndStateeSndState;
staticvolatileeMBRcvStateeRcvState;
volatileUCHARucRTUBuf[MB_SER_PDU_SIZE_MAX];
staticvolatileUCHAR*pucSndBufferCur;
staticvolatileUSHORTusSndBufferCount;
staticvolatileUSHORTusRcvBufferPos;
首先在宏定义中,指明了该模式下所支持的最小请求帧长度为4(1字节地址+1字节命
令+2字节校验),最大请求帧长度为256,CRC为两字节,地址为第一字节,PDU开始于
第二字节。
在全局变量中,只定义了一个串口缓存数组ucRTUBuf[MB_SER_PDU_SIZE_MAX]。
由于发送与接收不是同步的,故可采用该缓存数组实现Modbus协议。在接收过程中,将所
接收到的数据直接存放于缓存ucRTUBuf中,在发送过程中,通过指针*pucSndBufferCur来
访问该数组。
eMBErrorCodeeMBRTUInit(UCHARucSlaveAddress,UCHARucPort,ULONG
ulBaudRate,eMBParityeParity)
此函数为RTU模式的初始化函数。此函数中判断串行口初始化是否成功(通过判断串
行口初始化函数的返回值实现。当然,查看返回值必然先调用该函数,从而完成端口初始化),
如果成功,则根据波特率计算T35,初始化超时定时器。
voideMBRTUStart(void)
此函数为RTU模式开始函数。函数主要功能是,将接收状态eRcvState设为
STATE_RX_INIT(Receiverisininitialstate),使能接收同时关闭发送,使能超时定时器。
voideMBRTUStop(void)
此函数为RTU模式终止函数。函数主要功能是,关闭接收与发送,关闭超时定时器。
eMBErrorCodeeMBRTUReceive(UCHAR*pucRcvAddress,UCHAR**pucFrame,
USHORT*pusLength)
此函数为RTU接收数据帧信息提取函数。函数主要功能是,将接收帧(存放于缓存)
的地址指针赋给指针变量pucRcvAddress,将PDU编码首地址赋给指针*pucFrame,将PDU
长度地址赋给指针变量pusLength。使用指针访问缓存数组,而不是额外开辟缓存存放帧信
息,大大减少了内存的开支。
eMBErrorCodeeMBRTUSend(UCHARucSlaveAddress,constUCHAR*pucFrame,
USHORTusLength)
此函数为RTU回复帧信息组织函数。函数的功能是,此函数首先使发送内容指针:.
学****必备欢迎下载
pucSndBufferCur指向pucFrame之前的一个地址,并将该地址内容填充为ucSlaveAddress,
并使用直接访问方式向缓存数组ucRTUBuf的相应地址内存入CRC校验值。注意,此函数
中,对ucRTUBuf的访问既有间接方式(指针pucSndBufferCur与pucFrame),又有直接方
式(直接向相应地址内写值),比较难理解。
回复帧组织完后,将发送状态eSndState设为STATE_TX_XMIT(Transmitterisintransfer
state),并禁止接收使能发送。发送一旦使能,就会进入发送中断,完成相应字符的发送。
BOOLxMBRTUReceiveFSM(void)
此函数描述了一个接收状态机,供接收中断调用。状态机中,首先完成串口接收寄存器
读取,然后判断相应接收状态eRcvState,实现接收。在STATE_RX_INIT状态,重置超时
定时器,等待超时中断(超时中断会把eRcvState设为STATE_RX_IDLE);在
STATE_RX_ERROR状态,同样会重置超时定时器等待超时中断;在STATE_RX_IDLE状态,
会将接收字符个数置零,同时向缓存数组ucRTUBuf中存入接收到的字符,跳入状态
STATE_RX_RCV,并使重置超时定时器;在STATE_RX_RCV状态,不断将接收到的字符
存入缓存,并统计接收计数,重置超时定时器,接收计数大于帧最大长度时,会跳入
STATE_RX_ERROR状态。
在任何一处发生超时中断,都会将状态eRcvState置为STATE_RX_IDLE。在接收过程
(STATE_RX_RCV)中,发生超时中断,指示着一帧数据接收完成。
接收状态机如图1所示:
Power-onorRST
Characterreceived/startT35
STATE_R
X_INIT
T35expired
STATE_R
X_IDLE
Characterreceived/startT35
andstoredthecharacter
Characterreceivedandn_r<n_max/start
STATE_RT35expiredT35expired
T35andstoredthecharacter
X_RCV
n_r>=n_max/startT35
STATE_R
X_ERROR
图1接收状态机图
BOOLxMBRTUTransmitFSM(void)
此函数描述了一个接收状态机,供发送中断调用。状态机中,判断相应发送状态
eSndState,实现发送。在STATE_TX_IDLE状态,使能接收关闭发送;在STATE_TX_XMIT
状态,调用底层串口发送函数将缓存中的字符发送出去,并使发送指针加1,待发送字符数:.
学****必备欢迎下载
减1,待发送数为0时,将向系统发送事件EV_FRAME_SENT(Framesent),同时使能接
收关闭发送,并转向STATE_TX_IDLE状态。
发送状态机如图2所示:
Power-onorRST
/EnableRXanddisableTX
STATE_T
X_IDLE
Needsend
n_t=0/PostEV_FRAME_SENT
andenableRXdisableTX
n_t>0/Charactersend
STATE_T
X_XMIT
图2发送状态机图
BOOLxMBRTUTimerT35Expired(void)
此函数描述了发生超时中断时应处理的事务,供超时中断调用。通过判读接收状态
eRcvState来决定要处理的事务,思想上有点像摩尔类型的FSM的输出逻辑。若中断发生于
STATE_RX_INIT,则向系统发送事件EV_READY(Startupfinished);若中断发生于
STATE_RX_RCV,则向系统发送事件EV_FRAME_RECEIVED(Framereceived);若中断发
生于STATE_RX_ERROR,则跳出,不执行。在每个执行分支结束后,均关闭超时定时器,
并将eRcvState转为STATE_RX_IDLE。当然,这儿不像FSM的输出逻辑。

宏定义与变量
、函数指针及全局变量,并使用优先编译指令预编
译一些程序代码。定义与优先编译部分如下:
#