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