Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 20:43:26 by
1.7.2