example1

FreeModbus最简单使用
A typical application will want to call eMBInit() first.
If the deviceis ready to answer network requests it must then call eMBEnable() to activatethe protocol stack.
In the main loop the function eMBPoll() must be called periodically.
The time interval between pooling depends on the configured Modbus timeout.
If an RTOS is available a separate task should be created and the task should always call the function eMBPoll().

// Initialize protocol stack in RTU mode for a slave with address 10 = 0x0A
eMBInit( MB_RTU, 0x0A, 38400, MB_PAR_EVEN );
// Enable the Modbus Protocol Stack.
eMBEnable(  );
for( ;; )
{// Call the main polling loop of the Modbus protocol stack.eMBPoll(  );...
}

example2

FreeModbus在FreeRTOS系统中使用

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"/* ----------------------- Defines ------------------------------------------*/
#define REG_HOLDING_START 0x1000
#define REG_HOLDING_NREGS 130
#define REG_INPUT_START 0x1000
#define REG_INPUT_NREGS 4/* ----------------------- Static variables ---------------------------------*/
static USHORT usRegHoldingStart = REG_HOLDING_START;
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS];
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];/* ----------------------- Static functions ---------------------------------*/
STATIC void vModbusTask(void *pvParameters);
STATIC void vSetupHardware(void);/* ----------------------- Start implementation -----------------------------*/
int main(void)
{vSetupHardware();(void)xTaskCreate(vModbusTask, NULL, configMINIMAL_STACK_SIZE, NULL,tskIDLE_PRIORITY, NULL);vTaskStartScheduler();return 0;
}static void
vModbusTask(void *pvParameters)
{int i;/* Select either ASCII or RTU Mode. */(void)eMBInit(MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN);/* Initialize the holding register values before starting the* Modbus stack*/for (i = 0; i < REG_HOLDING_NREGS; i++){usRegHoldingBuf[i] = (unsigned short)i;}/* Initialize the input register values before starting the* Modbus stack*/for (i = 0; i < REG_INPUT_NREGS; i++){usRegInputBuf[i] = (unsigned short)i;}/* Enable the Modbus Protocol Stack. */(void)eMBEnable();for (;;){/* Call the main polling loop of the Modbus protocol stack. */(void)eMBPoll();}
}eMBErrorCode
eMBRegInputCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{eMBErrorCode eStatus = MB_ENOERR;int iRegIndex;if ((usAddress >= REG_INPUT_START) && (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS)){iRegIndex = (int)(usAddress - usRegInputStart);while (usNRegs > 0){*pucRegBuffer++ = (unsigned char)(usRegInputBuf[iRegIndex] >> 8);*pucRegBuffer++ = (unsigned char)(usRegInputBuf[iRegIndex] & 0xFF);iRegIndex++;usNRegs--;}}else{eStatus = MB_ENOREG;}return eStatus;
}eMBErrorCode
eMBRegHoldingCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{eMBErrorCode eStatus = MB_ENOERR;int iRegIndex;if ((usAddress >= REG_HOLDING_START) &&(usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS)){iRegIndex = (int)(usAddress - usRegHoldingStart);switch (eMode){/* Pass current register values to the protocol stack. */case MB_REG_READ:while (usNRegs > 0){*pucRegBuffer++ = (unsigned char)(usRegHoldingBuf[iRegIndex] >> 8);*pucRegBuffer++ = (unsigned char)(usRegHoldingBuf[iRegIndex] & 0xFF);iRegIndex++;usNRegs--;}break;/* Update current register values with new values from the* protocol stack. */case MB_REG_WRITE:while (usNRegs > 0){usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;iRegIndex++;usNRegs--;}}}else{eStatus = MB_ENOREG;}return eStatus;
}eMBErrorCode
eMBRegCoilsCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{return MB_ENOREG;
}eMBErrorCode
eMBRegDiscreteCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{return MB_ENOREG;
}

eMBInit

宏定义

Modbus serial transmission modes (RTU/ASCII).
Modbus serial supports two transmission modes. Either ASCII or RTU.
RTU is faster but has more hardware requirements and requires a network with a low jitter.
ASCII is slower and more reliable on slower links (E.g. modems)

