打印

准备做一个Freemodbus移植到PSoC4上,想盖个楼

[复制链接]
4810|42
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zb0830|  楼主 | 2015-4-20 14:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
先来介绍下Freemodbus:
FreeMODBUS一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议。Modbus通信协议栈包括两层:Modbus应用层协议,该层定义了数据模式和功能;另外一层是网络层。
沙发
zb0830|  楼主 | 2015-4-20 14:15 | 只看该作者
FreeMODBUS 提供了RTU/ASCII 传输模式及TCP协议支持。FreeModbus遵循BSD许可证,这意味着用户可以将FreeModbus应用于商业环境中。版本FreeModbus-V1.5提供如下的功能支持:

使用特权

评论回复
板凳
zb0830|  楼主 | 2015-4-20 14:16 | 只看该作者
功能

QQ截图20150420141759.jpg (137.2 KB )

QQ截图20150420141759.jpg

使用特权

评论回复
地板
zb0830|  楼主 | 2015-4-20 14:17 | 只看该作者
另一部分

QQ截图20150420141833.jpg (45.42 KB )

QQ截图20150420141833.jpg

使用特权

评论回复
5
zb0830|  楼主 | 2015-4-20 14:17 | 只看该作者
FreeModbus对硬件的需求
编辑

FreeModbus协议对硬件的需求非常少——基本上任何具有串行接口,并且有一些能够容纳modbus数据帧的RAM的微控制器都足够了。
u 一个异步串行接口,能够支持接收缓冲区满和发送缓存区空中断。
u 一个能够产生RTU传输所需要的t3.5字符超时定时器的时钟。
对于软件部分,仅仅需要一个简单的事件队列。在使用操作系统的处理器上,可通过单独定义一个任务完成Modbus时间的查询。小点的微控制器往往不允许使用操作系统,在那种情况下,可以使用一个全局变量来实现该事件队列(Atmel AVR 移植使用这种方式实现)。
实际的存储器需求决定于所使用的Modbus模块的多少。下表列出了所支持的功能编译后所需要的存储器。ARM是使用GNUARM编译器3.4.4使用-O1选项得到的。AVR项数值是使用WinAVR编译器3.4.5使用-Os选项编译得到的。

使用特权

评论回复
6
zb0830|  楼主 | 2015-4-20 14:17 | 只看该作者
再来

QQ截图20150420141916.jpg (168.16 KB )

QQ截图20150420141916.jpg

使用特权

评论回复
7
zb0830|  楼主 | 2015-4-20 14:18 | 只看该作者
FreeModbus的移植
编辑

1、 物理层接口文件的修改
在物理层,用户只需完成串行口及超时定时器的配置即可。具体应修改接口文件portserial.c及porttimer.c。
u portserial.c中函数的修改:
1) void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
此函数的功能为设置串口状态。有两个参数:xRxEnable及xTxEnable。当xRxEnable为真时,应使能串口接收及接收中断。在RS485通讯系统中,还要注意将RS485接口芯片设为接收使能状态;当xTxEnable为真时,应使能串口发送及发送中断。在RS485通讯系统中,还要注意将RS485接口芯片设为发送使能状态。

使用特权

评论回复
8
zb0830|  楼主 | 2015-4-20 14:18 | 只看该作者
2) void vMBPortClose( void )
此函数的功能是关闭Modbus通讯端口,具体的,应在此函数中关闭通讯端口的发送使能及接收使能。
3) BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
此函数的功能是初始化串行通讯端口。有四个参数: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) BOOL xMBPortSerialPutByte(CHAR ucByte)
此函数的功能为通讯端口发送一字节数据。参数为:ucByte,待发送的数据。应在此函数中编写发送一字节数据的函数。注意,由于使用的是中断发送,故只需将数据放到发送寄存器即可。函数返回值务必为TRUE。

使用特权

评论回复
9
zb0830|  楼主 | 2015-4-20 14:18 | 只看该作者
5) BOOL xMBPortSerialGetByte( CHAR * pucByte )
此函数的功能为通讯端口接收一字节数据。参数为:* pucByte,接收到的数据。应在此函数中编写接收的函数。注意,由于使用的是中断接收,故只需将接收寄存器的值放到* pucByte即可。函数返回值务必为TRUE。
6) void prvvUARTTxReadyISR(void)
发送中断函数。此函数无需修改。只需在用户的发送中断函数中调用此函数即可,同时,用户应在调用此函数后,清除发送中断标志位。

