⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mbascii.c

📁 freemodbus-v019.zip 是v0.19版本的代码
💻 C
字号:
 /*  * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.  * Copyright (C) 2006 Christian Walter <wolti@sil.at>  *  * This library is free software; you can redistribute it and/or  * modify it under the terms of the GNU Lesser General Public  * License as published by the Free Software Foundation; either  * version 2.1 of the License, or (at your option) any later version.  *  * This library is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  * Lesser General Public License for more details.  *  * You should have received a copy of the GNU Lesser General Public  * License along with this library; if not, write to the Free Software  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *  * File: $Id: mbascii.c,v 1.11 2006/06/18 09:57:03 wolti Exp $  *//* ----------------------- System includes ----------------------------------*/#include "stdlib.h"#include "string.h"/* ----------------------- Platform includes --------------------------------*/#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/#include "mb.h"#include "mbconfig.h"#include "mbascii.h"#include "mbframe.h"#include "mbcrc.h"#include "mbport.h"#if MB_ASCII_ENABLED > 0/* ----------------------- Defines ------------------------------------------*/#define MB_ASCII_DEFAULT_CR     '\r'    /*!< Default CR character for Modbus ASCII. */#define MB_ASCII_DEFAULT_LF     '\n'    /*!< Default LF character for Modbus ASCII. */#define MB_SER_PDU_SIZE_MIN     3       /*!< Minimum size of a Modbus ASCII frame. */#define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus ASCII frame. */#define MB_SER_PDU_SIZE_LRC     1       /*!< Size of LRC 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_IDLE,              /*!< Receiver is in idle state. */    STATE_RX_RCV,               /*!< Frame is beeing received. */    STATE_RX_WAIT_EOF           /*!< Wait for End of Frame. */} eMBRcvState;typedef enum{    STATE_TX_IDLE,              /*!< Transmitter is in idle state. */    STATE_TX_START,             /*!< Starting transmission (':' sent). */    STATE_TX_DATA,              /*!< Sending of data (Address, Data, LRC). */    STATE_TX_END,               /*!< End of transmission. */    STATE_TX_NOTIFY,            /*!< Notify sender that the frame has been sent. */} eMBSndState;typedef enum{    BYTE_HIGH_NIBBLE,           /*!< Character for high nibble of byte. */    BYTE_LOW_NIBBLE             /*!< Character for low nibble of byte. */} eMBBytePos;/* ----------------------- Static functions ---------------------------------*/static UCHAR    prvucMBCHAR2BIN( UCHAR ucCharacter );static UCHAR    prvucMBBIN2CHAR( UCHAR ucByte );static UCHAR    prvucMBLRC( UCHAR * pucFrame, USHORT usLen );/* ----------------------- Static variables ---------------------------------*/static volatile eMBSndState eSndState;static volatile eMBRcvState eRcvState;/* We reuse the Modbus RTU buffer because only one buffer is needed and the * RTU buffer is bigger. */extern volatile UCHAR ucRTUBuf[];static volatile UCHAR *ucASCIIBuf = ucRTUBuf;static volatile USHORT usRcvBufferPos;static volatile eMBBytePos eBytePos;static volatile UCHAR *pucSndBufferCur;static volatile USHORT usSndBufferCount;static volatile UCHAR ucLRC;static volatile UCHAR ucMBLFCharacter;/* ----------------------- Start implementation -----------------------------*/eMBErrorCodeeMBASCIIInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate,              eMBParity eParity ){    eMBErrorCode    eStatus = MB_ENOERR;    ENTER_CRITICAL_SECTION(  );    ucMBLFCharacter = MB_ASCII_DEFAULT_LF;    if( xMBPortSerialInit( ucPort, ulBaudRate, 7, eParity ) != TRUE )    {        eStatus = MB_EPORTERR;    }    if( xMBPortTimersInit( MB_ASCII_TIMEOUT_SEC * 20000UL ) != TRUE )    {        eStatus = MB_EPORTERR;    }    EXIT_CRITICAL_SECTION(  );    return eStatus;}voideMBASCIIStart( void ){    ENTER_CRITICAL_SECTION(  );    vMBPortSerialEnable( TRUE, FALSE );    eRcvState = STATE_RX_IDLE;    EXIT_CRITICAL_SECTION(  );    /* No special startup required for ASCII. */    ( void )xMBPortEventPost( EV_READY );}voideMBASCIIStop( void ){    ENTER_CRITICAL_SECTION(  );    vMBPortSerialEnable( FALSE, FALSE );    vMBPortTimersDisable(  );    EXIT_CRITICAL_SECTION(  );}eMBErrorCodeeMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ){    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 )        && ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, 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 = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus         * size of address field and CRC checksum.         */        *pusLength = usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC;        /* Return the start of the Modbus PDU to the caller. */        *pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF];    }    else    {        eStatus = MB_EIO;    }    EXIT_CRITICAL_SECTION(  );    return eStatus;}eMBErrorCodeeMBASCIISend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ){    eMBErrorCode    eStatus = MB_ENOERR;    UCHAR           usLRC;    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 LRC checksum for Modbus-Serial-Line-PDU. */        usLRC = prvucMBLRC( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );        ucASCIIBuf[usSndBufferCount++] = usLRC;        /* Activate the transmitter. */        eSndState = STATE_TX_START;        vMBPortSerialEnable( FALSE, TRUE );    }    else    {        eStatus = MB_EIO;    }    EXIT_CRITICAL_SECTION(  );    return eStatus;}BOOLxMBASCIIReceiveFSM( void ){    BOOL            xNeedPoll = FALSE;    UCHAR           ucByte;    UCHAR           ucResult;    assert( eSndState == STATE_TX_IDLE );    ( void )xMBPortSerialGetByte( &ucByte );    switch ( eRcvState )    {            /* A new character is received. If the character is a ':' the input             * buffer is cleared. A CR-character signals the end of the data             * block. Other characters are part of the data block and their             * ASCII value is converted back to a binary representation.             */        case STATE_RX_RCV:            /* Enable timer for character timeout. */            vMBPortTimersEnable(  );            if( ucByte == ':' )            {                /* Empty receive buffer. */                eBytePos = BYTE_HIGH_NIBBLE;                usRcvBufferPos = 0;            }            else if( ucByte == MB_ASCII_DEFAULT_CR )            {                eRcvState = STATE_RX_WAIT_EOF;            }            else            {                ucResult = prvucMBCHAR2BIN( ucByte );                switch ( eBytePos )                {                        /* High nibble of the byte comes first. We check for                         * a buffer overflow here. */                    case BYTE_HIGH_NIBBLE:                        if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )                        {                            ucASCIIBuf[usRcvBufferPos] = ucResult << 4;                            eBytePos = BYTE_LOW_NIBBLE;                            break;                        }                        else                        {                            /* not handled in Modbus specification but seems                             * a resonable implementation. */                            eRcvState = STATE_RX_IDLE;                            /* Disable previously activated timer because of error state. */                            vMBPortTimersDisable(  );                        }                        break;                    case BYTE_LOW_NIBBLE:                        ucASCIIBuf[usRcvBufferPos++] |= ucResult;                        eBytePos = BYTE_HIGH_NIBBLE;                        break;                }            }            break;        case STATE_RX_WAIT_EOF:            if( ucByte == ucMBLFCharacter )            {                /* Disable character timeout timer because all characters are                 * received. */                vMBPortTimersDisable(  );                /* Receiver is again in idle state. */                eRcvState = STATE_RX_IDLE;                /* Notify the caller of eMBASCIIReceive that a new frame                 * was received. */                xNeedPoll = xMBPortEventPost( EV_FRAME_RECEIVED );            }            else if( ucByte == ':' )            {                /* Empty receive buffer and back to receive state. */                eBytePos = BYTE_HIGH_NIBBLE;                usRcvBufferPos = 0;                eRcvState = STATE_RX_RCV;                /* Enable timer for character timeout. */                vMBPortTimersEnable(  );            }            else            {                /* Frame is not okay. Delete entire frame. */                eRcvState = STATE_RX_IDLE;            }            break;        case STATE_RX_IDLE:            if( ucByte == ':' )            {                /* Enable timer for character timeout. */                vMBPortTimersEnable(  );                /* Reset the input buffers to store the frame. */                usRcvBufferPos = 0;;                eBytePos = BYTE_HIGH_NIBBLE;                eRcvState = STATE_RX_RCV;            }            break;    }    return xNeedPoll;}BOOLxMBASCIITransmitFSM( void ){    BOOL            xNeedPoll = FALSE;    UCHAR           ucByte;    assert( eRcvState == STATE_RX_IDLE );    switch ( eSndState )    {            /* Start of transmission. The start of a frame is defined by sending             * the character ':'. */        case STATE_TX_START:            ucByte = ':';            xMBPortSerialPutByte( ucByte );            eSndState = STATE_TX_DATA;            eBytePos = BYTE_HIGH_NIBBLE;            break;            /* Send the data block. Each data byte is encoded as a character hex             * stream with the high nibble sent first and the low nibble sent             * last. If all data bytes are exhausted we send a '\r' character             * to end the transmission. */        case STATE_TX_DATA:            if( usSndBufferCount > 0 )            {                switch ( eBytePos )                {                    case BYTE_HIGH_NIBBLE:                        ucByte = prvucMBBIN2CHAR( *pucSndBufferCur >> 4 );                        xMBPortSerialPutByte( ucByte );                        eBytePos = BYTE_LOW_NIBBLE;                        break;                    case BYTE_LOW_NIBBLE:                        ucByte = prvucMBBIN2CHAR( *pucSndBufferCur & 0x0F );                        xMBPortSerialPutByte( ucByte );                        pucSndBufferCur++;                        eBytePos = BYTE_HIGH_NIBBLE;                        usSndBufferCount--;                        break;                }            }            else            {                xMBPortSerialPutByte( MB_ASCII_DEFAULT_CR );                eSndState = STATE_TX_END;            }            break;            /* Finish the frame by sending a LF character. */        case STATE_TX_END:            xMBPortSerialPutByte( ucMBLFCharacter );            /* We need another state to make sure that the CR character has             * been sent. */            eSndState = STATE_TX_NOTIFY;            break;            /* Notify the task which called eMBASCIISend that the frame has             * been sent. */        case STATE_TX_NOTIFY:            eSndState = STATE_TX_IDLE;            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );            /* Disable transmitter. This prevents another transmit buffer             * empty interrupt. */            vMBPortSerialEnable( TRUE, FALSE );            eSndState = STATE_TX_IDLE;            break;            /* 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;    }    return xNeedPoll;}BOOLxMBASCIITimerT1SExpired( void ){    switch ( eRcvState )    {            /* If we have a timeout we go back to the idle state and wait for             * the next frame.             */        case STATE_RX_RCV:        case STATE_RX_WAIT_EOF:            eRcvState = STATE_RX_IDLE;            break;        default:            assert( ( eRcvState == STATE_RX_RCV )                    || ( eRcvState == STATE_RX_WAIT_EOF ) );            break;    }    vMBPortTimersDisable(  );    /* no context switch required. */    return FALSE;}UCHARprvucMBCHAR2BIN( UCHAR ucCharacter ){    if( ( ucCharacter >= '0' ) && ( ucCharacter <= '9' ) )    {        return ucCharacter - '0';    }    else if( ( ucCharacter >= 'A' ) && ( ucCharacter <= 'F' ) )    {        return ucCharacter - 'A' + 0x0A;    }    else    {        return 0xFF;    }}UCHARprvucMBBIN2CHAR( UCHAR ucByte ){    if( ucByte <= 0x09 )    {        return '0' + ucByte;    }    else if( ( ucByte >= 0x0A ) && ( ucByte <= 0x0F ) )    {        return ucByte - 0x0A + 'A';    }    else    {        /* Programming error. */        assert( 0 );    }    return '0';}UCHARprvucMBLRC( UCHAR * pucFrame, USHORT usLen ){    UCHAR           ucLRC = 0;  /* LRC char initialized */    while( usLen-- )    {        ucLRC += *pucFrame++;   /* Add buffer byte without carry */    }    /* Return twos complement */    ucLRC = ( UCHAR ) ( -( ( CHAR ) ucLRC ) );    return ucLRC;}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -