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

HAL串口库的分析于修改

[复制链接]
tanic 发布时间:2019-1-17 17:00
本帖最后由 tanic 于 2019-1-17 17:02 编辑 $ s0 c! S3 I* P5 A$ _7 A5 H3 x' g
4 C+ I( S- I, @2 G% C& [+ O% R
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。8 g5 c' F! t, o

  i" f' S( G: u6 u5 g这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。; r0 f. P- v5 W) |3 v/ s' B( c
HAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。7 x- t8 ^8 x- z2 ]5 j/ t7 m
一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。
0 ~& `, {8 d; Z" k$ D9 X研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。. R' O, z0 L9 j
HAL_UART_Transmit修改后如下3 I+ h% F  C/ M0 u; g( O
  1. HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    / l7 ]2 i  d/ u& U( ?
  2. {' j8 a- K/ E8 t3 @9 ~, P' L7 U+ }$ {
  3.   uint16_t* tmp;0 u$ \3 L1 Q2 b* K) ^5 {0 U; s; s
  4.   uint32_t tickstart = 0U;
    : s4 W/ K' e- L
  5. . \& f' @! R: B2 Q' L
  6.   /* Check that a Tx process is not already ongoing */9 r- w, o* o5 w- U4 E/ \+ V4 |
  7. //  if(huart->gState == HAL_UART_STATE_READY)1 P5 q/ C' O; I. D: o: [
  8.   {9 g3 D/ K) I) N# v" A
  9.     if((pData == NULL ) || (Size == 0U))) x, B- n# q4 ~7 e
  10.     {3 {) ^9 N6 C, n2 i+ l0 {
  11.       return  HAL_ERROR;
    3 K* z- S: }2 j* `! H% Q) L" ]$ s# U
  12.     }
    ' g# o  n' |; ^
  13. , U: ?+ ]- d0 k# H8 X" @
  14.     /* Process Locked */+ m& t4 u$ Y! c; B0 M5 |
  15.    // __HAL_LOCK(huart);6 u+ v9 U, p# [* M* _

  16. 1 x' y4 r9 n1 i# s) N- v" c5 Z
  17.     huart->ErrorCode = HAL_UART_ERROR_NONE;2 ?+ t! k0 W% a7 r) g, Y9 ^+ v' I
  18.     huart->gState = HAL_UART_STATE_BUSY_TX;  }0 Y/ D4 m4 @1 z4 T( G3 Y: _
  19. ( U- z+ P& h6 q& W
  20.     /* Init tickstart for timeout managment*/
    # Y; V+ Q+ H2 P' E& W) H1 z# M8 q
  21.     tickstart = HAL_GetTick();
    - p7 V& e( L8 k* ~

  22. ! S' k! o) F5 U* ~
  23.     huart->TxXferSize = Size;0 w; z6 W% ]7 e/ e
  24.     huart->TxXferCount = Size;9 k- ~1 j0 y2 O5 ^1 c0 f* u; l
  25.     while(huart->TxXferCount > 0U)8 f" \8 r0 c0 z  }/ U7 {
  26.     {' l5 j6 U7 m  Z; f2 b0 V
  27.       huart->TxXferCount--;
    % [4 M) Z. g9 L$ y4 h
  28.       if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
    ( |+ D' w% M% ?- E$ j/ d
  29.       {" l5 ^; U2 M% \% L0 g
  30.         return HAL_TIMEOUT;2 c% v) E6 R3 q% E
  31.       }( @$ ?# P, h9 m
  32.       if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)). J5 H9 d4 D# b; p
  33.       {3 N' W/ _9 n% ]' G' m* @% w1 H
  34.         tmp = (uint16_t*) pData;+ f. u3 d4 d% w" }. Z
  35.         huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);- Q/ }% N2 e, a6 b2 r/ M
  36.         pData += 2;
    . G8 I0 ~6 o# U% G, ]( Y
  37.       }
    / R- a! m! f& U2 k7 p- D
  38.       else
    - ]& A9 z$ C3 Z
  39.       {+ m/ i4 y; d8 T
  40.         huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
    & X$ O5 f3 B: o0 j& n. r# m
  41.       }
    2 u# O, L9 I8 Z) j# y0 }& ^
  42.     }
    3 t: r( }# f7 k% E5 R0 \
  43.     if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)( H( z) d3 c0 d4 c6 B
  44.     {" c  W/ X. G6 o2 H$ W1 K
  45.       return HAL_TIMEOUT;; }2 j4 c% u* f* b# o
  46.     }- y- K) N" X, d9 C5 h, l
  47. . W4 u7 Z+ t  v
  48.     /* At end of Tx process, restore huart->gState to Ready */& P/ }0 O- y! c4 b: V
  49.     huart->gState = HAL_UART_STATE_READY;
    # M. J& e& }4 d' j

  50. ; A$ N" R* }, k$ X5 K' i0 D1 X
  51.     /* Process Unlocked */$ y5 X3 I) p- R6 S. q
  52.    // __HAL_UNLOCK(huart);
    / I" U6 l2 j' l( f+ h5 S! x. V

  53. . V6 V6 J! {2 S" S
  54.     return HAL_OK;
    ! t; s8 B0 _" G1 O! c+ n6 c
  55.   }* V; v$ W  s4 ]' N! k: Z: w
  56. //  else- L( R) d5 f) f( E. B6 k/ @
  57. //  {0 n# {/ A4 b5 _* s
  58. //    return HAL_BUSY;
    ' b$ C, N# `1 j! V, I$ ?4 R
  59. //  }
    ) g7 v9 r9 X7 i; w* C
  60. }
