本帖最后由 tanic 于 2019-1-17 17:02 编辑
& I! v+ F/ Z" l0 W
, q2 e- h* |+ d- |8 x8 A: b, x之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。! S( V: p; C" _! `
8 I6 [( }$ Y) K4 m这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。. U" a- e* i" ]! b9 @3 @" M A4 R
HAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
$ }0 E0 n2 E' [一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。4 x4 B7 P i9 b; s k4 w. ?
研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。. n# L5 N0 v, q5 v! O8 z* |( q
HAL_UART_Transmit修改后如下
' _$ B+ G6 d: U6 e% f7 z- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
, x. ? E6 h9 G% |, Y% U/ u - {
! o& X; K L6 Y' N - uint16_t* tmp;
0 s* |, U, L5 M F9 v - uint32_t tickstart = 0U;
1 P+ P3 O' w$ |% k& L! J
( B3 {9 D5 \- k! \/ ?2 s- /* Check that a Tx process is not already ongoing */
: m6 \- s8 R1 m: c- e& o: A, u - // if(huart->gState == HAL_UART_STATE_READY): [( E0 _# S% C" M' M0 a
- {
8 a4 E- {8 `& y; H8 R5 \) m" I - if((pData == NULL ) || (Size == 0U))3 I5 m: h6 o6 F2 e6 A
- {$ R# x$ Q$ x9 J, C
- return HAL_ERROR;3 Y* G4 c* T4 \
- }$ S' t3 s& l( r$ |1 A+ x
- ; @7 H7 V$ Z( u; p7 B* O5 K9 t, B
- /* Process Locked */* N8 F8 w8 o4 f8 {! ^) n
- // __HAL_LOCK(huart);% i4 p( r/ M1 x3 K
- ! e6 s* b; q" H. D% S
- huart->ErrorCode = HAL_UART_ERROR_NONE;
+ u+ {; s" n, v4 d8 T- P0 y - huart->gState = HAL_UART_STATE_BUSY_TX;
% z, V4 A/ e: ~ - ( t0 u, j" S H9 p y
- /* Init tickstart for timeout managment*/& `( v" L' w8 |! w$ J3 q( {5 N
- tickstart = HAL_GetTick();
_) g' i* R' C - . P3 A {9 D0 b8 Y& ~. b
- huart->TxXferSize = Size;" g0 M% u& @2 Y5 B5 w% G
- huart->TxXferCount = Size;
. B* F+ D; O$ a) K+ h# v - while(huart->TxXferCount > 0U)* @8 t9 D A. e9 m: q
- {
3 `; N5 d. G# S5 p - huart->TxXferCount--;
& J k9 k* | z' M$ |, o$ G! i - if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
, t4 u# l+ [/ w* Y4 n } - {
" l- y# m: r: {) k) K% L) D; R3 |4 s3 c - return HAL_TIMEOUT;
$ [3 h( ~; v4 G2 {- @% n! I3 W - }! k7 u$ P4 ? q, u" y/ _
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
; K$ m/ g9 U% Q$ p5 H% l) P* g7 _ - {
/ P1 [1 a5 U& n! K - tmp = (uint16_t*) pData;
- t0 \$ F. p. P* y4 F - huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
0 c* U4 R- P7 r2 k* |1 a - pData += 2;
$ F3 {% z/ T: z" a0 @: p2 `* D4 i - }
4 j' ]& A I" @/ H6 d - else- u, g) ^% b" ?8 \& F2 o
- {7 ~0 X% ?2 O7 a6 B2 n
- huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);% ?9 c% }7 X) _# ~. g
- }( \2 r9 v$ m3 m1 J- i
- }! ~& d' f) f7 ]# k% e7 u8 Q
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
1 R) r9 m$ J: x* s9 D0 y - {/ @: ]# N; c) w3 b; H
- return HAL_TIMEOUT;
& m: R, X; r/ c7 `- J( p! Q - }$ O5 G* X% ?3 l* s. E- X
- # d9 c1 {2 q5 i" Y
- /* At end of Tx process, restore huart->gState to Ready */
8 S! S" A) r, f; y! b( v. s5 r& C - huart->gState = HAL_UART_STATE_READY;
& \: j1 J' d: B+ }/ r3 c
7 X B" Z6 c! K- /* Process Unlocked */
8 |, l" }8 v7 ~7 C5 c9 C - // __HAL_UNLOCK(huart);
0 Q# q: K- q# J% T" H - 7 F$ J0 X( a8 Z* {" e( B6 O
- return HAL_OK;+ J, y9 ~7 c3 {3 H J& }! ^
- }+ E! M% X7 o+ r9 l5 N2 F! \6 L; V8 T: k4 |
- // else2 d4 N& u, X/ g6 L& }* F
- // {
! U ]% p/ @) p - // return HAL_BUSY;; t4 O n1 x( Q' b: G
- // }; G+ l; M0 J9 A5 D# ^# k
- }
复制代码
& X% Z( i+ C* a1 Y+ S3 M! KHAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
7 M5 D5 e( _ p1 A2 Q- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
4 W. o- Y# z3 _4 s! @* J) J! ^2 S - {
/ v6 i! }! A& v2 T - /* Check that a Rx process is not already ongoing */" g5 h$ [# Y9 j$ @. I% D; J
- // if(huart->RxState == HAL_UART_STATE_READY). f0 } ]" n- {% E% r6 d
- {8 {7 u5 L% w2 Z. L( T
- if((pData == NULL ) || (Size == 0U))" W V+ I5 y0 Z( S3 ]- X4 m O) v S
- {! @/ I- R: R$ H# u% \
- return HAL_ERROR;
$ ?# O1 l% ]- V$ D; M& Q0 t8 L - }( p! t+ g; o6 u8 ^; @
- 8 { }6 ]* o+ s1 I* S# x# E
- /* Process Locked */0 n. A4 |7 `/ w
- // __HAL_LOCK(huart);/ Q6 |/ A0 u& s& |1 h4 N- T
+ v6 _) x5 ~( F2 T, }7 E- huart->pRxBuffPtr = pData;3 [" a1 U: N9 E0 j) ~( w" R8 \
- huart->RxXferSize = Size;
. j) `$ M2 s/ G - huart->RxXferCount = Size;6 B: M( E' v2 S/ @. N
3 H9 \* M/ z2 }- /* Computation of UART mask to apply to RDR register */
; R5 {6 f1 q3 F8 R4 h - UART_MASK_COMPUTATION(huart);2 \8 U: { V3 m$ V$ C
2 ? q, A2 x- Y* W; L: F- huart->ErrorCode = HAL_UART_ERROR_NONE;
" O. E# D3 V& l i( y: `& v( ? - huart->RxState = HAL_UART_STATE_BUSY_RX;- w$ |2 X' R* c+ \
- ! m4 P! V. k4 U2 Q1 E
- /* Process Unlocked */5 r. {9 M" p( ^/ h$ o. |
- // __HAL_UNLOCK(huart);/ ^: n( z, p/ g1 k, }
- ( b9 V: g! G8 Q$ i
- /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */5 k; L u+ _ z$ q+ }# m4 Y
- // SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
: L2 M* a4 \+ r4 _; P% G
: N7 M4 q2 q/ Q, Y7 |! Y* ^9 w- /* Enable the UART Parity Error and Data Register not empty Interrupts */
% I8 G) j) o: s2 f/ m6 u - // SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);; S8 m4 {% ^# q! W% _* @( V
, D3 ]* A' d- v; S# y9 K1 C- return HAL_OK;
) S9 g6 `- @7 M9 B; v9 b+ p - }; w$ y/ o. j( R: I
- // else
+ \7 G, y2 F/ }7 B - // {" \4 z( k5 X4 l
- // return HAL_BUSY;
" |" p. n8 @- K9 U) H- ?) G! u - // }0 v0 m& ^4 E7 i2 {. K0 W
- }
复制代码 0 F* @- z/ q6 X3 ]
接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了
- o) u/ H$ B& ~- _9 ~8 b& m- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)7 l. t$ I, w: Z- b" ^* k
- {! T5 D$ y9 u. }4 j9 U; K
- uint16_t* tmp;$ K' c& {8 t5 g5 c k4 B; X
- uint16_t uhMask = huart->Mask;
' ~- M V7 v5 G
- P, S- W0 u( s* c# V% b- /* Check that a Rx process is ongoing */- d* u }) d, i2 u/ A8 W e6 J1 \
- if(huart->RxState == HAL_UART_STATE_BUSY_RX)5 r; t4 G7 V+ o- \0 L* l2 q
- {; _; p. V. K1 T* C9 }2 `$ h. d
- 8 \5 f9 w! Q/ `. n
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
/ v6 U- }3 c0 o% E - {
+ h3 m+ s. t8 \7 D& F1 R1 o1 k - tmp = (uint16_t*) huart->pRxBuffPtr ;
: C- h. @. X( u/ s) y6 P8 j - *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
0 U. n5 z# F6 X( i/ y - huart->pRxBuffPtr +=2;
# Q: B9 J$ h; D8 Q9 | - }- q4 Y$ r: _1 f2 @( Y
- else4 \# I0 ~. U8 ~) }2 L: |' z* E
- {
# b! z9 _# i6 }0 F" z+ F0 G0 z - *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);" l+ V( r$ U" j7 T( f4 [
- }
8 ~! q, q. I, G+ ~9 _ - ! a5 R+ i9 p4 N( @& f
- if(--huart->RxXferCount == 0)
6 Y c$ y' g* T% P - {
; ?' {! [" N' Y2 w0 Y7 O3 { - /* Disable the UART Parity Error Interrupt and RXNE interrupt*/
% F y1 ?9 l) |' i - // CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));7 L2 m% ]5 X! p5 r' f2 Y
- F& t% [3 @" f h' k
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
4 K1 D4 p# c0 ^: ] - // CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
: E5 K6 r6 e. U" E( o m1 z# M - 3 D$ E) W+ Q) Z/ G8 l% X
- /* Rx process is completed, restore huart->RxState to Ready */- r) A _* }( ^/ u' p
- huart->RxState = HAL_UART_STATE_READY;; ~! N- q/ ?! |% v/ X" H: S# [
+ l6 \" \" ~( G- HAL_UART_RxCpltCallback(huart);- h; Y- T8 Q3 D5 Q2 v; I( K/ x
- % C( q% `+ \# Y' b( f9 G5 f7 x
- return HAL_OK;6 F/ \* R! B+ S* |
- }; p( X! k, M% }7 r5 \ c1 n
( y) I0 k' b$ ^. @: G$ W) c- return HAL_OK;
. ^/ h" l" O5 R; Q* u( m( s - }
' z. L% y& p! h7 S' V - else' T. N3 Q, J4 Y. {! C; e
- {5 L. j0 X9 _0 X
- /* Clear RXNE interrupt flag */6 ? i. B& X. h' s% J
- __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);4 m6 V2 {2 [& {* |' g
' L* w3 \$ u6 Q5 ~* m- return HAL_BUSY;4 c. ]6 }4 i9 N4 r5 j
- }- p! m$ H2 d% l# L' U
- }
复制代码 g6 G5 s1 g* G5 Y0 @; O
在usart.c添加如下代码,
# i. E$ ?9 C+ l2 p3 N( A. I3 X( w1 ^StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了! y: g, o, ^2 k6 P, M. }7 \
HAL_UART_RxCpltCallback:接收中断回调函数: X, v/ c$ E7 U# f$ f T" C: E
HAL_UART_ErrorCallback:错误中断回调函数/ F0 {2 }1 c5 R E/ H) N! d
" H# _/ p1 m& r ~
- /* USER CODE BEGIN 1 */& }( j' W8 J# W! U) L0 |5 v
- uint8_t a=0;0 T' @' f3 M. A+ c. H
- void (*UART3_Callback)(uint8_t)=NULL;
; P9 T/ X) k A( _ - void StartUART_Rx(UART_HandleTypeDef *uartHandle)0 W& B+ i" h* F
- {
6 N! q7 i8 A) y% t - if(uartHandle->Instance==USART3)9 q. a" ?. g7 I: y
- {+ D. V4 t- u1 V4 q
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
+ b t! M! {4 N& E - {
3 \6 r) U6 z$ R, w2 J5 C - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);- ]/ K$ G: g: q; m. `3 y
- }
( `! t* M) b; o6 W, V - SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE); 6 W4 T4 L. [ a, s6 k' ^
- SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE); 1 K6 ?2 Q0 P# c9 T W. M" F( q
- }
! N! b& z7 W* j( a4 r - }
4 L" t8 |5 F( v% q2 M0 Q
2 q/ B4 h* ~9 c8 u- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
. e; J% b# @3 k% B. u - {
0 N: a5 N, _0 c- g - if(uartHandle->Instance==USART3)
, f6 A' v" Q5 V) ?/ l' K - {
- ^4 c; q9 D6 C* z6 t - if(UART3_Callback!=NULL)
6 m/ o6 z7 K7 G$ c8 \. x o+ v - {5 W [; K/ J) Z2 u4 ?2 {/ T
- UART3_Callback(a);
/ O5 P3 u# P- O3 h( k - }
6 Y; O6 y o- ~+ g - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
4 d1 b y7 X% S0 m6 X2 A - {6 a t2 p: ~5 l) w& ^ }' a4 Y0 U% M
- assert("HAL_UART_Receive_IT",__FILE__,__LINE__);: c5 v% h0 M& `( U9 E5 ^
- } R9 \, t ~* K4 I
- }9 Z0 d H2 X& e1 Q4 M2 e' }
- }
( N/ g; Z G$ ]. a' @4 n - void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
; ?$ B/ T! ~- [3 q) A- x - {
3 a2 F: K# i. v$ C- D% @2 T - uartHandle->RxState = HAL_UART_STATE_READY;
4 [' Q) x2 S, H; f - if(uartHandle->Instance==USART3)
$ G ?6 [+ ~2 }$ Q( B - {: r- f r+ i6 f5 ]
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))
1 F# w' {) E2 Z6 ^ - {
# B" \ d' c# [. T - assert("HAL_UART_Receive_IT",__FILE__,__LINE__);8 I ` Y4 c* B0 B9 ~( K% [
- }
9 U1 h8 M$ ^/ X; g+ A0 _. a - }
9 w5 m) M* l6 _* d - }
# \5 Q t0 N4 _
* o* y) {& g1 R* |; o2 M. k
2 g, N) l4 ~6 `" }& L! Q- + z. r$ z8 U7 @6 u
- /* USER CODE END 1 */
复制代码
/ N. ?8 {# n& q/ ^: F/ }
7 P3 h) b7 ^7 e# A# i基本这样改就能应付大部分应用了, 6 s( F) g6 J3 }* W# N
当然有另外一种方式,就是开一个检测任务,定期检测串口状态,如果发现串口卡死,则重新初始化即可。 亦有人用了DMA+空闲中断的方法,不过感觉是不太稳定的。 估计HAL库其他驱动亦有类似的情况,自己把自己卡死。 改库有风险,跟风需谨慎! + n" m0 p }1 f* h
$ H. ^' u' ]. c- k% |& p+ K5 r% }7 M1 x0 E7 P
/ T1 ^; X/ x2 b- |0 k% D! x |