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

HAL串口库的分析于修改

[复制链接]
tanic 发布时间:2019-1-17 17:00
本帖最后由 tanic 于 2019-1-17 17:02 编辑 & K" l/ m' D9 N: s1 o3 H5 a
- ^* Y9 X% R# ]1 t+ f
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。
7 ^& B5 {, |: ^4 c0 @, C: a0 |: K. s8 _
这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。
  W9 ]( l0 b7 I9 Q9 P. a: W9 g9 nHAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
2 y3 P  I% Z0 Q一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。
4 m, \: Q, I9 T) h$ m) Y5 U/ B; S! n研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。5 w+ R* b- e$ a" Z
HAL_UART_Transmit修改后如下
! R7 t0 Z" l3 @
  1. HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    ' p& V  Y5 e  @" h; q: W: x
  2. {* T+ V7 `1 Z1 ]% d& T0 N4 n
  3.   uint16_t* tmp;
    $ Y+ s! C4 |; n
  4.   uint32_t tickstart = 0U;0 k, ?  w; a% k4 D

  5.   l9 U6 z2 F% [' `) @8 J3 y
  6.   /* Check that a Tx process is not already ongoing */" T/ t+ g, ~7 t; z! W- K
  7. //  if(huart->gState == HAL_UART_STATE_READY)
    ; H9 o5 y* Z1 g
  8.   {, d3 w# o1 V1 \# ^+ n0 p
  9.     if((pData == NULL ) || (Size == 0U))% Z0 m7 G3 ~. ]8 _+ J
  10.     {; [, [# k* Y* T7 X' d! K3 J2 M  f* D
  11.       return  HAL_ERROR;
    ) s. g+ a0 H! T$ [* q9 N- r1 k
  12.     }' I( z# }: E. l5 i

  13. . Q8 w- w2 ]# o3 F* ?, h
  14.     /* Process Locked */
    1 c) T, G6 w' h' N: j2 h% ?  E. G' y' p
  15.    // __HAL_LOCK(huart);
    . Z- [2 R' A- c  |

  16. . E3 h/ K6 y' J  A
  17.     huart->ErrorCode = HAL_UART_ERROR_NONE;
    5 d- W: `& T' ~5 ?
  18.     huart->gState = HAL_UART_STATE_BUSY_TX;1 P1 U: b, h9 I# O/ C
  19. , h9 `5 K+ u6 w9 s: K# p' H2 G  F
  20.     /* Init tickstart for timeout managment*/
    6 h7 y1 @- u& v& V0 V6 ]
  21.     tickstart = HAL_GetTick();
    + V" i' M( d5 t3 F5 h5 L
  22. / _6 v) S0 N$ @- t: W1 x2 Z$ c
  23.     huart->TxXferSize = Size;3 V% ]$ R' A$ o' c  {
  24.     huart->TxXferCount = Size;
    ) y0 J- W% a- V1 L* Y
  25.     while(huart->TxXferCount > 0U)4 l; n% ?: ^. y3 l
  26.     {0 c* v, G# Y1 C4 d8 }: D0 l1 `( G; T
  27.       huart->TxXferCount--;& K) J: q/ W4 S+ u- r2 _  Z
  28.       if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK): o# X8 z( n, c, R8 c# \  i% P( y
  29.       {
    9 a/ Q' c6 k! D& Y$ k0 Q7 M
  30.         return HAL_TIMEOUT;0 P" a( g# `( [! W" H. Y
  31.       }# y: c- F- G' ?' g0 [$ r
  32.       if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))0 T; b- C! U: N2 ^* i+ S' L7 ~
  33.       {2 K: i0 u. h) t) f1 X
  34.         tmp = (uint16_t*) pData;
    9 R' `8 v, C* p6 B7 Q3 E" b
  35.         huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
    * R! ^% P( ~; K+ N
  36.         pData += 2;
    ( O: u0 o1 Y4 h
  37.       }6 j' l9 b5 h2 _& T$ q* H' @
  38.       else
    / d0 j) |2 j0 I- f
  39.       {
    ' y, y& P( m8 {% p4 B9 G
  40.         huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
    # J( J/ {6 T9 f! v+ ]4 I2 W, E4 N$ r
  41.       }
    ' ^  `1 B  _. R9 F# r
  42.     }9 |1 e- I- M; }! d% [% W
  43.     if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK): ?4 f% y7 i# @; j$ c
  44.     {
    7 ?5 s% S+ N8 H5 o, c, e; F! m
  45.       return HAL_TIMEOUT;
    3 w  Z  O) P7 L/ z( V+ U4 H! b
  46.     }5 O2 u" T7 }  ^0 o; P1 i' p( A
  47. ; l  O' d% B) `7 k# a9 F# X6 }/ H
  48.     /* At end of Tx process, restore huart->gState to Ready */( X" w/ Q  |3 i8 c
  49.     huart->gState = HAL_UART_STATE_READY;8 R! m+ \" k+ ?% Q! g
  50. * N5 O4 g1 j& h
  51.     /* Process Unlocked */2 k, a5 [4 k2 W' U7 T9 p: r
  52.    // __HAL_UNLOCK(huart);
    3 f( I2 V5 T7 ~# S' J
  53. * ?/ N8 y0 O2 V; a* i0 v
  54.     return HAL_OK;5 j% S+ d& i% V: l6 k) ^
  55.   }5 a3 d  Z. ^7 |  S" T
  56. //  else) ]" [. c! h* T2 r3 `
  57. //  {8 Y$ {$ I! N2 C- [) r- \
  58. //    return HAL_BUSY;4 W1 r! s8 H9 `. |) q
  59. //  }
    5 F! r* {2 o" n7 L, ~9 X
  60. }
复制代码
8 [) q/ v1 w; v2 R9 g: n$ }
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
. O! ~! z  P& z4 I' p2 h
  1. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)# a+ {( H+ H3 Q8 w
  2. {
    7 ^0 I1 W$ e9 y1 X3 Y5 e
  3.   /* Check that a Rx process is not already ongoing */
    , m! F) p* o) I$ [
  4. // if(huart->RxState == HAL_UART_STATE_READY)
    4 h. p5 i0 _' H2 @- R# Y& \; N$ |
  5.   {
    8 z0 J0 b7 R( |7 G7 x8 |
  6.     if((pData == NULL ) || (Size == 0U))
    ' L9 h  F( p7 V) u$ a
  7.     {( a* ]$ _* q0 ~! t' f$ j0 `
  8.       return HAL_ERROR;- m6 R1 Q1 {+ @- W, ~) Q
  9.     }# o1 z, k; i; q, H

  10. - b" q) `2 ?7 J# w
  11.     /* Process Locked */5 y* K  o  M5 j: o) ~
  12.    // __HAL_LOCK(huart);
    % f+ }7 b. z9 f& s% Q" S, y/ T& M
  13. ( [% ]* c( o' L. c# \+ K5 w" `
  14.     huart->pRxBuffPtr = pData;, A1 ^. I' Q( j
  15.     huart->RxXferSize = Size;0 V7 t7 a$ T. E6 I# m6 `
  16.     huart->RxXferCount = Size;, o' `2 C. @! {) w

  17. 5 _; ?, p+ ]' N8 P
  18.     /* Computation of UART mask to apply to RDR register */
    5 b9 ]$ t2 ?, N( s, U( }+ v8 A
  19.     UART_MASK_COMPUTATION(huart);* ]- _7 a# i( s! k" Z

  20. 0 ]. B, L9 N* {2 I
  21.     huart->ErrorCode = HAL_UART_ERROR_NONE;
    & @! v0 v0 N0 B8 `0 W$ E+ K
  22.     huart->RxState = HAL_UART_STATE_BUSY_RX;
    4 y" n) m8 z3 I6 O: O9 t4 ~$ O; |: y
  23. % D9 r4 q4 P1 o( {# M
  24.     /* Process Unlocked */
    6 J5 k* i! U# ?1 M
  25.   //  __HAL_UNLOCK(huart);
    + G* g0 J. \8 |5 t; N

  26. 3 W! V) l9 d5 Q! j- {$ n9 M
  27.     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */9 d: P  M. ]/ P' I. D. ]' b
  28.    // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);  l  V* `7 n) Z
  29. 9 {/ w: @; l1 c2 H$ i. E
  30.     /* Enable the UART Parity Error and Data Register not empty Interrupts */
    . _; j/ _( z) e  ?' Z0 |; m( |
  31.   //  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);! o* \/ r4 c0 c5 \
  32. ; D( F3 C4 s2 L$ x" W8 O4 s# ~
  33.     return HAL_OK;
    # T* J+ Q, P# X8 N, @* A
  34.   }
    8 r! Z' }- R% u0 j2 r
  35. //  else
      P, {- c$ S% w* `' k
  36. //  {
    4 @; G: V2 }- C1 C- B
  37. //    return HAL_BUSY;2 k5 b7 ]1 J4 \
  38. //  }
    * O) k3 i+ D- \7 a7 d9 S" K
  39. }
复制代码

- W: w1 A# @" ]1 ~; o接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了$ {+ W" }4 o* ]" I- ?. n, B
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
    * h3 t& C9 }& E  h2 [
  2. {9 x. L! _* n6 y  e0 W; o
  3.   uint16_t* tmp;" l( P, t' G$ X6 S6 `8 L
  4.   uint16_t uhMask = huart->Mask;' K! f7 m# ~0 I  {8 N9 z/ F- G3 t7 c
  5. , O$ V' l! \. v$ q* ]
  6.   /* Check that a Rx process is ongoing */$ y' I' l+ o, C0 S# V5 q
  7.   if(huart->RxState == HAL_UART_STATE_BUSY_RX)$ i/ f' m- ~9 m( Y! s) |
  8.   {
    / a3 D& g/ ~" _6 ?2 V
  9. % H3 [1 Y* z2 y) l
  10.     if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))/ s# @  X3 m5 B' I6 O  {" |
  11.     {! C9 I- g5 V7 i+ n/ t+ m' W$ E
  12.       tmp = (uint16_t*) huart->pRxBuffPtr ;
    3 o8 p+ c) O. Y# t: A5 f
  13.       *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
    9 ^4 j' O$ g7 s! g  S$ y
  14.       huart->pRxBuffPtr +=2;
    ' p! s! \0 u% r& I6 m
  15.     }! F4 I* P. r# B* o: u2 j
  16.     else; W3 a6 L& y% \' {$ `
  17.     {( N, k/ A* O# q1 {9 @: j
  18.       *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
      z* q; R# [/ s3 I' O& H, N
  19.     }
    : s$ t# ~" E) K8 o6 L/ W

  20. % w/ y( @3 ^! r2 {
  21.     if(--huart->RxXferCount == 0)
    : A) Y* c  l- @" ?& G
  22.     {
    5 r# T" d* ]3 c6 Z- A
  23.       /* Disable the UART Parity Error Interrupt and RXNE interrupt*/$ l# R( ]/ w+ n9 O1 @  Q
  24.    //   CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));8 r" W9 e2 v6 o. i
  25. ' v% x1 ]' z0 ]( n& V" ]. H
  26.       /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    + v) x/ Y% q# U" Z# v
  27.    //   CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
    ) @/ d1 ^' ?+ R7 j. l
  28. ' i3 V) W" ^! K/ I4 s% D
  29.       /* Rx process is completed, restore huart->RxState to Ready */: q: F/ G8 N/ m. k8 X: u* O
  30.       huart->RxState = HAL_UART_STATE_READY;" v: Z* H( L3 j
  31. . ~3 _. p4 J/ J2 k' H  i! J9 m
  32.       HAL_UART_RxCpltCallback(huart);( T! j2 Z& I# \

  33. ( t' Z$ s, l3 Y6 U" y2 w
  34.       return HAL_OK;
    : v# p/ e) @, J$ R6 [% Q+ m% H
  35.     }
    + S" r/ x6 E& H; U7 M  p" H! C

  36. % Q& C* W' m3 q; n- B$ d5 H) N
  37.     return HAL_OK;
    1 S/ D' j: q1 m! M6 X8 B
  38.   }
    2 X& @+ V9 |8 a& M" U) q
  39.   else$ W2 E! M0 P8 q+ t
  40.   {" }6 \: p+ b9 s
  41.     /* Clear RXNE interrupt flag */
    & S: T/ j5 e4 a1 d! I
  42.     __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
    : K' @' j2 h( `! O# g5 V9 q, r
  43. & \7 u6 q+ X# F& W# |3 f8 B
  44.     return HAL_BUSY;  ~# H# j% M& R8 {9 g. }6 y, h+ Z
  45.   }# C' W- ?+ {; C' E
  46. }
复制代码
9 a& g4 a: z2 i! U+ H) f4 ~" j9 A
在usart.c添加如下代码,# b+ ^- Y' `3 v$ @
StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了
, N. A: @6 \- Y" }" LHAL_UART_RxCpltCallback:接收中断回调函数
7 ]3 L8 L# U$ v+ D. n, o/ lHAL_UART_ErrorCallback:错误中断回调函数3 q) M" j- f2 `% n" w

: |7 e5 l0 L" e; j
  1. /* USER CODE BEGIN 1 */
    ) [; L4 e. D' v/ d+ s3 h: E
  2. uint8_t a=0;. m' Q0 D% I$ h% T2 q) X
  3. void (*UART3_Callback)(uint8_t)=NULL;
    # j3 j! d6 k% |# P' Q
  4. void StartUART_Rx(UART_HandleTypeDef *uartHandle)9 s6 @: o% u5 d% Z; ^2 @
  5. {
    $ d# L" R5 a& r% t
  6.         if(uartHandle->Instance==USART3)+ g3 g3 ^$ T3 J
  7.         {% A" |  X, V  L
  8.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))* M# K6 l, u& T4 c. M/ @. `
  9.                 {, t- U3 u% \5 g4 r  o( M/ ]
  10.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    . n* g9 B/ T9 P) f1 P+ @
  11.                 }        0 P1 G4 z5 U/ p! _7 M+ n( Z) c
  12.                 SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE);        % V$ d$ O8 w4 r# b, g/ {% H
  13.                 SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);               
    % r- L1 S8 M! b! o6 j
  14.         }, `" u; [) S  H# J, Z6 f" C# |; N/ e
  15. }
    : @: [- {% A5 ~. V
  16. 2 F6 x2 L) X* ?3 _, I
  17. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
    8 N6 S" ~. g7 V. B. a! f
  18. {( a' }1 f7 K6 Q+ t7 U
  19.         if(uartHandle->Instance==USART3), l4 l5 E9 N* b9 _6 `, ~
  20.         {6 V' \+ V4 @5 h" a0 I8 v7 M
  21.                 if(UART3_Callback!=NULL). W$ C( |+ ~1 R  g; Z( ~. p6 v
  22.                 {
    ' S9 E/ R" Q" c5 n7 C0 y/ s$ w+ C- o
  23.                         UART3_Callback(a);6 I2 C2 n, M8 H9 J! q: ]% M5 _
  24.                 }) I7 m7 e4 T: h' A8 e" `( x
  25.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))! V7 N) C+ e& _9 M
  26.                 {9 ]( Q" K) ]: ]: X3 v
  27.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    ; Q5 ~9 G( o3 L# t
  28.                 }                        
    . p0 N& e/ A1 x" g2 z
  29.         }: a; D; [+ i2 v$ T, h$ {, U7 F
  30. }
    # H5 i' M& ?8 _5 p7 @( g
  31. void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
    5 l5 }9 f) P* S/ e
  32. {: \1 G' s% B% K4 @' P) e
  33.         uartHandle->RxState = HAL_UART_STATE_READY;4 v3 m+ H( {3 H# i% r. k, a3 }6 H
  34.         if(uartHandle->Instance==USART3)
    7 }7 A3 q& t7 k1 f
  35.         {
    & j9 Z, q' |$ N! C+ f2 a
  36.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))) R$ R; s$ ]2 G- m
  37.                 {0 n& J  c( w9 o7 x; b% H
  38.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    ( q2 ^, p* J1 ]. \
  39.                 }
    " _# ~  u) M( O. V, ?
  40.         }
    1 T& V1 n: g: h/ J4 X% N* I5 J
  41. }
    / B' h) t9 V4 h- }
  42. ! S/ Y: n% T& P6 V3 g7 ?$ z7 T

  43.   A9 ^. L$ U- F6 r

  44. ' b- ]3 }8 T2 ?  c* E
  45. /* USER CODE END 1 */
复制代码
0 o2 U% j( K5 K/ \/ Q' ^

/ G  L. M2 ~: `' o2 q: c9 K

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


, V9 D; u4 T/ i3 p. `7 z1 m

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

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

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

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

" {7 Y" M- t) Z( U) t; C& }
2 ~$ k+ ]& w7 V2 E

8 d% Z0 I8 Y5 w+ v' Z  K- n  h
, u4 p2 i; u% Q

评分

参与人数 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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版