天天看點

你遇到過序列槽當機嗎

作者:嵌入式小美老師

項目場景:

使用STM32L0單片機主頻設定2.097M,使用LPUART,115200波特率的序列槽進行通信,會出現偶發性的序列槽當機現象。

問題描述

1.序列槽配置

你遇到過序列槽當機嗎

2.回調函數接收資料

你遇到過序列槽當機嗎

嵌入式物聯網需要學的東西真的非常多,千萬不要學錯了路線和内容,導緻工資要不上去!

無償分享大家一個資料包,差不多150多G。裡面學習内容、面經、項目都比較新也比較全!某魚上買估計至少要好幾十。

點選這裡找小助理0元領取:加微信領取資料

你遇到過序列槽當機嗎

理論上通過以上配置,可以正常接收到DMA收到的不定長資料,經簡單測試,每次通過序列槽助手,手動發送資料,确實都能正常接收并處理。

但是,一旦發送資料幀的周期太快,或者發送的資料量超過20個位元組後,序列槽就很容易出現當機的現象。

通過進入仿真發現,暴力發送資料後,不會再進入中斷。把資料解析函數屏蔽掉以後,再進入仿真,進行同樣的測試,每次又都能進入中斷。

通過比較兩次的仿真,發現ISR中斷狀态寄存器ORE标志位被置位後,不會再進入中斷。

你遇到過序列槽當機嗎

原因分析:

查閱資料手冊:

你遇到過序列槽當機嗎

這個ORE的檢測是CubeMX預設打開的,在UART的配置裡面,如下:

你遇到過序列槽當機嗎
你遇到過序列槽當機嗎

産生中斷和狀态寄存器:

你遇到過序列槽當機嗎

**Interrupt & status register (LPUART_ISR)**

你遇到過序列槽當機嗎

清除中斷和狀态寄存器:

你遇到過序列槽當機嗎

**Interrupt flag clear register (LPUART_ICR)**

你遇到過序列槽當機嗎

通過以上資料手冊中内容可知,ORE标志位置位後,必須通過ORECF寄存器清除ORE,否則接收不到新資料。

分析HAL庫的代碼發現:

發生溢出錯誤後,HAL_UART_IRQHandler,經過了三個步驟:

1.清除ORE标志位

2.關閉序列槽接收

3.調用錯誤回調函數

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  uint32_t errorflags;
  uint32_t errorcode;
  /* If some errors occur */
  if ((errorflags != 0U)
      && (((cr3its & USART_CR3_EIE) != 0U)
          || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_RTOIE)) != 0U)))
  {
    /* UART Over-Run interrupt occurred -----------------------------------------*/
    if (((isrflags & USART_ISR_ORE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE) != 0U) ||
            ((cr3its & USART_CR3_EIE) != 0U)))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);//1.清除ORE标志位

      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }
          /* If Error is to be considered as blocking :
          - Receiver Timeout error in Reception
          - Overrun error in Reception
          - any error occurs in DMA mode reception
      */
      errorcode = huart->ErrorCode;
      if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) ||
          ((errorcode & (HAL_UART_ERROR_RTO | HAL_UART_ERROR_ORE)) != 0U))
      {
        /* Blocking error : transfer is aborted
           Set the UART state ready to be able to start again the process,
           Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
        UART_EndRxTransfer(huart);//2.關閉序列槽接收
      }
          /*Call legacy weak error callback*/
          HAL_UART_ErrorCallback(huart);//3.調用錯誤回調函數
    }
}
           

是以解決辦法是,隻需要在HAL_UART_ErrorCallback錯誤回調函數中重新打開序列槽接收即可。

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    if(huart == &hlpuart1)
    {
        __HAL_UNLOCK(huart);
       HAL_UARTEx_ReceiveToIdle_DMA(&hlpuart1, (uint8_t*)receive_buff, 20);
    }
}
           

經過以上分析,已經知道了序列槽當機的原因,和初步解決方案,在錯誤回調函數中重新打開序列槽。

但是在實際使用過程中,難免會有大量資料的收發,通過錯誤回調函數中重新打開序列槽隻是治标不治本,ORE溢出錯誤的本意是告知使用者目前通信超負荷需要進行調整,要麼波特率太高,要麼資料量太大資料處理不過來。

波特率一般是終端使用者指定要求的,無法更改,那麼隻能更改在中斷中的資料處理函數了。

解決方案:

要想知道怎麼改中斷中的資料處理函數,就需要明白在一個2MHz主頻系統下,使用中斷(或DMA)模式來接收序列槽資料,波特率為115200的情況下,中斷處理程式允許的理論最大安全時間是多少?

115200的波特率在典型的“1起始位+1終止位+無校驗位+8資料位”的配置下(每個資料幀對應10個bit),實際上對應最大11.52KB/s的資料率——或者說,USART完成中斷每秒鐘發生 11.52K次。即在這一系統中最大允許屏蔽中斷多長時間——1/11.52KHz ≈87us。

由此可知:中斷中的資料處理函數最長執行時間不能超過87us,否則将出現通信超載。

通過perf_counter工具實際測量data_parser代碼的周期消耗:

修改代碼如下:

你遇到過序列槽當機嗎
你遇到過序列槽當機嗎

經測量處理函數執行了2211個指令周期,由1MHz對應1us,1us對應一個指令周期可計算,2.097M的主頻,1us可以執行2.097個指令周期,是以一共需要2211/2.097=1054us,超過了87us。

優化掉data_parser内部的耗時代碼後,重新測量指令周期如下:

你遇到過序列槽當機嗎

經測量處理函數執行了137個指令周期,一共需要137/2.097=66us,低于87us,經暴力測試,未出現溢出中斷,符合預期。

注意:

STM32并不是所有的系列都有這個OverrunDisable開關,相應的也沒有OVRDIS這個寄存器,清除ORE flag标志的方式也不相同。通過在STM32F103上測試,預設是開啟了過載檢測,由軟體序列将其清零(先讀USART_SR,然後讀USART_CR)。

如果有OverrunDisable開關,在不使用HAL庫的中斷回調來實作序列槽接收時最好禁止Overrun,不然出現序列槽接收溢出,RDR寄存器中的值将不會改變,也就讀不到新資料了。

轉載自:AIoT開源項目分享

文章來源于你遇到過序列槽當機嗎

原文連結:https://mp.weixin.qq.com/s/Acj1GrexC57HDULj4voffQ