641 lines
26 KiB
C
641 lines
26 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2013 Armink
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* SPDX-FileContributor: 2016-2021 Espressif Systems (Shanghai) CO LTD
|
|
*/
|
|
/*
|
|
* FreeModbus Libary: 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.
|
|
*
|
|
* File: $Id: mbrtu_m.c,v 1.60 2013/08/20 11:18:10 Armink Add Master Functions $
|
|
*/
|
|
|
|
/* ----------------------- System includes ----------------------------------*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdatomic.h>
|
|
|
|
/* ----------------------- Platform includes --------------------------------*/
|
|
#include "port.h"
|
|
|
|
/* ----------------------- Modbus includes ----------------------------------*/
|
|
#include "mb_m.h"
|
|
#include "mbconfig.h"
|
|
#include "mbframe.h"
|
|
#include "mbproto.h"
|
|
#include "mbfunc.h"
|
|
|
|
#include "mbport.h"
|
|
#if MB_MASTER_RTU_ENABLED
|
|
#include "mbrtu.h"
|
|
#endif
|
|
#if MB_MASTER_ASCII_ENABLED
|
|
#include "mbascii.h"
|
|
#endif
|
|
#if MB_MASTER_TCP_ENABLED
|
|
#include "mbtcp.h"
|
|
#include "mbtcp_m.h"
|
|
#endif
|
|
|
|
#if MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|
|
|
|
#ifndef MB_PORT_HAS_CLOSE
|
|
#define MB_PORT_HAS_CLOSE 1
|
|
#endif
|
|
|
|
/*------------------------ Shared variables ---------------------------------*/
|
|
|
|
_lock_t xMBMLock; // base modbus object lock
|
|
volatile UCHAR ucMasterSndBuf[MB_SERIAL_BUF_SIZE];
|
|
volatile UCHAR ucMasterRcvBuf[MB_SERIAL_BUF_SIZE];
|
|
|
|
static _Atomic USHORT usMasterSendPDULength = 0;
|
|
static _Atomic eMBMasterErrorEventType eMBMasterCurErrorType = EV_ERROR_INIT;
|
|
static _Atomic BOOL xMBRunInMasterMode = FALSE;
|
|
static _Atomic UCHAR ucMBMasterDestAddress = 0;
|
|
static _Atomic BOOL xFrameIsBroadcast = FALSE;
|
|
|
|
static _Atomic eMBMasterTimerMode eMasterCurTimerMode;
|
|
|
|
/* ----------------------- Static variables ---------------------------------*/
|
|
static uint64_t xCurTransactionId = 0;
|
|
static UCHAR *pucMBSendFrame = NULL;
|
|
static UCHAR *pucMBRecvFrame = NULL;
|
|
static UCHAR ucRecvAddress = 0;
|
|
static eMBMode eMBMasterCurrentMode;
|
|
|
|
/* The transaction information structure which keep last processing state */
|
|
static TransactionInfo_t xTransactionInfo = {0};
|
|
|
|
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 happend 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[MB_FUNC_HANDLERS_MAX] = {
|
|
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
|
|
{MB_FUNC_OTHER_REPORT_SLAVEID, eMBMasterFuncReportSlaveID},
|
|
#endif
|
|
#if MB_FUNC_READ_INPUT_ENABLED > 0
|
|
{MB_FUNC_READ_INPUT_REGISTER, eMBMasterFuncReadInputRegister},
|
|
#endif
|
|
#if MB_FUNC_READ_HOLDING_ENABLED > 0
|
|
{MB_FUNC_READ_HOLDING_REGISTER, eMBMasterFuncReadHoldingRegister},
|
|
#endif
|
|
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
|
|
{MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBMasterFuncWriteMultipleHoldingRegister},
|
|
#endif
|
|
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
|
|
{MB_FUNC_WRITE_REGISTER, eMBMasterFuncWriteHoldingRegister},
|
|
#endif
|
|
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
|
|
{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBMasterFuncReadWriteMultipleHoldingRegister},
|
|
#endif
|
|
#if MB_FUNC_READ_COILS_ENABLED > 0
|
|
{MB_FUNC_READ_COILS, eMBMasterFuncReadCoils},
|
|
#endif
|
|
#if MB_FUNC_WRITE_COIL_ENABLED > 0
|
|
{MB_FUNC_WRITE_SINGLE_COIL, eMBMasterFuncWriteCoil},
|
|
#endif
|
|
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
|
|
{MB_FUNC_WRITE_MULTIPLE_COILS, eMBMasterFuncWriteMultipleCoils},
|
|
#endif
|
|
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
|
|
{MB_FUNC_READ_DISCRETE_INPUTS, eMBMasterFuncReadDiscreteInputs},
|
|
#endif
|
|
};
|
|
|
|
/* ----------------------- Start implementation -----------------------------*/
|
|
#if MB_MASTER_TCP_ENABLED
|
|
eMBErrorCode
|
|
eMBMasterTCPInit( USHORT ucTCPPort )
|
|
{
|
|
eMBErrorCode eStatus = MB_ENOERR;
|
|
|
|
if( ( eStatus = eMBMasterTCPDoInit( ucTCPPort ) ) != MB_ENOERR ) {
|
|
eMBState = STATE_DISABLED;
|
|
}
|
|
else if( !xMBMasterPortEventInit( ) ) {
|
|
/* Port dependent event module initialization failed. */
|
|
eStatus = MB_EPORTERR;
|
|
} else {
|
|
pvMBMasterFrameStartCur = eMBMasterTCPStart;
|
|
pvMBMasterFrameStopCur = eMBMasterTCPStop;
|
|
peMBMasterFrameReceiveCur = eMBMasterTCPReceive;
|
|
peMBMasterFrameSendCur = eMBMasterTCPSend;
|
|
pxMBMasterPortCBTimerExpired = xMBMasterTCPTimerExpired;
|
|
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterTCPPortClose : NULL;
|
|
eMBMasterCurrentMode = MB_TCP;
|
|
eMBState = STATE_DISABLED;
|
|
ucRecvAddress = MB_TCP_PSEUDO_ADDRESS;
|
|
xCurTransactionId = 0;
|
|
xTransactionInfo.xTransId = 0;
|
|
xTransactionInfo.ucDestAddr = 0;
|
|
xTransactionInfo.ucFuncCode = 0;
|
|
xTransactionInfo.eException = MB_EX_NONE;
|
|
xTransactionInfo.ucFrameError = 0;
|
|
|
|
/* initialize the state values. */
|
|
atomic_init(&usMasterSendPDULength, 0);
|
|
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
|
|
atomic_init(&xMBRunInMasterMode, FALSE);
|
|
atomic_init(&ucMBMasterDestAddress, MB_TCP_PSEUDO_ADDRESS);
|
|
|
|
// initialize the OS resource for modbus master.
|
|
vMBMasterOsResInit();
|
|
if (xMBMasterPortTimersInit(MB_MASTER_TIMEOUT_MS_RESPOND * MB_TIMER_TICS_PER_MS) != TRUE)
|
|
{
|
|
eStatus = MB_EPORTERR;
|
|
}
|
|
|
|
}
|
|
return eStatus;
|
|
}
|
|
#endif
|
|
|
|
eMBErrorCode
|
|
eMBMasterSerialInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
|
|
{
|
|
eMBErrorCode eStatus = MB_ENOERR;
|
|
|
|
switch (eMode)
|
|
{
|
|
#if MB_MASTER_RTU_ENABLED > 0
|
|
case MB_RTU:
|
|
pvMBMasterFrameStartCur = eMBMasterRTUStart;
|
|
pvMBMasterFrameStopCur = eMBMasterRTUStop;
|
|
peMBMasterFrameSendCur = eMBMasterRTUSend;
|
|
peMBMasterFrameReceiveCur = eMBMasterRTUReceive;
|
|
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
|
|
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
|
|
pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
|
|
pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;
|
|
eMBMasterCurrentMode = eMode;
|
|
|
|
eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
|
|
break;
|
|
#endif
|
|
#if MB_MASTER_ASCII_ENABLED > 0
|
|
case MB_ASCII:
|
|
pvMBMasterFrameStartCur = eMBMasterASCIIStart;
|
|
pvMBMasterFrameStopCur = eMBMasterASCIIStop;
|
|
peMBMasterFrameSendCur = eMBMasterASCIISend;
|
|
peMBMasterFrameReceiveCur = eMBMasterASCIIReceive;
|
|
pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
|
|
pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
|
|
pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
|
|
pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;
|
|
eMBMasterCurrentMode = eMode;
|
|
|
|
eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity );
|
|
break;
|
|
#endif
|
|
default:
|
|
eStatus = MB_EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (eStatus == MB_ENOERR)
|
|
{
|
|
if (!xMBMasterPortEventInit())
|
|
{
|
|
/* port dependent event module initalization failed. */
|
|
eStatus = MB_EPORTERR;
|
|
}
|
|
else
|
|
{
|
|
eMBState = STATE_DISABLED;
|
|
ucRecvAddress = 0;
|
|
xCurTransactionId = 0;
|
|
xTransactionInfo.xTransId = 0;
|
|
xTransactionInfo.ucDestAddr = 0;
|
|
xTransactionInfo.ucFuncCode = 0;
|
|
xTransactionInfo.eException = MB_EX_NONE;
|
|
xTransactionInfo.ucFrameError = 0;
|
|
|
|
/* initialize the state values. */
|
|
atomic_init(&usMasterSendPDULength, 0);
|
|
atomic_init(&eMBMasterCurErrorType, EV_ERROR_INIT);
|
|
atomic_init(&xMBRunInMasterMode, FALSE);
|
|
atomic_init(&ucMBMasterDestAddress, 0);
|
|
}
|
|
/* 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( );
|
|
/* Release the resource, because it created in busy state */
|
|
//vMBMasterRunResRelease( );
|
|
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 )
|
|
{
|
|
int i;
|
|
int j;
|
|
eMBErrorCode eStatus = MB_ENOERR;
|
|
xMBMasterEventType xEvent;
|
|
eMBMasterErrorEventType errorType = EV_ERROR_INIT;
|
|
static eMBException eException = MB_EX_NONE;
|
|
static UCHAR ucFunctionCode = 0;
|
|
static USHORT usRecvLength = 0;
|
|
|
|
/* 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( &xEvent ) == TRUE ) {
|
|
switch( xEvent.eEvent ) {
|
|
// In some cases it is possible that more than one event set
|
|
// together (even from one subset mask) than process them consistently
|
|
case EV_MASTER_READY:
|
|
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_READY", xEvent.xTransactionId);
|
|
vMBMasterSetErrorType( EV_ERROR_INIT );
|
|
vMBMasterRunResRelease( );
|
|
break;
|
|
case EV_MASTER_FRAME_TRANSMIT:
|
|
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_TRANSMIT", xEvent.xTransactionId);
|
|
/* Master is busy now. */
|
|
vMBMasterGetPDUSndBuf( &pucMBSendFrame );
|
|
ESP_LOG_BUFFER_HEX_LEVEL("POLL transmit buffer", (void*)pucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
|
|
eStatus = peMBMasterFrameSendCur( ucMBMasterGetDestAddress(), pucMBSendFrame, usMBMasterGetPDUSndLength() );
|
|
if (eStatus != MB_ENOERR) {
|
|
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
|
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Frame send error = %d", xEvent.xTransactionId, (unsigned)eStatus );
|
|
}
|
|
xCurTransactionId = xEvent.xTransactionId;
|
|
break;
|
|
case EV_MASTER_FRAME_SENT:
|
|
if (xCurTransactionId == xEvent.xTransactionId) {
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_SENT", xEvent.xTransactionId );
|
|
ESP_LOG_BUFFER_HEX_LEVEL("POLL sent buffer", (void*)pucMBSendFrame, usMBMasterGetPDUSndLength(), ESP_LOG_DEBUG);
|
|
}
|
|
break;
|
|
case EV_MASTER_FRAME_RECEIVED:
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_FRAME_RECEIVED", xEvent.xTransactionId );
|
|
eStatus = peMBMasterFrameReceiveCur( &ucRecvAddress, &pucMBRecvFrame, &usRecvLength);
|
|
if (xCurTransactionId == xEvent.xTransactionId) {
|
|
MB_PORT_CHECK(pucMBSendFrame, MB_EILLSTATE, "Send buffer initialization fail.");
|
|
// Check if the frame is for us. If not ,send an error process event.
|
|
if ( ( eStatus == MB_ENOERR ) && ( ( ucRecvAddress == ucMBMasterGetDestAddress() )
|
|
|| ( ucRecvAddress == MB_TCP_PSEUDO_ADDRESS) ) ) {
|
|
if ( ( pucMBRecvFrame[MB_PDU_FUNC_OFF] & ~MB_FUNC_ERROR ) == ( pucMBSendFrame[MB_PDU_FUNC_OFF] ) ) {
|
|
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ": Packet data received successfully (%u).", xEvent.xTransactionId, (unsigned)eStatus);
|
|
ESP_LOG_BUFFER_HEX_LEVEL("POLL receive buffer", (void*)pucMBRecvFrame, (uint16_t)usRecvLength, ESP_LOG_DEBUG);
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
|
|
} else {
|
|
ESP_LOGE( MB_PORT_TAG, "Drop incorrect frame, receive_func(%u) != send_func(%u)",
|
|
pucMBRecvFrame[MB_PDU_FUNC_OFF], pucMBSendFrame[MB_PDU_FUNC_OFF]);
|
|
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
|
}
|
|
} else {
|
|
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ": Packet data receive failed (addr=%u)(%u).",
|
|
xEvent.xTransactionId, (unsigned)ucRecvAddress, (unsigned)eStatus);
|
|
}
|
|
} else {
|
|
// Ignore the `EV_MASTER_FRAME_RECEIVED` event because the respond timeout occurred
|
|
// and this is likely respond to previous transaction
|
|
ESP_LOGE( MB_PORT_TAG, "Drop data received outside of transaction (%" PRIu64 ")", xEvent.xTransactionId );
|
|
}
|
|
break;
|
|
case EV_MASTER_EXECUTE:
|
|
if (xCurTransactionId == xEvent.xTransactionId) {
|
|
if ( xMBMasterRequestIsBroadcast()
|
|
&& (( ucMBMasterGetCommMode() == MB_RTU ) || ( ucMBMasterGetCommMode() == MB_ASCII ) ) ) {
|
|
pucMBRecvFrame = pucMBSendFrame;
|
|
}
|
|
MB_PORT_CHECK(pucMBRecvFrame, MB_EILLSTATE, "receive buffer initialization fail.");
|
|
ESP_LOGD(MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE", xEvent.xTransactionId);
|
|
ucFunctionCode = pucMBRecvFrame[MB_PDU_FUNC_OFF];
|
|
eException = MB_EX_ILLEGAL_FUNCTION;
|
|
/* If receive frame has exception. The receive function code highest bit is 1.*/
|
|
if (ucFunctionCode & MB_FUNC_ERROR) {
|
|
eException = (eMBException)pucMBRecvFrame[MB_PDU_DATA_OFF];
|
|
} else {
|
|
for ( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
|
|
{
|
|
/* No more function handlers registered. Abort. */
|
|
if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
|
|
break;
|
|
}
|
|
if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) {
|
|
vMBMasterSetCBRunInMasterMode(TRUE);
|
|
/* If master request is broadcast,
|
|
* the master need execute function for all slave.
|
|
*/
|
|
if ( xMBMasterRequestIsBroadcast() ) {
|
|
USHORT usLength = usMBMasterGetPDUSndLength();
|
|
for(j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++)
|
|
{
|
|
vMBMasterSetDestAddress(j);
|
|
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usLength);
|
|
}
|
|
} else {
|
|
eException = xMasterFuncHandlers[i].pxHandler(pucMBRecvFrame, &usRecvLength);
|
|
}
|
|
vMBMasterSetCBRunInMasterMode( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* If master has exception, will send error process event. Otherwise the master is idle.*/
|
|
if ( eException != MB_EX_NONE ) {
|
|
vMBMasterSetErrorType( EV_ERROR_EXECUTE_FUNCTION );
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
|
} else {
|
|
if ( eMBMasterGetErrorType( ) == EV_ERROR_INIT ) {
|
|
vMBMasterSetErrorType(EV_ERROR_OK);
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":set event EV_ERROR_OK", xEvent.xTransactionId );
|
|
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
|
|
}
|
|
}
|
|
} else {
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_EXECUTE is expired", xEvent.xTransactionId );
|
|
}
|
|
break;
|
|
case EV_MASTER_ERROR_PROCESS:
|
|
if (xCurTransactionId == xEvent.xTransactionId) {
|
|
ESP_LOGD( MB_PORT_TAG, "%" PRIu64 ":EV_MASTER_ERROR_PROCESS", xEvent.xTransactionId);
|
|
/* Execute specified error process callback function. */
|
|
errorType = eMBMasterGetErrorType( );
|
|
vMBMasterGetPDUSndBuf( &pucMBSendFrame );
|
|
switch ( errorType )
|
|
{
|
|
case EV_ERROR_RESPOND_TIMEOUT:
|
|
vMBMasterErrorCBRespondTimeout( xEvent.xTransactionId,
|
|
ucMBMasterGetDestAddress( ),
|
|
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
|
|
break;
|
|
case EV_ERROR_RECEIVE_DATA:
|
|
vMBMasterErrorCBReceiveData( xEvent.xTransactionId,
|
|
ucMBMasterGetDestAddress( ),
|
|
pucMBRecvFrame, usRecvLength,
|
|
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
|
|
break;
|
|
case EV_ERROR_EXECUTE_FUNCTION:
|
|
vMBMasterErrorCBExecuteFunction( xEvent.xTransactionId,
|
|
ucMBMasterGetDestAddress( ),
|
|
pucMBRecvFrame, usRecvLength,
|
|
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
|
|
break;
|
|
case EV_ERROR_OK:
|
|
vMBMasterCBRequestSuccess( xEvent.xTransactionId,
|
|
ucMBMasterGetDestAddress( ),
|
|
pucMBRecvFrame, usRecvLength,
|
|
pucMBSendFrame, usMBMasterGetPDUSndLength( ) );
|
|
break;
|
|
default:
|
|
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":incorrect error type = %d.", xEvent.xTransactionId, (int)errorType);
|
|
break;
|
|
}
|
|
}
|
|
vMBMasterPortTimersDisable( );
|
|
uint64_t xProcTime = xCurTransactionId ? ( xEvent.xPostTimestamp - xCurTransactionId ) : 0;
|
|
ESP_LOGD( MB_PORT_TAG, "Transaction (%" PRIu64 "), processing time(us) = %" PRId64, xCurTransactionId, xProcTime );
|
|
MB_ATOMIC_SECTION {
|
|
xTransactionInfo.xTransId = xCurTransactionId;
|
|
xTransactionInfo.ucDestAddr = ucMBMasterGetDestAddress();
|
|
xTransactionInfo.ucFuncCode = ucFunctionCode;
|
|
xTransactionInfo.eException = eException;
|
|
xTransactionInfo.ucFrameError = errorType;
|
|
}
|
|
xCurTransactionId = 0;
|
|
vMBMasterSetErrorType( EV_ERROR_INIT );
|
|
vMBMasterRunResRelease( );
|
|
break;
|
|
default:
|
|
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ":Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
|
|
break;
|
|
}
|
|
} else {
|
|
// Something went wrong and task unblocked but there are no any correct events set
|
|
ESP_LOGE( MB_PORT_TAG, "%" PRIu64 ": Unexpected event triggered 0x%02x.", xEvent.xTransactionId, (int)xEvent.eEvent );
|
|
eStatus = MB_EILLSTATE;
|
|
}
|
|
return eStatus;
|
|
}
|
|
|
|
// Get whether the Modbus Master is run in master mode.
|
|
BOOL xMBMasterGetCBRunInMasterMode( void )
|
|
{
|
|
return atomic_load(&xMBRunInMasterMode);
|
|
}
|
|
|
|
// Set whether the Modbus Master is run in master mode.
|
|
void vMBMasterSetCBRunInMasterMode( BOOL IsMasterMode )
|
|
{
|
|
atomic_store(&xMBRunInMasterMode, IsMasterMode);
|
|
}
|
|
|
|
// Get Modbus Master send destination address.
|
|
UCHAR ucMBMasterGetDestAddress( void )
|
|
{
|
|
return atomic_load(&ucMBMasterDestAddress);
|
|
}
|
|
|
|
// Set Modbus Master send destination address.
|
|
void vMBMasterSetDestAddress( UCHAR Address )
|
|
{
|
|
atomic_store(&ucMBMasterDestAddress, Address);
|
|
}
|
|
|
|
// Get Modbus Master current error event type.
|
|
eMBMasterErrorEventType inline eMBMasterGetErrorType( void )
|
|
{
|
|
return atomic_load(&eMBMasterCurErrorType);
|
|
}
|
|
|
|
// Set Modbus Master current error event type.
|
|
void IRAM_ATTR vMBMasterSetErrorType( eMBMasterErrorEventType errorType )
|
|
{
|
|
atomic_store(&eMBMasterCurErrorType, errorType);
|
|
}
|
|
|
|
/* Get Modbus Master send PDU's buffer address pointer.*/
|
|
void vMBMasterGetPDUSndBuf( UCHAR ** pucFrame )
|
|
{
|
|
*pucFrame = ( UCHAR * ) &ucMasterSndBuf[MB_SEND_BUF_PDU_OFF];
|
|
}
|
|
|
|
/* Set Modbus Master send PDU's buffer length.*/
|
|
void vMBMasterSetPDUSndLength( USHORT SendPDULength )
|
|
{
|
|
atomic_store(&usMasterSendPDULength, SendPDULength);
|
|
}
|
|
|
|
/* Get Modbus Master send PDU's buffer length.*/
|
|
USHORT usMBMasterGetPDUSndLength( void )
|
|
{
|
|
return atomic_load(&usMasterSendPDULength);
|
|
}
|
|
|
|
/* Set Modbus Master current timer mode.*/
|
|
void vMBMasterSetCurTimerMode( eMBMasterTimerMode eMBTimerMode )
|
|
{
|
|
atomic_store(&eMasterCurTimerMode, eMBTimerMode);
|
|
}
|
|
|
|
/* Get Modbus Master current timer mode.*/
|
|
eMBMasterTimerMode MB_PORT_ISR_ATTR xMBMasterGetCurTimerMode( void )
|
|
{
|
|
return atomic_load(&eMasterCurTimerMode);
|
|
}
|
|
|
|
/* The master request is broadcast? */
|
|
BOOL MB_PORT_ISR_ATTR xMBMasterRequestIsBroadcast( void )
|
|
{
|
|
return atomic_load(&xFrameIsBroadcast);
|
|
}
|
|
|
|
/* The master request is broadcast? */
|
|
void vMBMasterRequestSetType( BOOL xIsBroadcast )
|
|
{
|
|
atomic_store(&xFrameIsBroadcast, xIsBroadcast);
|
|
}
|
|
|
|
// Get Modbus Master communication mode.
|
|
eMBMode ucMBMasterGetCommMode(void)
|
|
{
|
|
return eMBMasterCurrentMode;
|
|
}
|
|
|
|
/* Get current transaction information */
|
|
BOOL xMBMasterGetLastTransactionInfo( uint64_t *pxTransId, UCHAR *pucDestAddress,
|
|
UCHAR *pucFunctionCode, UCHAR *pucException,
|
|
USHORT *pusErrorType )
|
|
{
|
|
BOOL xState = (eMBState == STATE_ENABLED);
|
|
if (xState && pxTransId && pucDestAddress && pucFunctionCode
|
|
&& pucException && pusErrorType) {
|
|
MB_ATOMIC_SECTION {
|
|
*pxTransId = xTransactionInfo.xTransId;
|
|
*pucDestAddress = xTransactionInfo.ucDestAddr;
|
|
*pucFunctionCode = xTransactionInfo.ucFuncCode;
|
|
*pucException = xTransactionInfo.eException;
|
|
*pusErrorType = xTransactionInfo.ucFrameError;
|
|
}
|
|
}
|
|
return xState;
|
|
}
|
|
|
|
#endif // MB_MASTER_RTU_ENABLED || MB_MASTER_ASCII_ENABLED || MB_MASTER_TCP_ENABLED
|