使用特权

评论回复
10
zb0830|  楼主 | 2015-4-20 14:19 | 只看该作者
7) void prvvUARTRxISR(void)
发送中断函数。此函数无需修改。只需在用户的接收中断函数中调用此函数即可,同时,用户应在调用此函数后,清除接收中断标志位。
u porttimer.c中函数的修改:
1) BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
此函数的功能为初始化超时定时器。参数为:usTim1Timerout50us,50us的个数。用户应根据所使用的硬件初始化超时定时器,使之能产生中断时间为usTim1Timerout50us*50us的中断。函数返回值务必为TRUE。
2) void vMBPortTimersEnable( )
此函数的功能为使能超时定时器。用户需在此函数中清除中断标志位、清零定时器计数值,并重新使能定时器中断。

使用特权

评论回复
11
zb0830|  楼主 | 2015-4-20 14:19 | 只看该作者
3) void vMBPortTimersDisable( )
此函数的功能为关闭超时定时器。用户需在此函数中清零定时器计数值,并关闭定时器中断。
4) void TIMERExpiredISR( void )
定时器中断函数。此函数无需修改。只需在用户的定时器中断中调用此函数即可,同时,用户应在调用此函数后清除中断标志位。

使用特权

评论回复
12
zb0830|  楼主 | 2015-4-20 14:19 | 只看该作者
2、 应用层回函数的修改
在应用层,用户需要定义所需要使用的寄存器,并修改对应的回函数。回函数有如下几个:
1) eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
输入寄存器回函数。* pucRegBuffer为要添加到协议中的数据,usAddress为输入寄存器地址,usNRegs为要读取寄存器的个数。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中。
2) eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
保持寄存器回函数。* pucRegBuffer为要协议中的数据,usAddress为输入寄存器地址,usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址usAddress放到相应保持寄存器中。

使用特权

评论回复
13
zb0830|  楼主 | 2015-4-20 14:20 | 只看该作者
3) eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
读写线圈回函数。* pucRegBuffer为要添加到协议中的数据,usAddress为线圈地址,usNCoils为要访问线圈的个数,eMode为访问类型(MB_REG_READ为读线圈状态,MB_REG_WRITE为写线圈)。用户应根据要访问的线圈地址usAddress将相应线圈的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的线圈地址usAddress放到相应线圈中。
4) eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
读离散线圈回函数。* pucRegBuffer为要添加到协议中的数据,usAddress为线圈地址,usNDiscrete为要访问线圈的个数。用户应根据要访问的线圈地址usAddress将相应线圈的值按顺序添加到pucRegBuffer中。

使用特权

评论回复
14
zb0830|  楼主 | 2015-4-20 14:20 | 只看该作者
3、 应用层初始化及协议访问
用户只需在主函数中调用协议初始化代码,及消息处理函数即可。需用户调用的函数有如下几个:
1) eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
协议初始化函数。eMode为所要使用的模式,用户可选MB_RTU(RTU模式)、MB_ASCII(ASCII模式)或MB_TCP(TCP模式);ucSlaveAddress为从机地址,用户根据需要,取值为1~247(0为广播地址,248~255协议保留);ulBaudRate为通信波特率,用户根据需要选用,但务必使主机能支持此波特率;eParity为校验方式,用户根据需要选用,但务必使主机能支持此校验方式。

使用特权

评论回复
15
zb0830|  楼主 | 2015-4-20 14:20 | 只看该作者
2) eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning, UCHAR const *pucAdditional, USHORT usAdditionalLen )
从机ID设置函数。注意,ID表示的是设备的类型,不同于ucSlaveAddress(从机地址)。对同一通讯系统中,可以有相同的ucSlaveID,但不可以有相同的ucSlaveAddress。ucSlaveID为一字节的设备ID号;xIsRunning为设备的运行状态,0xFF为运行,0x00为停止;* pucAdditional为设备的附加描述,根据需要添加;usAdditionalLen为附加描述的长度(按字节计算)。此函数不是必须调用的。但当一个Modbus通讯系统中有不同种设备时,应调用此函数添加对应设备的描述。

使用特权

评论回复
16
zb0830|  楼主 | 2015-4-20 14:21 | 只看该作者
3) eMBErrorCode eMBPoll( void )
轮询事件查询处理函数。用户需在主循环中调用此函数。对于使用操作系统的程序,应单独创建一个任务,使操作系统能周期调用此函数。

使用特权

评论回复
17
zb0830|  楼主 | 2015-4-20 14:21 | 只看该作者
FreeModbus初始化及运行流程
编辑

FreeModbus是基于消息队列的协议。协议通过检测相应的消息来完成对应功能。协议栈的初始化及运行流程如下:
1) 首先调用eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )完成物理层设备的初始化,主要包括:
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )串口初始化,设定波特率、数据位数、校验方式;BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )定时器初始化,设定T35定时所需要的定时器常数。

使用特权

评论回复
18
zb0830|  楼主 | 2015-4-20 14:21 | 只看该作者
2) 调用(此处非必需)eMBErrorCode eMBSetSlaveID( UCHAR ucSlaveID, BOOL xIsRunning,UCHAR const *pucAdditional, USHORT usAdditionalLen )指定设备ID。
3) 调用eMBErrorCode eMBEnable(void)使能协议栈,主要包括:static pvMBFrameStart pvMBFrameStartCur(函数指针)协议栈开始,将eRcvState设为STATE_RX_INIT状态,调用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能接收,调用void vMBPortTimersEnable( )使能超时定时器。
4) 在3中使能了超时定时器,故经过T35时间后,发生第一次超时中断,在中断中,向协议栈发送消息EV_READY(Startup finished),并调用void vMBPortTimersDisable( )关闭超时定时器,同时将eRcvState设为STATE_RX_IDLE。此时,协议栈可以接收串口数据。注意,此处首先启用一次超时定时器是因为初始化完成时,串口有可能已经有数据,因为无法判断第一个数据是请求的开始,故等待T35,接收下一帧请求。

使用特权

评论回复
19
zb0830|  楼主 | 2015-4-20 14:22 | 只看该作者
5) 此时,主函数调用eMBErrorCode eMBPoll( void )检测事件。
6) 若发生串口接收中断,且eRcvState为STATE_RX_IDLE(4中已将eRcvState设为STATE_RX_IDLE),则向接收缓存中存入接收到的字符,同时将eRcvState设为STATE_RX_RCV状态,并清零超时定时器。在下一个数据来到时,不断将数据存入接收缓存,并清零超时定时器。
7) 如果没有接收完成,则不可能发生超时中断。发生超时中断,说明T35时间内未收到新的串口数据,根据Modbus协议的规定,这指示着一帧请求数据接收完成。在中断中,向协议栈发送消息EV_FRAME_RECEIVED(Frame received),等待协议栈处理此消息。

使用特权

评论回复
20
zb0830|  楼主 | 2015-4-20 14:22 | 只看该作者
8) 主函数调用eMBErrorCode eMBPoll( void )检测到事件EV_FRAME_RECEIVED后,调用static peMBFrameReceive peMBFrameReceiveCur简单判断请求帧数据,并向协议栈发送消息EV_EXECUTE(Execute function)。
9) 主函数调用eMBErrorCode eMBPoll( void )检测到事件EV_EXECUTE后,根据相应的请求代码查找处理该功能的函数指针来处理该功能。若不是广播消息,则调用static peMBFrameSend peMBFrameSendCur发送回复消息,在此函数中,只把要回复的数据复制到了串口缓存中,同时将eSndState设为STATE_TX_XMIT(Transmitter is in transfer state),并通过调用void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )使能发送中断。注意,发送中断使能后,由于串口发送寄存器本来就是空的,故在使能后将进入发送中断中。
10) 发送中断中,且eSndState为STATE_TX_XMIT(9中已将eSndState设为STATE_TX_XMIT),则将串口缓存中的数据发送出去,同时不断对发送字符个数统计,当发送完成后,向协议栈发送消息EV_FRAME_SENT(Frame sent)。
11) 主函数调用eMBErrorCode eMBPoll( void )检测到事件EV_FRAME_SENT后,不处理此消息。
12) 当串口接收到数据后,协议栈将重复6-11处理消息。

使用特权

评论回复
评论
碧螺春_123 2017-4-4 15:04 回复TA
写的非常详细,非常好 赞一个 
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

63

主题

879

帖子

2

粉丝