Modbus RTU/ASCII/TCP with lwip TCP working partial, but with errors (retransmitions)
Dependencies: EthernetNetIf mbed
mbrtu.cpp
00001 /* 00002 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. 00003 * Copyright (c) 2006 Christian Walter <wolti@sil.at> 00004 * All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 3. The name of the author may not be used to endorse or promote products 00015 * derived from this software without specific prior written permission. 00016 * 00017 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 00018 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00019 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 00020 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 00021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 00022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 00026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00027 * 00028 * File: $Id: mbrtu.c,v 1.18 2007/09/12 10:15:56 wolti Exp $ 00029 */ 00030 00031 /* ----------------------- System includes ----------------------------------*/ 00032 #include "stdlib.h" 00033 #include "string.h" 00034 00035 /* ----------------------- Platform includes --------------------------------*/ 00036 #include "port.h" 00037 00038 /* ----------------------- Modbus includes ----------------------------------*/ 00039 #include "mb.h" 00040 #include "mbrtu.h" 00041 #include "mbframe.h" 00042 00043 #include "mbcrc.h" 00044 #include "mbport.h" 00045 00046 /* ----------------------- Defines ------------------------------------------*/ 00047 #define MB_SER_PDU_SIZE_MIN 4 /*!< Minimum size of a Modbus RTU frame. */ 00048 #define MB_SER_PDU_SIZE_MAX 256 /*!< Maximum size of a Modbus RTU frame. */ 00049 #define MB_SER_PDU_SIZE_CRC 2 /*!< Size of CRC field in PDU. */ 00050 #define MB_SER_PDU_ADDR_OFF 0 /*!< Offset of slave address in Ser-PDU. */ 00051 #define MB_SER_PDU_PDU_OFF 1 /*!< Offset of Modbus-PDU in Ser-PDU. */ 00052 00053 /* ----------------------- Type definitions ---------------------------------*/ 00054 typedef enum 00055 { 00056 STATE_RX_INIT, /*!< Receiver is in initial state. */ 00057 STATE_RX_IDLE, /*!< Receiver is in idle state. */ 00058 STATE_RX_RCV, /*!< Frame is beeing received. */ 00059 STATE_RX_ERROR /*!< If the frame is invalid. */ 00060 } eMBRcvState; 00061 00062 typedef enum 00063 { 00064 STATE_TX_IDLE, /*!< Transmitter is in idle state. */ 00065 STATE_TX_XMIT /*!< Transmitter is in transfer state. */ 00066 } eMBSndState; 00067 00068 /* ----------------------- Static variables ---------------------------------*/ 00069 static volatile eMBSndState eSndState; 00070 static volatile eMBRcvState eRcvState; 00071 00072 volatile UCHAR ucRTUBuf[MB_SER_PDU_SIZE_MAX]; 00073 00074 static volatile UCHAR *pucSndBufferCur; 00075 static volatile USHORT usSndBufferCount; 00076 00077 static volatile USHORT usRcvBufferPos; 00078 00079 /* ----------------------- Start implementation -----------------------------*/ 00080 eMBErrorCode 00081 eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) 00082 { 00083 eMBErrorCode eStatus = MB_ENOERR ; 00084 ULONG usTimerT35_50us; 00085 00086 ( void )ucSlaveAddress; 00087 ENTER_CRITICAL_SECTION( ); 00088 00089 /* Modbus RTU uses 8 Databits. */ 00090 if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ) 00091 { 00092 eStatus = MB_EPORTERR ; 00093 } 00094 else 00095 { 00096 /* If baudrate > 19200 then we should use the fixed timer values 00097 * t35 = 1750us. Otherwise t35 must be 3.5 times the character time. 00098 */ 00099 if( ulBaudRate > 19200 ) 00100 { 00101 usTimerT35_50us = 35; /* 1800us. */ 00102 } 00103 else 00104 { 00105 /* The timer reload value for a character is given by: 00106 * 00107 * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 ) 00108 * = 11 * Ticks_per_1s / Baudrate 00109 * = 220000 / Baudrate 00110 * The reload for t3.5 is 1.5 times this value and similary 00111 * for t3.5. 00112 */ 00113 usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate ); 00114 } 00115 if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE ) 00116 { 00117 eStatus = MB_EPORTERR ; 00118 } 00119 } 00120 EXIT_CRITICAL_SECTION( ); 00121 00122 return eStatus; 00123 } 00124 00125 void 00126 eMBRTUStart( void ) 00127 { 00128 ENTER_CRITICAL_SECTION( ); 00129 /* Initially the receiver is in the state STATE_RX_INIT. we start 00130 * the timer and if no character is received within t3.5 we change 00131 * to STATE_RX_IDLE. This makes sure that we delay startup of the 00132 * modbus protocol stack until the bus is free. 00133 */ 00134 eRcvState = STATE_RX_INIT; 00135 vMBPortSerialEnable( TRUE, FALSE ); 00136 vMBPortTimersEnable( ); 00137 EXIT_CRITICAL_SECTION( ); 00138 } 00139 00140 void 00141 eMBRTUStop( void ) 00142 { 00143 ENTER_CRITICAL_SECTION( ); 00144 vMBPortSerialEnable( FALSE, FALSE ); 00145 vMBPortTimersDisable( ); 00146 EXIT_CRITICAL_SECTION( ); 00147 } 00148 00149 eMBErrorCode 00150 eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) 00151 { 00152 BOOL xFrameReceived = FALSE; 00153 eMBErrorCode eStatus = MB_ENOERR ; 00154 00155 ENTER_CRITICAL_SECTION( ); 00156 assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); 00157 00158 /* Length and CRC check */ 00159 if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) 00160 && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) 00161 { 00162 /* Save the address field. All frames are passed to the upper layer 00163 * and the decision if a frame is used is done there. 00164 */ 00165 *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; 00166 00167 /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus 00168 * size of address field and CRC checksum. 00169 */ 00170 *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); 00171 00172 /* Return the start of the Modbus PDU to the caller. */ 00173 *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; 00174 xFrameReceived = TRUE; 00175 00176 // Added by Cam 00177 // Now that the poll routine knows about the received frame, 00178 // clear the receive buffer position ready for the next frame received 00179 usRcvBufferPos = 0; 00180 00181 } 00182 else 00183 { 00184 eStatus = MB_EIO ; 00185 } 00186 00187 EXIT_CRITICAL_SECTION( ); 00188 return eStatus; 00189 } 00190 00191 eMBErrorCode 00192 eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) 00193 { 00194 eMBErrorCode eStatus = MB_ENOERR ; 00195 USHORT usCRC16; 00196 00197 ENTER_CRITICAL_SECTION( ); 00198 00199 /* Check if the receiver is still in idle state. If not we where to 00200 * slow with processing the received frame and the master sent another 00201 * frame on the network. We have to abort sending the frame. 00202 */ 00203 if( eRcvState == STATE_RX_IDLE ) 00204 { 00205 /* First byte before the Modbus-PDU is the slave address. */ 00206 pucSndBufferCur = ( UCHAR * ) pucFrame - 1; 00207 usSndBufferCount = 1; 00208 00209 /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ 00210 pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; 00211 usSndBufferCount += usLength; 00212 00213 /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ 00214 usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); 00215 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); 00216 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); 00217 00218 /* Activate the transmitter. */ 00219 eSndState = STATE_TX_XMIT; 00220 vMBPortSerialEnable( FALSE, TRUE ); 00221 } 00222 else 00223 { 00224 eStatus = MB_EIO ; 00225 } 00226 EXIT_CRITICAL_SECTION( ); 00227 return eStatus; 00228 } 00229 00230 BOOL 00231 xMBRTUReceiveFSM( void ) 00232 { 00233 BOOL xTaskNeedSwitch = FALSE; 00234 UCHAR ucByte; 00235 00236 assert( eSndState == STATE_TX_IDLE ); 00237 00238 /* Always read the character. */ 00239 ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); 00240 00241 switch ( eRcvState ) 00242 { 00243 /* If we have received a character in the init state we have to 00244 * wait until the frame is finished. 00245 */ 00246 case STATE_RX_INIT: 00247 vMBPortTimersEnable( ); 00248 break; 00249 00250 /* In the error state we wait until all characters in the 00251 * damaged frame are transmitted. 00252 */ 00253 case STATE_RX_ERROR: 00254 vMBPortTimersEnable( ); 00255 break; 00256 00257 /* In the idle state we wait for a new character. If a character 00258 * is received the t1.5 and t3.5 timers are started and the 00259 * receiver is in the state STATE_RX_RECEIVE. 00260 */ 00261 case STATE_RX_IDLE: 00262 ucRTUBuf[usRcvBufferPos++] = ucByte; 00263 eRcvState = STATE_RX_RCV; 00264 00265 /* Enable t3.5 timers. */ 00266 vMBPortTimersEnable( ); 00267 break; 00268 00269 /* We are currently receiving a frame. Reset the timer after 00270 * every character received. If more than the maximum possible 00271 * number of bytes in a modbus frame is received the frame is 00272 * ignored. 00273 */ 00274 case STATE_RX_RCV: 00275 if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) 00276 { 00277 ucRTUBuf[usRcvBufferPos++] = ucByte; 00278 } 00279 else 00280 { 00281 eRcvState = STATE_RX_ERROR; 00282 } 00283 vMBPortTimersEnable( ); 00284 break; 00285 } 00286 return xTaskNeedSwitch; 00287 } 00288 00289 BOOL 00290 xMBRTUTransmitFSM( void ) 00291 { 00292 BOOL xNeedPoll = FALSE; 00293 00294 assert( eRcvState == STATE_RX_IDLE ); 00295 switch ( eSndState ) 00296 { 00297 /* We should not get a transmitter event if the transmitter is in 00298 * idle state. */ 00299 case STATE_TX_IDLE: 00300 /* enable receiver/disable transmitter. */ 00301 vMBPortSerialEnable( TRUE, FALSE ); 00302 break; 00303 00304 case STATE_TX_XMIT: 00305 /* check if we are finished. */ 00306 if( usSndBufferCount != 0 ) 00307 { 00308 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); 00309 pucSndBufferCur++; /* next byte in sendbuffer. */ 00310 usSndBufferCount--; 00311 } 00312 else 00313 { 00314 xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); 00315 /* Disable transmitter. This prevents another transmit buffer 00316 * empty interrupt. */ 00317 vMBPortSerialEnable( TRUE, FALSE ); 00318 eSndState = STATE_TX_IDLE; 00319 } 00320 break; 00321 } 00322 00323 return xNeedPoll; 00324 } 00325 00326 BOOL 00327 xMBRTUTimerT35Expired( void ) 00328 { 00329 BOOL xNeedPoll = FALSE; 00330 00331 switch ( eRcvState ) 00332 { 00333 /* Timer t35 expired. Startup phase is finished. */ 00334 case STATE_RX_INIT: 00335 xNeedPoll = xMBPortEventPost( EV_READY ); 00336 break; 00337 00338 /* A frame was received and t35 expired. Notify the listener that 00339 * a new frame was received. */ 00340 case STATE_RX_RCV: 00341 xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); 00342 break; 00343 00344 /* An error occured while receiving the frame. */ 00345 case STATE_RX_ERROR: 00346 break; 00347 00348 /* Function called in an illegal state. */ 00349 default: 00350 assert( ( eRcvState == STATE_RX_INIT ) || 00351 ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) ); 00352 } 00353 00354 vMBPortTimersDisable( ); 00355 eRcvState = STATE_RX_IDLE; 00356 00357 return xNeedPoll; 00358 }
Generated on Tue Jul 12 2022 21:29:48 by 1.7.2