【STM32】FreeModbus-RTU主机模式下数据接受函数传递
前言 最近在使用RTT提供的FreeModbus软件包进行开发,由于想使用DMA进行数据传输,于是对接收部分函数进行了探究,写下此文章。如何实现DMA方式收发将会写在另一篇文章中。
环境
- 芯片:STM32L1x系列芯片
- 配置工具:CubeMX
- RT-thread版本:3.1.3
- HAL库版本:1.9.0
- IDE:KEIL v5.28
并没有使用官方推荐的Env工具进行配置,并且重写串口收发方式为硬件方式FreeModbus接收函数传递 当串口出现接收中断后,随后调用MB的写好的一个函数
prvvUARTRxISR()
/**
* This function is serial receive callback function
*/
void USARTx_IRQHandler(void)
{
/* 接收中断处理 */
if(__HAL_UART_GET_FLAG(&huartx, UART_FLAG_RXNE))
{
__HAL_UART_CLEAR_FLAG(&huartx, UART_FLAG_RXNE);
prvvUARTRxISR();
/* MB回调函数 */
}
/* 省略部分代码... */
}
在该函数中又会继续调用另一个函数
pxMBMasterFrameCBByteReceived()
其中对于该函数的注释意思大致是:创建一个接收中断处理程序。然后这个函数将会调用pxMBFramecBByteReceived()。协议栈将调用xMBPortSerialGetBvte()来接收字符串。
/*
* Create an interrupt handler for the receive interrupt for your target
* processor. This function should then call pxMBFrameCBByteReceived( ). The
* protocol stack will then call xMBMasterPortSerialGetByte( ) to retrieve the
* character.
*/
void prvvUARTRxISR(void)
{
pxMBMasterFrameCBByteReceived();
}
通过使用在文件中搜索功能,找到
pxMBFramecBByteReceived()
的定义这个其实是一个函数指针,在MB初始化的时候指向了另一个函数
xMBMasterRTUReceiveFSM()
BOOL( *pxMBMasterFrameCBByteReceived )( void );
eMBMasterInit( eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
/* 省略部分代码... */
switch (eMode)
{
case MB_RTU:
/* 省略部分代码... */
pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM;
/* 省略部分代码... */
break
/* 省略部分代码... */}
找到这个函数,便是MB做接收处理函数
- 先调用
xMBMasterPortSerialGetByte()
函数接收储存在USART的数据寄存器的一个字节的数据 - 用switch判断当前状态,如果是在正常运行阶段,应该是处于
STATE_M_RX_IDLE
接收空闲状态。当第一个字节数据到达的时候,状态改变为STATE_M_RX_RCV
正在接收状态,同时启动1.5ms和3.5ms的定时器。当定时器超时的时候,会重新变回接收空闲状态。
BOOL xMBMasterRTUReceiveFSM( void )
{
BOOLxTaskNeedSwitch = FALSE;
UCHARucByte;
RT_ASSERT(( eSndState == STATE_M_TX_IDLE ) || ( eSndState == STATE_M_TX_XFWR ));
/* 调用串口接收函数 */
( void )xMBMasterPortSerialGetByte( ( CHAR * ) & ucByte );
switch ( eRcvState )
{
case STATE_M_RX_INIT:
vMBMasterPortTimersT35Enable( );
break;
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;
ucMasterRTURcvBuf[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 )
{
ucMasterRTURcvBuf[usMasterRcvBufferPos++] = ucByte;
}
else
{
eRcvState = STATE_M_RX_ERROR;
}
vMBMasterPortTimersT35Enable();
break;
}
return xTaskNeedSwitch;
}
查找定时器初始化函数就可以看到,定时器超时回调函数
timer_timeout_ind()
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
/* backup T35 ticks */
usT35TimeOut50us = usTimeOut50us;
rt_timer_init(&timer, "master timer",
timer_timeout_ind, /* 超时回调函数 */
RT_NULL,
(50 * usT35TimeOut50us) / (1000 * 1000 / RT_TICK_PER_SECOND) + 1,
RT_TIMER_FLAG_ONE_SHOT);
/* one shot */return TRUE;
}
找到
timer_timeout_ind()
后发现,这简直是一个循环嵌套啊。timer_timeout_ind()
调用prvvTIMERExpiredISR()
prvvTIMERExpiredISR()
再调用pxMBMasterPortCBTimerExpired()
而
pxMBMasterPortCBTimerExpired()
是一个函数指针,在MB初始化的时候指向xMBMasterRTUTimerExpired()
最终在这个函数中,经过判断确认处于
STATE_M_RX_RCV
正在接收状态,通过xMBMasterPortEventPost
发出帧接收完成信号EV_MASTER_FRAME_RECEIVED
,并且重置接收状态为STATE_M_RX_IDLE
接收空闲状态和失能定时器BOOL xMBMasterRTUTimerExpired(void)
{
BOOL xNeedPoll = FALSE;
switch (eRcvState)
{
/* 省略部分代码... */ case STATE_M_RX_RCV:
xNeedPoll = xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
break;
/* 省略部分代码... */
}
eRcvState = STATE_M_RX_IDLE;
/* 省略部分代码... */ vMBMasterPortTimersDisable( );
/* 省略部分代码... */
}
那么这个信号最终在
eMBMasterPoll()
中得到响应,完成最终的接收和解码。eMBErrorCode eMBMasterPoll(void)
{
/* 省略部分代码... */if( xMBMasterPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
/* 省略部分代码... */case EV_MASTER_FRAME_RECEIVED:
eStatus = peMBMasterFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
/* Check if the frame is for us. If not ,send an error process event. */
if ( ( eStatus == MB_ENOERR ) && ( ucRcvAddress == ucMBMasterGetDestAddress() ) )
{
( void ) xMBMasterPortEventPost( EV_MASTER_EXECUTE );
}
else
{
vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA);
( void ) xMBMasterPortEventPost( EV_MASTER_ERROR_PROCESS );
}
break;
/* 以下代码省略... */}
}
}
总结 【【STM32】FreeModbus-RTU主机模式下数据接受函数传递】这次查找其实废了挺大的工夫,主要是嵌套的函数实在是太多了。在后续的文章中使用32硬件自带的IDLE中断来代替这一系列繁琐的定时器流程,并且采用DMA传输的方式减轻CPU负荷,并降低中断占用时间。
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长