typedef enum
{MB_RTU,   /*!< RTU transmission mode. */MB_ASCII, /*!< ASCII transmission mode. */MB_TCP    /*!< TCP mode. */
} eMBMode;/*! \ingroup modbus* \brief Parity used for characters in serial mode.** The parity which should be applied to the characters sent over the serial* link. Please note that this values are actually passed to the porting* layer and therefore not all parity modes might be available.*/
typedef enum
{MB_PAR_NONE, /*!< No parity. */MB_PAR_ODD,  /*!< Odd parity. */MB_PAR_EVEN  /*!< Even parity. */
} eMBParity;static enum {STATE_ENABLED,STATE_DISABLED,STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;

函数声明

/*! \ingroup modbus* \brief Initialize the Modbus protocol stack.** This functions initializes the ASCII or RTU module and calls the* init functions of the porting layer to prepare the hardware. Please* note that the receiver is still disabled and no Modbus frames are* processed until eMBEnable( ) has been called.** \param eMode If ASCII or RTU mode should be used.* \param ucSlaveAddress The slave address. Only frames sent to this*   address or to the broadcast address are processed.* \param ucPort The port to use. E.g. 1 for COM1 on windows. This value*   is platform dependent and some ports simply choose to ignore it.* \param ulBaudRate The baudrate. E.g. 19200. Supported baudrates depend*   on the porting layer.* \param eParity Parity used for serial transmission.** \return If no error occurs the function returns eMBErrorCode::MB_ENOERR.*   The protocol is then in the disabled state and ready for activation*   by calling eMBEnable( ). Otherwise one of the following error codes*   is returned:*    - eMBErrorCode::MB_EINVAL If the slave address was not valid. Valid*        slave addresses are in the range 1 - 247.*    - eMBErrorCode::MB_EPORTERR IF the porting layer returned an error.*/
eMBErrorCode eMBInit(eMBMode eMode, UCHAR ucSlaveAddress,UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity);

函数定义

eMBErrorCode eMBInit(eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG 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){#if MB_RTU_ENABLED > 0case 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
#if MB_ASCII_ENABLED > 0case 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;
#endifdefault:eStatus = MB_EINVAL;}if (eStatus == MB_ENOERR)  // 初始化,没有报错{if (!xMBPortEventInit()){/* port dependent event module initalization failed. */eStatus = MB_EPORTERR;}else // 初始化 OK,设置当前状态{eMBCurrentMode = eMode;eMBState = STATE_DISABLED;}}}return eStatus;
}

宏定义

// mb.h
// Errorcodes used by all function in the protocol stack.
typedef enum
{MB_ENOERR,    /*!< no error. */MB_ENOREG,    /*!< illegal register address. */MB_EINVAL,    /*!< illegal argument. */MB_EPORTERR,  /*!< porting layer error. */MB_ENORES,    /*!< insufficient resources. */MB_EIO,       /*!< I/O error. */MB_EILLSTATE, /*!< protocol stack in illegal state. */MB_ETIMEDOUT  /*!< timeout error occurred. */
} eMBErrorCode;// mbproto.h
#define MB_ADDRESS_BROADCAST        (0)       /*! Modbus broadcast address. */
#define MB_ADDRESS_MIN              (1)       /*! Smallest possible slave address. */
#define MB_ADDRESS_MAX              (247)     /*! Biggest possible slave address. */

函数指针声明

// mb.c
/* 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;// mbframe.h
typedef void (*pvMBFrameStart)(void);
typedef void (*pvMBFrameStop)(void);
typedef eMBErrorCode (*peMBFrameReceive)(UCHAR *pucRcvAddress,UCHAR **pucFrame,USHORT *pusLength);
typedef eMBErrorCode (*peMBFrameSend)(UCHAR slaveAddress,const UCHAR *pucFrame,USHORT usLength);
typedef void (*pvMBFrameClose)(void);

eMBRTUInit

Modbus RTU初始化

// mbrut.c
eMBErrorCode
eMBRTUInit(UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity)
{eMBErrorCode eStatus = MB_ENOERR;ULONG usTimerT35_50us;(void)ucSlaveAddress;ENTER_CRITICAL_SECTION();/* Modbus RTU uses 8 Databits. */if (xMBPortSerialInit(ucPort, ulBaudRate, 8, eParity) != TRUE)   // 设备串口初始化,根据硬件不同,由用户实现{eStatus = MB_EPORTERR;}else{/* If baudrate > 19200 then we should use the fixed timer values* t35 = 1750us. Otherwise t35 must be 3.5 times the character time.*/if (ulBaudRate > 19200){usTimerT35_50us = 35; /* 1800us. */}else{/* The timer reload value for a character is given by:** ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )*             = 11 * Ticks_per_1s / Baudrate*             = 220000 / Baudrate* The reload for t3.5 is 1.5 times this value and similary* for t3.5.*/usTimerT35_50us = (7UL * 220000UL) / (2UL * ulBaudRate);}if (xMBPortTimersInit((USHORT)usTimerT35_50us) != TRUE)  // 设备定时器初始化,根据硬件不同,由用户实现{eStatus = MB_EPORTERR;}}EXIT_CRITICAL_SECTION();return eStatus;
}

xMBPortSerialInit

设备端,初始化串口

