本帖最后由 tanic 于 2019-1-17 17:02 编辑
# g. V/ ?* _# X# U, c8 m+ G) W; z! d, l$ A/ ~* Y
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。( A" u: y* Z. t0 C- \
+ t# m/ w- F3 K7 Y: h+ J
这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。0 k& l' r4 `- w3 m+ S0 k3 k
HAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
" `; z$ }1 M' o( k7 g一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。) \- f: I4 a7 e) Z# d; Z2 }! H
研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。* T+ w/ m5 b M6 S
HAL_UART_Transmit修改后如下$ E% |# m% K/ P3 G3 |
- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
' z: s5 M% D- Q6 J' y - {
, I: n8 G* b' D, | - uint16_t* tmp;4 T# n# |, \, H
- uint32_t tickstart = 0U;% {1 r8 ?/ b1 t2 A9 W: J
+ }1 R U, A, R# l: d# X" c- /* Check that a Tx process is not already ongoing */
1 X$ D/ ?$ C1 E) M - // if(huart->gState == HAL_UART_STATE_READY)
+ @9 s* Y3 k. q* h( ~ N6 V5 d - {1 x# z. D- S, r, j' V' r
- if((pData == NULL ) || (Size == 0U))
( c. e5 o5 P, l# x: G% k! p - {
5 M$ S* q* u) `8 p8 } u - return HAL_ERROR;4 O) Y! g8 T7 v% `: Z2 k
- }2 q" g; i \' Q9 v
" \; [5 A- @- t# p& j/ t- /* Process Locked */
# h5 O w& j: ^/ q6 S! P/ W) H; x - // __HAL_LOCK(huart);' Y* `0 Y# Q- i0 Q0 p9 S9 K* c
- 8 E6 g1 b! |+ [2 R8 M6 j
- huart->ErrorCode = HAL_UART_ERROR_NONE;
0 C2 E* G& P( N) }2 ] - huart->gState = HAL_UART_STATE_BUSY_TX;) l) H) `/ p4 k" o6 M$ l5 _
" Y2 j/ v/ ~& j5 v* k6 G6 U- /* Init tickstart for timeout managment*/7 y6 v" l$ o* l) Z0 J
- tickstart = HAL_GetTick();
; a! d$ l. N1 b8 V
* m8 c) |8 \% I( _% `& A7 g: \- huart->TxXferSize = Size;
9 v: i) a5 G* h9 k* K) ]' x( ^; G - huart->TxXferCount = Size;
% s7 Q) H E+ j - while(huart->TxXferCount > 0U)
+ y+ u; s/ D8 ~: |. X2 s - {- w" R! F5 ^, v5 B7 h, S- x, h) ^4 T
- huart->TxXferCount--;* K5 A* Y$ P# r0 z! O& }5 h
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)7 z$ E; G( k7 r; u% f. B
- {% M& m! P' `+ K; J# p5 s
- return HAL_TIMEOUT;
* X% q3 n, w( g6 a7 x7 Q - }
( W$ O$ d* k) p4 N! U - if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))( r9 u7 X% W& q% \& g
- {
. Y6 z; K# n! G - tmp = (uint16_t*) pData;4 S0 |9 @* I o
- huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);0 ]: F: q' o3 a { h. @
- pData += 2;( a: f& G7 y- d, r& ~& c
- }
( [, T" b) L! l) S" n: p- E - else5 I$ q- ~* [/ A' e, i: C* J! x% J7 C
- {! {/ U, V% A& B( Q% x
- huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
* o0 _: e' p/ T) d2 P% S& k - }2 I0 x2 l5 f; o' E, w7 u8 F
- }4 s. i: o! @- G8 r
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)1 D; x4 I, z9 _ ]8 o# a. u% D* A
- {6 [& X$ @5 a; |! l% f, n7 n
- return HAL_TIMEOUT;: b1 M; W/ W! C, P
- }, u" D2 s5 }1 Y" k$ o6 ]
- 3 t3 q& e+ f, K* {8 \
- /* At end of Tx process, restore huart->gState to Ready */
9 U3 \8 Z3 z( t - huart->gState = HAL_UART_STATE_READY;
( O, n/ g4 B: E& `# Z3 w: b
& o9 C- u& i! |) D X- /* Process Unlocked */
* \% o6 O" W9 E. x" o. |0 c+ d, ` - // __HAL_UNLOCK(huart);; @. v1 ]: C. r
+ C4 R% y7 k6 P+ |* V- return HAL_OK;4 Y7 {1 i' Z A( C
- }
- w7 `* g2 @- }& H' t% b- }) P - // else
8 F0 T) {& X+ J+ n% Q3 U% } - // {
7 C0 g; p. y, U - // return HAL_BUSY;5 e# s: b- W5 ^) D- Y) t4 g, S
- // }
8 e% }, K2 M! t4 G+ _- h7 q4 ~ - }
复制代码 . I2 a" t* b9 m0 @1 V3 u
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
- m u& V! G- ?; o# t7 F5 x$ [0 a- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)$ C5 c' Y* ]& Q! P* F8 W
- {+ ]1 y9 A, ?* M7 o/ ?4 S
- /* Check that a Rx process is not already ongoing */' H3 n0 k$ c+ v# o. ]7 g
- // if(huart->RxState == HAL_UART_STATE_READY)0 j* |4 G7 y, m2 `# \7 B+ T
- {) y% k l( N) e
- if((pData == NULL ) || (Size == 0U)) A$ y5 w% R, h/ i+ o
- {" D* X! ~5 ?/ D1 B; w
- return HAL_ERROR;' f) a* E$ k1 Y, ^0 G0 t
- }
# p# n3 Q9 q) a- T& n4 k
- \, `6 p) X9 s% `- /* Process Locked */
i I! V! H- B" ~! c& i - // __HAL_LOCK(huart);2 ~# ]* n4 @4 d2 |8 s3 B
( T8 @& f0 v$ ~" ]% @# [- huart->pRxBuffPtr = pData;! q7 `* O, ~% H- o: R# j
- huart->RxXferSize = Size;
8 S1 _% b) Y- C+ n# n - huart->RxXferCount = Size;7 |$ \$ H9 l1 T0 k' A8 Q
- $ l0 c4 F2 @, j
- /* Computation of UART mask to apply to RDR register */% ^& ], n- o2 w$ ]4 e1 ]. F
- UART_MASK_COMPUTATION(huart);) H2 V' ^+ a7 O1 z' ~# I
- 8 H5 ~8 `9 r! L$ G7 |: B& h
- huart->ErrorCode = HAL_UART_ERROR_NONE;+ ~6 W4 U4 }1 ~1 c- v8 w( m3 V2 U
- huart->RxState = HAL_UART_STATE_BUSY_RX;
2 z2 b( d2 _8 m v4 H
, c( M7 `9 |8 d; L$ ]+ [- /* Process Unlocked */$ _6 ^, Q3 O) D% z$ `
- // __HAL_UNLOCK(huart);* ]& w' l- S) q0 K. F+ m7 V
4 X2 m/ F* y. R G# g' c; Y( b- /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */& _6 g; G" A5 p# @
- // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);! S& t8 `: C& g- \' z+ ?4 F
- 3 \( T8 a0 P- _6 }) C
- /* Enable the UART Parity Error and Data Register not empty Interrupts */
: ~7 m) Z% J8 u2 I - // SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
. ?5 ~+ k' w; X1 H1 i - 6 Y% f3 G5 x6 f" o7 _% W% W2 r5 r5 C
- return HAL_OK;/ B" ~" h" F* `: {/ f6 L, j
- }
' g- U' D: C, {4 y- ]/ g$ J) m - // else
- w7 r5 N0 J0 w - // {
; y7 Z4 k' | C, e7 y+ j* W! j: O - // return HAL_BUSY;
& Z& g5 A& M" Z2 D: j- Z, o2 |, z - // }
6 r. h. r/ c# P+ d5 `* { - }
复制代码 ( B% z/ @: Z( n0 T
接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了: ~( J/ k0 B: h& i! j/ q( [
- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)0 {6 E5 L0 ?, r" k/ f' v: o$ O
- {0 }' B, P ~' Q$ ?0 Y4 L
- uint16_t* tmp;" O: `' c" Q2 b, w1 y, d- a
- uint16_t uhMask = huart->Mask;
3 [% P* }* r3 A O
+ {( w! m# D1 x i0 W0 L! B! j$ l- /* Check that a Rx process is ongoing */
+ M: B, Z& M* c - if(huart->RxState == HAL_UART_STATE_BUSY_RX), e" }/ f7 `2 V; T$ C6 h' X
- {
* F/ e- }: h* Z6 G
4 K2 J1 c [$ t( P' n; H2 I: K' [- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))9 Y" N4 }4 E# \, @2 n$ a! b
- {3 g4 g/ z9 j5 H
- tmp = (uint16_t*) huart->pRxBuffPtr ;
/ v* w; J: T k7 y t) b6 V - *tmp = (uint16_t)(huart->Instance->RDR & uhMask);8 ?! [1 I5 v' r' t8 _
- huart->pRxBuffPtr +=2;( k6 C$ g. t/ m% _! e0 B
- }6 P6 [* m, k. L- R: `% S
- else. d+ ]% O0 D8 Q/ Q$ u
- {
. [( s# V7 V Q) I9 }' |: B - *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);" K. y2 r) g5 @3 X
- }! L/ N8 D' z' m% |$ Q* O
- . J% g- I# B/ K) }4 v7 ]
- if(--huart->RxXferCount == 0)
8 e% C: m: C \, L1 B/ h - {
5 \6 J. J. _ @& K. O - /* Disable the UART Parity Error Interrupt and RXNE interrupt*/ V6 H1 H) `0 Y
- // CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
m- t7 p a, h0 i/ b3 ?2 b
6 @6 D9 M; Z. Z" j7 v9 r- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */' h/ M5 e% V* ~$ Q' b
- // CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
5 O- U+ @" g& q8 |) {# k - 3 w7 B( g1 ? Z b% A
- /* Rx process is completed, restore huart->RxState to Ready */
1 `3 W4 J X H% T; r" S - huart->RxState = HAL_UART_STATE_READY;
3 t: q6 q( r7 X2 X: C - * q- y% s/ w% c0 b5 E2 J/ S
- HAL_UART_RxCpltCallback(huart);
! h4 A5 W1 W/ c6 u- N9 v; n - % n( I' t* M. H
- return HAL_OK;
- |2 E. z+ d3 E2 k! \ - }; \) x% i9 h3 X* I ], R
- + J: W D+ o; K. m2 t& J0 }- [
- return HAL_OK;, ?7 @' p. x ^4 v: K8 ~: y9 \6 r
- }0 t" t3 d3 {' u9 |% [5 C. O2 a( S3 h
- else
- Q6 p$ ^2 `5 k, I4 j* V - {
! C& s& P* l7 b4 o - /* Clear RXNE interrupt flag */, n* u, R0 d W u
- __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);3 G5 ]$ Y0 e* n" _; P
; Q' q+ t$ \6 g- return HAL_BUSY;
6 u! B- Q# Y4 f+ o0 v - }0 i+ k+ d# s3 i Z1 m, w4 m' p
- }
复制代码 ( }/ d$ E- B$ Z
在usart.c添加如下代码,
4 l+ V7 _% A, ^4 FStartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了; }/ B5 d4 E" i! d' g( G
HAL_UART_RxCpltCallback:接收中断回调函数
, u8 x8 O% s, C) C$ M( `/ L# w, D3 pHAL_UART_ErrorCallback:错误中断回调函数5 K/ @5 a+ b0 [( G8 T( Z
) V* C# N# k* N- /* USER CODE BEGIN 1 */
: D* l" j& U; M5 W. N& ?! Y+ q5 O - uint8_t a=0;4 L- e9 ~( v# T& e7 k
- void (*UART3_Callback)(uint8_t)=NULL;4 o5 n# N1 [8 c; K. q
- void StartUART_Rx(UART_HandleTypeDef *uartHandle)
* q N( N) w* S( [: A - {5 ~# f4 u: q4 b1 X# a1 u3 o# r
- if(uartHandle->Instance==USART3)+ U% F0 U; \1 T) ^ d! |3 R8 {$ n. J
- {
! |$ O, e* X6 d. ~ - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
6 o8 a- [/ Z# n- E+ u$ Z* P+ `! t# H7 W - {
! j% N7 w- e" X L4 {0 I/ b- g1 ^ - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
# w1 u. p4 A+ J3 K" u: z; J - } - f2 q' g7 A- n0 ]$ U
- SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE); 0 Z4 }; l% e' x$ C: t
- SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); 4 {. s* O5 r8 E- x7 i+ A4 e6 ~
- }
2 J* a* z3 R& v - }
6 T3 L- c: G) q& ?& w. e - 0 r0 v; C1 i/ p+ r
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle); \9 q) S. i) z, a! e4 |: a' V; O8 N
- {
9 C# C, o& w1 i, g- I - if(uartHandle->Instance==USART3), ?: g- I6 W$ k
- {! A* M9 x+ }9 m, Z7 F) F( \6 S
- if(UART3_Callback!=NULL)7 U! n- Q1 m" z6 J
- {
, B; G+ u2 j# F# o1 Y1 a6 | - UART3_Callback(a);
/ ~$ ^8 l1 \5 I1 @ - }
! N( w* W3 ~" i. x - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
|* a/ A: a& i; i+ }* W3 D - {
2 e) N) O( s+ N9 j" ^1 O' L - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
' c7 \5 ^( c6 P- F' a7 D - } ( K9 v# _+ E" \9 }" ^
- }
" W3 |( u& [; `9 _2 E - }
8 C, Y) s; q, t- t( X$ y8 R8 B - void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)* ~7 I( r0 i4 F
- {
8 V7 M+ v( e! j& s, n+ r, W6 f - uartHandle->RxState = HAL_UART_STATE_READY;+ F; R' Z/ m/ n& [0 j/ R
- if(uartHandle->Instance==USART3)- H" d# N; F7 w' A3 d1 I
- {: g \6 y4 J. D$ I7 F# p, U
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
0 R4 }6 K" i7 ~. ? - {
; G+ E$ l% k$ u" Z5 X" Z - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
8 ~9 W# \ V% `/ p - }
% }( \7 K/ w, x& A. O! \ - }
+ m( c# N! ]! w& k8 A9 r/ I8 u - }" v2 E i' S2 t; M
; @3 w. R) o1 ~% J& j" J& B7 \- 4 E) [9 D, c+ p l! O, v
- 4 W. Z5 c( Y9 J
- /* USER CODE END 1 */
复制代码
# \; i" Z! r3 F0 W' m4 d
% y2 F8 ~ [$ R, |基本这样改就能应付大部分应用了,
2 N% g7 A G4 z* o, V, A/ g当然有另外一种方式,就是开一个检测任务,定期检测串口状态,如果发现串口卡死,则重新初始化即可。 亦有人用了DMA+空闲中断的方法,不过感觉是不太稳定的。 估计HAL库其他驱动亦有类似的情况,自己把自己卡死。 改库有风险,跟风需谨慎! 4 F. C N6 @# X" r s
+ O- K3 b5 C3 P4 F# z$ X0 y! i* P
- h" B9 q+ `2 x2 y, G
, V; S5 J( }" k |