1 /*
2  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
3  * Copyright (c) 2013 China Beijing Armink <armink.ztl@gmail.com>
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 #include "stdio.h"
35 
36 /* ----------------------- Platform includes --------------------------------*/
37 #include "port.h"
38 
39 /* ----------------------- Modbus includes ----------------------------------*/
40 
41 #include "mb_m.h"
42 #include "mbrtu.h"
43 #include "mbframe.h"
44 
45 #include "mbcrc.h"
46 #include "mbport.h"
47 
48 /* ----------------------- Defines ------------------------------------------*/
49 #define MB_RTU_SER_PDU_SIZE_MIN 4   /*!< Minimum size of a Modbus RTU frame. */
50 
51 /* ----------------------- Type definitions ---------------------------------*/
52 typedef enum
53 {
54     STATE_M_RX_INIT,              /*!< Receiver is in initial state. */
55     STATE_M_RX_IDLE,              /*!< Receiver is in idle state. */
56     STATE_M_RX_RCV,               /*!< Frame is beeing received. */
57     STATE_M_RX_ERROR,             /*!< If the frame is invalid. */
58 } eMBMasterRcvState;
59 
60 typedef enum
61 {
62     STATE_M_TX_IDLE,              /*!< Transmitter is in idle state. */
63     STATE_M_TX_XMIT,              /*!< Transmitter is in transfer state. */
64     STATE_M_TX_XFWR,              /*!< Transmitter is in transfer finish and wait receive state. */
65 } eMBMasterSndState;
66 
67 #if MB_MASTER_RTU_ENABLED > 0
68 /*------------------------ Shared variables ---------------------------------*/
69 extern volatile UCHAR   ucMasterRcvBuf[];
70 extern volatile UCHAR   ucMasterSndBuf[];
71 
72 /* ----------------------- Static variables ---------------------------------*/
73 static volatile eMBMasterSndState   eSndState;
74 static volatile eMBMasterRcvState   eRcvState;
75 
76 static volatile UCHAR   *pucMasterSndBufferCur;
77 static volatile USHORT  usMasterSndBufferCount;
78 static volatile USHORT  usMasterRcvBufferPos;
79 
80 static volatile UCHAR *ucMasterRTURcvBuf = ucMasterRcvBuf;
81 static volatile UCHAR *ucMasterRTUSndBuf = ucMasterSndBuf;
82 
83 /* ----------------------- Start implementation -----------------------------*/
84 eMBErrorCode
eMBMasterRTUInit(UCHAR ucPort,ULONG ulBaudRate,eMBParity eParity)85 eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
86 {
87     eMBErrorCode    eStatus = MB_ENOERR;
88     ULONG           usTimerT35_50us;
89 
90     ENTER_CRITICAL_SECTION(  );
91 
92     /* Modbus RTU uses 8 Databits. */
93     if( xMBMasterPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
94     {
95         eStatus = MB_EPORTERR;
96     }
97     else
98     {
99         /* If baudrate > 19200 then we should use the fixed timer values
100          * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
101          */
102         if( ulBaudRate > 19200 )
103         {
104             usTimerT35_50us = 35;       /* 1800us. */
105         }
106         else
107         {
108             /* The timer reload value for a character is given by:
109              *
110              * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
111              *             = 11 * Ticks_per_1s / Baudrate
112              *             = 220000 / Baudrate
113              * The reload for t3.5 is 1.5 times this value and similary
114              * for t3.5.
115              */
116             usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
117         }
118         if( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
119         {
120             eStatus = MB_EPORTERR;
121         }
122     }
123     EXIT_CRITICAL_SECTION(  );
124 
125     return eStatus;
126 }
127 
128 void
eMBMasterRTUStart(void)129 eMBMasterRTUStart( void )
130 {
131     ENTER_CRITICAL_SECTION(  );
132     /* Initially the receiver is in the state STATE_M_RX_INIT. we start
133      * the timer and if no character is received within t3.5 we change
134      * to STATE_M_RX_IDLE. This makes sure that we delay startup of the
135      * modbus protocol stack until the bus is free.
136      */
137     eRcvState = STATE_M_RX_IDLE; //STATE_M_RX_INIT (We start processing immediately)
138     vMBMasterPortSerialEnable( TRUE, FALSE );
139     vMBMasterPortTimersT35Enable(  );
140 
141     EXIT_CRITICAL_SECTION(  );
142 }
143 
144 void
eMBMasterRTUStop(void)145 eMBMasterRTUStop( void )
146 {
147     ENTER_CRITICAL_SECTION(  );
148     vMBMasterPortSerialEnable( FALSE, FALSE );
149     vMBMasterPortTimersDisable(  );
150     EXIT_CRITICAL_SECTION(  );
151 }
152 
153 eMBErrorCode
eMBMasterRTUReceive(UCHAR * pucRcvAddress,UCHAR ** pucFrame,USHORT * pusLength)154 eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
155 {
156     eMBErrorCode    eStatus = MB_ENOERR;
157 
158     ENTER_CRITICAL_SECTION(  );
159     assert( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX );
160 
161     /* Length and CRC check */
162     if( ( usMasterRcvBufferPos >= MB_RTU_SER_PDU_SIZE_MIN )
163         && ( usMBCRC16( ( UCHAR * ) ucMasterRTURcvBuf, usMasterRcvBufferPos ) == 0 ) )
164     {
165         /* Save the address field. All frames are passed to the upper layed
166          * and the decision if a frame is used is done there.
167          */
168         *pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];
169 
170         /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
171          * size of address field and CRC checksum.
172          */
173         *pusLength = ( USHORT )( usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );
174 
175         /* Return the start of the Modbus PDU to the caller. */
176         *pucFrame = ( UCHAR * ) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];
177     }
178     else
179     {
180         eStatus = MB_EIO;
181     }
182 
183     EXIT_CRITICAL_SECTION(  );
184     return eStatus;
185 }
186 
187 eMBErrorCode
eMBMasterRTUSend(UCHAR ucSlaveAddress,const UCHAR * pucFrame,USHORT usLength)188 eMBMasterRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
189 {
190     eMBErrorCode    eStatus = MB_ENOERR;
191     USHORT          usCRC16;
192 
193     if ( ucSlaveAddress > MB_MASTER_TOTAL_SLAVE_NUM ) return MB_EINVAL;
194 
195     ENTER_CRITICAL_SECTION(  );
196 
197     /* Check if the receiver is still in idle state. If not we where to
198      * slow with processing the received frame and the master sent another
199      * frame on the network. We have to abort sending the frame.
200      */
201     if( eRcvState == STATE_M_RX_IDLE )
202     {
203         /* First byte before the Modbus-PDU is the slave address. */
204         pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
205         usMasterSndBufferCount = 1;
206 
207         /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
208         pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
209         usMasterSndBufferCount += usLength;
210 
211         /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
212         usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
213         ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
214         ucMasterRTUSndBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
215 
216         /* Activate the transmitter. */
217         eSndState = STATE_M_TX_XMIT;
218         // The place to enable RS485 driver
219         vMBMasterPortSerialEnable( FALSE, TRUE );
220     }
221     else
222     {
223         eStatus = MB_EIO;
224     }
225     EXIT_CRITICAL_SECTION(  );
226     return eStatus;
227 }
228 
229 BOOL
xMBMasterRTUReceiveFSM(void)230 xMBMasterRTUReceiveFSM( void )
231 {
232     BOOL            xStatus = FALSE;
233     UCHAR           ucByte;
234 
235     assert(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
236 
237     /* Always read the character. */
238     xStatus = xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
239 
240     switch ( eRcvState )
241     {
242         /* If we have received a character in the init state we have to
243          * wait until the frame is finished.
244          */
245     case STATE_M_RX_INIT:
246         vMBMasterPortTimersT35Enable( );
247         break;
248 
249         /* In the error state we wait until all characters in the
250          * damaged frame are transmitted.
251          */
252     case STATE_M_RX_ERROR:
253         vMBMasterPortTimersT35Enable( );
254         break;
255 
256         /* In the idle state we wait for a new character. If a character
257          * is received the t1.5 and t3.5 timers are started and the
258          * receiver is in the state STATE_M_RX_RCV and disable early
259          * the timer of respond timeout .
260          */
261     case STATE_M_RX_IDLE:
262         /* In time of respond timeout,the receiver receive a frame.
263          * Disable timer of respond timeout and change the transmiter state to idle.
264          */
265         vMBMasterPortTimersDisable( );
266         eSndState = STATE_M_TX_IDLE;
267 
268         usMasterRcvBufferPos = 0;
269         ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
270         eRcvState = STATE_M_RX_RCV;
271 
272         /* Enable t3.5 timers. */
273         vMBMasterPortTimersT35Enable( );
274         break;
275 
276         /* We are currently receiving a frame. Reset the timer after
277          * every character received. If more than the maximum possible
278          * number of bytes in a modbus frame is received the frame is
279          * ignored.
280          */
281     case STATE_M_RX_RCV:
282         if( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
283         {
284             if ( xStatus ) {
285                 ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
286             }
287         }
288         else
289         {
290             eRcvState = STATE_M_RX_ERROR;
291         }
292         vMBMasterPortTimersT35Enable( );
293         break;
294     }
295     return xStatus;
296 }
297 
298 BOOL
xMBMasterRTUTransmitFSM(void)299 xMBMasterRTUTransmitFSM( void )
300 {
301     BOOL xNeedPoll = TRUE;
302     BOOL xFrameIsBroadcast = FALSE;
303 
304     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_XFWR:
311         xNeedPoll = FALSE;
312         break;
313 
314     case STATE_M_TX_IDLE:
315         break;
316 
317     case STATE_M_TX_XMIT:
318         /* check if we are finished. */
319         if( usMasterSndBufferCount != 0 )
320         {
321             xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
322             pucMasterSndBufferCur++;  /* next byte in sendbuffer. */
323             usMasterSndBufferCount--;
324         }
325         else
326         {
327             xFrameIsBroadcast = ( ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
328             vMBMasterRequestSetType( xFrameIsBroadcast );
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 MB_PORT_ISR_ATTR
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         assert(eRcvState == STATE_M_RX_IDLE);
374         break;
375     }
376     eRcvState = STATE_M_RX_IDLE;
377 
378     switch (eSndState)
379     {
380         /* A frame was send finish and convert delay or respond timeout expired.
381          * If the frame is broadcast,The master will idle,and if the frame is not
382          * broadcast. Notify the listener process error.*/
383     case STATE_M_TX_XFWR:
384         if ( xMBMasterRequestIsBroadcast( ) == FALSE ) {
385             vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
386             xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
387         }
388         break;
389         /* Function called in an illegal state. */
390     default:
391         assert( ( eSndState == STATE_M_TX_XMIT ) || ( eSndState == STATE_M_TX_IDLE ));
392         break;
393     }
394     eSndState = STATE_M_TX_IDLE;
395 
396     vMBMasterPortTimersDisable( );
397     /* If timer mode is convert delay, the master event then turns EV_MASTER_EXECUTE status. */
398     if (xMBMasterGetCurTimerMode() == MB_TMODE_CONVERT_DELAY) {
399         xNeedPoll = xMBMasterPortEventPost(EV_MASTER_EXECUTE);
400     }
401 
402     return xNeedPoll;
403 }
404 
405 
406 #endif
407