-
1.
FreeModbus
协议分析
2.
应用层协议
一个任务中调用
eMBPoll()
函数。
< br>
协议必须首先调用初始化功能
eMBinit()
函数。后调用
eMBEnable()<
/p>
,最后,在循环体或者单独
2.1.
系统的启动
2.1.1.
eMBInit()
函数的源码分析
以
RTU
方
式为例,首先,检查调用的地址是否合法。如不合法,返回错误。如果合法则继续执
行,
首先,针对
RTU
方式还是
ASCII
方式,选
择不同的编译模块。
对需要调用的
函数指针进行复制。
如果移植需要改变其他用途,
则要修改相应
的指针,
包括如下
赋值:
pvMBFrameStartCur = eMBRTUStart;
pvMBFrameStopCur = eMBRTUStop;
peMBFrameSendCur = eMBRTUSend;
peMBFrameReceiveCur = eMBRTUReceive;
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ?
vMBPortClose :NULL;
pxMBFrameCBByteReceived=
xMBRTUReceiveFSM;
pxMBFrameCBTransmitterEmpty =
xMBRTUTransmitFSM;
pxMBPortCBTimerExpired =
xMBRTUTimerT35Expired;
然后调用
eStatus =eMBRTUInit(
ucMBAddress, ucPort, ulBaudRate,
eParity);
具体初始化通讯
2.1.2.
eMBRTUInit
端口。
eMBRTUInit
这个函数主要干两件事:
第一,
初始化串口:
if(
xMBPortSerialInit(ucPort, ulBaudRate, 8, eParity )
!= TRUE )
{
eStatus =
MB_EPORTERR;
}
这个函数在
portserial.c
中,需要用户在移植的时候根据自己的处理器编写。
第二,
初
始化计时器:首先要根据波特率计算一下是
3.5~5.0
个字
节周期的时间,然后再调用
xMBPortTimersInit( ( USHORT
) usTimerT35_50us)
,初始化计时器。这个函数在
< br>
中,需要用
2.1.3.
eMBEnable
源码分析
户在移植的时候根据自己的处理器编写。
porttimer.c
首先
,看看
Modbus
功能是否是被关闭的,如果不是被关闭(可
能是没有被初始化或者已经打
开)
,就返回错误。
如果是
disable
状态,就干下面两件事:
l
调用
pvMBFrameStar
tCur()
。
由于这是个函数指针,
在模块
eMBInit
中,
指向了
p>
eMBRTUStart
函数
n
在源代码中有这样一段注释:<
/p>
,意思是,首先设置成
STATE_RX_INIT
,然后打开计时器,等
待
t3.5
< br>以后,进入
STATE_RX_IDLE
状态。
n
看源代码中,
首先有设置
Receiver
的状态,后调用
< br>vMBPortSerialEnable
,设置接收状态,
然后打开定时器。
n
当定时器中断后,自动调用中断服务程序,在中断服务程序中,只调用了
pxMBPortCBTimerExpired
,而这是一个函数指针,在
RTU
方式初始化时,被指向了
xMBRTUT
imerT35Expired()
函数。
n xMBRTUTimerT35Expired
函数在
mbrtu.c
中,在这里,我们只看第
一种方式,就是进入初始
化状态,在
t35
时间以后,只调用了一个
xNeedPoll =
xMBPortEventPost( EV_READY );
n xMBPortEventPost
函数就是在事件队列里加了一个
EV_RDY
事件。
l
然后,将<
/p>
eMB
状态改为使能状态,
l
初始化结束。
2.2.
总线侦听
eMBPoll()
首先,判断系统是否被使能,如果没有,则返回错误值。
然后,检查是否有事件发生,如果有,则根据不同类型的事件
响应:
l
如果是
EV_RDY
,表示系统刚刚进入侦听状态,则什么都
不做;
l
如果状态为
EV_FRAME_R
ECEIVED
,也就是接收到完整的帧,做下面两件事情:
n
调用
e
Status=peMBFrameReceiveCur(
&ucRcvAddress,&ucMBFrame, &usLength)
。
p>
这是一
个函数指针,在
eMBInit
p>
中,被初始化指向
eMBRTUReceive
。
n eMBRTURece
ive
这个函数首先校验帧的长度和
CRC
,然后从协议中解析出地址、数据和长
度。
n
然后检查地址,
如果是广播地址或者是本机地址,
就调用
xMBPor
tEventPost( EV
-
EXECUTE)
,
将接收器的状态更改为
EV_EXECUTE<
/p>
。
l
p>
如果状态为
EV_EXECUTE
,就在函
数列表中检查,有没有与命令字段相符合的函数来解析相
应则执行该函数,否则返回非法
功能代码。
数据发送
2.3.
发送数据通过指针
eMBRTUSend
,调用
eMBRTUSend
函数。
2.3.1.
eMBRTUSend
函数
这个函数的作用就是打包,将数据打包成帧。
l
首先,检查接收状态。因为
p>
MODBUS
是基于
RS
< br>-
485
半双工通讯,所以当正在接收数据时,
不发送该帧。
l
如果总线空,就将数据打包,将地址和
CRC
< br>加入数据帧
l
将总线状态改为发送。
2.4.
功能注册
l
对于指定的功能代码,需要一个功能回调函数来处理,格式
如下。
eMBException
eMXXXXXX ( UCHAR *pucFrame, USHORT * usLen )
l
需要通过函数
< br>eMBRegisterCB(
功能代码,函数名
)
p>
加到处理代码中。具体源码分析从略。
2.4.1.
prvvUARTTxReadyISR()
总线状态改为发送后,会在发送缓冲时,自动调用
prvvUARTTxRe
adyISR()
中断服务程序。
prvvUARTTxRea
dyISR()
只调用了一个函数,就是
pxMBFrameC
BTransmitterEmpty ()
。
2.4.2.
pxMBFrameCBByteReceived()
<
/p>
pxMBFrameCBTransmitterEmpty()
是一个指针,指向了
xMBRTUTransmitFSM
函数
。
3.
数据链路层协议
< br>数据链路层是最基本的打包部分,将数据打包成帧,送到应用层。
在数据链路层协
议中,使用中
断方式来接受。那么每次接收到字符就自动调用接收字符的
ISR
程序。按照规定,应该将中断
服务程序安装给<
/p>
prvvUARTRxISR(void)
函数。实际上这个函数
只调用了一个函数:
pxMBFr
ameCBByteReceived()
,这个指针调用了
x
MBRTUReceiveFSM
函数。
3.1.
xMBRTUReceiveFSM()
函数
函数首先检查是不是处于发送状态。如果处于发送状态,直接
退出。
l
首先调用
xMBPortSerialGetByte( (
CHAR * ) &
ucByte)
,获取从串口读到的字符。
l
然后检查接受状态:
n
如果是错误状态或者处于初始化状态,那么直接等待,错过
该帧。
n
如果是
STATE_RX_IDLE
空闲状态,则将指针重置
,将收到的第一个字节存储到缓冲区,并
将状态改为
STATE
_RX_RCV
状态。
n
如果处于接收状态,就判断,如果缓冲区未满,就将收到的
字节放入缓冲区,否则改为错误
状态。
l
不管在任何状态,最后都开启了
t35
计时器。在
t35
结束的时候,通过指针调用了
函数。
xMBRTUTimerT35Expired()
.
这么长
l xMBRTUTimerT35Expired
()
函数检查状态,如果是接收状态那就表明,已经有
t35E
V_FRAME_RECEIVED
的时间里,没有收到任新字节,当前的帧结束。在队
列里增加一个
事件。
如果是错误状态,什么都不做。
l
然后关掉计时器,将状态改为空闲。
l
3.2. xMBRTUTran
smitFSM()
函数
首先判断总线是否忙,如果
忙,则终止。如果不忙,则继续,根据
xMBRTUTransmitFS
M
发送状态变量:
(空闲)状态,
则打开端口发送
l
如果当前为
STATE_TX_IDLE
,则进一步判断发送队列是否为空,
l
如果当
前状态为
STATE_TX_XMIT
如果不空,则发送下一个字符
n
如果空,说明发送完成,关闭发送端口,改为侦听,并将状态
改为空闲。
n
4.
传输控制
除了传输控制以外,
还有传输控制的若干函数。
通过下面
-
-
-
-
-
-
-
-
-
上一篇:新概念英语第二册课堂笔记:第19课
下一篇:嵌入式系统设计课设报告