你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

HAL串口库的分析于修改

[复制链接]
tanic 发布时间:2019-1-17 17:00
本帖最后由 tanic 于 2019-1-17 17:02 编辑

之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。

这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。
HAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。
研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。
HAL_UART_Transmit修改后如下
  1. HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
  2. {
  3.   uint16_t* tmp;
  4.   uint32_t tickstart = 0U;

  5.   /* Check that a Tx process is not already ongoing */
  6. //  if(huart->gState == HAL_UART_STATE_READY)
  7.   {
  8.     if((pData == NULL ) || (Size == 0U))
  9.     {
  10.       return  HAL_ERROR;
  11.     }

  12.     /* Process Locked */
  13.    // __HAL_LOCK(huart);

  14.     huart->ErrorCode = HAL_UART_ERROR_NONE;
  15.     huart->gState = HAL_UART_STATE_BUSY_TX;

  16.     /* Init tickstart for timeout managment*/
  17.     tickstart = HAL_GetTick();

  18.     huart->TxXferSize = Size;
  19.     huart->TxXferCount = Size;
  20.     while(huart->TxXferCount > 0U)
  21.     {
  22.       huart->TxXferCount--;
  23.       if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
  24.       {
  25.         return HAL_TIMEOUT;
  26.       }
  27.       if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
  28.       {
  29.         tmp = (uint16_t*) pData;
  30.         huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
  31.         pData += 2;
  32.       }
  33.       else
  34.       {
  35.         huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
  36.       }
  37.     }
  38.     if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
  39.     {
  40.       return HAL_TIMEOUT;
  41.     }

  42.     /* At end of Tx process, restore huart->gState to Ready */
  43.     huart->gState = HAL_UART_STATE_READY;

  44.     /* Process Unlocked */
  45.    // __HAL_UNLOCK(huart);

  46.     return HAL_OK;
  47.   }
  48. //  else
  49. //  {
  50. //    return HAL_BUSY;
  51. //  }
  52. }
复制代码

HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
  1. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  2. {
  3.   /* Check that a Rx process is not already ongoing */
  4. // if(huart->RxState == HAL_UART_STATE_READY)
  5.   {
  6.     if((pData == NULL ) || (Size == 0U))
  7.     {
  8.       return HAL_ERROR;
  9.     }

  10.     /* Process Locked */
  11.    // __HAL_LOCK(huart);

  12.     huart->pRxBuffPtr = pData;
  13.     huart->RxXferSize = Size;
  14.     huart->RxXferCount = Size;

  15.     /* Computation of UART mask to apply to RDR register */
  16.     UART_MASK_COMPUTATION(huart);

  17.     huart->ErrorCode = HAL_UART_ERROR_NONE;
  18.     huart->RxState = HAL_UART_STATE_BUSY_RX;

  19.     /* Process Unlocked */
  20.   //  __HAL_UNLOCK(huart);

  21.     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  22.    // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

  23.     /* Enable the UART Parity Error and Data Register not empty Interrupts */
  24.   //  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);

  25.     return HAL_OK;
  26.   }
  27. //  else
  28. //  {
  29. //    return HAL_BUSY;
  30. //  }
  31. }
复制代码

接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
  2. {
  3.   uint16_t* tmp;
  4.   uint16_t uhMask = huart->Mask;

  5.   /* Check that a Rx process is ongoing */
  6.   if(huart->RxState == HAL_UART_STATE_BUSY_RX)
  7.   {

  8.     if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
  9.     {
  10.       tmp = (uint16_t*) huart->pRxBuffPtr ;
  11.       *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
  12.       huart->pRxBuffPtr +=2;
  13.     }
  14.     else
  15.     {
  16.       *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
  17.     }

  18.     if(--huart->RxXferCount == 0)
  19.     {
  20.       /* Disable the UART Parity Error Interrupt and RXNE interrupt*/
  21.    //   CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));

  22.       /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  23.    //   CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

  24.       /* Rx process is completed, restore huart->RxState to Ready */
  25.       huart->RxState = HAL_UART_STATE_READY;

  26.       HAL_UART_RxCpltCallback(huart);

  27.       return HAL_OK;
  28.     }

  29.     return HAL_OK;
  30.   }
  31.   else
  32.   {
  33.     /* Clear RXNE interrupt flag */
  34.     __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);

  35.     return HAL_BUSY;
  36.   }
  37. }
复制代码

在usart.c添加如下代码,
StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了
HAL_UART_RxCpltCallback:接收中断回调函数
HAL_UART_ErrorCallback:错误中断回调函数

  1. /* USER CODE BEGIN 1 */
  2. uint8_t a=0;
  3. void (*UART3_Callback)(uint8_t)=NULL;
  4. void StartUART_Rx(UART_HandleTypeDef *uartHandle)
  5. {
  6.         if(uartHandle->Instance==USART3)
  7.         {
  8.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
  9.                 {
  10.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
  11.                 }        
  12.                 SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE);        
  13.                 SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);               
  14.         }
  15. }

  16. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
  17. {
  18.         if(uartHandle->Instance==USART3)
  19.         {
  20.                 if(UART3_Callback!=NULL)
  21.                 {
  22.                         UART3_Callback(a);
  23.                 }
  24.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
  25.                 {
  26.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
  27.                 }                        
  28.         }
  29. }
  30. void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
  31. {
  32.         uartHandle->RxState = HAL_UART_STATE_READY;
  33.         if(uartHandle->Instance==USART3)
  34.         {
  35.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
  36.                 {
  37.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
  38.                 }
  39.         }
  40. }



  41. /* USER CODE END 1 */
复制代码


基本这样改就能应付大部分应用了,


当然有另外一种方式,就是开一个检测任务,定期检测串口状态,如果发现串口卡死,则重新初始化即可。

亦有人用了DMA+空闲中断的方法,不过感觉是不太稳定的。

估计HAL库其他驱动亦有类似的情况,自己把自己卡死。

改库有风险,跟风需谨慎!





评分

参与人数 2 ST金币 +6 收起 理由
小鄭QQ + 5
linuxdaxia + 1 很给力!

查看全部评分

收藏 2 评论3 发布时间:2019-1-17 17:00

举报

3个回答
chenbourne 回答时间:2019-1-18 10:06:21
感谢分享        
STMCU-管管 回答时间:2019-1-22 13:29:42
感谢分享
孤僻菠 回答时间:2019-7-29 18:34:52
完美解决,必须要赞

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版