// xxx/port/portserial.c
BOOL xMBPortSerialInit(UCHAR ucPort, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity)
{BOOL bOkay = TRUE;unsigned int uiUARTMode = 0;ENTER_CRITICAL_SECTION();if (!bIsInitalized){HDL_RESET(&xSerialHdls[0]);bIsInitalized = TRUE;}uiUARTMode = AT91C_US_USMODE_RS485 | AT91C_US_CLKS_CLOCK;switch (eParity){case MB_PAR_NONE:uiUARTMode |= AT91C_US_PAR_NONE;uiUARTMode |= AT91C_US_NBSTOP_2_BIT;break;case MB_PAR_EVEN:uiUARTMode |= AT91C_US_PAR_EVEN;uiUARTMode |= AT91C_US_NBSTOP_1_BIT;break;case MB_PAR_ODD:uiUARTMode |= AT91C_US_PAR_ODD;uiUARTMode |= AT91C_US_NBSTOP_1_BIT;break;default:break;}switch (ucDataBits){case 8:uiUARTMode |= AT91C_US_CHRL_8_BITS;break;case 7:uiUARTMode |= AT91C_US_CHRL_7_BITS;break;default:break;}if (bOkay){if ((ucPort == USART_USART0_IDX) && (NULL == xSerialHdls[0].pxCOM)){xSerialHdls[0].pxCOM = AT91C_BASE_US0;xSerialHdls[0].uiAT91C_ID_USX = AT91C_ID_US0;xSerialHdls[0].pvIRQHandlerFN = vUSART0ISR;}else if ((ucPort == USART_USART1_IDX) && (NULL == xSerialHdls[0].pxCOM)){xSerialHdls[0].pxCOM = AT91C_BASE_US1;xSerialHdls[0].uiAT91C_ID_USX = AT91C_ID_US1;xSerialHdls[0].pvIRQHandlerFN = vUSART1ISR;}else{bOkay = FALSE;}if (bOkay){AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC, 1 << xSerialHdls[0].uiAT91C_ID_USX);AT91F_US_Configure(xSerialHdls[0].pxCOM, configCPU_CLOCK_HZ, uiUARTMode, ulBaudRate,0);xSerialHdls[0].pxCOM->US_CR = AT91C_US_TXEN | AT91C_US_RXEN;AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, xSerialHdls[0].uiAT91C_ID_USX,USART_INTERRUPT_LEVEL, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,xSerialHdls[0].pvIRQHandlerFN);AT91F_AIC_EnableIt(AT91C_BASE_AIC, xSerialHdls[0].uiAT91C_ID_USX);UART_INIT(xSerialHdls[0].uiAT91C_ID_USX);}}else{bOkay = FALSE;}EXIT_CRITICAL_SECTION();return bOkay;
}

xMBPortTimersInit

设备端,初始化定时器

// xxx/port/porttimer.c
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{USHORT usTimeoutMS;ENTER_CRITICAL_SECTION();usTimeoutMS = (usTim1Timerout50us + 10) / 20;if (0 == usTimeoutMS){usTimeoutMS = 1;}arxTimerHdls[0].usNTimeOutMS = usTimeoutMS;arxTimerHdls[0].usNTimeLeft = TIMER_TIMEOUT_INVALID;bIsInitalized = TRUE;EXIT_CRITICAL_SECTION();return TRUE;
}

xMBPortEvent

设备端(从站)实现,保持事件

BOOL xMBPortEventInit(void)
{xEventInQueue = FALSE;return TRUE;
}BOOL xMBPortEventGet(eMBEventType *eEvent)
{BOOL xEventHappened = FALSE;if (xEventInQueue){*eEvent = eQueuedEvent;xEventInQueue = FALSE;xEventHappened = TRUE;}return xEventHappened;
}BOOL xMBPortEventPost(eMBEventType eEvent)
{xEventInQueue = TRUE;eQueuedEvent = eEvent;return TRUE;
}

eMBEnable

函数声明

// mb.h
/*! \ingroup modbus* \brief Enable the Modbus protocol stack.** This function enables processing of Modbus frames. Enabling the protocol* stack is only possible if it is in the disabled state.** \return If the protocol stack is now in the state enabled it returns*   eMBErrorCode::MB_ENOERR. If it was not in the disabled state it*   return eMBErrorCode::MB_EILLSTATE.*/
eMBErrorCode eMBEnable(void);

函数定义

使能Modbus

