本帖最后由 tanic 于 2019-1-17 17:02 编辑
! D9 S" b" l; R# h9 _; p! v- h* _% i3 q$ Z
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。
6 V1 P5 x; R4 H+ ] G$ k) Y0 \& N% s! m
这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。. U% b+ X+ ^+ H V7 z$ o! q6 w
HAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
# y4 }( n0 a4 Q( r( C一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。
6 l& @! B, ~( q* J$ h' `0 H, B0 z研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。
( J* J. U! e& hHAL_UART_Transmit修改后如下
: Y3 M: P, T5 J. s- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
4 W9 O) g- ^4 p - {
2 H+ Z; _" s ?- h, X5 J - uint16_t* tmp;: B' ^- d7 I' V9 x6 z
- uint32_t tickstart = 0U;
1 U" a" ^2 ~; v. B& J7 Y/ _ - 8 y1 R& Z" M& s* K" W P* b0 F; L
- /* Check that a Tx process is not already ongoing */ y' B+ w1 m6 E( m' c8 n3 O
- // if(huart->gState == HAL_UART_STATE_READY)
$ M- W$ o: E& I" ] - {
N) \6 b* k% N4 i0 c# ? - if((pData == NULL ) || (Size == 0U))
M( ]' E( N2 A& {& i* r1 E7 e - {
$ H4 q+ c- L! Q K5 d - return HAL_ERROR;
' W; ^3 \, [* S( P, }: F - }
+ L, K3 N' H& [4 f; v7 I
5 v+ }0 |) u" s. u% h- /* Process Locked */
; Z6 E; K: \- q9 n - // __HAL_LOCK(huart);. v- I8 {0 u0 s0 F! r8 m, a1 y
) V9 N6 ~4 N' Y# ^' h- huart->ErrorCode = HAL_UART_ERROR_NONE;3 l) q0 Z& ?) m; r( H! i
- huart->gState = HAL_UART_STATE_BUSY_TX;+ a t5 J, E0 b9 w& x m1 B
3 C* J( |3 g2 B" U% e+ ]: e- /* Init tickstart for timeout managment*/: O' X' @' Z1 {/ z+ ~5 o" y! \
- tickstart = HAL_GetTick();
/ H8 F+ H$ B% m7 ]
8 K U, B- ], p, Y; p# S5 B- huart->TxXferSize = Size;; B/ g* i2 o+ j0 m, v' ~1 L1 [* x. J5 }
- huart->TxXferCount = Size;
: E" i- w6 M2 e( l - while(huart->TxXferCount > 0U)$ Z/ d; t; B1 N4 M5 M# G4 F7 N
- {4 L% `/ _& V) g i1 d0 {
- huart->TxXferCount--;
$ L+ N; W. T8 k9 z- n - if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)6 i# ^. j& J# r G0 \4 Z
- {
. J0 `/ [& v' w9 L - return HAL_TIMEOUT;
6 H1 e9 A+ t7 h5 e- N* Y# j% Q6 h$ k - }7 j* d d/ A7 h+ f: X; t
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
% v' p @6 m& H( H. ? - {
1 P2 Y4 W( ]1 ]- i% T - tmp = (uint16_t*) pData;
9 h4 V7 e* \/ N( _# ` - huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
R9 T2 S: d4 k( r) c - pData += 2;; `5 K: A3 R7 u, ?" T3 O# N0 J8 q
- }4 K4 _: ]3 w$ X0 p
- else8 ^9 s' }( G% V; _" B3 A
- {
. j5 z/ E. w4 M3 v - huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);% F8 o. j" y0 m! [) o
- }6 z/ B A* X5 ^( g8 n8 P/ Y. ^
- }' C, {% x5 B7 ^3 N* u7 z% Z
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)1 f/ T* X7 L ~
- {
9 `" t) y! Z( \+ Q - return HAL_TIMEOUT;7 Q. l( m2 x9 v' p1 r: \0 q+ y
- }9 ^2 U+ F" W7 l8 A
% ?5 Z2 F3 i" m" I$ B- /* At end of Tx process, restore huart->gState to Ready */( e8 N7 V. ^4 F& c# G. `' J
- huart->gState = HAL_UART_STATE_READY;" V! X4 k! e% F% u& _1 J+ f3 P* B
* c0 \/ \( o7 T" S: M- /* Process Unlocked */, F: }2 N# w% ^. v1 Z6 y( x* v
- // __HAL_UNLOCK(huart);
8 J; M$ a5 ~ O. B$ X5 r& f: R
' ]5 |6 W% |1 ?' i. X/ {1 z) r- return HAL_OK;
1 K4 D: \# c0 a& p e - }! F0 u+ ]' a& w
- // else
, z& P5 y8 {8 _7 U3 [1 Y - // {
r+ j/ \5 p2 w% k# S* w2 n4 I - // return HAL_BUSY;
3 v0 T2 x0 i7 u( {& ?$ y) A - // }4 O. {5 L2 t! T! w9 I) M% b
- }
复制代码
% U" o A" {: ?8 e* _9 cHAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
+ O$ g9 @1 U5 P0 p& C- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
! F8 F4 x+ m" e# c$ o - {: k0 g' M1 @; t4 [5 x
- /* Check that a Rx process is not already ongoing */
# u' `9 M7 k1 n& \ - // if(huart->RxState == HAL_UART_STATE_READY); _0 p' U2 Q. q9 j/ t( ?9 _
- {
) }" f1 a# c, S. N1 \0 u - if((pData == NULL ) || (Size == 0U))
5 V( @* b+ h. |) v - {+ l5 ]2 s9 u, s) i. x, r
- return HAL_ERROR;
# \6 r) I5 G6 n! [, Y$ L0 u - }$ b8 e3 z; B% j0 w3 u
- : u3 p- i! c0 v" S5 u$ A
- /* Process Locked */
# A+ r+ T4 Z3 o8 t( p+ K - // __HAL_LOCK(huart);- h/ k% P# w5 Q4 w
) T) O9 _# y& q1 K( y# {* d- huart->pRxBuffPtr = pData;
0 d6 z9 ?- {. T/ B7 q - huart->RxXferSize = Size;
: d& T4 X8 C' I1 ~0 e _ - huart->RxXferCount = Size;
7 M( a f S9 r r - , a$ P# W. |6 A: H; q9 u2 | V
- /* Computation of UART mask to apply to RDR register */, A& h: H" n& O. O l0 u* S
- UART_MASK_COMPUTATION(huart);
, n2 H' f! B* H* Z. R+ g% T
7 I, D# c# }: q( t" a( R- huart->ErrorCode = HAL_UART_ERROR_NONE;
. h9 B/ S+ U; y. n$ u' @/ t. a - huart->RxState = HAL_UART_STATE_BUSY_RX;5 K1 F. m5 H0 c! I
- ' Q& E' _9 O: s- Y) h9 o
- /* Process Unlocked */7 I0 D5 b8 r# F1 M3 J
- // __HAL_UNLOCK(huart);2 J% G5 e4 }8 d' ?, z& K
% M Z/ y6 g1 U9 ?7 D+ P& _4 ^- /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
6 g$ L' r* J7 b+ c( U - // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);: p/ w. U9 P0 h; ^# i
- # @3 d; S& [+ V- n. k, Z# E
- /* Enable the UART Parity Error and Data Register not empty Interrupts */$ D: S9 \( T$ h
- // SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);6 z9 j/ Q- Q+ v# i, m7 i& Y; _
- % G8 F7 l9 w, S" ~* K5 g
- return HAL_OK;2 W" L, C }8 m% x( T: V
- }8 m$ K8 V# ^1 R5 d1 m" S
- // else
, r3 G6 q$ f- }- E. z$ { - // {+ ^: i, k) R: P$ P: R" g! v6 h
- // return HAL_BUSY;" g5 q- M% Z7 {1 g. L
- // }3 \- ^3 Z* D) C; C0 W& z
- }
复制代码
- n B" G6 v& U% Y( e: i9 t3 j. v* W7 ?接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了
( D2 {1 N2 ~ i- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)( ]$ k! V* x- @# ~8 h% f% K! O; q
- {
) c) u$ ?* V2 B5 _5 L: r8 ~% k& N - uint16_t* tmp;5 W3 c8 b3 R% q6 A5 R
- uint16_t uhMask = huart->Mask;
/ _! }; o& B- |9 S
, q) z' T: Q; `) C$ O/ u( L- /* Check that a Rx process is ongoing */
- _7 ], t+ A& v) F# l/ `- s; N - if(huart->RxState == HAL_UART_STATE_BUSY_RX)
9 a a$ b4 L9 ^8 A N - {
7 j$ v5 y) I- i9 L4 f; P - 4 M5 `, d; D& t0 X) F9 X# k
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))/ p, s) C: c9 D0 T
- {
4 O! o0 \6 w) { _! O+ `% b; Z - tmp = (uint16_t*) huart->pRxBuffPtr ;
( i/ H8 x2 ?% Z5 X6 m - *tmp = (uint16_t)(huart->Instance->RDR & uhMask);! @7 D5 h( k% h$ {
- huart->pRxBuffPtr +=2;' I7 W. r1 C* L w- j
- }+ y, a0 R: ]; f
- else
7 L8 p n3 ]8 L' v0 W4 B - {2 L R" _& p0 Q- d& ?9 X
- *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
. V! M. c2 D' u) d - }
/ N- a' L- k! G. d) P A( a l; w
1 k$ O/ W3 S6 F' |9 z- if(--huart->RxXferCount == 0)
( e( ^' O# ^0 o4 g2 q* Y* G - {
$ i9 ?6 m9 `6 o- q8 L - /* Disable the UART Parity Error Interrupt and RXNE interrupt*/
$ Y, z: K' u% M! d - // CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
8 v! d Q( l& `; \" {- i
3 _% `$ L8 U0 @! e3 F+ n3 e- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */& [3 H0 z: U& t3 v$ g$ E
- // CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
2 f( k1 V' ?3 d& J - ) u" U: {) U+ j4 d: M
- /* Rx process is completed, restore huart->RxState to Ready */; a/ q& e% ~! b8 z+ [: }$ `5 c
- huart->RxState = HAL_UART_STATE_READY;
1 \* W( C3 W7 c# i: g5 z+ i, s+ Z2 _ - : g. c; M0 t) v. I: K3 o
- HAL_UART_RxCpltCallback(huart);
, D* j) Q% S2 F9 D h4 _( J - 2 S0 {+ {' ?0 ]
- return HAL_OK;
s6 k2 T0 Y# n: O0 { - }( i: I% ]2 `9 ?" {6 R
% F5 b9 E* S- n$ {2 H- return HAL_OK;
$ [; t4 ^9 |8 `, J% W - }9 d8 W" O2 i9 j) n3 ]
- else
/ t. P! @7 U; _& A+ j - {0 ~7 J9 a, m( ~/ f' U
- /* Clear RXNE interrupt flag */
( ? s. U6 J+ B! }5 b `2 z3 d - __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
: X2 b7 { }9 A- \2 J+ [
4 A& Y- k+ U# D, b' E1 p- return HAL_BUSY;
/ J$ Z R. V' T; ] - }1 V8 r% {. O$ m N1 r: c
- }
复制代码
0 I! m( l& z2 a在usart.c添加如下代码,
/ c4 R0 x$ V! N6 H2 h7 ^StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了
: V8 C& E+ X0 b7 ^6 |8 C! f0 hHAL_UART_RxCpltCallback:接收中断回调函数
8 k: M5 H4 C" [! \HAL_UART_ErrorCallback:错误中断回调函数
9 a5 ?( s% \8 y, y! V/ m8 ?( O
: j) v0 I' y$ u5 ^( ?0 [8 w- /* USER CODE BEGIN 1 */0 n1 |+ h. P3 U0 y' b0 J- k( q2 p! U
- uint8_t a=0;. {3 f. U' u4 S* ]8 T, `
- void (*UART3_Callback)(uint8_t)=NULL;
, k) Q/ ]/ ?- Y; h8 v - void StartUART_Rx(UART_HandleTypeDef *uartHandle): T2 d: K* k( y1 h
- {2 n2 [7 f0 |! d' J- M
- if(uartHandle->Instance==USART3)
) e" A" ~ u8 f/ @; ^: t - {
% v8 R( M* O4 q- i! r$ K - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
$ S5 Q$ m: w. y Y - {
; o1 _, v% c& _7 E5 J - assert("HAL_UART_Receive_IT",__FILE__,__LINE__); y5 q# H- u0 R0 N" |9 [) k
- } ; K( e' i7 X; L; N, S
- SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE); 1 }0 d% e) h8 N) Q
- SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); . s7 }2 T J' c
- }' ^: |" B! ?4 o- n3 B# `- o6 ?
- }
2 C) ~( t3 q8 E9 Y9 J5 l
" x! o9 H0 t/ f j2 K( ?% [- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
6 R$ t5 b8 L* x6 Z& C - {
& @' E8 ~- ^" _ - if(uartHandle->Instance==USART3)7 {' k& n6 i/ `
- {
7 c$ a3 v( b4 j7 l% M - if(UART3_Callback!=NULL)
9 ~/ ?5 H: s' q2 q - {
1 g5 _ P+ ^+ s# d {& X - UART3_Callback(a);
) R( j( C6 _9 \+ d( s& ` - }- K% \' P" I1 p/ H9 r
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
, I) l& o1 q0 J8 [4 _4 S: v - {
6 I% I) _. z; d4 \ - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);# G2 d- R- a* }* s
- }
4 E; ?! ~9 c% M* F4 W - }
( [' M( z9 t$ h( j/ y5 g - }3 }5 y, B+ a, K- K i, f1 t$ J
- void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)7 H6 f. [- S4 o- K3 b- a. V0 P' I
- {& ?" r; `2 {: M2 H& \- ^
- uartHandle->RxState = HAL_UART_STATE_READY;
% U8 p) m( \, k5 r& q4 ~4 Y" p - if(uartHandle->Instance==USART3)% l q/ v/ b5 k/ {, c0 V
- {
! x1 d4 O" y1 u& I* y7 R4 A - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
- O% d' j5 I+ W; e% h3 n - {
* }, p* [& k6 A. a; J$ z - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
! y; c/ {7 W$ I! y+ | - }
/ P/ Q) L) e4 z. d - }$ ]3 |# j) F2 s) F. V9 T; y" H8 V
- }
# t1 f, I6 D+ ^. K8 c# a' \; w
6 j% w1 d" b! m- P) z
% W6 X1 t% Q8 R7 A
# T3 N% R0 R3 {) Y4 z- /* USER CODE END 1 */
复制代码
0 j" _3 a/ D+ b/ l
/ y: Q2 [( @/ P3 f( q! Q z基本这样改就能应付大部分应用了,
, t! e& J, R X l1 u+ \6 Y( W当然有另外一种方式,就是开一个检测任务,定期检测串口状态,如果发现串口卡死,则重新初始化即可。 亦有人用了DMA+空闲中断的方法,不过感觉是不太稳定的。 估计HAL库其他驱动亦有类似的情况,自己把自己卡死。 改库有风险,跟风需谨慎!
" R! B: E! `2 ?, L. C. j( K+ g* Z0 P- \1 ^. q+ j: W
5 Y K. ^4 U. d
) i" k" q% D- S. g
|