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