解決了個bug,想說點啥卻又難以啟齒

嵌入式胖胖 發佈 2022-11-25T02:15:06.837320+00:00

小夥伴被安排了一個小任務,做一個小工裝,按照公司的私有協議實現一些數據的讀取、轉發,用的是最常用的串口通訊。

說起這個bug,讓我想到了前幾個月一個入職的實習生小夥伴。小夥伴被安排了一個小任務,做一個小工裝,按照公司的私有協議實現一些數據的讀取、轉發,用的是最常用的串口通訊。

小伙子也是滿懷鬥志,計劃使用cubemx和HAL庫開發,底層勾勾點點,只把應用層移植過來,簡單快速。但有時候,理想和現實總是差一點「運氣」和細心....

一切準備就緒,代碼移植過來,呵,不出意外的話肯定有意外...不但通訊沒成功,點個燈都滅了,然後開始仿真、屏蔽代碼,一部分一部分排除法...最終定位在了串口中斷上,不管有沒有數據來,都會瘋狂的進中斷。

繼續仿真,進入到了這個錯誤,來看看是什麼錯誤。

校驗位錯誤,看起來很奇怪,外部串口已經斷開了,理論上不會再進中斷了才是,現在還報了個校驗位錯誤,然後開始瘋狂百度,找不到頭緒了,可把孩子急壞了。

然後開始新建工程,先驗證串口收發,自己寫的基本的驗證收發都沒問題呀,怎麼就移植過來別人的代碼就跑崩了呀,費解...

最後實在是沒辦法了,就幫小伙子調試一下吧,不調不信,一調差點給我整崩潰了,這不最近調藍牙模組,突然又遇到這個問題了,忽然想起來這個事情,就簡單記錄一下,希望可以幫助一些小夥伴排憂解難,特別是用hal庫的小夥伴

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

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

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




一般我們使用cubemx配置串口的時候,僅僅是配置了一些參數、IO等,串口號名稱不能像IO口一樣定義label。

然後初始化代碼如下:

void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart2.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

結構體句柄採用的是系統自定義的:

UART_HandleTypeDef huart2;

然後中斷是這樣的:

/**
  * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
 #if 1
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
#else
 HAL_UART_IRQHandler(&BluetoothUart);
#endif
  /* USER CODE END USART2_IRQn 1 */
}

這樣全採用HAL庫的配置是完全沒有問題的,那麼為什麼會出現上面所說的那個問題呢?且往下看

相信不少同學在開發的時候,對於一些定義命名都喜歡「顧名思義」,也是變量命名的一種最基本要求,加入用了4個串口,各自負責不同的功能,這樣命名,誰看得出來哪個串口是幹什麼用的:

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
UART_HandleTypeDef huart3;
UART_HandleTypeDef huart4;

一般都會命名為具體的功能描述:

UART_HandleTypeDef ble_uart;
UART_HandleTypeDef print_uart;
UART_HandleTypeDef gsm_uart;
UART_HandleTypeDef gps_uart;

然後串口在初始化的時候,就會重新初始化,寫成自己的函數:

static void BluetoothUartInit(void)
{
 BluetoothUart.Instance = USART2;
 BluetoothUart.Init.BaudRate = 115200;
 BluetoothUart.Init.WordLength = UART_WORDLENGTH_8B;
 BluetoothUart.Init.StopBits = UART_STOPBITS_1;
 BluetoothUart.Init.Parity = UART_PARITY_NONE;
 BluetoothUart.Init.Mode = UART_MODE_TX_RX;
 BluetoothUart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
 BluetoothUart.Init.OverSampling = UART_OVERSAMPLING_16;
 if (HAL_UART_Init(&BluetoothUart) != HAL_OK)
 {
  Error_Handler();
 }
 HAL_UART_Receive_IT(&BluetoothUart, &ble_com.recbyte, 1);
}

這時候我們開始運行代碼,看一下huart2和BluetoothUart的值。

BluetoothUart->Instance = 0x400044000;

huart2->Instance = 0;

再來看下USART2對應的值是多少,加起來不正是上面的值嗎,也就是說huart2實際上是沒有被賦予正確的值的:


#define PERIPH_BASE           (0x40000000UL)  /*!< Peripheral base address */
#define APBPERIPH_BASE        (PERIPH_BASE)
#define USART2_BASE           (APBPERIPH_BASE + 0x00004400UL)

當然,其他值也是沒有被正確賦值的:

至於為什麼仿真會報PE錯誤,繼續看看報這個錯誤的原因:

  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;

主要看ISR、CR1和USART_ISR_PE、USART_CR1_PEIE這幾個寄存器。

#define USART_ISR_PE_Pos             (0U)
#define USART_ISR_PE_Msk             (0x1UL << USART_ISR_PE_Pos)               /*!< 0x00000001 */
#define USART_ISR_PE                 USART_ISR_PE_Msk                          /*!< Parity Error */

#define USART_CR1_PEIE_Pos           (8U)
#define USART_CR1_PEIE_Msk           (0x1UL << USART_CR1_PEIE_Pos)             /*!< 0x00000100 */
#define USART_CR1_PEIE               USART_CR1_PEIE_Msk                        /*!< PE Interrupt Enable */

CR1 = 0x20001160;
ISR = 0;

//最終計算
isrflags & USART_ISR_PE = 0&1 = 0;
cr1its & USART_CR1_PEIE = 0x20001160&(1<<8) = 0x100;

觸發PE錯誤條件是,當然滿足了,我覺得觸發這個錯誤可能純屬偶然...

    /* UART parity error interrupt occurred -------------------------------------*/
    if (((isrflags & USART_ISR_PE) != 0U) && ((cr1its & USART_CR1_PEIE) != 0U))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_PEF);

      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }

最終如何解決呢,很簡單,還是上面說到的,你用cubemx配套生成的,肯定不會出問題,當然了,你改為自己的,肯定也不會出問題,一個用一半顯然是不行的啦~

為了不受cubemx生成代碼影響,可以採取宏定義的方法:

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
 #if 0
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
#else
 HAL_UART_IRQHandler(&BluetoothUart);
#endif
  /* USER CODE END USART2_IRQn 1 */
}

問題就到這裡了,問題不大,卻需要多多留心!



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

轉載自:嵌入式微處理器

原文連結:解決了個bug,想說點啥卻又難以啟齒

本文來源網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯繫我進行刪除。

關鍵字: