xref: /nrf52832-nimble/rt-thread/components/net/freemodbus/modbus/rtu/mbrtu_m.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
3  * Copyright (c) 2013 China Beijing Armink <[email protected]>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * File: $Id: mbrtu_m.c,v 1.60 2013/08/17 11:42:56 Armink Add Master Functions $
29  */
30 
31 /* ----------------------- System includes ----------------------------------*/
32 #include "stdlib.h"
33 #include "string.h"
34 
35 /* ----------------------- Platform includes --------------------------------*/
36 #include "port.h"
37 
38 /* ----------------------- Modbus includes ----------------------------------*/
39 #include "mb.h"
40 #include "mb_m.h"
41 #include "mbrtu.h"
42 #include "mbframe.h"
43 
44 #include "mbcrc.h"
45 #include "mbport.h"
46 
47 #if MB_MASTER_RTU_ENABLED > 0
48 /* ----------------------- Defines ------------------------------------------*/
49 #define MB_SER_PDU_SIZE_MIN     4       /*!< Minimum size of a Modbus RTU frame. */
50 #define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus RTU frame. */
51 #define MB_SER_PDU_SIZE_CRC     2       /*!< Size of CRC field in PDU. */
52 #define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */
53 #define MB_SER_PDU_PDU_OFF      1       /*!< Offset of Modbus-PDU in Ser-PDU. */
54 
55 /* ----------------------- Type definitions ---------------------------------*/
56 typedef enum
57 {
58     STATE_M_RX_INIT,              /*!< Receiver is in initial state. */
59     STATE_M_RX_IDLE,              /*!< Receiver is in idle state. */
60     STATE_M_RX_RCV,               /*!< Frame is beeing received. */
61     STATE_M_RX_ERROR,              /*!< If the frame is invalid. */
62 } eMBMasterRcvState;
63 
64 typedef enum
65 {
66     STATE_M_TX_IDLE,              /*!< Transmitter is in idle state. */
67     STATE_M_TX_XMIT,              /*!< Transmitter is in transfer state. */
68     STATE_M_TX_XFWR,              /*!< Transmitter is in transfer finish and wait receive state. */
69 } eMBMasterSndState;
70 
71 /* ----------------------- Static variables ---------------------------------*/
72 static volatile eMBMasterSndState eSndState;
73 static volatile eMBMasterRcvState eRcvState;
74 
75 static volatile UCHAR  ucMasterRTUSndBuf[MB_PDU_SIZE_MAX];
76 static volatile UCHAR  ucMasterRTURcvBuf[MB_SER_PDU_SIZE_MAX];
77 static volatile USHORT usMasterSendPDULength;
78 
79 static volatile UCHAR *pucMasterSndBufferCur;
80 static volatile USHORT usMasterSndBufferCount;
81 
82 static volatile USHORT usMasterRcvBufferPos;
83 static volatile BOOL   xFrameIsBroadcast = FALSE;
84 
85 static volatile eMBMasterTimerMode eMasterCurTimerMode;
86 
87 /* ----------------------- Start implementation -----------------------------*/
88 eMBErrorCode
eMBMasterRTUInit(UCHAR ucPort,ULONG ulBaudRate,eMBParity eParity)89 eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
90 {
91     eMBErrorCode    eStatus = MB_ENOERR;
92     ULONG           usTimerT35_50us;
93 
94     ENTER_CRITICAL_SECTION(  );
95 
96     /* Modbus RTU uses 8 Databits. */
97     if( xMBMasterPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
98     {
99         eStatus = MB_EPORTERR;
100     }
101     else
102     {
103         /* If baudrate > 19200 then we should use the fixed timer values
104          * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
105          */
106         if( ulBaudRate > 19200 )
107         {
108             usTimerT35_50us = 35;       /* 1800us. */
109         }
110         else
111         {
112             /* The timer reload value for a character is given by:
113              *
114              * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
115              *             = 11 * Ticks_per_1s / Baudrate
116              *             = 220000 / Baudrate
117              * The reload for t3.5 is 1.5 times this value and similary
118              * for t3.5.
119              */
120             usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
121         }
122         if( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
123         {
124             eStatus = MB_EPORTERR;
125         }
126     }
127     EXIT_CRITICAL_SECTION(  );
128 
129     return eStatus;
130 }
131 
132 void
eMBMasterRTUStart(void)133 eMBMasterRTUStart( void )
134 {
135     ENTER_CRITICAL_SECTION(  );
136     /* Initially the receiver is in the state STATE_M_RX_INIT. we start
137      * the timer and if no character is received within t3.5 we change
138      * to STATE_M_RX_IDLE. This makes sure that we delay startup of the
139      * modbus protocol stack until the bus is free.
140      */
141     eRcvState = STATE_M_RX_INIT;
142     vMBMasterPortSerialEnable( TRUE, FALSE );
143     vMBMasterPortTimersT35Enable(  );
144 
145     EXIT_CRITICAL_SECTION(  );
146 }
147 
148 void
eMBMasterRTUStop(void)149 eMBMasterRTUStop( void )
150 {
151     ENTER_CRITICAL_SECTION(  );
152     vMBMasterPortSerialEnable( FALSE, FALSE );
153     vMBMasterPortTimersDisable(  );
154     EXIT_CRITICAL_SECTION(  );
155 }
156 
157 eMBErrorCode
eMBMasterRTUReceive(UCHAR * pucRcvAddress,UCHAR ** pucFrame,USHORT * pusLength)158 eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
159 {
160     eMBErrorCode    eStatus = MB_ENOERR;
161 
162     ENTER_CRITICAL_SECTION(  );
163     RT_ASSERT( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX );
164 
165     /* Length and CRC check */
166     if( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
167         && ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) )
168     {
169         /* Save the address field. All frames are passed to the upper layed
170          * and the decision if a frame is used is done there.
171          */
172         *pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];
173 
174         /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
175          * size of address field and CRC checksum.
176          */
177         *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
178 
179         /* Return the start of the Modbus PDU to the caller. */
180         *pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];
181     }
182     else
183     {
184         eStatus = MB_EIO;
185     }
186 
187     EXIT_CRITICAL_SECTION(  );
188     return eStatus;
189 }
190 
191 eMBErrorCode
eMBMasterRTUSend(UCHAR ucSlaveAddress,const UCHAR * pucFrame,USHORT usLength)192 eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
193 {
194     eMBErrorCode    eStatus = MB_ENOERR;
195     USHORT          usCRC16;
196 
197     if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
198 
199     ENTER_CRITICAL_SECTION(  );
200 
201     /* Check if the receiver is still in idle state. If not we where to
202      * slow with processing the received frame and the master sent another
203      * frame on the network. We have to abort sending the frame.
204      */
205     if( eRcvState == STATE_M_RX_IDLE )
206     {
207         /* First byte before the Modbus-PDU is the slave address. */
208         pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
209         usMasterSndBufferCount = 1;
210 
211         /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
212         pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
213         usMasterSndBufferCount += usLength;
214 
215         /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
216         usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
217         ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
218         ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
219 
220         /* Activate the transmitter. */
221         eSndState = STATE_M_TX_XMIT;
222         vMBMasterPortSerialEnable( FALSE, TRUE );
223     }
224     else
225     {
226         eStatus = MB_EIO;
227     }
228     EXIT_CRITICAL_SECTION(  );
229     return eStatus;
230 }
231 
232 BOOL
xMBMasterRTUReceiveFSM(void)233 xMBMasterRTUReceiveFSM( void )
234 {
235     BOOL            xTaskNeedSwitch = FALSE;
236     UCHAR           ucByte;
237 
238     RT_ASSERT(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
239 
240     /* Always read the character. */
241     ( void )xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
242 
243     switch ( eRcvState )
244     {
245         /* If we have received a character in the init state we have to
246          * wait until the frame is finished.
247          */
248     case STATE_M_RX_INIT:
249         vMBMasterPortTimersT35Enable( );
250         break;
251 
252         /* In the error state we wait until all characters in the
253          * damaged frame are transmitted.
254          */
255     case STATE_M_RX_ERROR:
256         vMBMasterPortTimersT35Enable( );
257         break;
258 
259         /* In the idle state we wait for a new character. If a character
260          * is received the t1.5 and t3.5 timers are started and the
261          * receiver is in the state STATE_RX_RECEIVCE and disable early
262          * the timer of respond timeout .
263          */
264     case STATE_M_RX_IDLE:
265     	/* In time of respond timeout,the receiver receive a frame.
266     	 * Disable timer of respond timeout and change the transmiter state to idle.
267     	 */
268     	vMBMasterPortTimersDisable( );
269     	eSndState = STATE_M_TX_IDLE;
270 
271         usMasterRcvBufferPos = 0;
272         ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
273         eRcvState = STATE_M_RX_RCV;
274 
275         /* Enable t3.5 timers. */
276         vMBMasterPortTimersT35Enable( );
277         break;
278 
279         /* We are currently receiving a frame. Reset the timer after
280          * every character received. If more than the maximum possible
281          * number of bytes in a modbus frame is received the frame is
282          * ignored.
283          */
284     case STATE_M_RX_RCV:
285         if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
286         {
287             ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
288         }
289         else
290         {
291             eRcvState = STATE_M_RX_ERROR;
292         }
293         vMBMasterPortTimersT35Enable();
294         break;
295     }
296     return xTaskNeedSwitch;
297 }
298 
299 BOOL
xMBMasterRTUTransmitFSM(void)300 xMBMasterRTUTransmitFSM( void )
301 {
302     BOOL            xNeedPoll = FALSE;
303 
304     RT_ASSERT( eRcvState == STATE_M_RX_IDLE );
305 
306     switch ( eSndState )
307     {
308         /* We should not get a transmitter event if the transmitter is in
309          * idle state.  */
310     case STATE_M_TX_IDLE:
311         /* enable receiver/disable transmitter. */
312         vMBMasterPortSerialEnable( TRUE, FALSE );
313         break;
314 
315     case STATE_M_TX_XMIT:
316         /* check if we are finished. */
317         if( usMasterSndBufferCount != 0 )
318         {
319             xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
320             pucMasterSndBufferCur++;  /* next byte in sendbuffer. */
321             usMasterSndBufferCount--;
322         }
323         else
324         {
325             xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
326             /* Disable transmitter. This prevents another transmit buffer
327              * empty interrupt. */
328             vMBMasterPortSerialEnable( TRUE, FALSE );
329             eSndState = STATE_M_TX_XFWR;
330             /* If the frame is broadcast ,master will enable timer of convert delay,
331              * else master will enable timer of respond timeout. */
332             if ( xFrameIsBroadcast == TRUE )
333             {
334             	vMBMasterPortTimersConvertDelayEnable( );
335             }
336             else
337             {
338             	vMBMasterPortTimersRespondTimeoutEnable( );
339             }
340         }
341         break;
342     }
343 
344     return xNeedPoll;
345 }
346 
347 BOOL
xMBMasterRTUTimerExpired(void)348 xMBMasterRTUTimerExpired(void)
349 {
350 	BOOL xNeedPoll = FALSE;
351 
352 	switch (eRcvState)
353 	{
354 		/* Timer t35 expired. Startup phase is finished. */
355 	case STATE_M_RX_INIT:
356 		xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
357 		break;
358 
359 		/* A frame was received and t35 expired. Notify the listener that
360 		 * a new frame was received. */
361 	case STATE_M_RX_RCV:
362 		xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
363 		break;
364 
365 		/* An error occured while receiving the frame. */
366 	case STATE_M_RX_ERROR:
367 		vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
368 		xNeedPoll = xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
369 		break;
370 
371 		/* Function called in an illegal state. */
372 	default:
373 		RT_ASSERT(
374 				( eRcvState == STATE_M_RX_INIT ) || ( eRcvState == STATE_M_RX_RCV ) ||
375 				( eRcvState == STATE_M_RX_ERROR ) || ( eRcvState == STATE_M_RX_IDLE ));
376 		break;
377 	}
378 	eRcvState = STATE_M_RX_IDLE;
379 
380 	switch (eSndState)
381 	{
382 		/* A frame was send finish and convert delay or respond timeout expired.
383 		 * If the frame is broadcast,The master will idle,and if the frame is not
384 		 * broadcast.Notify the listener process error.*/
385 	case STATE_M_TX_XFWR:
386 		if ( xFrameIsBroadcast == FALSE ) {
387 			vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
388 			xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
389 		}
390 		break;
391 		/* Function called in an illegal state. */
392 	default:
393 		RT_ASSERT(
394 				( eSndState == STATE_M_TX_XFWR ) || ( eSndState == STATE_M_TX_IDLE ));
395 		break;
396 	}
397 	eSndState = STATE_M_TX_IDLE;
398 
399 	vMBMasterPortTimersDisable( );
400 	/* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
401 	if (eMasterCurTimerMode == MB_TMODE_CONVERT_DELAY) {
402 		xNeedPoll = xMBMasterPortEventPost( EV_MASTER_EXECUTE );
403 	}
404 
405 	return xNeedPoll;
406 }
407 
408 /* Get Modbus Master send RTU's buffer address pointer.*/
vMBMasterGetRTUSndBuf(UCHAR ** pucFrame)409 void vMBMasterGetRTUSndBuf( UCHAR ** pucFrame )
410 {
411 	*pucFrame = ( UCHAR * ) ucMasterRTUSndBuf;
412 }
413 
414 /* Get Modbus Master send PDU's buffer address pointer.*/
vMBMasterGetPDUSndBuf(UCHAR ** pucFrame)415 void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
416 {
417 	*pucFrame = ( UCHAR * ) &ucMasterRTUSndBuf[MB_SER_PDU_PDU_OFF];
418 }
419 
420 /* Set Modbus Master send PDU's buffer length.*/
vMBMasterSetPDUSndLength(USHORT SendPDULength)421 void vMBMasterSetPDUSndLength( USHORT SendPDULength )
422 {
423 	usMasterSendPDULength = SendPDULength;
424 }
425 
426 /* Get Modbus Master send PDU's buffer length.*/
usMBMasterGetPDUSndLength(void)427 USHORT usMBMasterGetPDUSndLength( void )
428 {
429 	return usMasterSendPDULength;
430 }
431 
432 /* Set Modbus Master current timer mode.*/
vMBMasterSetCurTimerMode(eMBMasterTimerMode eMBTimerMode)433 void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
434 {
435 	eMasterCurTimerMode = eMBTimerMode;
436 }
437 
438 /* The master request is broadcast? */
xMBMasterRequestIsBroadcast(void)439 BOOL xMBMasterRequestIsBroadcast( void ){
440 	return xFrameIsBroadcast;
441 }
442 #endif
443 
444