| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 | /*  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. * Copyright (c) 2006-2018 Christian Walter <cwalter@embedded-solutions.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. * *//* ----------------------- System includes ----------------------------------*/#include "stdlib.h"#include "string.h"/* ----------------------- Platform includes --------------------------------*/#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/#include "mb.h"#include "mbrtu.h"#include "mbframe.h"#include "mbcrc.h"#include "mbport.h"//#include "tinystdio.h"/* ----------------------- Defines ------------------------------------------*/#define MB_SER_PDU_SIZE_MIN     4       /*!< Minimum size of a Modbus RTU frame. */#define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus RTU frame. */#define MB_SER_PDU_SIZE_CRC     2       /*!< Size of CRC field in PDU. */#define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */#define MB_SER_PDU_PDU_OFF      1       /*!< Offset of Modbus-PDU in Ser-PDU. *//* ----------------------- Type definitions ---------------------------------*/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;typedef enum{    STATE_TX_IDLE,              /*!< Transmitter is in idle state. */    STATE_TX_XMIT               /*!< Transmitter is in transfer state. */} eMBSndState;/* ----------------------- Static variables ---------------------------------*/static volatile eMBSndState eSndState;static volatile eMBRcvState eRcvState;volatile UCHAR  ucRTUBuf[MB_SER_PDU_SIZE_MAX];static volatile UCHAR *pucSndBufferCur;static volatile USHORT usSndBufferCount;static volatile USHORT usRcvBufferPos;/* ----------------------- Start implementation -----------------------------*/eMBErrorCodeeMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate,             eMBParity eParity, unsigned int stop_bit ){    eMBErrorCode    eStatus = MB_ENOERR;    ULONG           usTimerT35_50us;    ( void )ucSlaveAddress;    ENTER_CRITICAL_SECTION(  );    /* Modbus RTU uses 8 Databits. */    if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity, stop_bit ) != 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;}voideMBRTUStart( 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(  );}voideMBRTUStop( void ){    ENTER_CRITICAL_SECTION(  );    vMBPortSerialEnable( FALSE, FALSE );    vMBPortTimersDisable(  );    EXIT_CRITICAL_SECTION(  );}eMBErrorCodeeMBRTUReceive( 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];        xFrameReceived = TRUE;    }    else    {        eStatus = MB_EIO;    }    EXIT_CRITICAL_SECTION(  );    return eStatus;}eMBErrorCodeeMBRTUSend( 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 );        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );		//usSndBufferCount++;        /* Activate the transmitter. */        eSndState = STATE_TX_XMIT;        vMBPortSerialEnable( FALSE, TRUE );    }    else    {        eStatus = MB_EIO;    }    EXIT_CRITICAL_SECTION(  );    return eStatus;}BOOLxMBRTUReceiveFSM( 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;}BOOLxMBRTUTransmitFSM( 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 );            //printf("%X\r\n", *pucSndBufferCur);            pucSndBufferCur++;  /* next byte in sendbuffer. */            usSndBufferCount--;        }        else        {            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );            //printf("mb_event: EV_FRAME_SENT\r\n");            /* Disable transmitter. This prevents another transmit buffer             * empty interrupt. */            vMBPortSerialEnable( TRUE, FALSE );            eSndState = STATE_TX_IDLE;        }        break;    }    return xNeedPoll;}BOOLxMBRTUTimerT35Expired( void ){    BOOL            xNeedPoll = FALSE;    switch ( eRcvState )    {        /* Timer t35 expired. Startup phase is finished. */    case STATE_RX_INIT:        xNeedPoll = xMBPortEventPost( EV_READY );        //printf("mb_event: EV_READY\r\n");        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 );        //printf("mb_event: EV_FRAME_RECEIVED\r\n");        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;}
 |