Daniel McKinnell / Modbus-Slave
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers mb.cpp Source File

mb.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: mb.c,v 1.27 2007/02/18 23:45:41 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 "mbconfig.h"
00041 #include "mbframe.h"
00042 #include "mbproto.h"
00043 #include "mbfunc.h"
00044 #include "mbport.h"
00045 
00046 #if MB_RTU_ENABLED == 1
00047 #include "mbrtu.h"
00048 #endif
00049 #if MB_RTU_CUSTOM_ENABLED == 1
00050 #include "mbrtucustom.h"
00051 #endif
00052 #if MB_ASCII_ENABLED == 1
00053 #include "mbascii.h"
00054 #endif
00055 #if MB_TCP_ENABLED == 1
00056 #include "mbtcp.h"
00057 #endif
00058 
00059 #ifndef MB_PORT_HAS_CLOSE
00060 #define MB_PORT_HAS_CLOSE 0
00061 #endif
00062 
00063 /* ----------------------- Static variables ---------------------------------*/
00064 
00065 
00066 static UCHAR    ucMBAddress;
00067 static eMBMode  eMBCurrentMode;
00068 
00069 static enum {
00070     STATE_ENABLED,
00071     STATE_DISABLED,
00072     STATE_NOT_INITIALIZED
00073 } eMBState = STATE_NOT_INITIALIZED;
00074 
00075 /* Functions pointer which are initialized in eMBInit( ). Depending on the
00076  * mode (RTU or ASCII) the are set to the correct implementations.
00077  */
00078 static peMBFrameSend peMBFrameSendCur;
00079 static pvMBFrameStart pvMBFrameStartCur;
00080 static pvMBFrameStop pvMBFrameStopCur;
00081 static peMBFrameReceive peMBFrameReceiveCur;
00082 static pvMBFrameClose pvMBFrameCloseCur;
00083 
00084 /* Callback functions required by the porting layer. They are called when
00085  * an external event has happend which includes a timeout or the reception
00086  * or transmission of a character.
00087  */
00088 BOOL( *pxMBFrameCBByteReceived ) ( void );
00089 BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
00090 BOOL( *pxMBPortCBTimerExpired ) ( void );
00091 
00092 BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
00093 BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );
00094 
00095 /* An array of Modbus functions handlers which associates Modbus function
00096  * codes with implementing functions.
00097  */
00098 static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
00099 #if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
00100     {MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
00101 #endif
00102 #if MB_FUNC_READ_INPUT_ENABLED > 0
00103     {MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister},
00104 #endif
00105 #if MB_FUNC_READ_HOLDING_ENABLED > 0
00106     {MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister},
00107 #endif
00108 #if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
00109     {MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister},
00110 #endif
00111 #if MB_FUNC_WRITE_HOLDING_ENABLED > 0
00112     {MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister},
00113 #endif
00114 #if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
00115     {MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},
00116 #endif
00117 #if MB_FUNC_READ_COILS_ENABLED > 0
00118     {MB_FUNC_READ_COILS, eMBFuncReadCoils},
00119 #endif
00120 #if MB_FUNC_WRITE_COIL_ENABLED > 0
00121     {MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil},
00122 #endif
00123 #if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
00124     {MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils},
00125 #endif
00126 #if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
00127     {MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs},
00128 #endif
00129 };
00130 
00131 /* ----------------------- Start implementation -----------------------------*/
00132 eMBErrorCode
00133 eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) {
00134     eMBErrorCode    eStatus = MB_ENOERR ;
00135 
00136     /* check preconditions */
00137     if ( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
00138             ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) {
00139         eStatus = MB_EINVAL ;
00140     } else {
00141         ucMBAddress = ucSlaveAddress;
00142 
00143         switch ( eMode ) {
00144 #if MB_RTU_ENABLED > 0
00145             case MB_RTU :
00146                 pvMBFrameStartCur = eMBRTUStart;
00147                 pvMBFrameStopCur = eMBRTUStop;
00148                 peMBFrameSendCur = eMBRTUSend;
00149                 peMBFrameReceiveCur = eMBRTUReceive;
00150                 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
00151                 pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
00152                 pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
00153                 pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;
00154 
00155                 eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
00156                 break;
00157 #endif
00158 #if MB_ASCII_ENABLED > 0
00159             case MB_ASCII :
00160                 pvMBFrameStartCur = eMBASCIIStart;
00161                 pvMBFrameStopCur = eMBASCIIStop;
00162                 peMBFrameSendCur = eMBASCIISend;
00163                 peMBFrameReceiveCur = eMBASCIIReceive;
00164                 pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
00165                 pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;
00166                 pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;
00167                 pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;
00168 
00169                 eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );
00170                 break;
00171 #endif
00172             default:
00173                 eStatus = MB_EINVAL ;
00174         }
00175 
00176         if ( eStatus == MB_ENOERR  ) {
00177             if ( !xMBPortEventInit(  ) ) {
00178                 /* port dependent event module initalization failed. */
00179                 eStatus = MB_EPORTERR ;
00180             } else {
00181                 eMBCurrentMode = eMode;
00182                 eMBState = STATE_DISABLED;
00183             }
00184         }
00185     }
00186     return eStatus;
00187 }
00188 
00189 #if MB_RTU_CUSTOM_ENABLED > 0
00190 eMBErrorCode
00191 eMBRTUCustomInit( UCHAR ucSlaveAddress ) {
00192     eMBErrorCode    eStatus = MB_ENOERR ;
00193     ucMBAddress = ucSlaveAddress;
00194     eStatus = eMBRTUCustInit( ucSlaveAddress );
00195     // if ( ( eStatus = eMBRTUCustInit( ucMBAddress ) ) != MB_ENOERR ) {
00196         eMBState = STATE_DISABLED;
00197     // } else 
00198     if ( !xMBPortEventInit(  ) ) {
00199         /* Port dependent event module initalization failed. */
00200         eStatus = MB_EPORTERR ;
00201     }
00202      else {
00203         pvMBFrameStartCur = eMBRTUCustStart;
00204         pvMBFrameStopCur = eMBRTUCustStop;
00205         peMBFrameSendCur = eMBRTUCustSend;
00206         peMBFrameReceiveCur = eMBRTUCustReceive;
00207         pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
00208 
00209         eMBCurrentMode = MB_RTU_CUSTOM ;
00210         eMBState = STATE_DISABLED;
00211     }
00212     return eStatus;
00213 }
00214 #endif
00215 
00216 #if MB_TCP_ENABLED > 0
00217 eMBErrorCode
00218 eMBTCPInit( USHORT ucTCPPort ) {
00219     eMBErrorCode    eStatus = MB_ENOERR ;
00220 
00221     if ( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR  ) {
00222         eMBState = STATE_DISABLED;
00223     } else if ( !xMBPortEventInit(  ) ) {
00224         /* Port dependent event module initalization failed. */
00225         eStatus = MB_EPORTERR ;
00226     } else {
00227         pvMBFrameStartCur = eMBTCPStart;
00228         pvMBFrameStopCur = eMBTCPStop;
00229         peMBFrameReceiveCur = eMBTCPReceive;
00230         peMBFrameSendCur = eMBTCPSend;
00231         pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL;
00232         ucMBAddress = MB_TCP_PSEUDO_ADDRESS;
00233         eMBCurrentMode = MB_TCP ;
00234         eMBState = STATE_DISABLED;
00235     }
00236     return eStatus;
00237 }
00238 #endif
00239 
00240 
00241 eMBErrorCode
00242 eMBRegisterCB( UCHAR ucFunctionCode, pxMBFunctionHandler pxHandler ) {
00243     int             i;
00244     eMBErrorCode    eStatus;
00245 
00246     if ( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) ) {
00247         ENTER_CRITICAL_SECTION(  );
00248         if ( pxHandler != NULL ) {
00249             for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) {
00250                 if ( ( xFuncHandlers[i].pxHandler == NULL ) ||
00251                         ( xFuncHandlers[i].pxHandler == pxHandler ) ) {
00252                     xFuncHandlers[i].ucFunctionCode = ucFunctionCode;
00253                     xFuncHandlers[i].pxHandler = pxHandler;
00254                     break;
00255                 }
00256             }
00257             eStatus = ( i != MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR  : MB_ENORES ;
00258         } else {
00259             for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) {
00260                 if ( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) {
00261                     xFuncHandlers[i].ucFunctionCode = 0;
00262                     xFuncHandlers[i].pxHandler = NULL;
00263                     break;
00264                 }
00265             }
00266             /* Remove can't fail. */
00267             eStatus = MB_ENOERR ;
00268         }
00269         EXIT_CRITICAL_SECTION(  );
00270     } else {
00271         eStatus = MB_EINVAL ;
00272     }
00273     return eStatus;
00274 }
00275 
00276 
00277 eMBErrorCode
00278 eMBClose( void ) {
00279     eMBErrorCode    eStatus = MB_ENOERR ;
00280 
00281     if ( eMBState == STATE_DISABLED ) {
00282         if ( pvMBFrameCloseCur != NULL ) {
00283             pvMBFrameCloseCur(  );
00284         }
00285     } else {
00286         eStatus = MB_EILLSTATE ;
00287     }
00288     return eStatus;
00289 }
00290 
00291 eMBErrorCode
00292 eMBEnable( void ) {
00293     eMBErrorCode    eStatus = MB_ENOERR ;
00294 
00295     if ( eMBState == STATE_DISABLED ) {
00296         /* Activate the protocol stack. */
00297         pvMBFrameStartCur(  );
00298         eMBState = STATE_ENABLED;
00299     } else {
00300         eStatus = MB_EILLSTATE ;
00301     }
00302     return eStatus;
00303 }
00304 
00305 eMBErrorCode
00306 eMBDisable( void ) {
00307     eMBErrorCode    eStatus;
00308 
00309     if ( eMBState == STATE_ENABLED ) {
00310         pvMBFrameStopCur(  );
00311         eMBState = STATE_DISABLED;
00312         eStatus = MB_ENOERR ;
00313     } else if ( eMBState == STATE_DISABLED ) {
00314         eStatus = MB_ENOERR ;
00315     } else {
00316         eStatus = MB_EILLSTATE ;
00317     }
00318     return eStatus;
00319 }
00320 
00321 eMBErrorCode
00322 eMBPoll( void ) {
00323     static UCHAR   *ucMBFrame;
00324     static UCHAR    ucRcvAddress;
00325     static UCHAR    ucFunctionCode;
00326     static USHORT   usLength;
00327     static eMBException eException;
00328 
00329     int             i;
00330     eMBErrorCode    eStatus = MB_ENOERR ;
00331     eMBEventType    eEvent;
00332 
00333     /* Check if the protocol stack is ready. */
00334     if ( eMBState != STATE_ENABLED ) {
00335         return MB_EILLSTATE ;
00336     }
00337 
00338     /* Check if there is a event available. If not return control to caller.
00339      * Otherwise we will handle the event. */
00340     if ( xMBPortEventGet( &eEvent ) == TRUE ) {
00341         switch ( eEvent ) {
00342             case EV_READY:
00343                 break;
00344 
00345             case EV_FRAME_RECEIVED:
00346                 eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
00347                 if ( eStatus == MB_ENOERR  ) {
00348                     /* Check if the frame is for us. If not ignore the frame. */
00349                     if ( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) {
00350                         ( void )xMBPortEventPost( EV_EXECUTE );
00351                     }
00352                 }
00353                 break;
00354 
00355             case EV_EXECUTE:
00356                 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
00357                 eException = MB_EX_ILLEGAL_FUNCTION;
00358                 
00359  
00360                 for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ ) {
00361                     /* No more function handlers registered. Abort. */
00362                     if ( xFuncHandlers[i].ucFunctionCode == 0 ) {
00363                         break;
00364                     } else if ( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) {
00365                         eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
00366                         break;
00367                     }
00368                 }
00369 
00370                 /* If the request was not sent to the broadcast address we
00371                  * return a reply. */
00372                 if ( ucRcvAddress != MB_ADDRESS_BROADCAST ) {
00373                     if ( eException != MB_EX_NONE ) {
00374                         /* An exception occured. Build an error frame. */
00375                         usLength = 0;
00376                         ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
00377                         ucMBFrame[usLength++] = eException;
00378                     }
00379                     eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
00380                 }
00381                 break;
00382 
00383             case EV_FRAME_SENT:
00384                 break;
00385         }
00386     }
00387     return MB_ENOERR ;
00388 }