请选择 进入手机版 | 继续访问电脑版

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

HAL串口库的分析于修改

[复制链接]
tanic 发布时间:2019-1-17 17:00
本帖最后由 tanic 于 2019-1-17 17:02 编辑
: d% D& Y' _1 C( f+ a7 W8 T3 J+ @8 V- H, L
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。
( B/ ?3 ?% o: |' a# a& c
. z- j6 F6 c: h+ e8 `2 X这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。
' [& c2 a. E8 m2 S% OHAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
- S' X- a4 d3 d. r" c1 W- C: g2 p一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。8 y7 g9 A$ G8 {8 ^; r
研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。" u; H. S5 P' R! q( I
HAL_UART_Transmit修改后如下
$ _; v$ z$ A- m8 m$ l- v
  1. HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    & H4 }' R6 K6 T" }- Y# _0 d# L) s
  2. {
    3 }. H  O  C2 C' H& L
  3.   uint16_t* tmp;( X2 Q2 ]' v7 w: }3 b- C8 s
  4.   uint32_t tickstart = 0U;8 a% }; M  {- [0 \6 ~7 {/ V

  5.   A) q. e8 f# m5 t$ ]& [
  6.   /* Check that a Tx process is not already ongoing */
    $ A% Y: s2 U. {8 g& \# y
  7. //  if(huart->gState == HAL_UART_STATE_READY)# h7 ]! i" Z9 b+ g6 j% K
  8.   {# i  U3 @& z+ M0 o
  9.     if((pData == NULL ) || (Size == 0U))+ c7 ^& b* d1 L8 B
  10.     {
    # w+ V# t- s  A* d* G  Z( Y" _
  11.       return  HAL_ERROR;2 _3 a6 W! j# r/ v) b3 m( ^1 @- j
  12.     }8 A$ n9 g0 e& S4 m" i3 e4 J8 o

  13. + K' Y! f5 F0 G1 W" [: ?
  14.     /* Process Locked */
    " O& j) M! p# p  C' a3 {
  15.    // __HAL_LOCK(huart);$ t; _3 F3 w( F
  16. ; w7 h$ s7 B: |/ X/ T1 D' a
  17.     huart->ErrorCode = HAL_UART_ERROR_NONE;
    1 U9 x! |, r6 r1 Z; \, I2 Y2 m
  18.     huart->gState = HAL_UART_STATE_BUSY_TX;
    1 x* u" E7 _; K# J2 o
  19. . H1 V2 x. C- q. j  n* \4 J
  20.     /* Init tickstart for timeout managment*/8 Z" I) a1 V: }4 R
  21.     tickstart = HAL_GetTick();9 k6 a7 Q+ e' [. f5 w
  22. 6 ~; f9 m* I! ~# O
  23.     huart->TxXferSize = Size;, a5 P! \; {7 F, c# g
  24.     huart->TxXferCount = Size;. ^3 t0 h' [4 V* g0 T! W
  25.     while(huart->TxXferCount > 0U)8 z& N8 ], A- [
  26.     {' u+ }- t  ^3 b5 ~* R4 s, q8 i: Z
  27.       huart->TxXferCount--;4 z" Z  y& m/ z0 R
  28.       if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)2 G: L) H/ C4 g. ]' r# q# E- S
  29.       {
    / a( T! e4 I5 [7 q( D3 ^
  30.         return HAL_TIMEOUT;: W/ G' }) E  x  G* i6 @- V5 A
  31.       }
      W2 [# |% y: y/ a1 L
  32.       if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    $ k! s- N: m" q5 ]) E2 i* z& Q
  33.       {
    4 ^8 U" E6 p! z5 A" R
  34.         tmp = (uint16_t*) pData;
    ( Z( i! J7 ^5 H. c9 C6 x
  35.         huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
    0 G" b$ Z# d. f) F7 K
  36.         pData += 2;3 A8 k/ @, ]' v- h
  37.       }
    5 p. V& _* V1 _3 T  a
  38.       else
    ; u. `3 ?, z2 B2 ~
  39.       {9 R  V0 j* [5 r( x8 D& _4 e
  40.         huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
    7 s3 B! j. o) A# t0 K5 {. J
  41.       }' v2 u# C5 R" T: d( @% j; X
  42.     }
    1 ~+ e) W: g& y0 v8 ~7 d& ^0 G( N
  43.     if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    5 w1 D9 F- c3 u0 s& |, T
  44.     {2 ^8 P, N5 o4 l9 X; n: t
  45.       return HAL_TIMEOUT;+ _6 S9 Y" j, N7 x- v
  46.     }  r4 E2 t9 Z) \. o( Z" T
  47. - K3 {5 s3 F6 x
  48.     /* At end of Tx process, restore huart->gState to Ready */7 _7 [! W+ ?: M
  49.     huart->gState = HAL_UART_STATE_READY;+ J) U% K4 O( b* x( u* m* `
  50. 1 P# |& X+ e1 ~/ ^+ l
  51.     /* Process Unlocked */  T, M8 D) i- @, x- S  M; c
  52.    // __HAL_UNLOCK(huart);
    7 m" ~4 a( [: d

  53. 1 Q5 n; f5 r% Y4 v) p+ Q
  54.     return HAL_OK;
    / f1 Z. e+ i- g* F0 t
  55.   }/ x* I+ D, J' F4 C5 s/ ~
  56. //  else
    6 ?7 [3 h" {9 |$ }6 |* X0 h2 @+ j
  57. //  {. M/ i  r& H8 t5 F. |
  58. //    return HAL_BUSY;
    2 ^: L" g5 n& n* w* N2 F9 T% p
  59. //  }
    3 E% q$ B$ l2 i, g
  60. }
复制代码
2 v! ]; V/ m5 s8 Z
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
$ a" f+ T' |. i8 I
  1. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); a9 k" |: s" U, {' G/ C
  2. {
    ' A2 f+ L4 `1 A4 A& B
  3.   /* Check that a Rx process is not already ongoing */$ Q# U% v- [2 _, K* X
  4. // if(huart->RxState == HAL_UART_STATE_READY)" j% ~) G( C$ }, w, `  I
  5.   {% U% h/ b) a" J( u5 `- x: I
  6.     if((pData == NULL ) || (Size == 0U))
    * \/ t9 H' j: \
  7.     {6 |, F: x8 ?2 v
  8.       return HAL_ERROR;8 _/ ?2 |/ l1 b' h0 l  S
  9.     }2 i0 X( P. _7 q7 q
  10. " o( I( `: o. ]4 q4 O
  11.     /* Process Locked */
    " ?" T7 m- l( N0 O! K
  12.    // __HAL_LOCK(huart);! D7 c# ^8 w# s& B8 u3 V
  13. ; l% e# e4 |' F8 k0 e, h8 h+ X
  14.     huart->pRxBuffPtr = pData;
    3 b( a+ ?" z$ i" d8 a
  15.     huart->RxXferSize = Size;
    5 S* D: n: }8 H9 h! x
  16.     huart->RxXferCount = Size;( F+ R' g& k# s% \' I
  17. 3 H9 k3 h( M) O& X
  18.     /* Computation of UART mask to apply to RDR register */% W/ U* Q& `: B
  19.     UART_MASK_COMPUTATION(huart);/ Y0 u7 B, ?2 ]1 w

  20. ) ~* o) h% W; g
  21.     huart->ErrorCode = HAL_UART_ERROR_NONE;
    ( j- j' a8 d, y" H9 Z) o
  22.     huart->RxState = HAL_UART_STATE_BUSY_RX;
    7 w& V$ R3 o+ q- J2 ^% l

  23. 7 x% k  N+ Z4 l
  24.     /* Process Unlocked */
    ! w* d/ l* j$ E
  25.   //  __HAL_UNLOCK(huart);
    1 J! w& U8 ^; ~! e: r8 o. k

  26. + i7 E% A1 z6 O% E. Q3 Z
  27.     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    : |7 p8 y/ p$ W% p6 ^
  28.    // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
    # |' d* {! Y3 P$ _1 M5 S$ p
  29. : A9 o* _' J% A0 ?/ H& P
  30.     /* Enable the UART Parity Error and Data Register not empty Interrupts */1 @; E# b$ x) S+ ]" U& o
  31.   //  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
    4 R! f9 ]4 e) X8 f
  32. & n9 V0 G- d7 M" A: }: f# }. e3 z' [2 p
  33.     return HAL_OK;
    * y4 V9 i1 N. Y* v) o
  34.   }* K  y5 d* s9 o6 V, t, V
  35. //  else
      |! [+ F* e. N: H0 z/ F. [0 I
  36. //  {; O8 ], U! F# \3 h/ M1 j- q
  37. //    return HAL_BUSY;
    5 O$ w8 s6 O$ _. K6 A( `7 i
  38. //  }. b0 c1 n5 B- Z" L; E/ V7 I1 \
  39. }
复制代码

: [- i3 m% V9 l. A接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了' r3 e  l( V' l* C9 k
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
    5 j/ a* d" B5 t0 F3 m% e  c2 A- f
  2. {7 M8 V$ ^! x% t
  3.   uint16_t* tmp;: s, J% F1 \0 B( `5 j4 o7 E4 T6 G
  4.   uint16_t uhMask = huart->Mask;0 a5 [" x5 [6 i/ i; U' w6 }

  5. ; T" p: u. t0 \! O* _2 z
  6.   /* Check that a Rx process is ongoing */0 H' C7 L: l' c; x. p/ x
  7.   if(huart->RxState == HAL_UART_STATE_BUSY_RX): n1 r1 T8 i$ m+ Z
  8.   {
    + p  t# K: F* ~9 C
  9. / t2 m  O) Y! {( k$ k8 W  R
  10.     if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    5 c3 B: e3 i$ u/ l$ H! k
  11.     {- e- l# A# ^! h" M6 u' K% S
  12.       tmp = (uint16_t*) huart->pRxBuffPtr ;( U. n8 ?/ ~8 {
  13.       *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
    5 _4 y" P" w. q- _  E' o' R2 D: g
  14.       huart->pRxBuffPtr +=2;6 A; I" a0 C5 d. Z
  15.     }
    $ ]' c% ]" b# A3 Q8 `) p- x
  16.     else
    8 \6 K' S/ f0 _" p) s% m6 h. F
  17.     {
    0 B4 T) X9 q1 F
  18.       *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
    * ^; ?2 G3 T) Y! S8 W
  19.     }3 b: V$ n; K% s* m

  20. + K, A. l4 p: ?; E! G2 m
  21.     if(--huart->RxXferCount == 0)5 Q* X" T' E3 T4 D% ?
  22.     {
    # x. k. e; u1 |  Z) {
  23.       /* Disable the UART Parity Error Interrupt and RXNE interrupt*/
    " p* y3 w& @1 W" N  p6 a8 m
  24.    //   CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
    & q2 M4 I( L+ [* L# ?2 V

  25. " C8 h: y# Z( ^) o
  26.       /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    8 ~9 d# T; v) v4 N8 {
  27.    //   CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
    - s0 D" v: E7 l6 h1 f' s
  28. 7 N1 ~6 B; _$ Q  L
  29.       /* Rx process is completed, restore huart->RxState to Ready */
    2 w0 T. J  a2 K% C  \
  30.       huart->RxState = HAL_UART_STATE_READY;
    . L# d! ], a1 q" ?4 x; `- k3 d& P
  31. $ H! Y! X, d8 {+ U# W, D. h
  32.       HAL_UART_RxCpltCallback(huart);+ g  ~3 {! ~( @. f5 f* a8 l: i9 y  T
  33.   B2 C: T. D6 T2 V+ l- n
  34.       return HAL_OK;
    2 Q$ `9 x2 o7 L' N( G. c
  35.     }# }. b- E6 a8 \6 D3 F
  36. 9 p0 x% ]" I5 ]. y5 ~( Z
  37.     return HAL_OK;, {  z7 P: o5 y3 W# W* Y
  38.   }- E( z- t/ i( A3 {* w
  39.   else
    1 F+ M' P- X6 a9 p) }; |
  40.   {/ M3 ~8 [1 g4 y; r8 a/ d5 z- q
  41.     /* Clear RXNE interrupt flag */
    + z" ]+ c/ i8 u/ r2 {
  42.     __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);/ J3 L- ~, A% r; V8 r( {

  43. ( R! ]; [! \6 g, s
  44.     return HAL_BUSY;/ ]# h9 r. j6 l* B
  45.   }) \5 h% X' X% C# W% Z  y
  46. }
复制代码

. T) a1 e$ G$ R: G8 F在usart.c添加如下代码,1 ^1 v5 T  \3 N( A
StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了
- k1 |$ S8 E$ p! B' H0 zHAL_UART_RxCpltCallback:接收中断回调函数. {1 |) u% T5 ?& F$ k8 K
HAL_UART_ErrorCallback:错误中断回调函数
# ~$ v- w' v4 S  Y" e& i7 l+ E6 h$ s" M/ h  z) D! q0 e' ?4 d; x
  1. /* USER CODE BEGIN 1 */# S+ W( K8 F; t2 q( O# W  J
  2. uint8_t a=0;
    4 S, ?* o% @# A
  3. void (*UART3_Callback)(uint8_t)=NULL;, G) W& F, ^7 q7 y, i3 }$ z
  4. void StartUART_Rx(UART_HandleTypeDef *uartHandle)
    - o) z* V: g% N. ?
  5. {  M  V/ W  k$ k8 d. K" A
  6.         if(uartHandle->Instance==USART3)5 {7 T) T4 V) s% c0 ?# l' W2 q2 A  x
  7.         {
    - Z; D2 v2 c" k! G. n
  8.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
    - {6 C8 Q' g/ g
  9.                 {& x1 m3 x0 M# E% \; @: f4 V( }
  10.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);, C& u3 ~! X+ l. g( M# Y# H, T
  11.                 }        & T" E! [* [: v+ C( W3 f
  12.                 SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE);        
    ; q# S6 C; z  ^: d# w& Z8 z
  13.                 SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);                1 C! B0 ?, i& b6 u+ H- s
  14.         }
    ; K7 }" B3 ]  M; l" h6 ~: D
  15. }
    $ k3 O) o  D  l9 Z7 Q8 [6 b% k8 R

  16. & x; D( s) [5 |' {
  17. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)& @/ H& O. A3 \( `, O( F9 k& ?- y
  18. {
    8 c, m, C2 i, M: _' p
  19.         if(uartHandle->Instance==USART3)
    ! R. [1 d) m  r* `$ f" Y
  20.         {0 Y3 d/ t+ V" h( f3 @0 a1 [9 k
  21.                 if(UART3_Callback!=NULL)
    ) A& `* Q# N4 k& M5 N% C8 H% [
  22.                 {# p) C) g6 W6 S  Z% v
  23.                         UART3_Callback(a);4 G) M# w. [$ ?, [: b
  24.                 }
    7 \$ L, @3 j, V9 o/ K+ d
  25.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1)). V- f" A+ N$ I% s. n
  26.                 {
    ' a: n7 z& k. {* T1 W
  27.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    1 e* m' v3 s! y0 U! J: W
  28.                 }                        
    + w8 T3 L; o! Q2 y* r8 |
  29.         }
    2 D; _/ B: j. ?; i! z+ h/ a: W0 `
  30. }  o& b( o5 C  j8 R+ I2 K
  31. void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)# w' h1 N5 ?. S; {( `# `
  32. {3 \/ m$ K: L* a
  33.         uartHandle->RxState = HAL_UART_STATE_READY;6 t& q9 v% _' w# c- O
  34.         if(uartHandle->Instance==USART3). n  u" Y# Z# A5 H$ b+ o
  35.         {( h$ M. r& p: H. i1 |0 s
  36.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))5 ]6 u/ i3 f4 O, b; c& P* t; f) J
  37.                 {0 _9 S2 X+ p8 @7 x* {  Q
  38.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    " o' @9 v/ h! X" A
  39.                 }
    " H8 s8 O' s9 Z3 ~4 T
  40.         }! I' K1 g1 E/ f# p! t7 R
  41. }5 @8 R, N0 `) E4 T( d% U
  42. 2 `, h0 f: x  j0 d! M% S6 h; o% K
  43. % O" d) z% p4 T" u  ?8 U6 R; _
  44. ' F& G, g% Z2 |0 E" z2 _4 [
  45. /* USER CODE END 1 */
复制代码

! H3 X' F% r2 ]/ t: H
& E6 W9 Z( j. E- t. a0 B

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

, P9 ?( b& P) k9 l

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

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

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

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


! G# Q$ u8 C. L( v6 S* ~& x3 F! I2 k+ T, U

/ z4 f' L* M3 L% R3 z
# \; I! B6 w- }" P' E- X, I' {

评分

参与人数 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 手机版