You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
12 KiB
415 lines
12 KiB
/* |
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. |
|
* Copyright (c) 2006 Christian Walter <wolti@sil.at> |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* 1. Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* 2. Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* 3. The name of the author may not be used to endorse or promote products |
|
* derived from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
* |
|
* File: $Id: mb.c,v 1.28 2010/06/06 13:54:40 wolti Exp $ |
|
*/ |
|
|
|
/* ----------------------- System includes ----------------------------------*/ |
|
#include <nuttx/config.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
/* ----------------------- Platform includes --------------------------------*/ |
|
#include "port.h" |
|
|
|
/* ----------------------- Modbus includes ----------------------------------*/ |
|
#include <apps/modbus/mb.h> |
|
#include <apps/modbus/mbframe.h> |
|
#include <apps/modbus/mbproto.h> |
|
#include <apps/modbus/mbfunc.h> |
|
|
|
#include <apps/modbus/mbport.h> |
|
|
|
#ifdef CONFIG_MB_RTU_ENABLED |
|
#include "mbrtu.h" |
|
#endif |
|
|
|
#ifdef CONFIG_MB_ASCII_ENABLED |
|
#include "mbascii.h" |
|
#endif |
|
|
|
#ifdef CONFIG_MB_TCP_ENABLED |
|
#include "mbtcp.h" |
|
#endif |
|
|
|
#ifndef MB_PORT_HAS_CLOSE |
|
#define MB_PORT_HAS_CLOSE 0 |
|
#endif |
|
|
|
/* ----------------------- Static variables ---------------------------------*/ |
|
|
|
static uint8_t ucMBAddress; |
|
static eMBMode eMBCurrentMode; |
|
|
|
static enum |
|
{ |
|
STATE_ENABLED, |
|
STATE_DISABLED, |
|
STATE_NOT_INITIALIZED |
|
} eMBState = STATE_NOT_INITIALIZED; |
|
|
|
/* Functions pointer which are initialized in eMBInit( ). Depending on the |
|
* mode (RTU or ASCII) the are set to the correct implementations. |
|
*/ |
|
static peMBFrameSend peMBFrameSendCur; |
|
static pvMBFrameStart pvMBFrameStartCur; |
|
static pvMBFrameStop pvMBFrameStopCur; |
|
static peMBFrameReceive peMBFrameReceiveCur; |
|
static pvMBFrameClose pvMBFrameCloseCur; |
|
|
|
/* Callback functions required by the porting layer. They are called when |
|
* an external event has happend which includes a timeout or the reception |
|
* or transmission of a character. |
|
*/ |
|
bool( *pxMBFrameCBByteReceived ) ( void ); |
|
bool( *pxMBFrameCBTransmitterEmpty ) ( void ); |
|
bool( *pxMBPortCBTimerExpired ) ( void ); |
|
|
|
bool( *pxMBFrameCBReceiveFSMCur ) ( void ); |
|
bool( *pxMBFrameCBTransmitFSMCur ) ( void ); |
|
|
|
/* An array of Modbus functions handlers which associates Modbus function |
|
* codes with implementing functions. |
|
*/ |
|
static xMBFunctionHandler xFuncHandlers[CONFIG_MB_FUNC_HANDLERS_MAX] = { |
|
#ifdef CONFIG_MB_FUNC_OTHER_REP_SLAVEID_ENABLED |
|
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_READ_INPUT_ENABLE |
|
{MB_FUNC_READ_INPUT_REGISTER, eMBFuncReadInputRegister}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_READ_HOLDING_ENABLED |
|
{MB_FUNC_READ_HOLDING_REGISTER, eMBFuncReadHoldingRegister}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED |
|
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBFuncWriteMultipleHoldingRegister}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_WRITE_HOLDING_ENABLED |
|
{MB_FUNC_WRITE_REGISTER, eMBFuncWriteHoldingRegister}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_READWRITE_HOLDING_ENABLED |
|
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_READ_COILS_ENABLED |
|
{MB_FUNC_READ_COILS, eMBFuncReadCoils}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_WRITE_COIL_ENABLED |
|
{MB_FUNC_WRITE_SINGLE_COIL, eMBFuncWriteCoil}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED |
|
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBFuncWriteMultipleCoils}, |
|
#endif |
|
#ifdef CONFIG_MB_FUNC_READ_DISCRETE_INPUTS_ENABLED |
|
{MB_FUNC_READ_DISCRETE_INPUTS, eMBFuncReadDiscreteInputs}, |
|
#endif |
|
}; |
|
|
|
/* ----------------------- Start implementation -----------------------------*/ |
|
eMBErrorCode |
|
eMBInit( eMBMode eMode, uint8_t ucSlaveAddress, uint8_t ucPort, speed_t ulBaudRate, eMBParity eParity ) |
|
{ |
|
eMBErrorCode eStatus = MB_ENOERR; |
|
|
|
/* check preconditions */ |
|
if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) || |
|
( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ) |
|
{ |
|
eStatus = MB_EINVAL; |
|
} |
|
else |
|
{ |
|
ucMBAddress = ucSlaveAddress; |
|
|
|
switch ( eMode ) |
|
{ |
|
#ifdef CONFIG_MB_RTU_ENABLED |
|
case MB_RTU: |
|
pvMBFrameStartCur = eMBRTUStart; |
|
pvMBFrameStopCur = eMBRTUStop; |
|
peMBFrameSendCur = eMBRTUSend; |
|
peMBFrameReceiveCur = eMBRTUReceive; |
|
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; |
|
pxMBFrameCBByteReceived = xMBRTUReceiveFSM; |
|
pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; |
|
pxMBPortCBTimerExpired = xMBRTUTimerT35Expired; |
|
|
|
eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity ); |
|
break; |
|
#endif |
|
#ifdef CONFIG_MB_ASCII_ENABLED |
|
case MB_ASCII: |
|
pvMBFrameStartCur = eMBASCIIStart; |
|
pvMBFrameStopCur = eMBASCIIStop; |
|
peMBFrameSendCur = eMBASCIISend; |
|
peMBFrameReceiveCur = eMBASCIIReceive; |
|
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; |
|
pxMBFrameCBByteReceived = xMBASCIIReceiveFSM; |
|
pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM; |
|
pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired; |
|
|
|
eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity ); |
|
break; |
|
#endif |
|
default: |
|
eStatus = MB_EINVAL; |
|
} |
|
|
|
if( eStatus == MB_ENOERR ) |
|
{ |
|
if( !xMBPortEventInit( ) ) |
|
{ |
|
/* port dependent event module initalization failed. */ |
|
eStatus = MB_EPORTERR; |
|
} |
|
else |
|
{ |
|
eMBCurrentMode = eMode; |
|
eMBState = STATE_DISABLED; |
|
} |
|
} |
|
} |
|
return eStatus; |
|
} |
|
|
|
#ifdef CONFIG_MB_TCP_ENABLED |
|
eMBErrorCode |
|
eMBTCPInit( uint16_t ucTCPPort ) |
|
{ |
|
eMBErrorCode eStatus = MB_ENOERR; |
|
|
|
if( ( eStatus = eMBTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) |
|
{ |
|
eMBState = STATE_DISABLED; |
|
} |
|
else if( !xMBPortEventInit( ) ) |
|
{ |
|
/* Port dependent event module initalization failed. */ |
|
eStatus = MB_EPORTERR; |
|
} |
|
else |
|
{ |
|
pvMBFrameStartCur = eMBTCPStart; |
|
pvMBFrameStopCur = eMBTCPStop; |
|
peMBFrameReceiveCur = eMBTCPReceive; |
|
peMBFrameSendCur = eMBTCPSend; |
|
pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBTCPPortClose : NULL; |
|
ucMBAddress = MB_TCP_PSEUDO_ADDRESS; |
|
eMBCurrentMode = MB_TCP; |
|
eMBState = STATE_DISABLED; |
|
} |
|
return eStatus; |
|
} |
|
#endif |
|
|
|
eMBErrorCode |
|
eMBRegisterCB( uint8_t ucFunctionCode, pxMBFunctionHandler pxHandler ) |
|
{ |
|
int i; |
|
eMBErrorCode eStatus; |
|
|
|
if( ( 0 < ucFunctionCode ) && ( ucFunctionCode <= 127 ) ) |
|
{ |
|
ENTER_CRITICAL_SECTION( ); |
|
if( pxHandler != NULL ) |
|
{ |
|
for( i = 0; i < CONFIG_MB_FUNC_HANDLERS_MAX; i++ ) |
|
{ |
|
if( ( xFuncHandlers[i].pxHandler == NULL ) || |
|
( xFuncHandlers[i].pxHandler == pxHandler ) ) |
|
{ |
|
xFuncHandlers[i].ucFunctionCode = ucFunctionCode; |
|
xFuncHandlers[i].pxHandler = pxHandler; |
|
break; |
|
} |
|
} |
|
eStatus = ( i != CONFIG_MB_FUNC_HANDLERS_MAX ) ? MB_ENOERR : MB_ENORES; |
|
} |
|
else |
|
{ |
|
for( i = 0; i < CONFIG_MB_FUNC_HANDLERS_MAX; i++ ) |
|
{ |
|
if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) |
|
{ |
|
xFuncHandlers[i].ucFunctionCode = 0; |
|
xFuncHandlers[i].pxHandler = NULL; |
|
break; |
|
} |
|
} |
|
/* Remove can't fail. */ |
|
eStatus = MB_ENOERR; |
|
} |
|
EXIT_CRITICAL_SECTION( ); |
|
} |
|
else |
|
{ |
|
eStatus = MB_EINVAL; |
|
} |
|
return eStatus; |
|
} |
|
|
|
|
|
eMBErrorCode |
|
eMBClose( void ) |
|
{ |
|
eMBErrorCode eStatus = MB_ENOERR; |
|
|
|
if( eMBState == STATE_DISABLED ) |
|
{ |
|
if( pvMBFrameCloseCur != NULL ) |
|
{ |
|
pvMBFrameCloseCur( ); |
|
} |
|
} |
|
else |
|
{ |
|
eStatus = MB_EILLSTATE; |
|
} |
|
return eStatus; |
|
} |
|
|
|
eMBErrorCode |
|
eMBEnable( void ) |
|
{ |
|
eMBErrorCode eStatus = MB_ENOERR; |
|
|
|
if( eMBState == STATE_DISABLED ) |
|
{ |
|
/* Activate the protocol stack. */ |
|
pvMBFrameStartCur( ); |
|
eMBState = STATE_ENABLED; |
|
} |
|
else |
|
{ |
|
eStatus = MB_EILLSTATE; |
|
} |
|
return eStatus; |
|
} |
|
|
|
eMBErrorCode |
|
eMBDisable( void ) |
|
{ |
|
eMBErrorCode eStatus; |
|
|
|
if( eMBState == STATE_ENABLED ) |
|
{ |
|
pvMBFrameStopCur( ); |
|
eMBState = STATE_DISABLED; |
|
eStatus = MB_ENOERR; |
|
} |
|
else if( eMBState == STATE_DISABLED ) |
|
{ |
|
eStatus = MB_ENOERR; |
|
} |
|
else |
|
{ |
|
eStatus = MB_EILLSTATE; |
|
} |
|
return eStatus; |
|
} |
|
|
|
eMBErrorCode |
|
eMBPoll( void ) |
|
{ |
|
static uint8_t *ucMBFrame; |
|
static uint8_t ucRcvAddress; |
|
static uint8_t ucFunctionCode; |
|
static uint16_t usLength; |
|
static eMBException eException; |
|
|
|
int i; |
|
eMBErrorCode eStatus = MB_ENOERR; |
|
eMBEventType eEvent; |
|
|
|
/* Check if the protocol stack is ready. */ |
|
if( eMBState != STATE_ENABLED ) |
|
{ |
|
return MB_EILLSTATE; |
|
} |
|
|
|
/* Check if there is a event available. If not return control to caller. |
|
* Otherwise we will handle the event. */ |
|
if( xMBPortEventGet( &eEvent ) == true ) |
|
{ |
|
switch ( eEvent ) |
|
{ |
|
case EV_READY: |
|
break; |
|
|
|
case EV_FRAME_RECEIVED: |
|
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); |
|
if( eStatus == MB_ENOERR ) |
|
{ |
|
/* Check if the frame is for us. If not ignore the frame. */ |
|
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) ) |
|
{ |
|
( void )xMBPortEventPost( EV_EXECUTE ); |
|
} |
|
} |
|
break; |
|
|
|
case EV_EXECUTE: |
|
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; |
|
eException = MB_EX_ILLEGAL_FUNCTION; |
|
for( i = 0; i < CONFIG_MB_FUNC_HANDLERS_MAX; i++ ) |
|
{ |
|
/* No more function handlers registered. Abort. */ |
|
if( xFuncHandlers[i].ucFunctionCode == 0 ) |
|
{ |
|
break; |
|
} |
|
else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode ) |
|
{ |
|
eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength ); |
|
break; |
|
} |
|
} |
|
|
|
/* If the request was not sent to the broadcast address we |
|
* return a reply. */ |
|
if( ucRcvAddress != MB_ADDRESS_BROADCAST ) |
|
{ |
|
if( eException != MB_EX_NONE ) |
|
{ |
|
/* An exception occured. Build an error frame. */ |
|
usLength = 0; |
|
ucMBFrame[usLength++] = ( uint8_t )( ucFunctionCode | MB_FUNC_ERROR ); |
|
ucMBFrame[usLength++] = eException; |
|
} |
|
if( ( eMBCurrentMode == MB_ASCII ) && CONFIG_MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ) |
|
{ |
|
vMBPortTimersDelay( CONFIG_MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS ); |
|
} |
|
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength ); |
|
} |
|
break; |
|
|
|
case EV_FRAME_SENT: |
|
break; |
|
} |
|
} |
|
return MB_ENOERR; |
|
}
|
|
|