eMBErrorCode
eMBEnable(void)
{eMBErrorCode eStatus = MB_ENOERR;if (eMBState == STATE_DISABLED){/* Activate the protocol stack. */pvMBFrameStartCur();  // 调用 eMBRTUStart();eMBState = STATE_ENABLED;}else{eStatus = MB_EILLSTATE;}return eStatus;
}

eMBRTUStart

RTU时,pvMBFrameStartCur()的实现,使能设备的串口和定时器

void eMBRTUStart(void)
{ENTER_CRITICAL_SECTION();/* Initially the receiver is in the state STATE_RX_INIT. we start* the timer and if no character is received within t3.5 we change* to STATE_RX_IDLE. This makes sure that we delay startup of the* modbus protocol stack until the bus is free.*/eRcvState = STATE_RX_INIT;vMBPortSerialEnable(TRUE, FALSE);  // 根据设备不同,由用户实现设备的串口和定时器的使能vMBPortTimersEnable();EXIT_CRITICAL_SECTION();
}

宏定义

typedef enum
{STATE_RX_INIT, /*!< Receiver is in initial state. */STATE_RX_IDLE, /*!< Receiver is in idle state. */STATE_RX_RCV,  /*!< Frame is beeing received. */STATE_RX_ERROR /*!< If the frame is invalid. */
} eMBRcvState;

vMBPortSerialEnable

// xxx/port/portserial.c
void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{ENTER_CRITICAL_SECTION();assert(NULL != xSerialHdls[0].pxCOM);if (xRxEnable){AT91F_US_EnableIt(xSerialHdls[0].pxCOM, AT91C_US_RXRDY);xSerialHdls[0].bIsRxEnabled = TRUE;}else{AT91F_US_DisableIt(xSerialHdls[0].pxCOM, AT91C_US_RXRDY);xSerialHdls[0].bIsRxEnabled = FALSE;}if (xTxEnable){AT91F_US_EnableIt(xSerialHdls[0].pxCOM, AT91C_US_TXRDY);xSerialHdls[0].bIsTxEnabled = TRUE;}else{AT91F_US_DisableIt(xSerialHdls[0].pxCOM, AT91C_US_TXRDY);xSerialHdls[0].bIsTxEnabled = FALSE;}EXIT_CRITICAL_SECTION();
}

vMBPortTimersEnable

// xxx/port/porttimer.c
void vMBPortTimersEnable()
{assert(bIsInitalized);ENTER_CRITICAL_SECTION();arxTimerHdls[0].usNTimeLeft = arxTimerHdls[0].usNTimeOutMS;EXIT_CRITICAL_SECTION();
}

eMBPoll

eMBErrorCode
eMBPoll(void)
{static UCHAR *ucMBFrame;static UCHAR ucRcvAddress;static UCHAR ucFunctionCode;static USHORT 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:// 接收报文,调用 eMBRTUReceive(),获取接收地址/报文/长度eStatus = peMBFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);  if (eStatus == MB_ENOERR){/* Check if the frame is for us. If not ignore the frame. */// 接收报文的从站地址ucRcvAddress 与设定的地址ucMBAddress是否一致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 < 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++] = (UCHAR)(ucFunctionCode | MB_FUNC_ERROR); // 填充发送报文,功能码 + 报错信息ucMBFrame[usLength++] = eException;}if ((eMBCurrentMode == MB_ASCII) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS){vMBPortTimersDelay(MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS);}// 发送响应报文,调用 eMBRTUSend(),如果报错,发送错误帧,否则发送接收到的报文eStatus = peMBFrameSendCur(ucMBAddress, ucMBFrame, usLength); }break;case EV_FRAME_SENT:break;}}return MB_ENOERR;
}

函数声明

/*! \ingroup modbus* \brief The main pooling loop of the Modbus protocol stack.** This function must be called periodically. The timer interval required* is given by the application dependent Modbus slave timeout. Internally the* function calls xMBPortEventGet() and waits for an event from the receiver or* transmitter state machines.** \return If the protocol stack is not in the enabled state the function*   returns eMBErrorCode::MB_EILLSTATE. Otherwise it returns*   eMBErrorCode::MB_ENOERR.*/
eMBErrorCode eMBPoll(void);

宏定义

typedef enum
{EV_READY,          /*!< Startup finished. */EV_FRAME_RECEIVED, /*!< Frame received. */EV_EXECUTE,        /*!< Execute function. */EV_FRAME_SENT      /*!< Frame sent. */
} eMBEventType;

eMBRTUReceive

volatile UCHAR ucRTUBuf[MB_SER_PDU_SIZE_MAX];  // 存放接收的数据
static volatile USHORT usRcvBufferPos;  // 对应数组 ucRTUBuf 中的位置信息eMBErrorCode
eMBRTUReceive(UCHAR *pucRcvAddress, UCHAR **pucFrame, USHORT *pusLength)
{BOOL xFrameReceived = FALSE;eMBErrorCode eStatus = MB_ENOERR;ENTER_CRITICAL_SECTION();assert(usRcvBufferPos < MB_SER_PDU_SIZE_MAX);/* Length and CRC check */if ((usRcvBufferPos >= MB_SER_PDU_SIZE_MIN) && (usMBCRC16((UCHAR *)ucRTUBuf, usRcvBufferPos) == 0)){/* Save the address field. All frames are passed to the upper layed* and the decision if a frame is used is done there.*/*pucRcvAddress = ucRTUBuf[MB_SER_PDU_ADDR_OFF];  // 输出接收数组中的地址/* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus* size of address field and CRC checksum.*/*pusLength = (USHORT)(usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC); // 输出接收的数据长度/* Return the start of the Modbus PDU to the caller. */*pucFrame = (UCHAR *)&ucRTUBuf[MB_SER_PDU_PDU_OFF];  // 输出Modbus pud 起始地址xFrameReceived = TRUE;}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION();return eStatus;
}

xFuncHandlers

An array of Modbus functions handlers which associates Modbus function codes with implementing functions.

typedef eMBException (*pxMBFunctionHandler)(UCHAR *pucFrame, USHORT *pusLength);typedef struct
{UCHAR ucFunctionCode;pxMBFunctionHandler pxHandler;
} xMBFunctionHandler;static xMBFunctionHandler xFuncHandlers[MB_FUNC_HANDLERS_MAX] = {{MB_FUNC_OTHER_REPORT_SLAVEID,       eMBFuncReportSlaveID},{MB_FUNC_READ_INPUT_REGISTER,         eMBFuncReadInputRegister},{MB_FUNC_READ_HOLDING_REGISTER,   eMBFuncReadHoldingRegister},{MB_FUNC_WRITE_MULTIPLE_REGISTERS,  eMBFuncWriteMultipleHoldingRegister},{MB_FUNC_WRITE_REGISTER,           eMBFuncWriteHoldingRegister},{MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBFuncReadWriteMultipleHoldingRegister},{MB_FUNC_READ_COILS,               eMBFuncReadCoils},{MB_FUNC_WRITE_SINGLE_COIL,       eMBFuncWriteCoil},{MB_FUNC_WRITE_MULTIPLE_COILS,        eMBFuncWriteMultipleCoils},{MB_FUNC_READ_DISCRETE_INPUTS,       eMBFuncReadDiscreteInputs},
};

ucFunctionCode

#define MB_ADDRESS_BROADCAST        (0)       /*! Modbus broadcast address. */
#define MB_ADDRESS_MIN              (1)       /*! Smallest possible slave address. */
#define MB_ADDRESS_MAX              (247)     /*! Biggest possible slave address. */
#define MB_FUNC_NONE                (0)#define MB_FUNC_READ_COILS                   (1)
#define MB_FUNC_READ_DISCRETE_INPUTS         (2)
#define MB_FUNC_WRITE_SINGLE_COIL            (5)
#define MB_FUNC_WRITE_MULTIPLE_COILS         (15)
#define MB_FUNC_READ_HOLDING_REGISTER        (3)
#define MB_FUNC_READ_INPUT_REGISTER          (4)
#define MB_FUNC_WRITE_REGISTER               (6)
#define MB_FUNC_WRITE_MULTIPLE_REGISTERS     (16)
#define MB_FUNC_READWRITE_MULTIPLE_REGISTERS (23)
#define MB_FUNC_OTHER_REPORT_SLAVEID         (17)#define MB_FUNC_DIAG_READ_EXCEPTION          (7)
#define MB_FUNC_DIAG_DIAGNOSTIC              (8)
#define MB_FUNC_DIAG_GET_COM_EVENT_CNT       (11)
#define MB_FUNC_DIAG_GET_COM_EVENT_LOG       (12)#define MB_FUNC_ERROR                        (128)

pxMBFunctionHandler

被对应的功能码调用,读取对应的数据

eMBException
eMBFuncReadInputRegister(UCHAR *pucFrame, USHORT *usLen)
{USHORT usRegAddress;USHORT usRegCount;UCHAR *pucFrameCur;eMBException eStatus = MB_EX_NONE;eMBErrorCode eRegStatus;if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN)){usRegAddress = (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);usRegAddress |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);usRegAddress++;usRegCount = (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8);usRegCount |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1]);/* Check if the number of registers to read is valid. If not* return Modbus illegal data value exception.*/if ((usRegCount >= 1) && (usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX)){/* Set the current PDU data pointer to the beginning. */pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];*usLen = MB_PDU_FUNC_OFF;/* First byte contains the function code. */*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;*usLen += 1;/* Second byte in the response contain the number of bytes. */*pucFrameCur++ = (UCHAR)(usRegCount * 2);*usLen += 1;eRegStatus =eMBRegInputCB(pucFrameCur, usRegAddress, usRegCount);  // 从设备端获取从站的数据/* If an error occured convert it into a Modbus exception. */if (eRegStatus != MB_ENOERR){eStatus = prveMBError2Exception(eRegStatus);}else{*usLen += usRegCount * 2;}}else{eStatus = MB_EX_ILLEGAL_DATA_VALUE;}}else{/* Can't be a valid read input register request because the length* is incorrect. */eStatus = MB_EX_ILLEGAL_DATA_VALUE;}return eStatus;
}