复制代码
6 k$ }7 H, `' N5 V4 c# o$ t/ u
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因  _% y. F4 T8 d( ~9 u' Y% W# d0 Y
  1. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)" i& i+ x/ @# N+ {
  2. {7 v: x$ n% Z# [" g
  3.   /* Check that a Rx process is not already ongoing */
    ; l6 I7 L/ C$ x
  4. // if(huart->RxState == HAL_UART_STATE_READY)' ]9 p/ v) g6 B+ l1 Y" z
  5.   {* k! W' c, X" k: f" p& ]
  6.     if((pData == NULL ) || (Size == 0U))) L3 j2 d. @4 \- K& j
  7.     {& p  m9 K3 W: k- N, T/ k7 e
  8.       return HAL_ERROR;! C( T8 Q7 m+ T  Y# L- S
  9.     }
    6 u0 E' G0 ]9 {7 x1 ]# [' ?3 ~# |

  10. 2 T3 \/ K" M; v3 k& N1 ?8 R
  11.     /* Process Locked */
    ' u" R" F* [- Y6 x9 E
  12.    // __HAL_LOCK(huart);' S% h( S$ N6 ^/ O

  13. " g% ^! z2 F/ Y, s
  14.     huart->pRxBuffPtr = pData;
    6 K! z9 I, S5 Q
  15.     huart->RxXferSize = Size;
      f+ v4 h0 I6 J3 b! r- v' e
  16.     huart->RxXferCount = Size;* |8 f$ L% \8 G8 Z/ _
  17. 3 r3 c3 Q0 T& G, v7 R
  18.     /* Computation of UART mask to apply to RDR register */
    1 M) `$ r, ^/ w" ?
  19.     UART_MASK_COMPUTATION(huart);
    # I# b6 N4 P! Z( R. l2 |
  20. ! n' d9 O: k' X! f
  21.     huart->ErrorCode = HAL_UART_ERROR_NONE;( H0 J9 D" Z. A! U# O; O2 ~; t
  22.     huart->RxState = HAL_UART_STATE_BUSY_RX;/ g& U( W' V' a9 n. q
  23. 4 N8 k6 h% }0 }6 _$ q4 c/ m
  24.     /* Process Unlocked */& ]9 ^( u0 Q9 P/ `
  25.   //  __HAL_UNLOCK(huart);
    + V; {3 K" u& K

  26. 0 B) J/ w- Q! p6 o" L9 R
  27.     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) *// j/ t: W" `" s+ R! @* u
  28.    // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);/ O9 v; h+ q0 [* A& W3 h& Q
  29. 3 A) G7 q- K# ]4 {) S
  30.     /* Enable the UART Parity Error and Data Register not empty Interrupts */! C, o( X* s7 a
  31.   //  SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);6 E' A: L3 D/ M6 O' I
  32. 2 m+ I. j; h9 F
  33.     return HAL_OK;2 e* z0 c2 q  o' E) g
  34.   }. m2 E) V0 E% m( j
  35. //  else
    ; S, a5 O5 {3 Z0 K8 z$ C
  36. //  {
    , m( q& d6 R  p9 h' E( n7 P
  37. //    return HAL_BUSY;
    ; p. [- O5 i7 n& }& r  B7 E
  38. //  }
    0 ^1 e: o0 \0 [) S
  39. }
复制代码

& I! n, J7 `/ G5 E& i# `. T接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了! F" R8 Y' b# n
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
    5 y3 v7 [, t8 ^3 |7 t4 V* P
  2. {
    " t' A# ], Z8 S2 \# ~) K
  3.   uint16_t* tmp;
    7 `% T- \9 f$ C' H! y6 ~
  4.   uint16_t uhMask = huart->Mask;! P) ?5 W# V6 Q- j5 d
  5. , |6 Y/ T! ?! M! i% ^! S7 A" x5 R
  6.   /* Check that a Rx process is ongoing */) K+ ^8 q  g/ {& z
  7.   if(huart->RxState == HAL_UART_STATE_BUSY_RX)
    / q4 r* Y" q& C* z
  8.   {
    . g7 X" h( R7 ^$ A7 X; B. g

  9. $ O$ k( l6 M* [
  10.     if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    $ e9 C% N1 c: [; U4 c6 x
  11.     {
    : m# ?. k# U. k% {: Q5 _
  12.       tmp = (uint16_t*) huart->pRxBuffPtr ;
    0 i/ S3 f- \  O- K# F! G0 [
  13.       *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
    ( Y) @4 i/ H. |
  14.       huart->pRxBuffPtr +=2;
    5 g/ I/ b! [: I/ Z% f
  15.     }9 @, r+ i6 d) g4 M' r: \3 _
  16.     else
    ( Q, h4 \+ B- ?" a
  17.     {
    2 ^8 Q4 Z# V5 f5 z
  18.       *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
    5 G9 y+ z4 R: V
  19.     }7 m8 X% X( R" A6 V. F
  20. - `& p) D+ S, t) ?
  21.     if(--huart->RxXferCount == 0)
    5 y# C/ r( f" B, \
  22.     {
    ) T0 X/ c: v0 j* Q' T# y& i
  23.       /* Disable the UART Parity Error Interrupt and RXNE interrupt*/1 G$ X$ w* r. G: x! F# k: g
  24.    //   CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
    / U& j1 O8 ^! G% f/ k
  25. 1 [! T5 Z' W: ?- w, A
  26.       /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    8 @# y0 x. O# m" c4 D; u
  27.    //   CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
    ! {$ ~, m3 M, Y4 v4 g+ N8 Q; e

  28. . B; p7 T# H* n! ~( B
  29.       /* Rx process is completed, restore huart->RxState to Ready */1 R: |3 Z% Z- n2 A. u4 }
  30.       huart->RxState = HAL_UART_STATE_READY;+ N# m2 C$ p* Z$ `  u

  31. " A8 I$ m" V2 s. O8 n4 q
  32.       HAL_UART_RxCpltCallback(huart);
    # E' b9 X8 W' j6 Z" ~" ~. e, C4 r3 l8 ~3 g

  33. ) y9 a( ]0 t5 n2 k* `1 {; m1 n
  34.       return HAL_OK;' V, D  H( ?' S  F4 ?
  35.     }
    ' A$ P. j% Z+ }0 Y) j6 P9 i

  36. & }& A) V: w* e/ f5 W; f8 N
  37.     return HAL_OK;- x' E  F/ M) G
  38.   }
    ' x# x5 y) j- ]* L2 n7 P4 b% R' M
  39.   else6 F; w6 X+ k- Z) {0 h/ T
  40.   {5 I/ H& D0 |4 o* X. P# g
  41.     /* Clear RXNE interrupt flag */! \, u8 i; _- A: M+ v
  42.     __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);* T5 `% ?- m0 k% ?

  43. ! R/ I2 D0 i/ ]* l% m0 j7 ]3 y
  44.     return HAL_BUSY;" I5 B2 C5 h" H& d
  45.   }% I, H% K" F7 Y4 |1 Y0 c7 y, O
  46. }
