Protocole MB
Embed:
(wiki syntax)
Show/hide line numbers
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 00138 EXIT_CRITICAL_SECTION( ); 00139 } 00140 00141 void 00142 eMBRTUStop( void ) 00143 { 00144 ENTER_CRITICAL_SECTION( ); 00145 vMBPortSerialEnable( FALSE, FALSE ); 00146 vMBPortTimersDisable( ); 00147 EXIT_CRITICAL_SECTION( ); 00148 } 00149 00150 eMBErrorCode 00151 eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) 00152 { 00153 BOOL xFrameReceived = FALSE; 00154 eMBErrorCode eStatus = MB_ENOERR ; 00155 00156 ENTER_CRITICAL_SECTION( ); 00157 assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); 00158 00159 /* Length and CRC check */ 00160 if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) 00161 && ( usMBCRC16( ( UCHAR * ) ucRTUBuf, usRcvBufferPos ) == 0 ) ) 00162 { 00163 /* Save the address field. All frames are passed to the upper layed 00164 * and the decision if a frame is used is done there. 00165 */ 00166 *pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF]; 00167 00168 /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus 00169 * size of address field and CRC checksum. 00170 */ 00171 *pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC ); 00172 00173 /* Return the start of the Modbus PDU to the caller. */ 00174 *pucFrame = ( UCHAR * ) & ucRTUBuf[MB_SER_PDU_PDU_OFF]; 00175 xFrameReceived = TRUE; 00176 00177 // Added by Cam 00178 // Now that the poll routine knows about the received frame, 00179 // clear the receive buffer position ready for the next frame received 00180 //usRcvBufferPos = 0; 00181 00182 } 00183 else 00184 { 00185 eStatus = MB_EIO ; 00186 } 00187 00188 EXIT_CRITICAL_SECTION( ); 00189 return eStatus; 00190 } 00191 00192 eMBErrorCode 00193 eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) 00194 { 00195 eMBErrorCode eStatus = MB_ENOERR ; 00196 USHORT usCRC16; 00197 00198 ENTER_CRITICAL_SECTION( ); 00199 00200 /* Check if the receiver is still in idle state. If not we where to 00201 * slow with processing the received frame and the master sent another 00202 * frame on the network. We have to abort sending the frame. 00203 */ 00204 if( eRcvState == STATE_RX_IDLE ) 00205 { 00206 /* First byte before the Modbus-PDU is the slave address. */ 00207 pucSndBufferCur = ( UCHAR * ) pucFrame - 1; 00208 usSndBufferCount = 1; 00209 00210 /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ 00211 pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; 00212 usSndBufferCount += usLength; 00213 00214 /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ 00215 usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); 00216 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); 00217 ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); 00218 00219 /* Activate the transmitter. */ 00220 eSndState = STATE_TX_XMIT; 00221 vMBPortSerialEnable( FALSE, TRUE ); 00222 } 00223 else 00224 { 00225 eStatus = MB_EIO ; 00226 } 00227 EXIT_CRITICAL_SECTION( ); 00228 return eStatus; 00229 } 00230 00231 BOOL 00232 xMBRTUReceiveFSM( void ) 00233 { 00234 BOOL xTaskNeedSwitch = FALSE; 00235 UCHAR ucByte; 00236 00237 assert( eSndState == STATE_TX_IDLE ); 00238 00239 /* Always read the character. */ 00240 ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte ); 00241 00242 switch ( eRcvState ) 00243 { 00244 /* If we have received a character in the init state we have to 00245 * wait until the frame is finished. 00246 */ 00247 case STATE_RX_INIT: 00248 vMBPortTimersEnable( ); 00249 break; 00250 00251 /* In the error state we wait until all characters in the 00252 * damaged frame are transmitted. 00253 */ 00254 case STATE_RX_ERROR: 00255 vMBPortTimersEnable( ); 00256 break; 00257 00258 /* In the idle state we wait for a new character. If a character 00259 * is received the t1.5 and t3.5 timers are started and the 00260 * receiver is in the state STATE_RX_RECEIVCE. 00261 */ 00262 case STATE_RX_IDLE: 00263 usRcvBufferPos = 0; //cam will comment this 00264 ucRTUBuf[usRcvBufferPos++] = ucByte; 00265 eRcvState = STATE_RX_RCV; 00266 00267 /* Enable t3.5 timers. */ 00268 vMBPortTimersEnable( ); 00269 break; 00270 00271 /* We are currently receiving a frame. Reset the timer after 00272 * every character received. If more than the maximum possible 00273 * number of bytes in a modbus frame is received the frame is 00274 * ignored. 00275 */ 00276 case STATE_RX_RCV: 00277 if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) 00278 { 00279 ucRTUBuf[usRcvBufferPos++] = ucByte; 00280 } 00281 else 00282 { 00283 eRcvState = STATE_RX_ERROR; 00284 } 00285 vMBPortTimersEnable( ); 00286 break; 00287 } 00288 return xTaskNeedSwitch; 00289 } 00290 00291 BOOL 00292 xMBRTUTransmitFSM( void ) 00293 { 00294 BOOL xNeedPoll = FALSE; 00295 00296 assert( eRcvState == STATE_RX_IDLE ); 00297 00298 switch ( eSndState ) 00299 { 00300 /* We should not get a transmitter event if the transmitter is in 00301 * idle state. */ 00302 case STATE_TX_IDLE: 00303 /* enable receiver/disable transmitter. */ 00304 vMBPortSerialEnable( TRUE, FALSE ); 00305 break; 00306 00307 case STATE_TX_XMIT: 00308 /* check if we are finished. */ 00309 if( usSndBufferCount != 0 ) 00310 { 00311 xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur ); 00312 pucSndBufferCur++; /* next byte in sendbuffer. */ 00313 usSndBufferCount--; 00314 } 00315 else 00316 { 00317 xNeedPoll = xMBPortEventPost( EV_FRAME_SENT ); 00318 /* Disable transmitter. This prevents another transmit buffer 00319 * empty interrupt. */ 00320 vMBPortSerialEnable( TRUE, FALSE ); 00321 eSndState = STATE_TX_IDLE; 00322 } 00323 break; 00324 } 00325 00326 return xNeedPoll; 00327 } 00328 00329 BOOL 00330 xMBRTUTimerT35Expired( void ) 00331 { 00332 BOOL xNeedPoll = FALSE; 00333 00334 switch ( eRcvState ) 00335 { 00336 /* Timer t35 expired. Startup phase is finished. */ 00337 case STATE_RX_INIT: 00338 xNeedPoll = xMBPortEventPost( EV_READY ); 00339 break; 00340 00341 /* A frame was received and t35 expired. Notify the listener that 00342 * a new frame was received. */ 00343 case STATE_RX_RCV: 00344 xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED ); 00345 break; 00346 00347 /* An error occured while receiving the frame. */ 00348 case STATE_RX_ERROR: 00349 break; 00350 00351 /* Function called in an illegal state. */ 00352 default: 00353 assert( ( eRcvState == STATE_RX_INIT ) || 00354 ( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) ); 00355 } 00356 00357 vMBPortTimersDisable( ); 00358 eRcvState = STATE_RX_IDLE; 00359 00360 return xNeedPoll; 00361 }
Generated on Wed Jul 13 2022 14:22:48 by
1.7.2