eMBRegInputCB

设备端实现,usRegInputBuf 中保存从站的数据,发送给主站

#define REG_INPUT_START 0x1000
#define REG_INPUT_NREGS 4
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];  // 保存要发送的数据eMBErrorCode
eMBRegInputCB(UCHAR *pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{eMBErrorCode eStatus = MB_ENOERR;int iRegIndex;if ((usAddress >= REG_INPUT_START) && (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS)){iRegIndex = (int)(usAddress - usRegInputStart);while (usNRegs > 0){*pucRegBuffer++ = (unsigned char)(usRegInputBuf[iRegIndex] >> 8);   // 把设备端的数据输出到调用方*pucRegBuffer++ = (unsigned char)(usRegInputBuf[iRegIndex] & 0xFF);iRegIndex++;usNRegs--;}}else{eStatus = MB_ENOREG;}return eStatus;
}

eMBRTUSend

上层调用,发送报文,具体的发送调用设备的串口发送API

static volatile UCHAR *pucSndBufferCur;  // 发送报文的指针
static volatile USHORT usSndBufferCount;  // 发送报文的长度eMBErrorCode
eMBRTUSend(UCHAR ucSlaveAddress, const UCHAR *pucFrame, USHORT usLength)
{eMBErrorCode eStatus = MB_ENOERR;USHORT usCRC16;ENTER_CRITICAL_SECTION();/* Check if the receiver is still in idle state. If not we where to* slow with processing the received frame and the master sent another* frame on the network. We have to abort sending the frame.*/if (eRcvState == STATE_RX_IDLE){/* First byte before the Modbus-PDU is the slave address. */pucSndBufferCur = (UCHAR *)pucFrame - 1;  // 保存传入的报文usSndBufferCount = 1;/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; // 保存传入的地址usSndBufferCount += usLength;/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */usCRC16 = usMBCRC16((UCHAR *)pucSndBufferCur, usSndBufferCount); // 计算crcucRTUBuf[usSndBufferCount++] = (UCHAR)(usCRC16 & 0xFF);ucRTUBuf[usSndBufferCount++] = (UCHAR)(usCRC16 >> 8);/* Activate the transmitter. */eSndState = STATE_TX_XMIT;vMBPortSerialEnable(FALSE, TRUE);}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION();return eStatus;
}

others

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;
}

Interface register should be written or read
This value is passed to the callback functions which support either reading or writing register values.
Writing means that the application registers should be updated and
reading means that the modbus protocol stack needs to know the current register values.

typedef enum
{MB_REG_READ, /*!< Read register values and pass to protocol stack. */MB_REG_WRITE /*!< Update register values. */
} eMBRegisterMode;

mbframe.h

定义帧协议
Constants which defines the format of a modbus frame.
The example is shown for a Modbus RTU/ASCII frame.
Note that the Modbus PDU is not dependent on the underlying transport.

协议栈定义

#define MB_PDU_SIZE_MAX 253 /*!< Maximum size of a PDU. */
#define MB_PDU_SIZE_MIN 1   /*!< Function Code */
#define MB_PDU_FUNC_OFF 0   /*!< Offset of function code in PDU. */
#define MB_PDU_DATA_OFF 1   /*!< Offset for response data in PDU. */

函数指针声明,初始化时根据RTU和ASCII赋不同的函数

typedef void (*pvMBFrameStart)(void);
typedef void (*pvMBFrameStop)(void);
typedef eMBErrorCode (*peMBFrameReceive)(UCHAR *pucRcvAddress,UCHAR **pucFrame,USHORT *pusLength);
typedef eMBErrorCode (*peMBFrameSend)(UCHAR slaveAddress,const UCHAR *pucFrame,USHORT usLength);
typedef void (*pvMBFrameClose)(void);

异常

typedef enum
{MB_EX_NONE                  = 0x00,MB_EX_ILLEGAL_FUNCTION      = 0x01,MB_EX_ILLEGAL_DATA_ADDRESS  = 0x02,MB_EX_ILLEGAL_DATA_VALUE    = 0x03,MB_EX_SLAVE_DEVICE_FAILURE  = 0x04,MB_EX_ACKNOWLEDGE           = 0x05,MB_EX_SLAVE_BUSY            = 0x06,MB_EX_MEMORY_PARITY_ERROR   = 0x08,MB_EX_GATEWAY_PATH_FAILED   = 0x0A,MB_EX_GATEWAY_TGT_FAILED    = 0x0B
} eMBException;

mbconfig.h

配置文件

#define MB_FUNC_OTHER_REP_SLAVEID_ENABLED       (  1 )
#define MB_FUNC_READ_INPUT_ENABLED              (  1 )
#define MB_FUNC_READ_HOLDING_ENABLED            (  1 )
#define MB_FUNC_WRITE_HOLDING_ENABLED           (  1 )
#define MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED  (  1 )
#define MB_FUNC_READ_COILS_ENABLED              (  1 )
#define MB_FUNC_WRITE_COIL_ENABLED              (  1 )
#define MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED    (  1 )
#define MB_FUNC_READ_DISCRETE_INPUTS_ENABLED    (  1 )
#define MB_FUNC_READWRITE_HOLDING_ENABLED       (  1 )

xMBRTUReceiveFSM

设备接收中断调用

BOOL xMBRTUReceiveFSM(void)
{BOOL xTaskNeedSwitch = FALSE;UCHAR ucByte;  // 从设备串口接收到的数据assert(eSndState == STATE_TX_IDLE);/* Always read the character. */(void)xMBPortSerialGetByte((CHAR *)&ucByte);switch (eRcvState){/* If we have received a character in the init state we have to* wait until the frame is finished.*/case STATE_RX_INIT:vMBPortTimersEnable();break;/* In the error state we wait until all characters in the* damaged frame are transmitted.*/case STATE_RX_ERROR:vMBPortTimersEnable();break;/* In the idle state we wait for a new character. If a character* is received the t1.5 and t3.5 timers are started and the* receiver is in the state STATE_RX_RECEIVCE.*/case STATE_RX_IDLE:usRcvBufferPos = 0;  // 表示新的一帧ucRTUBuf[usRcvBufferPos++] = ucByte;  // 保存接收到的数据,地址eRcvState = STATE_RX_RCV;/* Enable t3.5 timers. */vMBPortTimersEnable();break;/* We are currently receiving a frame. Reset the timer after* every character received. If more than the maximum possible* number of bytes in a modbus frame is received the frame is* ignored.*/case STATE_RX_RCV:if (usRcvBufferPos < MB_SER_PDU_SIZE_MAX){ucRTUBuf[usRcvBufferPos++] = ucByte; // 保存接收到的数据,功能码 + 数据}else{eRcvState = STATE_RX_ERROR;}vMBPortTimersEnable();break;}return xTaskNeedSwitch;
}

xMBRTUTransmitFSM

设备发送中断调用

BOOL xMBRTUTransmitFSM(void)
{BOOL xNeedPoll = FALSE;assert(eRcvState == STATE_RX_IDLE);switch (eSndState){/* We should not get a transmitter event if the transmitter is in* idle state.  */case STATE_TX_IDLE:/* enable receiver/disable transmitter. */vMBPortSerialEnable(TRUE, FALSE);break;case STATE_TX_XMIT:/* check if we are finished. */if (usSndBufferCount != 0){xMBPortSerialPutByte((CHAR)*pucSndBufferCur);  // 调用设备的串口发送函数,发送报文pucSndBufferCur++; /* next byte in sendbuffer. */usSndBufferCount--;}else{xNeedPoll = xMBPortEventPost(EV_FRAME_SENT);/* Disable transmitter. This prevents another transmit buffer* empty interrupt. */vMBPortSerialEnable(TRUE, FALSE);eSndState = STATE_TX_IDLE;}break;}return xNeedPoll;
}

被对应的功能码调用,读取对应的数据

eMBException
eMBFuncReadInputRegister(UCHAR *pucFrame, USHORT *usLen)
{USHORT usRegAddress;USHORT usRegCount;UCHAR *pucFrameCur;eMBException eStatus = MB_EX_NONE;eMBErrorCode eRegStatus;if (*usLen == (MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN)){usRegAddress = (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8);usRegAddress |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1]);usRegAddress++;usRegCount = (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8);usRegCount |= (USHORT)(pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1]);/* Check if the number of registers to read is valid. If not* return Modbus illegal data value exception.*/if ((usRegCount >= 1) && (usRegCount < MB_PDU_FUNC_READ_REGCNT_MAX)){/* Set the current PDU data pointer to the beginning. */pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];*usLen = MB_PDU_FUNC_OFF;/* First byte contains the function code. */*pucFrameCur++ = MB_FUNC_READ_INPUT_REGISTER;*usLen += 1;/* Second byte in the response contain the number of bytes. */*pucFrameCur++ = (UCHAR)(usRegCount * 2);*usLen += 1;eRegStatus =eMBRegInputCB(pucFrameCur, usRegAddress, usRegCount);  // 从设备端获取从站的数据/* If an error occured convert it into a Modbus exception. */if (eRegStatus != MB_ENOERR){eStatus = prveMBError2Exception(eRegStatus);}else{*usLen += usRegCount * 2;}}else{eStatus = MB_EX_ILLEGAL_DATA_VALUE;}}else{/* Can't be a valid read input register request because the length* is incorrect. */eStatus = MB_EX_ILLEGAL_DATA_VALUE;}return eStatus;
}
BOOL xMBRTUTimerT35Expired(void)
{BOOL xNeedPoll = FALSE;switch (eRcvState){/* Timer t35 expired. Startup phase is finished. */case STATE_RX_INIT:xNeedPoll = xMBPortEventPost(EV_READY);break;/* A frame was received and t35 expired. Notify the listener that* a new frame was received. */case STATE_RX_RCV:xNeedPoll = xMBPortEventPost(EV_FRAME_RECEIVED);break;/* An error occured while receiving the frame. */case STATE_RX_ERROR:break;/* Function called in an illegal state. */default:assert((eRcvState == STATE_RX_INIT) ||(eRcvState == STATE_RX_RCV) || (eRcvState == STATE_RX_ERROR));}vMBPortTimersDisable();eRcvState = STATE_RX_IDLE;return xNeedPoll;
}

FreeModbus相关推荐

  1. FreeModbus 移植于STM32 实现Modbus RTU通信

    http://ntn314.blog.163.com/blog/static/161743584201233084434579/ 毕业设计自己要做个基于STM32的PLC能直接跑语句表的,现在看来好像 ...

  2. freemodbus源码/获取地址

    官网源码地址:点击访问 FreeMODBUS是一个奥地利人写的Modbus协议.它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植.Modbus是一个工业制造环境中应用的一个通用协议 ...

  3. FreeModbus移植到STM32F107(以太网传输方式)

    1.创建工程 配置好之后生成工程 2.将FreeModbus源码,拷贝到工程目录 3.将FreeModbus文件添加进工程 打开mbtcp.c文件发现,受MB_TCP_ENABLED宏定义的影响,所有 ...

  4. FreeModbus移植到STM32F103(串行传输方式)

    1.创建工程 2.将FreeModbus源码,拷贝到工程目录 3.将FreeModbus文件添加进工程 添加好之后,编译出现错误 4.移植底层接口 先看第一个错误,缺少port.h 借鉴AVR架构的程 ...

  5. FreeModbus源码获取

    1.进入官网:https://www.embedded-solutions.at/en/freemodbus/ 2.进入下载页面 3.下载源码 4.解压源码 5.查看FreeRTOS源码

  6. freemodbus收藏学习网址

    https://www.cnblogs.com/axinno1/p/8521481.html https://blog.csdn.net/xukai871105/article/details/216 ...

  7. Freemodbus 1.5

    源:http://blog.sina.com.cn/s/blog_4935209001012eax.html 网站位置:http://www.freemodbus.org/index.php?lang ...

  8. STM32F103/107 移植Freemodbus RTU

    1.简介 FreeMODBUS一个奥地利人写的Modbus协议.它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植.Modbus是一个工业制造环境中应用的一个通用协议.Modbus通 ...

  9. FreeModbus源码结构分析

    FreeModbus 官网 目录结构 [demo]-各种平台的例程 [doc]-文档资料 [modbus]-核心源码 [tools]-相关工具 FreeModus的移植主要用到-\modbus目录和 ...

  10. uip+freemodbus网络通信

    前言: TCP/IP协议通过uip协议栈来实现,应用层的modbus协议使用freemodbus来实现,另外还需要一个网卡(笔者使用的是KSZ8851网卡控制器)来实现底层的数据传输(包括物理层.数据 ...

最新文章

  1. BZOJ 3566: [SHOI2014]概率充电器
  2. 计算机组成微程序设计,计算机组成原理微程序设计.doc
  3. 机器学习中的特征——特征选择的方法以及注意点
  4. 我的Linux成长路---001 Linux学习初期计划
  5. C++ vector的用法
  6. 全局对象_C++全局变量初始化
  7. cookie 和 token 都存放在 header 中,为什么不会劫持 token?____token和cookie的区别
  8. docker 安装 mysql 并映射数据库存放路径及配置文件
  9. LeetCode 538. 把二叉搜索树转换为累加树
  10. java 记录用户_JavaWeb学习记录(六)——用户登录功能
  11. MonoTouch 二三事(三)mono mkbundle 打包程序的解包支持
  12. 教你正确设置CrossOver的Wine配置(三)
  13. java jsp 404错误页面模板_JSP页面404错,点击分页的按钮就报错
  14. ios app图标尺寸设置
  15. 2、树莓派声卡设置和alsactl命令的使用
  16. 小猫钓鱼游戏(c++实现)
  17. 目前最为出色的Wii模拟器,可以在电脑上运行绝大多数Wii游戏,对低端配置完美支持,绝对的神器!
  18. 树莓派CM4烧录系统
  19. vue中的this.$router.replace()和.push()和.go()的区别解析
  20. 酒仙桥 asp.net 面试

热门文章

  1. 知云文献翻译没反应_论文翻译工具--Copytranslate
  2. 诺顿等效电路 用计算机,r t norton equivalent circuits (诺顿等效电路).ppt
  3. Everything Is Generated In Equal Probability HDU-6595 期望DP
  4. 计算机类普刊有哪些,基础数学类的容易发表的普刊有哪些
  5. 攻防世界--进阶区--forgot
  6. 程序的与时俱进之一——面向接口编程
  7. Python的整数与浮点数计算
  8. 一个人如何完成一整个网站的开发(推荐好文,看完绝对让你回味无穷)
  9. apache2 启动、重启、停止方法
  10. 计算机软件研发的相关会计分录,研发专利技术会计分录怎么做