nuttx-apps/modbus/mb_m.c
2024-02-17 05:01:18 -08:00

488 lines
14 KiB
C

/****************************************************************************
* FreeModbus Library: A portable Modbus implementation for Modbus ASCII/RTU.
*
* Copyright (C) 2013 Armink <armink.ztl@gmail.com>
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <string.h>
#include "port.h"
#include "modbus/mb.h"
#include "modbus/mb_m.h"
#include "modbus/mbframe.h"
#include "modbus/mbproto.h"
#include "modbus/mbfunc.h"
#include "modbus/mbport.h"
#ifdef CONFIG_MB_RTU_MASTER
# include "mbrtu_m.h"
#endif
#ifdef CONFIG_MB_ASCII_MASTER
# include "mbascii.h"
#endif
#ifdef CONFIG_MB_TCP_MASTER
# include "mbtcp.h"
#endif
#if defined(CONFIG_MB_RTU_MASTER) || defined(CONFIG_MB_ASCII_MASTER)
#ifndef CONFIG_MB_PORT_HAS_CLOSE
# define MB_PORT_HAS_CLOSE 0
#else
# define MB_PORT_HAS_CLOSE 1
#endif
/****************************************************************************
* Private Data
****************************************************************************/
static uint8_t ucMBMasterDestAddress;
static bool xMBRunInMasterMode = false;
static eMBMasterErrorEventType eMBMasterCurErrorType;
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.
* Using for Modbus Master,Add by Armink 20130813
*/
static peMBFrameSend peMBMasterFrameSendCur;
static pvMBFrameStart pvMBMasterFrameStartCur;
static pvMBFrameStop pvMBMasterFrameStopCur;
static peMBFrameReceive peMBMasterFrameReceiveCur;
static pvMBFrameClose pvMBMasterFrameCloseCur;
/* Callback functions required by the porting layer. They are called when
* an external event has happened which includes a timeout or the reception
* or transmission of a character.
* Using for Modbus Master,Add by Armink 20130813
*/
bool(*pxMBMasterFrameCBByteReceived) (void);
bool(*pxMBMasterFrameCBTransmitterEmpty) (void);
bool(*pxMBMasterPortCBTimerExpired) (void);
bool(*pxMBMasterFrameCBReceiveFSMCur) (void);
bool(*pxMBMasterFrameCBTransmitFSMCur) (void);
/* An array of Modbus functions handlers which associates Modbus function
* codes with implementing functions.
*/
static xMBFunctionHandler xMasterFuncHandlers[CONFIG_MB_FUNC_HANDLERS_MAX] = {
#ifdef CONFIG_MB_FUNC_OTHER_REP_SLAVEID_ENABLED
/* TODO Add Master function define */
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_READ_INPUT_ENABLED
{MB_FUNC_READ_INPUT_REGISTER, eMBMasterFuncReadInputRegister},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_READ_HOLDING_ENABLED
{MB_FUNC_READ_HOLDING_REGISTER, eMBMasterFuncReadHoldingRegister},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBMasterFuncWriteMultipleHoldingRegister},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_WRITE_HOLDING_ENABLED
{MB_FUNC_WRITE_REGISTER, eMBMasterFuncWriteHoldingRegister},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_READWRITE_HOLDING_ENABLED
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS,
eMBMasterFuncReadWriteMultipleHoldingRegister},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_READ_COILS_ENABLED
{MB_FUNC_READ_COILS, eMBMasterFuncReadCoils},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_WRITE_COIL_ENABLED
{MB_FUNC_WRITE_SINGLE_COIL, eMBMasterFuncWriteCoil},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_WRITE_MULTIPLE_COILS_ENABLED
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBMasterFuncWriteMultipleCoils},
#endif
#ifdef CONFIG_MB_MASTER_FUNC_READ_DISCRETE_INPUTS_ENABLED
{MB_FUNC_READ_DISCRETE_INPUTS, eMBMasterFuncReadDiscreteInputs},
#endif
};
/****************************************************************************
* Public Functions
****************************************************************************/
eMBErrorCode eMBMasterInit(eMBMode eMode, uint8_t ucPort,
speed_t ulBaudRate, eMBParity eParity)
{
eMBErrorCode eStatus = MB_ENOERR;
switch (eMode)
{
#ifdef CONFIG_MB_RTU_MASTER
case MB_RTU:
pvMBMasterFrameStartCur = eMBMasterRTUStart;
pvMBMasterFrameStopCur = eMBMasterRTUStop;
peMBMasterFrameSendCur = eMBMasterRTUSend;
peMBMasterFrameReceiveCur = eMBMasterRTUReceive;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
break;
#endif
#ifdef CONFIG_MB_ASCII_MASTER
case MB_ASCII:
pvMBMasterFrameStartCur = eMBMasterASCIIStart;
pvMBMasterFrameStopCur = eMBMasterASCIIStop;
peMBMasterFrameSendCur = eMBMasterASCIISend;
peMBMasterFrameReceiveCur = eMBMasterASCIIReceive;
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity);
break;
#endif
default:
eStatus = MB_EINVAL;
break;
}
if (eStatus == MB_ENOERR)
{
if (!xMBMasterPortEventInit())
{
/* Port dependent event module initialization failed. */
eStatus = MB_EPORTERR;
}
else
{
eMBState = STATE_DISABLED;
}
/* Initialize the OS resource for modbus master. */
vMBMasterOsResInit();
}
return eStatus;
}
eMBErrorCode eMBMasterClose(void)
{
eMBErrorCode eStatus = MB_ENOERR;
if (eMBState == STATE_DISABLED)
{
if (pvMBMasterFrameCloseCur != NULL)
{
pvMBMasterFrameCloseCur();
}
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode eMBMasterEnable(void)
{
eMBErrorCode eStatus = MB_ENOERR;
if (eMBState == STATE_DISABLED)
{
/* Activate the protocol stack. */
pvMBMasterFrameStartCur();
eMBState = STATE_ENABLED;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode eMBMasterDisable(void)
{
eMBErrorCode eStatus;
if (eMBState == STATE_ENABLED)
{
pvMBMasterFrameStopCur();
eMBState = STATE_DISABLED;
eStatus = MB_ENOERR;
}
else if (eMBState == STATE_DISABLED)
{
eStatus = MB_ENOERR;
}
else
{
eStatus = MB_EILLSTATE;
}
return eStatus;
}
eMBErrorCode eMBMasterPoll(void)
{
static uint8_t *ucMBFrame;
static uint8_t ucRcvAddress;
static uint8_t ucFunctionCode;
static uint16_t usLength;
static eMBException eException;
int i;
int j;
eMBErrorCode eStatus = MB_ENOERR;
eMBMasterEventType eEvent;
eMBMasterErrorEventType errorType;
/* 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 (xMBMasterPortEventGet(&eEvent) == true)
{
switch (eEvent)
{
case EV_MASTER_READY:
break;
case EV_MASTER_FRAME_RECEIVED:
eStatus =
peMBMasterFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);
/* Check if the frame is for us. If not, send an error process event. */
if ((eStatus == MB_ENOERR) &&
(ucRcvAddress == ucMBMasterGetDestAddress()))
{
xMBMasterPortEventPost(EV_MASTER_EXECUTE);
}
else
{
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
break;
case EV_MASTER_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
/* If receive frame has exception. The receive function code highest
* bit is 1.
*/
if (ucFunctionCode >> 7)
{
eException = (eMBException) ucMBFrame[MB_PDU_DATA_OFF];
}
else
{
for (i = 0; i < CONFIG_MB_FUNC_HANDLERS_MAX; i++)
{
/* No more function handlers registered. Abort. */
if (xMasterFuncHandlers[i].ucFunctionCode == 0)
{
break;
}
else if (xMasterFuncHandlers[i].ucFunctionCode ==
ucFunctionCode)
{
vMBMasterSetCBRunInMasterMode(true);
/* If master request is broadcast, the master need
* execute function for all slave. */
if (xMBMasterRequestIsBroadcast())
{
usLength = usMBMasterGetPDUSndLength();
for (j = 1; j <= CONFIG_MB_MASTER_TOTAL_SLAVE_NUM;
j++)
{
vMBMasterSetDestAddress(j);
eException =
xMasterFuncHandlers[i].pxHandler(ucMBFrame,
&usLength);
}
}
else
{
eException =
xMasterFuncHandlers[i].pxHandler(ucMBFrame,
&usLength);
}
vMBMasterSetCBRunInMasterMode(false);
break;
}
}
}
/* If master has exception, Master will send error process. Otherwise
* the Master is idle.
*/
if (eException != MB_EX_NONE)
{
vMBMasterSetErrorType(EV_ERROR_EXECUTE_FUNCTION);
xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
else
{
vMBMasterCBRequestSuccess();
vMBMasterRunResRelease();
}
break;
case EV_MASTER_FRAME_SENT:
/* Master is busy now. */
vMBMasterGetPDUSndBuf(&ucMBFrame);
eStatus =
peMBMasterFrameSendCur(ucMBMasterGetDestAddress(), ucMBFrame,
usMBMasterGetPDUSndLength());
break;
case EV_MASTER_ERROR_PROCESS:
/* Execute specified error process callback function. */
errorType = eMBMasterGetErrorType();
vMBMasterGetPDUSndBuf(&ucMBFrame);
switch (errorType)
{
case EV_ERROR_RESPOND_TIMEOUT:
vMBMasterErrorCBRespondTimeout(ucMBMasterGetDestAddress(),
ucMBFrame,
usMBMasterGetPDUSndLength());
break;
case EV_ERROR_RECEIVE_DATA:
vMBMasterErrorCBReceiveData(ucMBMasterGetDestAddress(),
ucMBFrame,
usMBMasterGetPDUSndLength());
break;
case EV_ERROR_EXECUTE_FUNCTION:
vMBMasterErrorCBExecuteFunction(ucMBMasterGetDestAddress(),
ucMBFrame,
usMBMasterGetPDUSndLength());
break;
}
vMBMasterRunResRelease();
break;
case EV_MASTER_PROCESS_SUCCESS:
case EV_MASTER_ERROR_RESPOND_TIMEOUT:
case EV_MASTER_ERROR_RECEIVE_DATA:
case EV_MASTER_ERROR_EXECUTE_FUNCTION:
default:
break;
}
}
return MB_ENOERR;
}
/* Get whether the Modbus Master is run in master mode.*/
bool xMBMasterGetCBRunInMasterMode(void)
{
return xMBRunInMasterMode;
}
/* Set whether the Modbus Master is run in master mode.*/
void vMBMasterSetCBRunInMasterMode(bool IsMasterMode)
{
xMBRunInMasterMode = IsMasterMode;
}
/* Get Modbus Master send destination address. */
uint8_t ucMBMasterGetDestAddress(void)
{
return ucMBMasterDestAddress;
}
/* Set Modbus Master send destination address. */
void vMBMasterSetDestAddress(uint8_t Address)
{
ucMBMasterDestAddress = Address;
}
/* Get Modbus Master current error event type. */
eMBMasterErrorEventType eMBMasterGetErrorType(void)
{
return eMBMasterCurErrorType;
}
/* Set Modbus Master current error event type. */
void vMBMasterSetErrorType(eMBMasterErrorEventType errorType)
{
eMBMasterCurErrorType = errorType;
}
#endif /* defined(CONFIG_MB_RTU_MASTER) || defined(CONFIG_MB_ASCII_MASTER) */