📄 mbrtu.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: mbrtu.c,v 1.14 2006/11/19 03:04:11 wolti Exp $ *//* ----------------------- 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"/* ----------------------- 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 ){ eMBErrorCode eStatus = MB_ENOERR; ULONG usTimerT35_50us; ENTER_CRITICAL_SECTION( ); /* Modbus RTU uses 8 Databits. */ if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ) { eStatus = MB_EPORTERR; } /* 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 = 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++] = usCRC16 & 0xFF; ucRTUBuf[usSndBufferCount++] = usCRC16 >> 8; /* 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 ); 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; ( void )xMBPortSerialGetByte( ( CHAR * )&ucByte ); 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 ) { ( void )xMBPortSerialGetByte( ( CHAR * )&ucByte ); 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( *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;}BOOLxMBRTUTimerT35Expired( 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -