Uart之数据溢出

Uart之数据溢出

问题背景:

在项目现场实施项目的过程之中,出现了一个问题。stm32的控制器,莫名其妙会死机,不定时会出现这个问题。出现之后系统指示灯不在闪烁,网络死机,服务器再也ping不同,下位机的控制器。经过debug发现了抓到了这个死机的点。我发现并不是硬件错误,没有调用hart_fault这个中断服务函数。但是主线程却没有跑起来。意外发现原来程序是反复进入,串口的中断里面。检查错误标志,发现是串口溢出错误。原来是假死机,系统并没有崩溃,只是全部的资源都被这个错误中断给占用了。下面开始解决这个问题。

串口dma加空闲中断的方式。

老代码里面是使用,单字节串口接收中断来实现的。由于新增的功能变得越来越多,关中断的地方也越来越多,因此中断响应不及时就会造成这种现象。目前使用的是4个串口同时工作。为了解决这个问题,使用dma加空闲中断的方式来解决的这个问题。不需要每个字节都进入中断,减少进入中断的次数,使用dma方式,无需cpu参与,减少这种情况发生的概率。增加对溢出中断的错误处理。

  1. 串口初始化的配置
    这里需要注意的是,调用UART_Init的时候就需要调用Start_UART_DMA_Receive(eDevice);因为开启了串口,就会接收数据,如果此时不开始dma接收,就会导致过载。因此刚开始的时候,就要开始dma接收。
void UART_Init(Uart_Dev eDevice, u32 dwbound, u32 WordLength, u32 dwStopBits, u32 dwParity)
{
    memset((u8 *)&g_sUartDrvRxd[eDevice], 0, sizeof(APP_UART_MSG));

    UART_Handler[eDevice].Instance         = pUART_BASE_ADDR[eDevice];
    UART_Handler[eDevice].Init.BaudRate    = dwbound;                  
    UART_Handler[eDevice].Init.WordLength  = WordLength;
    UART_Handler[eDevice].Init.StopBits    = dwStopBits;
    UART_Handler[eDevice].Init.Parity      = dwParity;
    UART_Handler[eDevice].Init.HwFlowCtl   = UART_HWCONTROL_NONE;
    UART_Handler[eDevice].Init.Mode        = UART_MODE_TX_RX;  //UART_DEV6 == eDevice ? UART_MODE_TX : UART_MODE_TX_RX;
    HAL_UART_Init(&UART_Handler[eDevice]);

    Start_UART_DMA_Receive(eDevice);    
}

static void USART1_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_Initure;
    static DMA_HandleTypeDef hdma_tx1;
    static DMA_HandleTypeDef hdma_rx1;
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART1_CLK_ENABLE();
	/* Enable DMA2 clock */
	__HAL_RCC_DMA2_CLK_ENABLE(); 

    GPIO_Initure.Pin        = GPIO_PIN_9 | GPIO_PIN_10; 
    GPIO_Initure.Mode       = GPIO_MODE_AF_PP;
    GPIO_Initure.Pull       = GPIO_PULLUP; 
    GPIO_Initure.Speed      = GPIO_SPEED_FAST;
    GPIO_Initure.Alternate  = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_Initure); 
    
    /* Configure the DMA streams ##########################################*/
    /* Configure the DMA handler for Transmission process */
	hdma_tx1.Instance                 = USART1_TX_DMA_STREAM;
	hdma_tx1.Init.Channel             = USART1_TX_DMA_CHANNEL;
	hdma_tx1.Init.Direction           = DMA_MEMORY_TO_PERIPH;
	hdma_tx1.Init.PeriphInc           = DMA_PINC_DISABLE;
	hdma_tx1.Init.MemInc              = DMA_MINC_ENABLE;
	hdma_tx1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_tx1.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
	hdma_tx1.Init.Mode                = DMA_NORMAL;
	hdma_tx1.Init.Priority            = DMA_PRIORITY_LOW;
	hdma_tx1.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
	hdma_tx1.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	hdma_tx1.Init.MemBurst            = DMA_MBURST_INC4;
	hdma_tx1.Init.PeriphBurst         = DMA_PBURST_INC4;
	HAL_DMA_Init(&hdma_tx1);   

	/* Associate the initialized DMA handle to the the UART handle */
	__HAL_LINKDMA(huart, hdmatx, hdma_tx1);
	/* Configure the DMA handler for Transmission process */
	hdma_rx1.Instance                 = USART1_RX_DMA_STREAM;
	hdma_rx1.Init.Channel             = USART1_RX_DMA_CHANNEL;
	hdma_rx1.Init.Direction           = DMA_PERIPH_TO_MEMORY;
	hdma_rx1.Init.PeriphInc           = DMA_PINC_DISABLE;
	hdma_rx1.Init.MemInc              = DMA_MINC_ENABLE;
	hdma_rx1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_rx1.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
	hdma_rx1.Init.Mode                = DMA_NORMAL;
	hdma_rx1.Init.Priority            = DMA_PRIORITY_HIGH;
	hdma_rx1.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;         
	hdma_rx1.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	hdma_rx1.Init.MemBurst            = DMA_MBURST_INC4;
	hdma_rx1.Init.PeriphBurst         = DMA_PBURST_INC4; 
    HAL_DMA_Init(&hdma_rx1); 
  /* Associate the initialized DMA handle to the the UART handle */
    __HAL_LINKDMA(huart, hdmarx, hdma_rx1);

    __HAL_UART_ENABLE_IT(&UART_Handler[UART_DEV1], UART_IT_IDLE);
    HAL_NVIC_EnableIRQ(USART1_IRQn); 
    HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); 
    
    g_sDma_buf[UART_DEV1] = USART1_DMA_BUF;
}

static BOOLEAN Start_UART_DMA_Receive(Uart_Dev eDevice)
{
    if(eDevice >= UART_NUM)
    {
       // AGV_Logo(AGV_MODULE_ID_SENSOR_NAVI,LEVEL_ERROR,"Invalid uart_index %d in Start_UART_DMA_Receive\n",eDevice);
        return FALSE;
    }
    g_sUartDrvRxd[eDevice].byDmaReceiveFlag = 0;
    memset(g_sUartDrvRxd[eDevice].byRxdBuf,0,USART_REC_MAXLEN);
    HAL_UART_Receive_DMA(&UART_Handler[eDevice], g_sDma_buf[eDevice], dma_size_buf[eDevice]);
    
    return TRUE;
}
  1. 中断处理函数
void USART1_IRQHandler()                    
{
    OSIntEnter(); 
    
    if(__HAL_UART_GET_FLAG(&UART_Handler[UART_DEV1],UART_FLAG_ORE) != RESET) 
    {
        __HAL_UART_CLEAR_OREFLAG(&UART_Handler[UART_DEV1]);
        Start_UART_DMA_Receive(UART_DEV1);
        CT_PRINTF("uart1 overrun\n");
    }

    if((__HAL_UART_GET_FLAG(&UART_Handler[UART_DEV1],UART_FLAG_IDLE) != RESET))
    { 
        __HAL_UART_CLEAR_IDLEFLAG(&UART_Handler[UART_DEV1]);
        Uart_Receive_Idle(&UART_Handler[UART_DEV1], UART_DEV1);
    } 
    OSIntExit();
} 

static void Uart_Receive_Idle(UART_HandleTypeDef *huart, Uart_Dev eDevice)
{ 
    uint32_t temp;
    temp = huart->Instance->SR;
    HAL_UART_DMAStop(huart);
    temp = huart->hdmarx->Instance->NDTR;
    g_sUartDrvRxd[eDevice].wRxdLen = dma_size_buf[eDevice] - temp; 
    g_sUartDrvRxd[eDevice].dwTick =  HAL_GetTick();
    g_sUartDrvRxd[eDevice].byDmaReceiveFlag = 1;
    memcpy(g_sUartDrvRxd[eDevice].byRxdBuf, g_sDma_buf[eDevice], g_sUartDrvRxd[eDevice].wRxdLen);
    HAL_UART_DMAResume(huart);
    __HAL_UART_ENABLE_IT(&UART_Handler[UART_DEV6], UART_IT_IDLE);
    HAL_UART_Receive_DMA(huart, g_sDma_buf[eDevice], dma_size_buf[eDevice]);
}

这里清除ore这个flag是很有意思的。网上有很多说法,但是我还是以自己调试代码的实际情况为准:
首先过载的时候,调用__HAL_UART_GET_FLAG就可以知道接下来需要清除ore,调用函数__HAL_UART_CLEAR_OREFLAG

#define __HAL_UART_CLEAR_PEFLAG(__HANDLE__)     \
  do{                                           \
    __IO uint32_t tmpreg = 0x00U;                \
    tmpreg = (__HANDLE__)->Instance->SR;        \
    tmpreg = (__HANDLE__)->Instance->DR;        \
    UNUSED(tmpreg);                             \
  } while(0U)
  
#define __HAL_UART_CLEAR_OREFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)

可以看到清除标志位只是去读了,sr和dr寄存器
在这里插入图片描述
因此实际上清除ore的同时rxne等寄存器也已经清除掉了,因此不需要单独再清除rxne。最后一定要再次调用dma开始读下一次数据,这样避免再一次过载。程序就可以稳健的运行。

  1. 用户获取数据
Get_Frame_Status Uart_Get_One_Frame(Uart_Dev eDevice, APP_UART_MSG *buffer)
{
    if (g_sUartDrvRxd[eDevice].byDmaReceiveFlag == 0 ||
        g_sUartDrvRxd[eDevice].wRxdLen == 0) {
        return NO_FRAME;
    }

    memcpy(buffer, &g_sUartDrvRxd[eDevice], sizeof(g_sUartDrvRxd[eDevice]));
    g_sUartDrvRxd[eDevice].byDmaReceiveFlag = 0;
    return HAS_FRAME;
}

这样用户每次调用就可以直接获取一帧数据,非常方便,而且可以避免串口数据溢出的问题。

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值