1、【更新】《FreeModbus 主机分析图》中关于发送及接受状态图,其余还需后期完善;

2、【增加】FreeModbus主机传输层逻辑实现,稳定性还需后期测试;

Signed-off-by: armink <armink.ztl@gmail.com>
This commit is contained in:
armink 2013-08-19 16:51:08 +08:00
parent 31fab3ed4d
commit dfe745c166
8 changed files with 277 additions and 199 deletions

Binary file not shown.

View File

@ -46,6 +46,15 @@ typedef enum
EV_FRAME_SENT /*!< Frame sent. */
} eMBEventType;
typedef enum
{
EV_MASTER_READY, /*!< Startup finished. */
EV_MASTER_FRAME_RECEIVED, /*!< Frame received. */
EV_MASTER_EXECUTE, /*!< Execute function. */
EV_MASTER_FRAME_SENT, /*!< Frame sent. */
EV_MASTER_ERROR_PROCESS /*!< Frame error process*/
} eMBMasterEventType;
/*! \ingroup modbus
* \brief Parity used for characters in serial mode.
*
@ -69,9 +78,9 @@ BOOL xMBPortEventGet( /*@out@ */ eMBEventType * eEvent );
BOOL xMBMasterPortEventInit( void );
BOOL xMBMasterPortEventPost( eMBEventType eEvent );
BOOL xMBMasterPortEventPost( eMBMasterEventType eEvent );
BOOL xMBMasterPortEventGet( /*@out@ */ eMBEventType * eEvent );
BOOL xMBMasterPortEventGet( /*@out@ */ eMBMasterEventType * eEvent );
/* ----------------------- Serial port functions ----------------------------*/
@ -100,7 +109,7 @@ void vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );
INLINE BOOL xMBMasterPortSerialGetByte( CHAR * pucByte );
INLINE BOOL xMBPortSerialPutByte( CHAR ucByte );
INLINE BOOL xMBMasterPortSerialPutByte( CHAR ucByte );
/* ----------------------- Timers functions ---------------------------------*/
BOOL xMBPortTimersInit( USHORT usTimeOut50us );
@ -115,7 +124,11 @@ BOOL xMBMasterPortTimersInit( USHORT usTimeOut50us );
void xMBMasterPortTimersClose( void );
INLINE void vMBMasterPortTimersEnable( void );
INLINE void vMBMasterPortTimersT35Enable( void );
INLINE void vMBMasterPortTimersConvertDelayEnable( void );
INLINE void vMBMasterPortTimersRespondTimeoutEnable( void );
INLINE void vMBMasterPortTimersDisable( void );

View File

@ -346,6 +346,7 @@ xMBRTUTimerT35Expired( void )
default:
assert_param( ( eRcvState == STATE_RX_INIT ) ||
( eRcvState == STATE_RX_RCV ) || ( eRcvState == STATE_RX_ERROR ) );
break;
}
vMBPortTimersDisable( );

View File

