您的位置:首页 > 其它

modbus rtu 工作流程分析

2015-12-26 16:26 232 查看
这里以freemodbus-v1.5.0为例,分析一下modbus 的工作流程,从接收到处理再到发送。如果有什么问题欢迎一起交流。

首先如果接收到主机发来的消息,程序会进入串口中断函数,中断函数必须调用portserial.c的void prvvUARTRxISR(void)函数,在prvvUARTRxISR函数中调用了pxMBFrameCBByteReceived();函数,该函数是个函数指针,在modbus初始化的时候已经被指向xMBRTUReceiveFSM函数,所以这里就相当于调用xMBRTUReceiveFSM();该函数负责接收相关状态的切换并并把接收到的数据以字节一字节的保存的全局变量ucRTUBuf[]中,其中接收长度保存在变量usRcvBufferPos,每次接收完一个字节之后都会调用vMBPortTimersEnable()函数来启动定时器,必须在定时时间到达之前接收到下一字节,否则下次接收到的数据就当作是另外一个帧的,一般这个定时器中断时间应该设置为接收3.5个字符所需要的时间。

接收状态共有5个,分别是:STATE_RX_INIT、STATE_RX_ERROR、STATE_RX_IDLE和STATE_RX_RCV,在初始化的时候会调用eMBRTUStart()函数来启动modbus,同时使eRcvState = STATE_RX_INIT,然后开启定时器,等定时时间到之后会发生定时器中断,在定时器中断调用xMBRTUTimerT35Expired()函数,在函数中把状态切换到STATE_RX_IDLE,同时把定时器关掉。只要发生定时器中断,最后都是将状态切换到STATE_RX_IDLE状态。

平时modbus应该是处在STATE_RX_IDLE状态,当数据到来后从STATE_RX_IDLE切换到STATE_RX_RCV,然后先把usRcvBufferPos清零,并把第一个字节数据保存在ucRTUBuf[usRcvBufferPos++];然后在STATE_RX_RCV状态下接收完剩下的数据。如果数据的长度没有超出最大值,则皆大欢喜,全部保存在全局变量ucRTUBuf[];否则接收出错,进入STATE_RX_ERROR状态,等待超市,数据作废,前功尽弃。

如果数据接收完成,即当状态机处于STATE_RX_RCV的时候发生定时器中断,就会在定时器中断函数xMBRTUTimerT35Expired()中发送一个接收完成事件,即xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );然后在eMBPoll()中就会查询到这个事件,然后就调用peMBFrameReceiveCur函数把刚刚接收到的数据取出来,放在peMBFrameReceiveCur是个函数指针,在初始化的时候已经指向eMBRTUReceive;进入eMBRTUReceive()函数,里面首先是判断数据长度并进行CRC校验;然后我们发现了一个熟悉的身影ucRTUBuf[],这就是刚才接收数据的时候用来保存数据的全局变量,这个函数也没有对接收到的数据做什么处理,直接把形参指向ucRTUBuf[]就完事了,也就是让ucMBFrame指向ucRTUBuf,简单吧。

我们回到eMBPoll(),在成功获取接收到的数据之后,判断地址的合法性,如果是给本机的或者是广播的就切换到EV_EXECUTE状态;在EV_EXECUTE状态,先是从接收的数据中取出功能码,每个数据帧都必须有功能码。然后从码表xFuncHandlers[i].ucFunctionCode中查找看看有没有这个功能码,如果有对应的码则执行相应的功能函数eException = xFuncHandlers[i].pxHandler(
ucMBFrame, &usLength );这个xFuncHandlers[]中就是所有能支持的功能码,如果需要添加其他功能码只要在其中添加功能码和相应的功能函数就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  modbus