* apps/modbus/rtu/mbrtu_m.c
*
* FreeModbus Library: A portable Modbus implementation for Modbus ASCII/RTU.
* Copyright (c) 2013 China Beijing 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 <assert.h>
#include "port.h"
#include "modbus/mb.h"
#include "modbus/mb_m.h"
#include "mbrtu_m.h"
#include "modbus/mbframe.h"
#include "mbcrc.h"
#include "modbus/mbport.h"
#if defined(CONFIG_MB_RTU_MASTER)
* Included Files
****************************************************************************/
#define MB_SER_PDU_SIZE_MIN 4
* frame. */
#define MB_SER_PDU_SIZE_MAX 256
* frame. */
#define MB_SER_PDU_SIZE_CRC 2
#define MB_SER_PDU_ADDR_OFF 0
* Ser-PDU. */
#define MB_SER_PDU_PDU_OFF 1
* Private Type Definitions
****************************************************************************/
typedef enum
{
STATE_M_RX_INIT,
STATE_M_RX_IDLE,
STATE_M_RX_RCV,
STATE_M_RX_ERROR,
} eMBMasterRcvState;
typedef enum
{
STATE_M_TX_IDLE,
STATE_M_TX_XMIT,
STATE_M_TX_XFWR,
* wait receive state. */
} eMBMasterSndState;
* Private Data
****************************************************************************/
static volatile eMBMasterSndState eSndState;
static volatile eMBMasterRcvState eRcvState;
static volatile uint8_t ucMasterRTUSndBuf[MB_PDU_SIZE_MAX];
static volatile uint8_t ucMasterRTURcvBuf[MB_SER_PDU_SIZE_MAX];
static volatile uint16_t usMasterSendPDULength;
static volatile uint8_t *pucMasterSndBufferCur;
static volatile uint16_t usMasterSndBufferCount;
static volatile uint16_t usMasterRcvBufferPos;
static volatile bool xFrameIsBroadcast = false;
static volatile eMBMasterTimerMode eMasterCurTimerMode;
* Public Functions
****************************************************************************/
eMBErrorCode eMBMasterRTUInit(uint8_t ucPort, speed_t ulBaudRate,
eMBParity eParity)
{
eMBErrorCode eStatus = MB_ENOERR;
speed_t usTimerT35_50us;
ENTER_CRITICAL_SECTION();
if (xMBMasterPortSerialInit(ucPort, ulBaudRate, 8, eParity) != true)
{
eStatus = MB_EPORTERR;
}
else
{
* 1750us. Otherwise t35 must be 3.5 times the character time.
*/
if (ulBaudRate > 19200)
{
usTimerT35_50us = 35;
}
else
{
* Ticks_per_1s / ( Baudrate / 11 ) = 11 * Ticks_per_1s / Baudrate =
* 220000 / Baudrate The reload for t3.5 is 1.5 times this value and
* similarly for t3.5.
*/
usTimerT35_50us = (7UL * 220000UL) / (2UL * ulBaudRate);
}
if (xMBMasterPortTimersInit((uint16_t) usTimerT35_50us) != true)
{
eStatus = MB_EPORTERR;
}
}
EXIT_CRITICAL_SECTION();
return eStatus;
}
void eMBMasterRTUStart(void)
{
ENTER_CRITICAL_SECTION();
* and if no character is received within t3.5 we change to STATE_M_RX_IDLE.
* This makes sure that we delay startup of the modbus protocol stack until
* the bus is free.
*/
eRcvState = STATE_M_RX_INIT;
vMBMasterPortSerialEnable(true, false);
vMBMasterPortTimersT35Enable();
EXIT_CRITICAL_SECTION();
}
void eMBMasterRTUStop(void)
{
ENTER_CRITICAL_SECTION();
vMBMasterPortSerialEnable(false, false);
vMBMasterPortTimersDisable();
EXIT_CRITICAL_SECTION();
}
eMBErrorCode eMBMasterRTUReceive(uint8_t *pucRcvAddress, uint8_t **pucFrame,
uint16_t *pusLength)
{
eMBErrorCode eStatus = MB_ENOERR;
ENTER_CRITICAL_SECTION();
DEBUGASSERT(usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX);
if ((usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN)
&& (usMBCRC16((uint8_t *) ucMasterRTURcvBuf, usMasterRcvBufferPos) == 0))
{
* the decision if a frame is used is done there.
*/
*pucRcvAddress = ucMasterRTURcvBuf[MB_SER_PDU_ADDR_OFF];
* address field and CRC checksum.
*/
*pusLength =
(uint16_t) (usMasterRcvBufferPos - MB_SER_PDU_PDU_OFF -
MB_SER_PDU_SIZE_CRC);
*pucFrame = (uint8_t *) & ucMasterRTURcvBuf[MB_SER_PDU_PDU_OFF];
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION();
return eStatus;
}
eMBErrorCode eMBMasterRTUSend(uint8_t ucSlaveAddress, const uint8_t *pucFrame,
uint16_t usLength)
{
eMBErrorCode eStatus = MB_ENOERR;
uint16_t usCRC16;
if (ucSlaveAddress > CONFIG_MB_MASTER_TOTAL_SLAVE_NUM)
{
return MB_EINVAL;
}
ENTER_CRITICAL_SECTION();
* processing the received frame and the master sent another frame on the
* network. We have to abort sending the frame.
*/
if (eRcvState == STATE_M_RX_IDLE)
{
pucMasterSndBufferCur = (uint8_t *) pucFrame - 1;
usMasterSndBufferCount = 1;
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usMasterSndBufferCount += usLength;
usCRC16 =
usMBCRC16((uint8_t *) pucMasterSndBufferCur, usMasterSndBufferCount);
ucMasterRTUSndBuf[usMasterSndBufferCount++] = (uint8_t) (usCRC16 & 0xFF);
ucMasterRTUSndBuf[usMasterSndBufferCount++] = (uint8_t) (usCRC16 >> 8);
eSndState = STATE_M_TX_XMIT;
vMBMasterPortSerialEnable(false, true);
}
else
{
eStatus = MB_EIO;
}
EXIT_CRITICAL_SECTION();
return eStatus;
}
bool xMBMasterRTUReceiveFSM(void)
{
bool xTaskNeedSwitch = false;
uint8_t ucByte;
DEBUGASSERT((eSndState == STATE_M_TX_IDLE) ||
(eSndState == STATE_M_TX_XFWR));
xMBMasterPortSerialGetByte((int8_t *) & ucByte);
switch (eRcvState)
{
* until the frame is finished.
*/
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable();
break;
* are transmitted.
*/
case STATE_M_RX_ERROR:
vMBMasterPortTimersT35Enable();
break;
* received the t1.5 and t3.5 timers are started and the receiver is in
* the state STATE_RX_RECEIVCE and disable early the timer of respond
* timeout.
*/
case STATE_M_RX_IDLE:
* of respond timeout and change the transmitter state to idle.
*/
vMBMasterPortTimersDisable();
eSndState = STATE_M_TX_IDLE;
usMasterRcvBufferPos = 0;
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
eRcvState = STATE_M_RX_RCV;
vMBMasterPortTimersT35Enable();
break;
* character received. If more than the maximum possible number of bytes
* in a modbus frame is received the frame is ignored.
*/
case STATE_M_RX_RCV:
if (usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX)
{
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
}
else
{
eRcvState = STATE_M_RX_ERROR;
}
vMBMasterPortTimersT35Enable();
break;
}
return xTaskNeedSwitch;
}
bool xMBMasterRTUTransmitFSM(void)
{
bool xNeedPoll = false;
DEBUGASSERT(eRcvState == STATE_M_RX_IDLE);
switch (eSndState)
{
* state.
*/
case STATE_M_TX_IDLE:
vMBMasterPortSerialEnable(true, false);
break;
case STATE_M_TX_XMIT:
if (usMasterSndBufferCount != 0)
{
xMBMasterPortSerialPutByte((uint8_t) * pucMasterSndBufferCur);
pucMasterSndBufferCur++;
usMasterSndBufferCount--;
}
else
{
xFrameIsBroadcast =
(ucMasterRTUSndBuf[MB_SER_PDU_ADDR_OFF] ==
MB_ADDRESS_BROADCAST) ? true : false;
* interrupt.
*/
vMBMasterPortSerialEnable(true, false);
eSndState = STATE_M_TX_XFWR;
* delay, else master will enable timer of respond timeout.
*/
if (xFrameIsBroadcast == true)
{
vMBMasterPortTimersConvertDelayEnable();
}
else
{
vMBMasterPortTimersRespondTimeoutEnable();
}
}
break;
case STATE_M_TX_XFWR:
default:
break;
}
return xNeedPoll;
}
bool xMBMasterRTUTimerExpired(void)
{
bool xNeedPoll = false;
switch (eRcvState)
{
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_READY);
break;
* frame was received.
*/
case STATE_M_RX_RCV:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
break;
case STATE_M_RX_ERROR:
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
break;
default:
DEBUGASSERT((eRcvState == STATE_M_RX_INIT) ||
(eRcvState == STATE_M_RX_RCV) ||
(eRcvState == STATE_M_RX_ERROR) ||
(eRcvState == STATE_M_RX_IDLE));
break;
}
eRcvState = STATE_M_RX_IDLE;
switch (eSndState)
{
* If the frame is broadcast,The master will idle,and if the frame is not
* broadcast.Notify the listener process error.
*/
case STATE_M_TX_XFWR:
if (xFrameIsBroadcast == false)
{
vMBMasterSetErrorType(EV_ERROR_RESPOND_TIMEOUT);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
}
break;
default:
DEBUGASSERT((eSndState == STATE_M_TX_XFWR) ||
(eSndState == STATE_M_TX_IDLE));
break;
}
eSndState = STATE_M_TX_IDLE;
vMBMasterPortTimersDisable();
* EV_MASTER_EXECUTE status.
*/
if (eMasterCurTimerMode == MB_TMODE_CONVERT_DELAY)
{
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_EXECUTE);
}
return xNeedPoll;
}
void vMBMasterGetRTUSndBuf(uint8_t **pucFrame)
{
*pucFrame = (uint8_t *) ucMasterRTUSndBuf;
}
void vMBMasterGetPDUSndBuf(uint8_t **pucFrame)
{
*pucFrame = (uint8_t *) & ucMasterRTUSndBuf[MB_SER_PDU_PDU_OFF];
}
void vMBMasterSetPDUSndLength(uint16_t SendPDULength)
{
usMasterSendPDULength = SendPDULength;
}
uint16_t usMBMasterGetPDUSndLength(void)
{
return usMasterSendPDULength;
}
void vMBMasterSetCurTimerMode(eMBMasterTimerMode eMBTimerMode)
{
eMasterCurTimerMode = eMBTimerMode;
}
bool xMBMasterRequestIsBroadcast(void)
{
return xFrameIsBroadcast;
}
#endif