@ -57,13 +57,14 @@ 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. */
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. */
STATE_M_TX_XFWR, /*!< Transmitter is in transfer finish and wait receive state. */
} eMBMasterSndState;
/* ----------------------- Static variables ---------------------------------*/
@ -72,11 +73,11 @@ static volatile eMBMasterRcvState eRcvState;
volatile UCHAR ucMasterRTUBuf[MB_SER_PDU_SIZE_MAX];
static volatile UCHAR *pucSndBufferCur;
static volatile USHORT usSndBufferCount;
static volatile USHORT usRcvBufferPos;
static volatile UCHAR *pucMasterSndBufferCur;
static volatile USHORT usMasterSndBufferCount;
static volatile USHORT usMasterRcvBufferPos;
static volatile BOOL bFrameIsBroadcast = FALSE;
/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterRTUInit(UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
@ -133,7 +134,7 @@ eMBMasterRTUStart( void )
*/
eRcvState = STATE_M_RX_INIT;
vMBMasterPortSerialEnable( TRUE, FALSE );
vMBMasterPortTimersEnable( );
vMBMasterPortTimersT35Enable( );
EXIT_CRITICAL_SECTION( );
}
@ -147,179 +148,191 @@ eMBMasterRTUStop( void )
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;
//}
//
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( ( usMasterRcvBufferPos >= MB_SER_PDU_SIZE_MIN )
&& ( usMBCRC16( ( UCHAR * ) ucMasterRTUBuf, usMasterRcvBufferPos ) == 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 )( usMasterRcvBufferPos - 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. */
pucMasterSndBufferCur = ( UCHAR * ) pucFrame - 1;
usMasterSndBufferCount = 1;
/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
pucMasterSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
usMasterSndBufferCount += usLength;
/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
usCRC16 = usMBCRC16( ( UCHAR * ) pucMasterSndBufferCur, usMasterSndBufferCount );
ucMasterRTUBuf[usMasterSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
ucMasterRTUBuf[usMasterSndBufferCount++] = ( 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_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
break;
/* In the error state we wait until all characters in the
* damaged frame are transmitted.
*/
case STATE_M_RX_ERROR:
vMBMasterPortTimersT35Enable( );
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 and disable early
* the timer of respond timeout .
*/
case STATE_M_RX_IDLE:
/* In time of respond timeout,the receiver receive a frame.
* Disable timer of respond timeout and change the transmiter state to idle.
*/
vMBMasterPortTimersDisable( );
eSndState = STATE_M_TX_IDLE;
usMasterRcvBufferPos = 0;
ucMasterRTUBuf[usMasterRcvBufferPos++] = ucByte;
eRcvState = STATE_M_RX_RCV;
/* Enable t3.5 timers. */
vMBMasterPortTimersT35Enable( );
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( usMasterRcvBufferPos < MB_SER_PDU_SIZE_MAX )
{
ucMasterRTUBuf[usMasterRcvBufferPos++] = ucByte;
}
else
{
eRcvState = STATE_M_RX_ERROR;
}
vMBMasterPortTimersT35Enable();
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( usMasterSndBufferCount != 0 )
{
xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
pucMasterSndBufferCur++; /* next byte in sendbuffer. */
usMasterSndBufferCount--;
}
else
{
bFrameIsBroadcast = ( ucMasterRTUBuf[MB_SER_PDU_ADDR_OFF] == MB_ADDRESS_BROADCAST ) ? TRUE : FALSE;
xNeedPoll = xMBMasterPortEventPost( EV_MASTER_FRAME_SENT );
/* Disable transmitter. This prevents another transmit buffer
* empty interrupt. */
vMBMasterPortSerialEnable( TRUE, FALSE );
eSndState = STATE_M_TX_XFWR;
/* If the frame is broadcast ,master will enable timer of convert delay,
* else master will enable timer of respond timeout. */
if ( bFrameIsBroadcast == TRUE ) vMBMasterPortTimersConvertDelayEnable( );
else vMBMasterPortTimersRespondTimeoutEnable( );
}
break;
}
return xNeedPoll;
}
BOOL
xMBMasterRTUTimerT35Expired(void)
{
@ -329,13 +342,13 @@ xMBMasterRTUTimerT35Expired(void)
{
/* Timer t35 expired. Startup phase is finished. */
case STATE_M_RX_INIT:
xNeedPoll = xMBMasterPortEventPost(EV_READY);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_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);
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
break;
/* An error occured while receiving the frame. */
@ -348,19 +361,24 @@ xMBMasterRTUTimerT35Expired(void)
( 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)
switch (eSndState)
{
/* Timer t35 expired. . */
case STATE_M_TX_XMIT :
/* A frame was send finish and convert delay or respond timeout expired.
* 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 ( bFrameIsBroadcast == FALSE ) xNeedPoll = xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS);
break;
/* Function called in an illegal state. */
default:
assert_param( eSndState == STATE_M_TX_XFWR );
break;
}
eSndState = STATE_M_TX_IDLE;
vMBMasterPortTimersDisable();
return xNeedPoll;
}
#endif

View File

@ -23,6 +23,7 @@
#define _PORT_H
#include <stm32f10x_conf.h>
#include "mbconfig.h"
#include <rthw.h>
#include <rtthread.h>

View File

@ -23,8 +23,9 @@
#include "mb.h"
#include "mbport.h"
#if MB_MASTER_RTU_ENABLED > 0
/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eMasterQueuedEvent;
static eMBMasterEventType eMasterQueuedEvent;
static BOOL xMasterEventInQueue;
/* ----------------------- Start implementation -----------------------------*/
@ -36,7 +37,7 @@ xMBMasterPortEventInit( void )
}
BOOL
xMBMasterPortEventPost( eMBEventType eEvent )
xMBMasterPortEventPost( eMBMasterEventType eEvent )
{
xMasterEventInQueue = TRUE;
eMasterQueuedEvent = eEvent;
@ -44,7 +45,7 @@ xMBMasterPortEventPost( eMBEventType eEvent )
}
BOOL
xMBMasterPortEventGet( eMBEventType * eEvent )
xMBMasterPortEventGet( eMBMasterEventType * eEvent )
{
BOOL xEventHappened = FALSE;
@ -56,3 +57,5 @@ xMBMasterPortEventGet( eMBEventType * eEvent )
}
return xEventHappened;
}
#endif

View File

@ -24,6 +24,8 @@
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#if MB_MASTER_RTU_ENABLED > 0
/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);
@ -188,3 +190,5 @@ void USART2_IRQHandler(void)
}
rt_interrupt_leave();
}
#endif

View File

@ -26,11 +26,15 @@
#include "mb.h"
#include "mbport.h"
#if MB_MASTER_RTU_ENABLED > 0
/* ----------------------- Variables ----------------------------------------*/
static USHORT usT35TimeOut50us;
/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR(void);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortTimersInit(USHORT usTim1Timerout50us)
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
uint16_t PrescalerValue = 0;
@ -48,7 +52,8 @@ BOOL xMBMasterPortTimersInit(USHORT usTim1Timerout50us)
PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
//¶¨Ê±Æ÷1³õʼ»¯
TIM_TimeBaseStructure.TIM_Period = (uint16_t) usTim1Timerout50us;
usT35TimeOut50us = usTimeOut50us; //保存T35定时器计数值
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
@ -72,8 +77,39 @@ BOOL xMBMasterPortTimersInit(USHORT usTim1Timerout50us)
return TRUE;
}
void vMBMasterPortTimersEnable()
void vMBMasterPortTimersT35Enable()
{
//装载计数值 基准50us
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = (uint16_t) usT35TimeOut50us;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
void vMBMasterPortTimersConvertDelayEnable()
{
//装载计数值 基准50us
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = (uint16_t)(MB_MASTER_DELAY_MS_CONVERT * 1000 / 50) ;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
void vMBMasterPortTimersRespondTimeoutEnable()
{
//装载计数值 基准50us
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = (uint16_t)(MB_MASTER_TIMEOUT_MS_RESPOND * 1000 / 50);
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_SetCounter(TIM2, 0);
@ -105,3 +141,5 @@ void TIM2_IRQHandler(void)
}
rt_interrupt_leave();
}
#endif