本帖最后由 tanic 于 2019-1-17 17:02 编辑 & K" l/ m' D9 N: s1 o3 H5 a
- ^* Y9 X% R# ]1 t+ f
之前做一个项目,串口收发频繁就老卡死,后来用了保守法度过去,近来有空详细分析,发现了问题,并解决,这里分享一下。
7 ^& B5 {, |: ^4 c0 @, C: a0 |: K. s8 _
这里不对基本操作分析,仅仅讨论一种方式,能让我们稳定的用串口,本文会对HAL库进行些微的修改。
W9 ]( l0 b7 I9 Q9 P. a: W9 g9 nHAL库的串口大致可以分解为2个部分,一个是寄存器控制部分,一个是数据传输部分。我接下来的只修改寄存器控制部分一点点内容,数据传输部分不变,任然采用其回调函数形式,下面具体说明。
2 y3 P I% Z0 Q一般来说最常用的是发送数据HAL_UART_Transmit,中断接收数据HAL_UART_Transmit_IT。串口其余,如DMA什么的不做分析。
4 m, \: Q, I9 T) h$ m) Y5 U/ B; S! n研究一下会发现,HAL库中强制对串口进行了半双工限制,其实STM32的串口是全双工的,很多时候卡死,是因为我们做了全双工操作导致的卡死(不是HAL_UART_STATE_READY状态)。5 w+ R* b- e$ a" Z
HAL_UART_Transmit修改后如下
! R7 t0 Z" l3 @- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
' p& V Y5 e @" h; q: W: x - {* T+ V7 `1 Z1 ]% d& T0 N4 n
- uint16_t* tmp;
$ Y+ s! C4 |; n - uint32_t tickstart = 0U;0 k, ? w; a% k4 D
l9 U6 z2 F% [' `) @8 J3 y- /* Check that a Tx process is not already ongoing */" T/ t+ g, ~7 t; z! W- K
- // if(huart->gState == HAL_UART_STATE_READY)
; H9 o5 y* Z1 g - {, d3 w# o1 V1 \# ^+ n0 p
- if((pData == NULL ) || (Size == 0U))% Z0 m7 G3 ~. ]8 _+ J
- {; [, [# k* Y* T7 X' d! K3 J2 M f* D
- return HAL_ERROR;
) s. g+ a0 H! T$ [* q9 N- r1 k - }' I( z# }: E. l5 i
. Q8 w- w2 ]# o3 F* ?, h- /* Process Locked */
1 c) T, G6 w' h' N: j2 h% ? E. G' y' p - // __HAL_LOCK(huart);
. Z- [2 R' A- c |
. E3 h/ K6 y' J A- huart->ErrorCode = HAL_UART_ERROR_NONE;
5 d- W: `& T' ~5 ? - huart->gState = HAL_UART_STATE_BUSY_TX;1 P1 U: b, h9 I# O/ C
- , h9 `5 K+ u6 w9 s: K# p' H2 G F
- /* Init tickstart for timeout managment*/
6 h7 y1 @- u& v& V0 V6 ] - tickstart = HAL_GetTick();
+ V" i' M( d5 t3 F5 h5 L - / _6 v) S0 N$ @- t: W1 x2 Z$ c
- huart->TxXferSize = Size;3 V% ]$ R' A$ o' c {
- huart->TxXferCount = Size;
) y0 J- W% a- V1 L* Y - while(huart->TxXferCount > 0U)4 l; n% ?: ^. y3 l
- {0 c* v, G# Y1 C4 d8 }: D0 l1 `( G; T
- huart->TxXferCount--;& K) J: q/ W4 S+ u- r2 _ Z
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK): o# X8 z( n, c, R8 c# \ i% P( y
- {
9 a/ Q' c6 k! D& Y$ k0 Q7 M - return HAL_TIMEOUT;0 P" a( g# `( [! W" H. Y
- }# y: c- F- G' ?' g0 [$ r
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))0 T; b- C! U: N2 ^* i+ S' L7 ~
- {2 K: i0 u. h) t) f1 X
- tmp = (uint16_t*) pData;
9 R' `8 v, C* p6 B7 Q3 E" b - huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
* R! ^% P( ~; K+ N - pData += 2;
( O: u0 o1 Y4 h - }6 j' l9 b5 h2 _& T$ q* H' @
- else
/ d0 j) |2 j0 I- f - {
' y, y& P( m8 {% p4 B9 G - huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
# J( J/ {6 T9 f! v+ ]4 I2 W, E4 N$ r - }
' ^ `1 B _. R9 F# r - }9 |1 e- I- M; }! d% [% W
- if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK): ?4 f% y7 i# @; j$ c
- {
7 ?5 s% S+ N8 H5 o, c, e; F! m - return HAL_TIMEOUT;
3 w Z O) P7 L/ z( V+ U4 H! b - }5 O2 u" T7 } ^0 o; P1 i' p( A
- ; l O' d% B) `7 k# a9 F# X6 }/ H
- /* At end of Tx process, restore huart->gState to Ready */( X" w/ Q |3 i8 c
- huart->gState = HAL_UART_STATE_READY;8 R! m+ \" k+ ?% Q! g
- * N5 O4 g1 j& h
- /* Process Unlocked */2 k, a5 [4 k2 W' U7 T9 p: r
- // __HAL_UNLOCK(huart);
3 f( I2 V5 T7 ~# S' J - * ?/ N8 y0 O2 V; a* i0 v
- return HAL_OK;5 j% S+ d& i% V: l6 k) ^
- }5 a3 d Z. ^7 | S" T
- // else) ]" [. c! h* T2 r3 `
- // {8 Y$ {$ I! N2 C- [) r- \
- // return HAL_BUSY;4 W1 r! s8 H9 `. |) q
- // }
5 F! r* {2 o" n7 L, ~9 X - }
复制代码 8 [) q/ v1 w; v2 R9 g: n$ }
HAL_UART_Transmit_IT修改如下,除了祛除限制,还屏蔽了中断使能,这个后面再说原因
. O! ~! z P& z4 I' p2 h- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)# a+ {( H+ H3 Q8 w
- {
7 ^0 I1 W$ e9 y1 X3 Y5 e - /* Check that a Rx process is not already ongoing */
, m! F) p* o) I$ [ - // if(huart->RxState == HAL_UART_STATE_READY)
4 h. p5 i0 _' H2 @- R# Y& \; N$ | - {
8 z0 J0 b7 R( |7 G7 x8 | - if((pData == NULL ) || (Size == 0U))
' L9 h F( p7 V) u$ a - {( a* ]$ _* q0 ~! t' f$ j0 `
- return HAL_ERROR;- m6 R1 Q1 {+ @- W, ~) Q
- }# o1 z, k; i; q, H
- b" q) `2 ?7 J# w- /* Process Locked */5 y* K o M5 j: o) ~
- // __HAL_LOCK(huart);
% f+ }7 b. z9 f& s% Q" S, y/ T& M - ( [% ]* c( o' L. c# \+ K5 w" `
- huart->pRxBuffPtr = pData;, A1 ^. I' Q( j
- huart->RxXferSize = Size;0 V7 t7 a$ T. E6 I# m6 `
- huart->RxXferCount = Size;, o' `2 C. @! {) w
5 _; ?, p+ ]' N8 P- /* Computation of UART mask to apply to RDR register */
5 b9 ]$ t2 ?, N( s, U( }+ v8 A - UART_MASK_COMPUTATION(huart);* ]- _7 a# i( s! k" Z
0 ]. B, L9 N* {2 I- huart->ErrorCode = HAL_UART_ERROR_NONE;
& @! v0 v0 N0 B8 `0 W$ E+ K - huart->RxState = HAL_UART_STATE_BUSY_RX;
4 y" n) m8 z3 I6 O: O9 t4 ~$ O; |: y - % D9 r4 q4 P1 o( {# M
- /* Process Unlocked */
6 J5 k* i! U# ?1 M - // __HAL_UNLOCK(huart);
+ G* g0 J. \8 |5 t; N
3 W! V) l9 d5 Q! j- {$ n9 M- /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */9 d: P M. ]/ P' I. D. ]' b
- // SET_BIT(huart->Instance->CR3, USART_CR3_EIE); l V* `7 n) Z
- 9 {/ w: @; l1 c2 H$ i. E
- /* Enable the UART Parity Error and Data Register not empty Interrupts */
. _; j/ _( z) e ?' Z0 |; m( | - // SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);! o* \/ r4 c0 c5 \
- ; D( F3 C4 s2 L$ x" W8 O4 s# ~
- return HAL_OK;
# T* J+ Q, P# X8 N, @* A - }
8 r! Z' }- R% u0 j2 r - // else
P, {- c$ S% w* `' k - // {
4 @; G: V2 }- C1 C- B - // return HAL_BUSY;2 k5 b7 ]1 J4 \
- // }
* O) k3 i+ D- \7 a7 d9 S" K - }
复制代码
- W: w1 A# @" ]1 ~; o接收中断,屏蔽了清除中断的两行,中断开启后就不再停止了$ {+ W" }4 o* ]" I- ?. n, B
- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
* h3 t& C9 }& E h2 [ - {9 x. L! _* n6 y e0 W; o
- uint16_t* tmp;" l( P, t' G$ X6 S6 `8 L
- uint16_t uhMask = huart->Mask;' K! f7 m# ~0 I {8 N9 z/ F- G3 t7 c
- , O$ V' l! \. v$ q* ]
- /* Check that a Rx process is ongoing */$ y' I' l+ o, C0 S# V5 q
- if(huart->RxState == HAL_UART_STATE_BUSY_RX)$ i/ f' m- ~9 m( Y! s) |
- {
/ a3 D& g/ ~" _6 ?2 V - % H3 [1 Y* z2 y) l
- if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))/ s# @ X3 m5 B' I6 O {" |
- {! C9 I- g5 V7 i+ n/ t+ m' W$ E
- tmp = (uint16_t*) huart->pRxBuffPtr ;
3 o8 p+ c) O. Y# t: A5 f - *tmp = (uint16_t)(huart->Instance->RDR & uhMask);
9 ^4 j' O$ g7 s! g S$ y - huart->pRxBuffPtr +=2;
' p! s! \0 u% r& I6 m - }! F4 I* P. r# B* o: u2 j
- else; W3 a6 L& y% \' {$ `
- {( N, k/ A* O# q1 {9 @: j
- *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
z* q; R# [/ s3 I' O& H, N - }
: s$ t# ~" E) K8 o6 L/ W
% w/ y( @3 ^! r2 {- if(--huart->RxXferCount == 0)
: A) Y* c l- @" ?& G - {
5 r# T" d* ]3 c6 Z- A - /* Disable the UART Parity Error Interrupt and RXNE interrupt*/$ l# R( ]/ w+ n9 O1 @ Q
- // CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));8 r" W9 e2 v6 o. i
- ' v% x1 ]' z0 ]( n& V" ]. H
- /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
+ v) x/ Y% q# U" Z# v - // CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
) @/ d1 ^' ?+ R7 j. l - ' i3 V) W" ^! K/ I4 s% D
- /* Rx process is completed, restore huart->RxState to Ready */: q: F/ G8 N/ m. k8 X: u* O
- huart->RxState = HAL_UART_STATE_READY;" v: Z* H( L3 j
- . ~3 _. p4 J/ J2 k' H i! J9 m
- HAL_UART_RxCpltCallback(huart);( T! j2 Z& I# \
( t' Z$ s, l3 Y6 U" y2 w- return HAL_OK;
: v# p/ e) @, J$ R6 [% Q+ m% H - }
+ S" r/ x6 E& H; U7 M p" H! C
% Q& C* W' m3 q; n- B$ d5 H) N- return HAL_OK;
1 S/ D' j: q1 m! M6 X8 B - }
2 X& @+ V9 |8 a& M" U) q - else$ W2 E! M0 P8 q+ t
- {" }6 \: p+ b9 s
- /* Clear RXNE interrupt flag */
& S: T/ j5 e4 a1 d! I - __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
: K' @' j2 h( `! O# g5 V9 q, r - & \7 u6 q+ X# F& W# |3 f8 B
- return HAL_BUSY; ~# H# j% M& R8 {9 g. }6 y, h+ Z
- }# C' W- ?+ {; C' E
- }
复制代码 9 a& g4 a: z2 i! U+ H) f4 ~" j9 A
在usart.c添加如下代码,# b+ ^- Y' `3 v$ @
StartUART_Rx:开启中断,同时开启HAL数据流。本来调用HAL_UART_Receive_IT会重新开启中断,不过前面我把它屏蔽了,这样,这里初始化开一次就行了,上面代码中关中断部分已经屏蔽了
, N. A: @6 \- Y" }" LHAL_UART_RxCpltCallback:接收中断回调函数
7 ]3 L8 L# U$ v+ D. n, o/ lHAL_UART_ErrorCallback:错误中断回调函数3 q) M" j- f2 `% n" w
: |7 e5 l0 L" e; j- /* USER CODE BEGIN 1 */
) [; L4 e. D' v/ d+ s3 h: E - uint8_t a=0;. m' Q0 D% I$ h% T2 q) X
- void (*UART3_Callback)(uint8_t)=NULL;
# j3 j! d6 k% |# P' Q - void StartUART_Rx(UART_HandleTypeDef *uartHandle)9 s6 @: o% u5 d% Z; ^2 @
- {
$ d# L" R5 a& r% t - if(uartHandle->Instance==USART3)+ g3 g3 ^$ T3 J
- {% A" | X, V L
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))* M# K6 l, u& T4 c. M/ @. `
- {, t- U3 u% \5 g4 r o( M/ ]
- assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
. n* g9 B/ T9 P) f1 P+ @ - } 0 P1 G4 z5 U/ p! _7 M+ n( Z) c
- SET_BIT(uartHandle->Instance->CR3, USART_CR3_EIE); % V$ d$ O8 w4 r# b, g/ {% H
- SET_BIT(uartHandle->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
% r- L1 S8 M! b! o6 j - }, `" u; [) S H# J, Z6 f" C# |; N/ e
- }
: @: [- {% A5 ~. V - 2 F6 x2 L) X* ?3 _, I
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *uartHandle)
8 N6 S" ~. g7 V. B. a! f - {( a' }1 f7 K6 Q+ t7 U
- if(uartHandle->Instance==USART3), l4 l5 E9 N* b9 _6 `, ~
- {6 V' \+ V4 @5 h" a0 I8 v7 M
- if(UART3_Callback!=NULL). W$ C( |+ ~1 R g; Z( ~. p6 v
- {
' S9 E/ R" Q" c5 n7 C0 y/ s$ w+ C- o - UART3_Callback(a);6 I2 C2 n, M8 H9 J! q: ]% M5 _
- }) I7 m7 e4 T: h' A8 e" `( x
- if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))! V7 N) C+ e& _9 M
- {9 ]( Q" K) ]: ]: X3 v
- assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
; Q5 ~9 G( o3 L# t - }
. p0 N& e/ A1 x" g2 z - }: a; D; [+ i2 v$ T, h$ {, U7 F
- }
# H5 i' M& ?8 _5 p7 @( g - void HAL_UART_ErrorCallback(UART_HandleTypeDef *uartHandle)
5 l5 }9 f) P* S/ e - {: \1 G' s% B% K4 @' P) e
- uartHandle->RxState = HAL_UART_STATE_READY;4 v3 m+ H( {3 H# i% r. k, a3 }6 H
- if(uartHandle->Instance==USART3)
7 }7 A3 q& t7 k1 f - {
& j9 Z, q' |$ N! C+ f2 a - if(HAL_OK!=HAL_UART_Receive_IT(uartHandle,&a,1))) R$ R; s$ ]2 G- m
- {0 n& J c( w9 o7 x; b% H
- assert("HAL_UART_Receive_IT",__FILE__,__LINE__);
( q2 ^, p* J1 ]. \ - }
" _# ~ u) M( O. V, ? - }
1 T& V1 n: g: h/ J4 X% N* I5 J - }
/ B' h) t9 V4 h- } - ! S/ Y: n% T& P6 V3 g7 ?$ z7 T
A9 ^. L$ U- F6 r
' b- ]3 }8 T2 ? c* E- /* USER CODE END 1 */
复制代码 0 o2 U% j( K5 K/ \/ Q' ^
/ G L. M2 ~: `' o2 q: c9 K基本这样改就能应付大部分应用了,
, V9 D; u4 T/ i3 p. `7 z1 m当然有另外一种方式,就是开一个检测任务,定期检测串口状态,如果发现串口卡死,则重新初始化即可。 亦有人用了DMA+空闲中断的方法,不过感觉是不太稳定的。 估计HAL库其他驱动亦有类似的情况,自己把自己卡死。 改库有风险,跟风需谨慎! " {7 Y" M- t) Z( U) t; C& }
2 ~$ k+ ]& w7 V2 E
8 d% Z0 I8 Y5 w+ v' Z K- n h
, u4 p2 i; u% Q |