本帖最后由 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
- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
/ l7 ]2 i d/ u& U( ? - {' j8 a- K/ E8 t3 @9 ~, P' L7 U+ }$ {
- uint16_t* tmp;0 u$ \3 L1 Q2 b* K) ^5 {0 U; s; s
- uint32_t tickstart = 0U;
: s4 W/ K' e- L - . \& f' @! R: B2 Q' L
- /* Check that a Tx process is not already ongoing */9 r- w, o* o5 w- U4 E/ \+ V4 |
- // if(huart->gState == HAL_UART_STATE_READY)1 P5 q/ C' O; I. D: o: [
- {9 g3 D/ K) I) N# v" A
- if((pData == NULL ) || (Size == 0U))) x, B- n# q4 ~7 e
- {3 {) ^9 N6 C, n2 i+ l0 {
- return HAL_ERROR;
3 K* z- S: }2 j* `! H% Q) L" ]$ s# U - }
' g# o n' |; ^ - , U: ?+ ]- d0 k# H8 X" @
- /* Process Locked */+ m& t4 u$ Y! c; B0 M5 |
- // __HAL_LOCK(huart);6 u+ v9 U, p# [* M* _
1 x' y4 r9 n1 i# s) N- v" c5 Z- huart->ErrorCode = HAL_UART_ERROR_NONE;2 ?+ t! k0 W% a7 r) g, Y9 ^+ v' I
- huart->gState = HAL_UART_STATE_BUSY_TX; }0 Y/ D4 m4 @1 z4 T( G3 Y: _
- ( U- z+ P& h6 q& W
- /* Init tickstart for timeout managment*/
# Y; V+ Q+ H2 P' E& W) H1 z# M8 q - tickstart = HAL_GetTick();
- p7 V& e( L8 k* ~
! S' k! o) F5 U* ~- huart->TxXferSize = Size;0 w; z6 W% ]7 e/ e
- huart->TxXferCount = Size;9 k- ~1 j0 y2 O5 ^1 c0 f* u; l
- while(huart->TxXferCount > 0U)8 f" \8 r0 c0 z }/ U7 {
- {' l5 j6 U7 m Z; f2 b0 V
- huart->TxXferCount--;
% [4 M) Z. g9 L$ y4 h - if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
( |+ D' w% M% ?- E$ j/ d - {" l5 ^; U2 M% \% L0 g
- return HAL_TIMEOUT;2 c% v) E6 R3 q% E
- }( @$ ?# P, h9 m
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE)). J5 H9 d4 D# b; p
- {3 N' W/ _9 n% ]' G' m* @% w1 H
- tmp = (uint16_t*) pData;+ f. u3 d4 d% w" }. Z
- huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);- Q/ }% N2 e, a6 b2 r/ M
- pData += 2;
. G8 I0 ~6 o# U% G, ]( Y - }
/ R- a! m! f& U2 k7 p- D - else
- ]& A9 z$ C3 Z - {+ m/ i4 y; d8 T
- huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
& X$ O5 f3 B: o0 j& n. r# m - }
2 u# O, L9 I8 Z) j# y0 }& ^ - }
3 t: r( }# f7 k% E5 R0 \ - if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)( H( z) d3 c0 d4 c6 B
- {" c W/ X. G6 o2 H$ W1 K
- return HAL_TIMEOUT;; }2 j4 c% u* f* b# o
- }- y- K) N" X, d9 C5 h, l
- . W4 u7 Z+ t v
- /* At end of Tx process, restore huart->gState to Ready */& P/ }0 O- y! c4 b: V
- huart->gState = HAL_UART_STATE_READY;
# M. J& e& }4 d' j
; A$ N" R* }, k$ X5 K' i0 D1 X- /* Process Unlocked */$ y5 X3 I) p- R6 S. q
- // __HAL_UNLOCK(huart);
/ I" U6 l2 j' l( f+ h5 S! x. V
. V6 V6 J! {2 S" S- return HAL_OK;
! t; s8 B0 _" G1 O! c+ n6 c - }* V; v$ W s4 ]' N! k: Z: w
- // else- L( R) d5 f) f( E. B6 k/ @
- // {0 n# {/ A4 b5 _* s
- // return HAL_BUSY;
' b$ C, N# `1 j! V, I$ ?4 R - // }
) g7 v9 r9 X7 i; w* C - }
复制代码 6 k$ }7 H, `' N5 V4 c# o$ t/ u
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因 _% y. F4 T8 d( ~9 u' Y% W# d0 Y
- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)" i& i+ x/ @# N+ {
- {7 v: x$ n% Z# [" g
- /* Check that a Rx process is not already ongoing */
; l6 I7 L/ C$ x - // if(huart->RxState == HAL_UART_STATE_READY)' ]9 p/ v) g6 B+ l1 Y" z
- {* k! W' c, X" k: f" p& ]
- if((pData == NULL ) || (Size == 0U))) L3 j2 d. @4 \- K& j
- {& p m9 K3 W: k- N, T/ k7 e
- return HAL_ERROR;! C( T8 Q7 m+ T Y# L- S
- }
6 u0 E' G0 ]9 {7 x1 ]# [' ?3 ~# |
2 T3 \/ K" M; v3 k& N1 ?8 R- /* Process Locked */
' u" R" F* [- Y6 x9 E - // __HAL_LOCK(huart);' S% h( S$ N6 ^/ O
" g% ^! z2 F/ Y, s- huart->pRxBuffPtr = pData;
6 K! z9 I, S5 Q - huart->RxXferSize = Size;
f+ v4 h0 I6 J3 b! r- v' e - huart->RxXferCount = Size;* |8 f$ L% \8 G8 Z/ _
- 3 r3 c3 Q0 T& G, v7 R
- /* Computation of UART mask to apply to RDR register */
1 M) `$ r, ^/ w" ? - UART_MASK_COMPUTATION(huart);
# I# b6 N4 P! Z( R. l2 | - ! n' d9 O: k' X! f
- huart->ErrorCode = HAL_UART_ERROR_NONE;( H0 J9 D" Z. A! U# O; O2 ~; t
- huart->RxState = HAL_UART_STATE_BUSY_RX;/ g& U( W' V' a9 n. q
- 4 N8 k6 h% }0 }6 _$ q4 c/ m
- /* Process Unlocked */& ]9 ^( u0 Q9 P/ `
- // __HAL_UNLOCK(huart);
+ V; {3 K" u& K
0 B) J/ w- Q! p6 o" L9 R- /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) *// j/ t: W" `" s+ R! @* u
- // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);/ O9 v; h+ q0 [* A& W3 h& Q
- 3 A) G7 q- K# ]4 {) S
- /* Enable the UART Parity Error and Data Register not empty Interrupts */! C, o( X* s7 a
- // SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);6 E' A: L3 D/ M6 O' I
- 2 m+ I. j; h9 F
- return HAL_OK;2 e* z0 c2 q o' E) g
- }. m2 E) V0 E% m( j
- // else
; S, a5 O5 {3 Z0 K8 z$ C - // {
, m( q& d6 R p9 h' E( n7 P - // return HAL_BUSY;
; p. [- O5 i7 n& }& r B7 E - // }
0 ^1 e: o0 \0 [) S - }
复制代码
& I! n, J7 `/ G5 E& i# `. T接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了! F" R8 Y' b# n
- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
5 y3 v7 [, t8 ^3 |7 t4 V* P - {
" t' A# ], Z8 S2 \# ~) K - uint16_t* tmp;
7 `% T- \9 f$ C' H! y6 ~ - uint16_t uhMask = huart->Mask;! P) ?5 W# V6 Q- j5 d
- , |6 Y/ T! ?! M! i% ^! S7 A" x5 R
- /* Check that a Rx process is ongoing */) K+ ^8 q g/ {& z
- if(huart->RxState == HAL_UART_STATE_BUSY_RX)
/ q4 r* Y" q& C* z - {
. g7 X" h( R7 ^$ A7 X; B. g
$ O$ k( l6 M* [- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
$ e9 C% N1 c: [; U4 c6 x - {
: m# ?. k# U. k% {: Q5 _ - tmp = (uint16_t*) huart->pRxBuffPtr ;
0 i/ S3 f- \ O- K# F! G0 [ - *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
( Y) @4 i/ H. | - huart->pRxBuffPtr +=2;
5 g/ I/ b! [: I/ Z% f - }9 @, r+ i6 d) g4 M' r: \3 _
- else
( Q, h4 \+ B- ?" a - {
2 ^8 Q4 Z# V5 f5 z - *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
5 G9 y+ z4 R: V - }7 m8 X% X( R" A6 V. F
- - `& p) D+ S, t) ?
- if(--huart->RxXferCount == 0)
5 y# C/ r( f" B, \ - {
) T0 X/ c: v0 j* Q' T# y& i - /* Disable the UART Parity Error Interrupt and RXNE interrupt*/1 G$ X$ w* r. G: x! F# k: g
- // CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
/ U& j1 O8 ^! G% f/ k - 1 [! T5 Z' W: ?- w, A
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
8 @# y0 x. O# m" c4 D; u - // CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
! {$ ~, m3 M, Y4 v4 g+ N8 Q; e
. B; p7 T# H* n! ~( B- /* Rx process is completed, restore huart->RxState to Ready */1 R: |3 Z% Z- n2 A. u4 }
- huart->RxState = HAL_UART_STATE_READY;+ N# m2 C$ p* Z$ ` u
" A8 I$ m" V2 s. O8 n4 q- HAL_UART_RxCpltCallback(huart);
# E' b9 X8 W' j6 Z" ~" ~. e, C4 r3 l8 ~3 g
) y9 a( ]0 t5 n2 k* `1 {; m1 n- return HAL_OK;' V, D H( ?' S F4 ?
- }
' A$ P. j% Z+ }0 Y) j6 P9 i
& }& A) V: w* e/ f5 W; f8 N- return HAL_OK;- x' E F/ M) G
- }
' x# x5 y) j- ]* L2 n7 P4 b% R' M - else6 F; w6 X+ k- Z) {0 h/ T
- {5 I/ H& D0 |4 o* X. P# g
- /* Clear RXNE interrupt flag */! \, u8 i; _- A: M+ v
- __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);* T5 `% ?- m0 k% ?
! R/ I2 D0 i/ ]* l% m0 j7 ]3 y- return HAL_BUSY;" I5 B2 C5 h" H& d
- }% I, H% K" F7 Y4 |1 Y0 c7 y, O
- }
复制代码 : 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" }
- /* USER CODE BEGIN 1 */
& d2 y% Z$ c( |" G - uint8_t a=0;
+ |6 o$ l, Y$ k. q1 h2 U3 U: j - void (*UART3_Callback)(uint8_t)=NULL;6 B- z. S% R) a0 y. I
- void StartUART_Rx(UART_HandleTypeDef *uartHandle), [) Z' }; I) |# Y: h8 ?
- {
; V8 Y+ ~2 X( i - if(uartHandle->Instance==USART3)2 w3 T {, `9 V* G
- {' X# l% \. z- M
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
1 P/ [& m' p" I6 v* v - {
. Z8 G- _" S/ J8 P: J - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
6 [+ R' N6 E) N K3 H - }
6 l% ^+ ~" b$ l' l" W! { - SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE);
3 ?! z% ]- W/ e& y - SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); 4 x' H4 J; z5 a9 R! Y- \+ N* z
- }9 u# r. F* X! l( l$ V8 e
- } \& R; V4 [, o6 A5 R
; ]0 A: P7 @9 K& \* w1 i) `- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)* @( F" ~0 L* I* `% n. a
- {) _* f/ N4 G9 G4 B
- if(uartHandle->Instance==USART3)* Y5 |" d" ?: H5 W
- {
. C* V* d- c) }- z9 W3 ^ - if(UART3_Callback!=NULL)
* R Z2 \# C- |0 Z4 K, o& { - {1 \+ h" d; s! h5 \* U
- UART3_Callback(a);
, T; ?' [( g5 d6 U5 d! _7 m' O; T# k - }
! s# W8 h& g9 s+ B9 [( r6 _" I - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))" J% J ?' }, {
- {
2 V4 N2 m. p1 N; `1 m1 r - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
& c* _, F" J7 [ - }
. n8 ]7 w4 h! R - }
, X2 F( L3 q" l9 d$ N$ `! b - }7 s: U8 v8 l$ ?% o9 i
- void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
3 C3 r9 T' Z: C/ R% X7 A - {: l& q# _0 j2 [) u8 s
- uartHandle->RxState = HAL_UART_STATE_READY;
9 R) d1 C1 i6 s3 s N" v - if(uartHandle->Instance==USART3): ^# O& u z8 ?$ s' A* t& {* o
- {. L, Y$ n. n: g5 b; ]& ~
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
9 V H- b$ I% Y: Q4 n/ ?+ h0 p F - { _7 ]: Z0 @0 O$ @0 Z
- assert("HAL_UART_Receive_IT",__FILE__,__LINE__);0 F( }: F# l, g& q2 A
- }
) _. b$ d7 c/ B8 n9 O - }) i4 j" E+ X+ ~8 Y+ I! i
- }
, Q: Z; z. K; u1 } R - 9 v& y, W& e: P+ ]0 L, w
- U+ N% t( w9 t- " D2 z1 |3 Q% H( ]% m
- /* 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
|