本篇按照FreeModbus协议栈的工作流程,对源代码进行总结解析;FreeModbus协议栈作为从机,等待主机传送的数据,当从机接收到一帧完整的报文后,对报文进行解析,然后响应主机,发送报文给主机,实现主机和从机之间的通信;
1:demo.c中三个函数,完成协议栈的准备工作;
eMBInit()函数:(mb.c)
1 /*函数功能:
2 *1:实现RTU模式和ASCALL模式的协议栈初始化;
3 *2:完成协议栈核心函数指针的赋值,包括Modbus协议栈的使能和禁止、报文的接收和响应、3.5T定时器中断回调函数、串口发送和接收中断回调函数;
4 *3:eMBRTUInit完成RTU模式下串口和3.5T定时器的初始化,需用户自己移植;
5 *4:设置Modbus协议栈的模式eMBCurrentMode为MB_RTU,设置Modbus协议栈状态eMBState为STATE_DISABLED;
6 */
7 eMBErrorCode
8 eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
9 {
10 //错误状态初始值
11 eMBErrorCode eStatus = MB_ENOERR;
12
13 //验证从机地址
14 if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
15 ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ))
16 {
17 eStatus = MB_EINVAL;
18 }
19 else
20 {
21 ucMBAddress = ucSlaveAddress; /*从机地址的赋值*/
22
23 switch ( eMode )
24 {
25 #if MB_RTU_ENABLED > 0
26 case MB_RTU:
27 pvMBFrameStartCur = eMBRTUStart; /*使能modbus协议栈*/
28 pvMBFrameStopCur = eMBRTUStop; /*禁用modbus协议栈*/
29 peMBFrameSendCur = eMBRTUSend; /*modbus从机响应函数*/
30 peMBFrameReceiveCur = eMBRTUReceive; /*modbus报文接收函数*/
31 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
32 //接收状态机
33 pxMBFrameCBByteReceived = xMBRTUReceiveFSM; /*串口接收中断最终调用此函数接收数据*/
34 //发送状态机
35 pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; /*串口发送中断最终调用此函数发送数据*/
36 //报文到达间隔检查
37 pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; /*定时器中断函数最终调用次函数完成定时器中断*/
38 //初始化RTU
39 eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
40 break;
41 #endif
42 #if MB_ASCII_ENABLED > 0
43 case MB_ASCII:
44 pvMBFrameStartCur = eMBASCIIStart;
45 pvMBFrameStopCur = eMBASCIIStop;
46 peMBFrameSendCur = eMBASCIISend;
47 peMBFrameReceiveCur = eMBASCIIReceive;
48 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
49 pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
50 pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
51 pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
52
53 eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
54 break;
55 #endif
56 default:
57 eStatus = MB_EINVAL;
58 }
59
60 //
61 if( eStatus == MB_ENOERR )
62 {
63 if( !xMBPortEventInit() )
64 {
65 /* port dependent event module initalization failed. */
66 eStatus = MB_EPORTERR;
67 }
68 else
69 {
70 //设定当前状态
71 eMBCurrentMode = eMode; //设定RTU模式
72 eMBState = STATE_DISABLED; //modbus协议栈初始化状态,在此初始化为禁止
73 }
74 }
75 }
76 return eStatus;
77 }
eMBEnable()函数:(mb.c)
1 /*函数功能
2 *1:设置Modbus协议栈工作状态eMBState为STATE_ENABLED;
3 *2:调用pvMBFrameStartCur()函数激活协议栈
4 */
5 eMBErrorCode
6 eMBEnable( void )
7 {
8 eMBErrorCode eStatus = MB_ENOERR;
9
10 if( eMBState == STATE_DISABLED )
11 {
12 /* Activate the protocol stack. */
13 pvMBFrameStartCur( ); /*pvMBFrameStartCur = eMBRTUStart;调用eMBRTUStart函数*/
14 eMBState = STATE_ENABLED;
15 }
16 else
17 {
18 eStatus = MB_EILLSTATE;
19 }
20 return eStatus;
21 }
eMBRTUStart()函数:(mbrtu.c)
1 /*函数功能
2 *1:设置接收状态机eRcvState为STATE_RX_INIT;
3 *2:使能串口接收,禁止串口发送,作为从机,等待主机传送的数据;
4 *3:开启定时器,3.5T时间后定时器发生第一次中断,此时eRcvState为STATE_RX_INIT,上报初始化完成事件,然后设置eRcvState为空闲STATE_RX_IDLE;
5 *4:每次进入3.5T定时器中断,定时器被禁止,等待串口有字节接收后,才使能定时器;
6 */
7 void
8 eMBRTUStart( void )
9 {
10 ENTER_CRITICAL_SECTION( );
11 /* Initially the receiver is in the state STATE_RX_INIT. we start
12 * the timer and if no character is received within t3.5 we change
13 * to STATE_RX_IDLE. This makes sure that we delay startup of the
14 * modbus protocol stack until the bus is free.
15 */
16 eRcvState = STATE_RX_INIT;
17 vMBPortSerialEnable( TRUE, FALSE );
18 vMBPortTimersEnable();
19
20 EXIT_CRITICAL_SECTION( );
21 }
eMBPoll()函数:(mb.c)
1 /*函数功能:
2 *1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
3 *2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
4 */
5 eMBErrorCode
6 eMBPoll( void )
7 {
8 static UCHAR *ucMBFrame; //接收和发送报文数据缓存区
9 static UCHAR ucRcvAddress; //modbus从机地址
10 static UCHAR ucFunctionCode; //功能码
11 static USHORT usLength; //报文长度
12 static eMBException eException; //错误码响应枚举
13
14 int i;
15 eMBErrorCode eStatus = MB_ENOERR; //modbus协议栈错误码
16 eMBEventType eEvent; //事件标志枚举
17
18 /* Check if the protocol stack is ready. */
19 if( eMBState != STATE_ENABLED ) //检查协议栈是否使能
20 {
21 return MB_EILLSTATE; //协议栈未使能,返回协议栈无效错误码
22 }
23
24 /* Check if there is a event available. If not return control to caller.
25 * Otherwise we will handle the event. */
26
27 //查询事件
28 if( xMBPortEventGet( &eEvent ) == TRUE ) //查询哪个事件发生
29 {
30 switch ( eEvent )
31 {
32 case EV_READY:
33 break;
34
35 case EV_FRAME_RECEIVED: /*接收到一帧数据,此事件发生*/
36 eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
37 if( eStatus == MB_ENOERR ) /*报文长度和CRC校验正确*/
38 {
39 /* Check if the frame is for us. If not ignore the frame. */
40 /*判断接收到的报文数据是否可接受,如果是,处理报文数据*/
41 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
42 {
43 ( void )xMBPortEventPost( EV_EXECUTE ); //修改事件标志为EV_EXECUTE执行事件
44 }
45 }
46 break;
47
48 case EV_EXECUTE: //对接收到的报文进行处理事件
49 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; //获取PDU中第一个字节,为功能码
50 eException = MB_EX_ILLEGAL_FUNCTION; //赋错误码初值为无效的功能码
51 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
52 {
53 /* No more function handlers registered. Abort. */
54 if( xFuncHandlers[i].ucFunctionCode == 0 )
55 {
56 break;
57 }
58 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) /*根据报文中的功能码,处理报文*/
59 {
60 eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );/*对接收到的报文进行解析*/
61 break;
62 }
63 }
64
65 /* If the request was not sent to the broadcast address we
66 * return a reply. */
67 if( ucRcvAddress != MB_ADDRESS_BROADCAST )
68 {
69 if( eException != MB_EX_NONE ) /*接收到的报文有错误*/
70 {
71 /* An exception occured. Build an error frame. */
72 usLength = 0; /*响应发送数据的首字节为从机地址*/
73 ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); /*响应发送数据帧的第二个字节,功能码最高位置1*/
74 ucMBFrame[usLength++] = eException; /*响应发送数据帧的第三个字节为错误码标识*/
75 }
76 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
77 {
78 vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
79 }
80 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); /*modbus从机响应函数,发送响应给主机*/
81 }
82 break;
83
84 case EV_FRAME_SENT:
85 break;
86 }
87 }
88 return MB_ENOERR;
89 }
至此:完成Modbus协议栈的初始化准备工作,eMBPoll()函数轮询等待接收完成事件发生,接收机状态eRcvState为STATE_RX_IDLE空闲;
2:FreeModbus协议栈接收一帧完整报文机制:
FreeModbus协议栈通过淳口中断接收一帧数据,用户需在串口接收中断中回调prvvUARTRxISR()函数;
prvvUARTRxISR()函数:(portserial.c)
1 static void prvvUARTRxISR( void )
2 {
3 pxMBFrameCBByteReceived( );
4 }
在第一阶段中eMBInit()函数中赋值pxMBFrameCBByteReceived = xMBRTUReceiveFSM,发生接收中断时,最终调用xMBRTUReceiveFSM函数对数据进行接收;
xMBRTUReceiveFSM()函数:(mbrtu.c)
1 /*函数功能
2 *1:将接收到的数据存入ucRTUBuf[]中;
3 *2:usRcvBufferPos为全局变量,表示接收数据的个数;
4 *3:每接收到一个字节的数据,3.5T定时器清0
5 */
6 BOOL
7 xMBRTUReceiveFSM( void )
8 {
9 BOOL xTaskNeedSwitch = FALSE;
10 UCHAR ucByte;
11
12 assert( eSndState == STATE_TX_IDLE ); /*确保没有数据在发送*/
13
14 ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); /*从串口数据寄存器读取一个字节数据*/
15
16 //根据不同的状态转移
17 switch ( eRcvState )
18 {
19 /* If we have received a character in the init state we have to
20 * wait until the frame is finished.
21 */
22 case STATE_RX_INIT:
23 vMBPortTimersEnable(); /*开启3.5T定时器*/
24 break;
25
26 /* In the error state we wait until all characters in the
27 * damaged frame are transmitted.
28 */
29 case STATE_RX_ERROR: /*数据帧被损坏,重启定时器,不保存串口接收的数据*/
30 vMBPortTimersEnable();
31 break;
32
33 /* In the idle state we wait for a new character. If a character
34 * is received the t1.5 and t3.5 timers are started and the
35 * receiver is in the state STATE_RX_RECEIVCE.
36 */
37 case STATE_RX_IDLE: /*接收器空闲,开始接收,进入STATE_RX_RCV状态*/
38 usRcvBufferPos = 0;
39 ucRTUBuf[usRcvBufferPos++] = ucByte; /*保存数据*/
40
41 eRcvState = STATE_RX_RCV;
42
43 /* Enable t3.5 timers. */
44 vMBPortTimersEnable(); /*每收到一个字节,都重启3.5T定时器*/
45 break;
46
47 /* We are currently receiving a frame. Reset the timer after
48 * every character received. If more than the maximum possible
49 * number of bytes in a modbus frame is received the frame is
50 * ignored.
51 */
52 case STATE_RX_RCV:
53 if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX)
54 {
55 ucRTUBuf[usRcvBufferPos++] = ucByte; /*接收数据*/
56 }
57 else
58 {
59 eRcvState = STATE_RX_ERROR; /*一帧报文的字节数大于最大PDU长度,忽略超出的数据*/
60 }
61
62 vMBPortTimersEnable(); /*每收到一个字节,都重启3.5T定时器*/
63 break;
64 }
65 return xTaskNeedSwitch;
66 }
当主机发送一帧完整的报文后,3.5T定时器中断发生,定时器中断最终回调xMBRTUTimerT35Expired函数;
xMBRTUTimerT35Expired()函数:(mbrtu.c)
1 /*函数功能
2 *1:从机接受完成一帧数据后,接收状态机eRcvState为STATE_RX_RCV;
3 *2:上报“接收到报文”事件(EV_FRAME_RECEIVED)
4 *3:禁止3.5T定时器,设置接收状态机eRcvState状态为STATE_RX_IDLE空闲;
5 */
6 BOOL
7 xMBRTUTimerT35Expired( void )
8 {
9 BOOL xNeedPoll = FALSE;
10
11 switch ( eRcvState )
12 {
13 /* Timer t35 expired. Startup phase is finished. */
14 /*上报modbus协议栈的事件状态给poll函数,EV_READY:初始化完成事件*/
15 case STATE_RX_INIT:
16 xNeedPoll = xMBPortEventPost( EV_READY );
17 break;
18
19 /* A frame was received and t35 expired. Notify the listener that
20 * a new frame was received. */
21 case STATE_RX_RCV: /*一帧数据接收完成*/
22 xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); /*上报协议栈事件,接收到一帧完整的数据*/
23 break;
24
25 /* An error occured while receiving the frame. */
26 case STATE_RX_ERROR:
27 break;
28
29 /* Function called in an illegal state. */
30 default:
31 assert( ( eRcvState == STATE_RX_INIT ) ||
32 ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );
33 }
34
35 vMBPortTimersDisable( ); /*当接收到一帧数据后,禁止3.5T定时器,只到接受下一帧数据开始,开始计时*/
36
37 eRcvState = STATE_RX_IDLE; /*处理完一帧数据,接收器状态为空闲*/
38
39 return xNeedPoll;
40 }
至此:从机接收到一帧完整的报文,存储在ucRTUBuf[MB_SER_PDU_SIZE_MAX]全局变量中,定时器禁止,接收机状态为空闲;
3:解析报文机制
在第二阶段,从机接收到一帧完整的报文后,上报“接收到报文”事件,eMBPoll函数轮询,发现“接收到报文”事件发生,调用peMBFrameReceiveCur函数,此函数指针在eMBInit被赋值eMBRTUReceive函数,最终调用eMBRTUReceive函数,从ucRTUBuf中取得从机地址、PDU单元和PDU单元的长度,然后判断从机地址地是否一致,若一致,上报“报文解析事件”EV_EXECUTE,(xMBPortEventPost( EV_EXECUTE ));“报文解析事件”发生后,根据功能码,调用xFuncHandlers[i].pxHandler( ucMBFrame, &usLength )对报文进行解析,此过程全部在eMBPoll函数中执行;
eMBPoll()函数:(mb.c)
1 /*函数功能:
2 *1:检查协议栈状态是否使能,eMBState初值为STATE_NOT_INITIALIZED,在eMBInit()函数中被赋值为STATE_DISABLED,在eMBEnable函数中被赋值为STATE_ENABLE;
3 *2:轮询EV_FRAME_RECEIVED事件发生,若EV_FRAME_RECEIVED事件发生,接收一帧报文数据,上报EV_EXECUTE事件,解析一帧报文,响应(发送)一帧数据给主机;
4 */
5 eMBErrorCode
6 eMBPoll( void )
7 {
8 static UCHAR *ucMBFrame; //接收和发送报文数据缓存区
9 static UCHAR ucRcvAddress; //modbus从机地址
10 static UCHAR ucFunctionCode; //功能码
11 static USHORT usLength; //报文长度
12 static eMBException eException; //错误码响应枚举
13
14 int i;
15 eMBErrorCode eStatus = MB_ENOERR; //modbus协议栈错误码
16 eMBEventType eEvent; //事件标志枚举
17
18 /* Check if the protocol stack is ready. */
19 if( eMBState != STATE_ENABLED ) //检查协议栈是否使能
20 {
21 return MB_EILLSTATE; //协议栈未使能,返回协议栈无效错误码
22 }
23
24 /* Check if there is a event available. If not return control to caller.
25 * Otherwise we will handle the event. */
26
27 //查询事件
28 if( xMBPortEventGet( &eEvent ) == TRUE ) //查询哪个事件发生
29 {
30 switch ( eEvent )
31 {
32 case EV_READY:
33 break;
34
35 case EV_FRAME_RECEIVED: /*接收到一帧数据,此事件发生*/
36 eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
37 if( eStatus == MB_ENOERR ) /*报文长度和CRC校验正确*/
38 {
39 /* Check if the frame is for us. If not ignore the frame. */
40 /*判断接收到的报文数据是否可接受,如果是,处理报文数据*/
41 if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
42 {
43 ( void )xMBPortEventPost( EV_EXECUTE ); //修改事件标志为EV_EXECUTE执行事件
44 }
45 }
46 break;
47
48 case EV_EXECUTE: //对接收到的报文进行处理事件
49 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; //获取PDU中第一个字节,为功能码
50 eException = MB_EX_ILLEGAL_FUNCTION; //赋错误码初值为无效的功能码
51 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
52 {
53 /* No more function handlers registered. Abort. */
54 if( xFuncHandlers[i].ucFunctionCode == 0 )
55 {
56 break;
57 }
58 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) /*根据报文中的功能码,处理报文*/
59 {
60 eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );/*对接收到的报文进行解析*/
61 break;
62 }
63 }
64
65 /* If the request was not sent to the broadcast address we
66 * return a reply. */
67 if( ucRcvAddress != MB_ADDRESS_BROADCAST )
68 {
69 if( eException != MB_EX_NONE ) /*接收到的报文有错误*/
70 {
71 /* An exception occured. Build an error frame. */
72 usLength = 0; /*响应发送数据的首字节为从机地址*/
73 ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR ); /*响应发送数据帧的第二个字节,功能码最高位置1*/
74 ucMBFrame[usLength++] = eException; /*响应发送数据帧的第三个字节为错误码标识*/
75 }
76 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
77 {
78 vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
79 }
80 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); /*modbus从机响应函数,发送响应给主机*/
81 }
82 break;
83
84 case EV_FRAME_SENT:
85 break;
86 }
87 }
88 return MB_ENOERR;
89 }
eMBRTUReceive()函数:(mbrtu.c)
1 /*eMBPoll函数轮询到EV_FRAME_RECEIVED事件时,调用peMBFrameReceiveCur(),此函数是用户为函数指针peMBFrameReceiveCur()的赋值
2 *此函数完成的功能:从一帧数据报文中,取得modbus从机地址给pucRcvAddress,PDU报文的长度给pusLength,PDU报文的首地址给pucFrame,函数
3 *形参全部为地址传递*/
4 eMBErrorCode
5 eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
6 {
7 BOOL xFrameReceived = FALSE;
8 eMBErrorCode eStatus = MB_ENOERR;
9
10 ENTER_CRITICAL_SECTION();
11 assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); /*断言宏,判断接收到的字节数<256,如果>256,终止程序*/
12
13 /* Length and CRC check */
14 if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
15 && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) )
16 {
17 /* Save the address field. All frames are passed to the upper layed
18 * and the decision if a frame is used is done there.
19 */
20 *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; //取接收到的第一个字节,modbus从机地址
21
22 /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
23 * size of address field and CRC checksum.
24 */
25 *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); //减3
26
27 /* Return the start of the Modbus PDU to the caller. */
28 *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF];
29 xFrameReceived = TRUE;
30 }
31 else
32 {
33 eStatus = MB_EIO;
34 }
35
36 EXIT_CRITICAL_SECTION();
37 return eStatus;
38 }
xMBPortEventPost()函数:(portevent.c)
1 BOOL
2 xMBPortEventPost( eMBEventType eEvent )
3 {
4 xEventInQueue = TRUE;
5 eQueuedEvent = eEvent;
6 return TRUE;
7 }
xFuncHandlers[i]是结构体数组,存放的是功能码以及对应的报文解析函数,原型如下:
1 typedef struct
2 {
3 UCHAR ucFunctionCode;
4 pxMBFunctionHandler pxHandler;
5 } xMBFunctionHandler;
以下列举读线圈函数举例:
eMBFuncReadCoils()读线圈寄存器函数: (mbfunccoils.c)
1 #if MB_FUNC_READ_COILS_ENABLED > 0
2
3 eMBException
4 eMBFuncReadCoils( UCHAR * pucFrame, USHORT * usLen )
5 {
6 USHORT usRegAddress;
7 USHORT usCoilCount;
8 UCHAR ucNBytes;
9 UCHAR *pucFrameCur;
10
11 eMBException eStatus = MB_EX_NONE;
12 eMBErrorCode eRegStatus;
13
14 if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
15 {
16 /*线圈寄存器的起始地址*/
17 usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
18 usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
19 //usRegAddress++;
20
21 /*线圈寄存器个数*/
22 usCoilCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF] << 8 );
23 usCoilCount |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_COILCNT_OFF + 1] );
24
25 /* Check if the number of registers to read is valid. If not
26 * return Modbus illegal data value exception.
27 */
28 /*判断线圈寄存器个数是否合理*/
29 if( ( usCoilCount >= 1 ) &&
30 ( usCoilCount < MB_PDU_FUNC_READ_COILCNT_MAX ) )
31 {
32 /* Set the current PDU data pointer to the beginning. */
33 /*为发送缓冲pucFrameCur赋值*/
34 pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
35 *usLen = MB_PDU_FUNC_OFF;
36
37 /* First byte contains the function code. */
38 /*响应报文第一个字节赋值为功能码0x01*/
39 *pucFrameCur++ = MB_FUNC_READ_COILS;
40 *usLen += 1;
41
42 /* Test if the quantity of coils is a multiple of 8. If not last
43 * byte is only partially field with unused coils set to zero. */
44 /*usCoilCount%8有余数,ucNBytes加1,不够的位填充0*/
45 if( ( usCoilCount & 0x0007 ) != 0 )
46 {
47 ucNBytes = ( UCHAR )( usCoilCount / 8 + 1 );
48 }
49 else
50 {
51 ucNBytes = ( UCHAR )( usCoilCount / 8 );
52 }
53 *pucFrameCur++ = ucNBytes;
54 *usLen += 1;
55
56 eRegStatus =
57 eMBRegCoilsCB( pucFrameCur, usRegAddress, usCoilCount,
58 MB_REG_READ );
59
60 /* If an error occured convert it into a Modbus exception. */
61 if( eRegStatus != MB_ENOERR )
62 {
63 eStatus = prveMBError2Exception( eRegStatus );
64 }
65 else
66 {
67 /* The response contains the function code, the starting address
68 * and the quantity of registers. We reuse the old values in the
69 * buffer because they are still valid. */
70 *usLen += ucNBytes;;
71 }
72 }
73 else
74 {
75 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
76 }
77 }
78 else
79 {
80 /* Can't be a valid read coil register request because the length
81 * is incorrect. */
82 eStatus = MB_EX_ILLEGAL_DATA_VALUE;
83 }
84 return eStatus;
85 }
至此:报文解析结束,得到ucMBFrame响应缓冲和usLength响应报文长度,等待发送报文;
4:发送响应报文
解析完一帧完整的报文后,eMBPoll()函数中调用peMBFrameSendCur()函数进行响应,eMBFrameSendCur()是函数指针,最终会调用eMBRTUSend()函数发送响应;
eMBRTUSend()函数:
1 /*函数功能
2 *1:对响应报文PDU前面加上从机地址;
3 *2:对响应报文PDU后加上CRC校;
4 *3:使能发送,启动传输;
5 */
6 eMBErrorCode
7 eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
8 {
9 eMBErrorCode eStatus = MB_ENOERR;
10 USHORT usCRC16;
11
12 ENTER_CRITICAL_SECTION( );
13
14 /* Check if the receiver is still in idle state. If not we where to
15 * slow with processing the received frame and the master sent another
16 * frame on the network. We have to abort sending the frame.
17 */
18 if( eRcvState == STATE_RX_IDLE )
19 {
20 /* First byte before the Modbus-PDU is the slave address. */
21 /*在协议数据单元前加从机地址*/
22 pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
23 usSndBufferCount = 1;
24
25 /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
26 pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
27 usSndBufferCount += usLength;
28
29 /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
30 usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
31 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
32 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
33
34 /* Activate the transmitter. */
35 eSndState = STATE_TX_XMIT; //发送状态
36 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); /*发送一个字节的数据,进入发送中断函数,启动传输*/
37 pucSndBufferCur++; /* next byte in sendbuffer. */
38 usSndBufferCount--;
39 vMBPortSerialEnable( FALSE, TRUE ); /*使能发送,禁止接收*/
40 }
41 else
42 {
43 eStatus = MB_EIO;
44 }
45 EXIT_CRITICAL_SECTION( );
46 return eStatus;
47 }
进入发送中断,串口发送中断中调用prvvUARTTxReadyISR()函数,继续调用pxMBFrameCBTransmitterEmpty()函数,pxMBFrameCBTransmitterEmpty为函数指针,最终调用xMBRTUTransmitFSM()函数;
xMBRTUTransmitFSM()函数:(mbrtu.c)
1 BOOL
2 xMBRTUTransmitFSM( void )
3 {
4 BOOL xNeedPoll = FALSE;
5
6 assert( eRcvState == STATE_RX_IDLE );
7
8 switch ( eSndState )
9 {
10 /* We should not get a transmitter event if the transmitter is in
11 * idle state.*/
12 case STATE_TX_IDLE: /*发送器处于空闲状态,使能接收,禁止发送*/
13 /* enable receiver/disable transmitter. */
14 vMBPortSerialEnable( TRUE, FALSE );
15 break;
16
17 case STATE_TX_XMIT: /*发送器处于发送状态,在从机发送函数eMBRTUSend中赋值STATE_TX_XMIT*/
18 /* check if we are finished. */
19 if( usSndBufferCount != 0 )
20 {
21 //发送数据
22 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
23 pucSndBufferCur++; /* next byte in sendbuffer. */
24 usSndBufferCount--;
25 }
26 else
27 {
28 //传递任务,发送完成
29 xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); /*协议栈事件状态赋值为EV_FRAME_SENT,发送完成事件,eMBPoll函数会对此事件进行处理*/
30 /* Disable transmitter. This prevents another transmit buffer
31 * empty interrupt. */
32 vMBPortSerialEnable( TRUE, FALSE ); /*使能接收,禁止发送*/
33 eSndState = STATE_TX_IDLE; /*发送器状态为空闲状态*/
34 }
35 break;
36 }
37
38 return xNeedPoll;
39 }
进入发送中断,串口发送中断中调用prvvUARTTxReadyISR()函数,继续调用pxMBFrameCBTransmitterEmpty()函数,pxMBFrameCBTransmitterEmpty为函数指针,最终调用xMBRTUTransmitFSM()函数;
xMBRTUTransmitFSM()函数:(mbrtu.c)
1 BOOL
2 xMBRTUTransmitFSM( void )
3 {
4 BOOL xNeedPoll = FALSE;
5
6 assert( eRcvState == STATE_RX_IDLE );
7
8 switch ( eSndState )
9 {
10 /* We should not get a transmitter event if the transmitter is in
11 * idle state.*/
12 case STATE_TX_IDLE: /*发送器处于空闲状态,使能接收,禁止发送*/
13 /* enable receiver/disable transmitter. */
14 vMBPortSerialEnable( TRUE, FALSE );
15 break;
16
17 case STATE_TX_XMIT: /*发送器处于发送状态,在从机发送函数eMBRTUSend中赋值STATE_TX_XMIT*/
18 /* check if we are finished. */
19 if( usSndBufferCount != 0 )
20 {
21 //发送数据
22 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
23 pucSndBufferCur++; /* next byte in sendbuffer. */
24 usSndBufferCount--;
25 }
26 else
27 {
28 //传递任务,发送完成
29 xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); /*协议栈事件状态赋值为EV_FRAME_SENT,发送完成事件,eMBPoll函数会对此事件进行处理*/
30 /* Disable transmitter. This prevents another transmit buffer
31 * empty interrupt. */
32 vMBPortSerialEnable( TRUE, FALSE ); /*使能接收,禁止发送*/
33 eSndState = STATE_TX_IDLE; /*发送器状态为空闲状态*/
34 }
35 break;
36 }
37
38 return xNeedPoll;
39 }