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 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 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 149 eMBMasterRTUStop( void ) 150 { 151 ENTER_CRITICAL_SECTION( ); 152 vMBMasterPortSerialEnable( FALSE, FALSE ); 153 vMBMasterPortTimersDisable( ); 154 EXIT_CRITICAL_SECTION( ); 155 } 156 157 eMBErrorCode 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 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 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 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 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.*/ 409 void vMBMasterGetRTUSndBuf( UCHAR ** pucFrame ) 410 { 411 *pucFrame = ( UCHAR * ) ucMasterRTUSndBuf; 412 } 413 414 /* Get Modbus Master send PDU's buffer address pointer.*/ 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.*/ 421 void vMBMasterSetPDUSndLength( USHORT SendPDULength ) 422 { 423 usMasterSendPDULength = SendPDULength; 424 } 425 426 /* Get Modbus Master send PDU's buffer length.*/ 427 USHORT usMBMasterGetPDUSndLength( void ) 428 { 429 return usMasterSendPDULength; 430 } 431 432 /* Set Modbus Master current timer mode.*/ 433 void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode ) 434 { 435 eMasterCurTimerMode = eMBTimerMode; 436 } 437 438 /* The master request is broadcast? */ 439 BOOL xMBMasterRequestIsBroadcast( void ){ 440 return xFrameIsBroadcast; 441 } 442 #endif 443 444