复制代码
: N1 L% u  [' d0 a
在usart.c添加如下代码,6 `* o# d$ s; \' u2 }
StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了) W$ a9 z6 i0 t3 P; T
HAL_UART_RxCpltCallback:接收中断回调函数
4 z0 _, n7 g4 \& q5 \+ wHAL_UART_ErrorCallback:错误中断回调函数
+ c2 w% z; ~* K0 Z/ h% i4 w" O+ f. A) P0 K! E) ^  Z" }
  1. /* USER CODE BEGIN 1 */
    & d2 y% Z$ c( |" G
  2. uint8_t a=0;
    + |6 o$ l, Y$ k. q1 h2 U3 U: j
  3. void (*UART3_Callback)(uint8_t)=NULL;6 B- z. S% R) a0 y. I
  4. void StartUART_Rx(UART_HandleTypeDef *uartHandle), [) Z' }; I) |# Y: h8 ?
  5. {
    ; V8 Y+ ~2 X( i
  6.         if(uartHandle->Instance==USART3)2 w3 T  {, `9 V* G
  7.         {' X# l% \. z- M
  8.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
    1 P/ [& m' p" I6 v* v
  9.                 {
    . Z8 G- _" S/ J8 P: J
  10.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    6 [+ R' N6 E) N  K3 H
  11.                 }        
    6 l% ^+ ~" b$ l' l" W! {
  12.                 SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE);        
    3 ?! z% ]- W/ e& y
  13.                 SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);                4 x' H4 J; z5 a9 R! Y- \+ N* z
  14.         }9 u# r. F* X! l( l$ V8 e
  15. }  \& R; V4 [, o6 A5 R

  16. ; ]0 A: P7 @9 K& \* w1 i) `
  17. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)* @( F" ~0 L* I* `% n. a
  18. {) _* f/ N4 G9 G4 B
  19.         if(uartHandle->Instance==USART3)* Y5 |" d" ?: H5 W
  20.         {
    . C* V* d- c) }- z9 W3 ^
  21.                 if(UART3_Callback!=NULL)
    * R  Z2 \# C- |0 Z4 K, o& {
  22.                 {1 \+ h" d; s! h5 \* U
  23.                         UART3_Callback(a);
    , T; ?' [( g5 d6 U5 d! _7 m' O; T# k
  24.                 }
    ! s# W8 h& g9 s+ B9 [( r6 _" I
  25.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))" J% J  ?' }, {
  26.                 {
    2 V4 N2 m. p1 N; `1 m1 r
  27.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
    & c* _, F" J7 [
  28.                 }                        
    . n8 ]7 w4 h! R
  29.         }
    , X2 F( L3 q" l9 d$ N$ `! b
  30. }7 s: U8 v8 l$ ?% o9 i
  31. void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
    3 C3 r9 T' Z: C/ R% X7 A
  32. {: l& q# _0 j2 [) u8 s
  33.         uartHandle->RxState = HAL_UART_STATE_READY;
    9 R) d1 C1 i6 s3 s  N" v
  34.         if(uartHandle->Instance==USART3): ^# O& u  z8 ?$ s' A* t& {* o
  35.         {. L, Y$ n. n: g5 b; ]& ~
  36.                 if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
    9 V  H- b$ I% Y: Q4 n/ ?+ h0 p  F
  37.                 {  _7 ]: Z0 @0 O$ @0 Z
  38.                         assert("HAL_UART_Receive_IT",__FILE__,__LINE__);0 F( }: F# l, g& q2 A
  39.                 }
    ) _. b$ d7 c/ B8 n9 O
  40.         }) i4 j" E+ X+ ~8 Y+ I! i
  41. }
    , Q: Z; z. K; u1 }  R
  42. 9 v& y, W& e: P+ ]0 L, w

  43. - U+ N% t( w9 t
  44. " D2 z1 |3 Q% H( ]% m
  45. /* USER CODE END 1 */
复制代码
. t" r4 ~  j' f8 G+ N2 Q1 M

, o1 X8 ], W% H0 ?6 ^  K2 }/ E

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

) H9 G* D. C- X7 q8 z/ [

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

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

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

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

$ i- X4 @$ }  S% @

/ O0 _, s* i9 ~2 o  X% F$ c+ z5 f+ i) r. M6 N% H3 B
" ?; J: F& b1 h& z. E- g

评分

参与人数 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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版