/* * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU. * Copyright (c) 2013 China Beijing Armink * 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/17 11:42:56 Armink Add Master Functions , $ */ /* ----------------------- 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" #if MB_MASTER_RTU_ENABLED > 0 /* ----------------------- 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_M_RX_INIT, /*!< Receiver is in initial state. */ STATE_M_RX_IDLE, /*!< Receiver is in idle state. */ STATE_M_RX_RCV, /*!< Frame is beeing received. */ STATE_M_RX_ERROR /*!< If the frame is invalid. */ } eMBMasterRcvState; typedef enum { STATE_M_TX_IDLE, /*!< Transmitter is in idle state. */ STATE_M_TX_XMIT, /*!< Transmitter is in transfer state. */ } eMBMasterSndState; /* ----------------------- Static variables ---------------------------------*/ static volatile eMBMasterSndState eSndState; static volatile eMBMasterRcvState eRcvState; volatile UCHAR ucMasterRTUBuf[MB_SER_PDU_SIZE_MAX]; static volatile UCHAR *pucSndBufferCur; static volatile USHORT usSndBufferCount; static volatile USHORT usRcvBufferPos; /* ----------------------- Start implementation -----------------------------*/ eMBErrorCode eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) { eMBErrorCode eStatus = MB_ENOERR; ULONG usTimerT35_50us; ENTER_CRITICAL_SECTION( ); /* Modbus RTU uses 8 Databits. */ if( xMBMasterPortSerialInit( 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( xMBMasterPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE ) { eStatus = MB_EPORTERR; } } EXIT_CRITICAL_SECTION( ); return eStatus; } void eMBMasterRTUStart( void ) { ENTER_CRITICAL_SECTION( ); /* Initially the receiver is in the state STATE_M_RX_INIT. we start * the timer 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 ); vMBMasterPortTimersEnable( ); EXIT_CRITICAL_SECTION( ); } void eMBMasterRTUStop( void ) { ENTER_CRITICAL_SECTION( ); vMBMasterPortSerialEnable( FALSE, FALSE ); vMBMasterPortTimersDisable( ); EXIT_CRITICAL_SECTION( ); } //eMBErrorCode //eMBMasterRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) //{ // BOOL xFrameReceived = FALSE; // eMBErrorCode eStatus = MB_ENOERR; // // ENTER_CRITICAL_SECTION( ); // assert_param( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ); // // /* Length and CRC check */ // if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN ) // && ( usMBCRC16( ( UCHAR * ) ucMasterRTUBuf, 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 = ucMasterRTUBuf[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 * ) & ucMasterRTUBuf[MB_SER_PDU_PDU_OFF]; // xFrameReceived = TRUE; // } // else // { // eStatus = MB_EIO; // } // // EXIT_CRITICAL_SECTION( ); // return eStatus; //} // //eMBErrorCode //eMBMasterRTUSend( 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_M_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 ); // ucMasterRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); // ucMasterRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); // // /* Activate the transmitter. */ // eSndState = STATE_M_TX_XMIT; // vMBMasterPortSerialEnable( FALSE, TRUE ); // } // else // { // eStatus = MB_EIO; // } // EXIT_CRITICAL_SECTION( ); // return eStatus; //} // //BOOL //xMBMasterRTUReceiveFSM( void ) //{ // BOOL xTaskNeedSwitch = FALSE; // UCHAR ucByte; // // assert_param( eSndState == STATE_TX_IDLE ); // // /* Always read the character. */ // ( void )xMBMasterPortSerialGetByte( ( 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: // vMBMasterPortTimersEnable( ); // break; // // /* In the error state we wait until all characters in the // * damaged frame are transmitted. // */ // case STATE_RX_ERROR: // vMBMasterPortTimersEnable( ); // 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_M_RX_IDLE: // usRcvBufferPos = 0; // ucMasterRTUBuf[usRcvBufferPos++] = ucByte; // eRcvState = STATE_M_RX_RCV; // // /* Enable t3.5 timers. */ // vMBMasterPortTimersEnable( ); // 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_M_RX_RCV: // if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX ) // { // ucMasterRTUBuf[usRcvBufferPos++] = ucByte; // } // else // { // eRcvState = STATE_RX_ERROR; // } // vMBMasterPortTimersEnable(); // break; // } // return xTaskNeedSwitch; //} // //BOOL //xMBMasterRTUTransmitFSM( void ) //{ // BOOL xNeedPoll = FALSE; // // assert_param( eRcvState == STATE_RX_IDLE ); // // switch ( eSndState ) // { // /* We should not get a transmitter event if the transmitter is in // * idle state. */ // case STATE_M_TX_IDLE: // /* enable receiver/disable transmitter. */ // vMBMasterPortSerialEnable( TRUE, FALSE ); // break; // // case STATE_M_TX_XMIT: // /* check if we are finished. */ // if( usSndBufferCount != 0 ) // { // xMBMasterPortSerialPutByte( ( CHAR )*pucSndBufferCur ); // pucSndBufferCur++; /* next byte in sendbuffer. */ // usSndBufferCount--; // } // else // { // xNeedPoll = xMBMasterPortEventPost( EV_FRAME_SENT ); // /* Disable transmitter. This prevents another transmit buffer // * empty interrupt. */ // vMBMasterPortSerialEnable( TRUE, FALSE ); // eSndState = STATE_M_TX_IDLE; // } // break; // } // // return xNeedPoll; //} // BOOL xMBMasterRTUTimerT35Expired(void) { BOOL xNeedPoll = FALSE; switch (eRcvState) { /* Timer t35 expired. Startup phase is finished. */ case STATE_M_RX_INIT: xNeedPoll = xMBMasterPortEventPost(EV_READY); break; /* A frame was received and t35 expired. Notify the listener that * a new frame was received. */ case STATE_M_RX_RCV: xNeedPoll = xMBMasterPortEventPost(EV_FRAME_RECEIVED); break; /* An error occured while receiving the frame. */ case STATE_M_RX_ERROR: break; /* Function called in an illegal state. */ default: assert_param( ( eRcvState == STATE_M_RX_INIT ) || ( eRcvState == STATE_M_RX_RCV ) || ( eRcvState == STATE_M_RX_ERROR )); break; } vMBMasterPortTimersDisable(); eRcvState = STATE_M_RX_IDLE; //TODO 发送状态在超时后的状态转换需要添加 switch(eSndState) { /* Timer t35 expired. . */ case STATE_M_TX_XMIT : } return xNeedPoll; } #endif