前言
7 L* O+ X7 g7 G用STM32CubeMx生成初始化配置代码是十分方便的,但是在处理多个项目的时候,就会发现,自动生成的中断函数调用的都是同一个回调函数,在这个回调函数中又要根据不同的句柄来处理,不同的项目外设又有所区别,还是要花点时间来进行移植。
. |( @; g* O% t3 k& _/ e$ t比如说,我在项目A中使用到了串口1、串口2、串口3,但是在项目B中,我只使用到了串口2,并且在项目B中,串口2的功能是项目A的串口1的功能,在这种情况下,我就需要再定义一次 HAL_UART_RxCpltCallback() ,然后移植对应的功能。当然也可以重新 define 一个宏,来对应所使用的串口,这样移植上也会快许多。但是假如,后面新来了一个需求,需要实现项目C的串口3的功能,原本只要添加对应的头文件,模块处理这些外设是最简便的,但是这时候会发现,项目C中也定义了HAL_UART_RxCpltCallback(),这就需要人工去移植这部分的代码了。
" N/ }. M; Y8 i0 z而我就在想,项目C的串口3是已经实现的模块,且这个模块是单独的.c文件和.h文件,并且不和其他的串口耦合在一起,这样的轮子造起车子来就快了许多了。但是找遍全网,会发现,关于HAL库中串口中断的教程都是基本的实现,没有更深层次的解释了,在学习MEMS库的过程中,看到官方的代码,在使用按钮触发外部中断的时候,竟然可以自定义外部中断的回调函数,进行一番学习之后,终于掌握了自定义中断回调的方法,不仅限于串口中断函数的回调,其他的外设都是相类似的,都是可以自定义中断的回调函数。 b7 ]$ S: F6 ?
高手可以直接跳过第一节的内容,直接跳到第二节看如何进行操作。; b4 C. I5 D( t y& Q
一、HAL库的中断实现" [1 G3 d4 q. ]. \4 ]+ Z% ~
中断的原理网上到处都有,也可以参考正点原子这种开源的教程。$ G, P. _' ~1 v% z
我们正常在STM32CubeMX配置好中断后,通过 GENERATE CODE 生成代码,在 stm32f7xx_it.c 中,就会自动生成中断的处理函数,比如 USART2_IRQHandler,可以看到串口2的全局中断仅仅调用了 HAL_UART_IRQHandler 函数,该函数用于处理UART中断请求。7 I6 }1 j3 ]" x0 e; q* ^5 E
. y' \( g( c! ^- i2 T# R4 Y
- /**' O/ g- ^; L8 m) {
- * @brief This function handles USART2 global interrupt.
% g, i# A' M$ U - */
" L1 E# N* }, l" a: J) F7 g3 [8 I - void USART2_IRQHandler(void)
3 O' t5 b3 T9 y4 K! G) O - {" ?, u. y; L! U; }
- /* USER CODE BEGIN USART2_IRQn 0 */6 l, I) D, L9 h6 x
0 E) V. k7 w( j) L- /* USER CODE END USART2_IRQn 0 */6 h. f, \/ f; ~6 O' z* L
- HAL_UART_IRQHandler(&huart2);+ L' i( m4 C2 J9 j' V/ K) B2 g
- /* USER CODE BEGIN USART2_IRQn 1 */
5 O7 l" q. s8 R( z6 a! F! f. x - ! ]5 _( N& ^0 c" d6 G7 _
- /* USER CODE END USART2_IRQn 1 */ R8 o2 _) p' _1 |
- }
复制代码
' p* i- u, `4 `; N, n我们进入 HAL_UART_IRQHandler 看看里面具体的实现。这个实现虽然很长,但是逻辑比较简单。首先判断是否有错误产生,没有错误就会调用 UART_Receive_IT,用于在非阻塞模式下接收大量数据。如果在接收的过程中有错误发生,那么就会进行标志位的处理,再下面一点就可以看到 USE_HAL_UART_REGISTER_CALLBACKS 这个宏定义,这就是我们自定义中断函数的关键了。如果我们定义了 USE_HAL_UART_REGISTER_CALLBACKS,那么我们通过注册自定义的中断函数后,在这里 huart->ErrorCallback(huart) 就可以直接跳转到我们的自定义的接收异常函数了,而不是 HAL_UART_ErrorCallback(huart) 这个HAL通用的接收异常处理函数。
# F* O1 i* R" C3 M) q! l% J注:F7的板子在公司,写这个教程的时候手头只有 NUCLEO-L152RE 的开发板,使用的库是 STM32Cube_FW_L1_V1.10.2。 STM32CubeMx生成的代码有所区别,但原理上是一样的。F767ZI 是通过 huart->RxISR(huart) 这个方式来调用中断处理函数的,最终调用的是 UART_RxISR_8BIT 这样子的函数。当然后面推出了新的固件版本,也可能导致具体的实现有所区别。' A* `7 O9 Q- L. a" S: W% o
* e* J; ^" ]2 y% I6 c) E
- void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)/ x5 D, l& g% }4 `5 ^- K
- {
3 d% u3 h$ m( A! P+ V - uint32_t isrflags = READ_REG(huart->Instance->SR);
3 b5 P$ k( \: I, U. w7 y. F - uint32_t cr1its = READ_REG(huart->Instance->CR1);; Q! i' T# s/ i8 H- ]( S
- uint32_t cr3its = READ_REG(huart->Instance->CR3);( R% e6 U' \8 M6 M/ c3 I
- uint32_t errorflags = 0x00U;
+ v j* w' U6 k4 @7 i - uint32_t dmarequest = 0x00U;
* L5 k1 N8 H- A! j8 z. |; y& [ - $ D" t5 p3 f7 H3 g* k$ k. n
- /* 如果没有错误发生 */% _, N$ K! y- M% z) g7 S) l" X- L
- errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
. O2 }7 v* F4 H+ w+ n" H - if (errorflags == RESET)
& N0 G x7 Y+ B - {" ^! d& i" ` k4 K
- /* UART处于接收模式 -------------------------------------------------*/
1 n- J( l* H7 L3 q - if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
1 [+ C/ ` S4 M! R - {
: r! q/ [6 G( S( x - UART_Receive_IT(huart);1 Z5 k8 P6 w y* h) \
- return;
" m+ |( g7 n' |* Z0 Z- f - }4 p' i9 L# c8 z+ d* A
- }2 W+ B6 f, c4 q, r8 ~3 Y
- N' U; @9 \; f, K! n( _- /* 如果发生一些错误 */" T P1 j, W1 O; t9 c+ ^( c
- if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
/ k" M% V) K8 S7 d3 G: n0 c - {
& N; H. b' b& v+ l3 W# O6 ]1 b U - /* UART parity error interrupt occurred ----------------------------------*/
3 z6 r/ c- A4 g - if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))) x# W. a8 u2 q9 `- d8 J
- {* c( f2 W# h# U8 T2 z9 E
- huart->ErrorCode |= HAL_UART_ERROR_PE;
: \6 r. n# z$ z; g% } - }
' v5 f3 X/ b6 J1 r. v, {% R! F - ! t f. l; r$ K, E; [: L- V
- /* UART noise error interrupt occurred -----------------------------------*/ M& ^7 M% o* j1 E& d
- if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
7 j6 |. E0 w3 l, t - {1 K% M" t4 [( V% S0 n0 X
- huart->ErrorCode |= HAL_UART_ERROR_NE;5 M3 g3 j( z' y5 x6 ^
- }
) z4 r/ g- |0 r/ g9 p5 r1 |- ^
) G5 {( O& U! r3 ~! h" ~- /* UART frame error interrupt occurred -----------------------------------*/7 W7 Y+ r/ Z$ K/ j
- if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))# v6 q' e O; A$ _
- {5 K, ~0 y) l; k5 o# Q# A/ |
- huart->ErrorCode |= HAL_UART_ERROR_FE; t8 ?7 t0 z' L
- }
4 b; {+ b- x+ x+ l! O" f. k/ s - ! r& S0 v+ {5 Q8 Z+ k" |
- /* UART Over-Run interrupt occurred --------------------------------------*/& Q( V& q9 n: E. J) U: T/ A
- if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET)))5 h) B f" ?+ x7 I. y
- { M T Q$ g& J+ P8 L+ U3 T
- huart->ErrorCode |= HAL_UART_ERROR_ORE;
: ~3 t* q; @5 P" T0 Y& ]$ } i - }& V8 }% H3 a7 m7 }$ [. i, b
- : u6 ^% h/ A; C1 `( D
- /* Call UART Error Call back function if need be --------------------------*/
2 ]; M+ m2 s( @) p - if (huart->ErrorCode != HAL_UART_ERROR_NONE)
A8 k6 i2 K. n/ p8 N- a, W - {6 a# M; r/ B Y
- /* UART in mode Receiver -----------------------------------------------*/
8 m' U; u, j7 k! ^" ? - if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
! M/ O1 Q+ U' z, ~1 Y - {4 V; J u' T& ]1 |2 |! u
- UART_Receive_IT(huart);( S( j0 o( K6 o9 h W, Y, y
- }
" `+ Z6 q4 F m3 p& t5 Y7 t
9 a, E5 [1 O& v# x: w5 C3 K' r7 K- /* If Overrun error occurs, or if any error occurs in DMA mode reception, `. ]7 c7 J" C( e" E3 w
- consider error as blocking */
5 e* N# d, n2 E0 j6 b( Y - dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
8 i7 r1 t- c1 S2 `; e - if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
0 B h9 I1 `1 D" G; X# U$ S$ M3 e - {
L; m6 g' d+ t) k - /* Blocking error : transfer is aborted: X2 ?: V t% `- i
- Set the UART state ready to be able to start again the process,
& X3 @- q# o9 ?2 a - Disable Rx Interrupts, and disable Rx DMA request, if ongoing */" \% u/ N+ r4 K7 \& ?0 s. ^1 S% x' O
- UART_EndRxTransfer(huart);$ b* K( a" J# s$ k: S# _8 H9 c
+ R; \5 x. R+ g. B( ?- /* Disable the UART DMA Rx request if enabled */, n+ U+ z: ~9 R: z3 I
- if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
1 b* h) w- J9 ?. M - {
6 s+ }( x* V9 U8 Q3 \! H6 a - CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);6 L/ x" R' K6 m% D! ^" b/ ^. P: o- G
; m) h' M' K1 y* T- /* Abort the UART DMA Rx channel */
2 }7 I" ?6 c3 }7 v$ m, r9 I6 b - if (huart->hdmarx != NULL)
/ M& E" b% a9 @ - {' ?2 f( `# v b/ j( @/ T2 M
- /* Set the UART DMA Abort callback :
: I* T0 s4 a$ B. `+ O& i% M% k/ P - will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
6 t# F3 k/ z6 b7 D0 ? - huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;) B+ t1 _1 j- Z5 {. O
- if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
" }% @; p0 l4 f6 b! ]3 K$ m7 h6 x - {
! y" R1 Y8 d1 E4 o1 m% l# Q! h# X - /* Call Directly XferAbortCallback function in case of error */
2 f" u* ?# R$ d1 f7 t7 k5 ~ - huart->hdmarx->XferAbortCallback(huart->hdmarx);
" t! l1 o+ A6 \/ d3 [# l - }
8 [/ A, g1 v# \* j& V; H, n - }/ w H) t% b( _, t2 t2 K2 s; w
- else! [3 {3 @* o# P3 b6 c0 e; z
- {
2 w) w7 a' `5 u. m( I! K - /* Call user error callback */
: R4 G, R2 }8 s& }( `7 B& W' p+ V# c - #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)' E+ W' \# E' M6 c a
- /*Call registered error callback*/$ t! v0 W% Y- i N, U ]
- huart->ErrorCallback(huart);' i' K* K: ?2 m+ K0 a! @5 ?- z3 K
- #else
) C; s4 }/ `! }& ?- q6 m2 X7 ? - /*Call legacy weak error callback*/9 o6 D. F% T8 H* Y
- HAL_UART_ErrorCallback(huart);
& W; W5 v: K8 i- D4 W& a - #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
L/ F! G4 Z# T7 _. r( F- O: C* R - }
' e( C# S4 w/ ?% P3 ?, [ - }
* \0 h( h, V3 L1 N1 y* V* ~ - else. c* O. @5 ~( d+ `9 h% {+ B) S
- {
( Q, z3 g* p) e8 M- l - /* Call user error callback */# H* h4 A1 g. q" B# K+ ~. C
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
7 i, Q8 j3 f- ]0 B - /*Call registered error callback*/: l4 w* p# K+ \5 M% c
- huart->ErrorCallback(huart);/ K* R7 y ~: Z9 F l8 P
- #else
) w' a. n8 T S4 d - /*Call legacy weak error callback*/
1 u& _* f4 B( z O8 I$ j - HAL_UART_ErrorCallback(huart);3 T6 Z, B/ r# [3 u- P
- #endif /* USE_HAL_UART_REGISTER_CALLBACKS */6 Z' o5 r, V: ~
- }
& s' [- [, G# L ]7 j - }" E6 i& r' N! z( ?
- else
5 c2 Q: Z1 a ~6 l" I - {
; L6 E% S- F& `7 s - /* Non Blocking error : transfer could go on.
/ D$ Q/ S1 F& @7 g G - Error is notified to user through user error callback */
& f( m. Q1 D8 X7 H0 B9 i* ?. w8 J - #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
) h2 x; V6 {* y% e8 C - /*Call registered error callback*/
+ t% b7 w' X9 | n5 u8 I - huart->ErrorCallback(huart);& T% C" [. Z# A6 W8 U
- #else
7 T2 c# H6 ^+ R4 E/ I* k - /*Call legacy weak error callback*/
7 F; v, d1 V) U - HAL_UART_ErrorCallback(huart);
[- L6 L! d& Q/ `* p - #endif /* USE_HAL_UART_REGISTER_CALLBACKS *// @5 t& S8 [/ C1 x
- ! i x+ A3 ^% u0 z M) z- w1 g
- huart->ErrorCode = HAL_UART_ERROR_NONE;4 x2 l G" d( g1 E' M7 ]4 n$ {
- }/ G N v8 X% u4 L* P/ n
- }6 v* f3 }0 J8 F, ]
- return;
@. W& k( M! x' V4 d - } /* 如果发生错误则结束 */$ t2 O/ e1 a* L# K7 V6 B0 F9 x
- 7 r! i$ b. r c8 M) d; |
- /* UART 处于发送模式,准备在非阻塞模式下发送大量数据。 ---------------------------*/9 R! F$ u$ {8 K0 C9 L- o# V. x3 \
- if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
7 d/ i) ?+ f$ Y7 s - {/ v) ]8 f* B; q
- UART_Transmit_IT(huart);/ ^& w5 w( m9 _" \- E
- return;
' o2 e" n4 h/ S! E4 R7 r, J' Q - }5 i: z1 G" o9 k' }4 E5 x4 c
7 j7 s6 @& |) p$ a- /* UART 处于发送结束模式,在非阻塞模式下结束传输。 -------------------------------*/$ n/ \ h. f9 H& c; t: }
- if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))' H3 Z* ^5 l7 q* S" b
- {+ j* p$ V, ?; f4 m, R% u( Q
- UART_EndTransmit_IT(huart);7 p) c' F: z: p) l! _+ Z2 H
- return;: N6 X2 U* @( ]- U3 B2 ^% e
- }
0 i! R. h0 x- Y9 [" A' l4 S2 D2 l: B - }
复制代码 / N' e8 c* U( `; A4 g. t
然后我们看看没有接收错误发生时,调用的函数 UART_Receive_IT(huart)。这个首先根据数据位的长度来进行接收数据,(9bit和少于9bit的处理是不一样的),串口数据就保存在 huart->pRxBuffPtr,每接收一个数据,那么 huart->RxXferCount 就减一,直至为0,这时候就会触发接收完成的中断了。至于需要接收几个字节来触发中断,就取决于初始化时候的设置了。
( m' G# K$ W) Q4 A) s | mhuart->pRxBuffPtr变为0后,会先关闭中断,如果使能了USE_HAL_UART_REGISTER_CALLBACKS ,就会调用 huart->RxCpltCallback(huart),我们就可以通过注册中断处理函数来跳转到我们自定义的函数,而不是HAL库本身的接收完成中断函数HAL_UART_RxCpltCallback(huart)。而如果我们使能了USE_HAL_UART_REGISTER_CALLBACKS,却没有去注册自定义函数,最终调用的还是HAL库本身的中断处理函数,这是因为在 HAL_UART_Init 初始化中会调用函数 UART_InitCallbacksToDefault,这个函数会将所有的回调函数初始化为其默认值。9 {( }0 k. F `( J M( z. ?' c
注:接收完成后,这个会自动将关闭中断,接收完指定的字节之后,还需要继续接收的话就需要再次打开中断了。- \! Y# f2 X6 |. r; U
% `* ?" d7 z: a4 L
- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)/ d3 z/ B3 K7 \9 j* |+ X
- {
. O6 T+ x6 `) ?7 T7 C% \ - uint16_t *tmp;
! v% B$ W2 R* n1 I( d J/ q) s" i - , L1 q: M) m% _# M7 s7 r# X1 B0 D; @- @
- /* 检查接收进程是否正在进行 */
) M$ y0 I$ A4 @7 }) } - if (huart->RxState == HAL_UART_STATE_BUSY_RX)+ F' G. k4 }7 @% d( r) |
- {' u7 v+ P/ E' q5 b* `4 M
- if (huart->Init.WordLength == UART_WORDLENGTH_9B)/ W. n* h' o: `% r8 G0 @
- {
8 B* Y/ d( Q3 R, W: Z8 Q- b* s& P - tmp = (uint16_t *) huart->pRxBuffPtr;
( M# G2 m$ h. l7 }; v - if (huart->Init.Parity == UART_PARITY_NONE)/ d( P1 C4 L: o/ U
- {% d5 v9 B% @5 N8 ]! l% u
- *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);& _* m: ^ E; Y; m. y
- huart->pRxBuffPtr += 2U;3 X) G' y! A% |% C$ ^
- }- M( Y% \* v* X% A! }( v8 t2 D8 t* a* J
- else
! }/ \, y; R$ T - {
6 @ q% Z- Y! c& E" j - *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
: t6 q( ~- L- j3 A0 i3 e1 |! r" v - huart->pRxBuffPtr += 1U;
; P4 S4 \% ^$ b9 z: Z" P - }
: _# y* _# X R& w5 E7 |3 P - }
7 H, r! U, Y+ x% x- G& B - else4 m n4 m1 M( Y' s; }' d7 k5 T
- {
- b8 @% \ T# q( A( U* p" | - if (huart->Init.Parity == UART_PARITY_NONE)
5 G- L; L4 k! k7 r! v k - {7 X6 ?! x u# d6 z2 F" t
- *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);2 R$ r6 [# I( T2 p! i
- }8 @. r+ x0 r9 l. [8 [
- else) H* O9 s0 e5 D( T% p
- {( Y- ^+ E+ P# H. [* I
- *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
. I( F c. w# P- a5 I - }
/ J2 n+ K" L' i# I; ]$ S - }0 B( H3 G$ R7 f' O
) ]4 r6 m# p8 q& a. t' Q& f- if (--huart->RxXferCount == 0U)2 N; h& ?# @! [* b7 D
- {3 x" E; Q9 q( o, L2 p4 f
- /* 禁止UART数据寄存器不为空的中断 */
$ Q& P& W+ o; G: \9 _+ X: W _ - __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);# L* B' ~1 U& ?+ H9 }
S( z- b# S3 |7 u& B1 U N- /* 禁用UART奇偶校验错误中断 */
' c# c' z$ U. f - __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
! U1 a7 l" |! D0 ?7 U6 A - . ~: l0 v& o% V$ R% \
- /* 禁用UART错误中断:(帧错误,噪声错误,溢出错误) */1 l0 ^- Z1 y" |9 ]1 n" d1 [
- __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
2 s4 d: m g2 V8 F - $ [( ^5 S* q5 [1 r% N5 L+ C
- /* Rx进程完成,将huart-> RxState还原为Ready */+ e. p' n1 L5 A/ Q3 i
- huart->RxState = HAL_UART_STATE_READY;! S. `5 V- q& D$ L
- * e ~' Z4 b: I7 m0 \/ ^7 g. v
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
; U" Z9 j1 Q( H X2 B8 X - /* 调用注册的Rx完成回调函数 */
; i9 n$ n, b6 U# S, ` - huart->RxCpltCallback(huart);. G3 k4 q; i! C2 x
- #else
( G J3 Q$ @ u - /*调用旧版弱Rx完成回调函数*/
) R( Y! k6 S1 T' x# A - HAL_UART_RxCpltCallback(huart);
: w* L/ v7 }) x6 U - #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
% V `5 n( D# e* }9 F; D, s
+ t) |3 ]4 T. B& _0 v& v- return HAL_OK;; Z: v: s; k( q' \7 k0 I. [* y' [
- }
6 T! R/ y6 a0 H0 p$ Y - return HAL_OK;
0 j! j- y6 f; `2 X - }
( f& F( c5 z" D6 k* e - else* R, a! P, F7 i4 s
- {* ~8 o) i7 q( J( |+ K
- return HAL_BUSY;. W% J2 C/ Y1 [7 I
- }
' g$ @9 J5 s7 i5 T - }
复制代码 # Q! X1 k& ^2 X# h8 {
二、使用方法
0 T3 z1 c) l% e% k* }6 I原理性的代码解析已经在前面一章节解析了,这一章节就是讲要如何实现,其他的都不难,重点是要知道 USE_HAL_UART_REGISTER_CALLBACKS 这个宏,其他的不懂的地方就可以根据这个线索来一步一步的追踪了。( c5 m# \: I5 J3 S2 I: p3 ?3 s$ {% o
' i6 s, Z2 h8 C6 s9 T
; x* Z5 M% r3 r: K. A0 J* c6 A0 N$ R, Z3 r
图2.1 设置串口参数2 ]; T6 s. b$ @! X& x
, e& t# b9 ]) b+ ^- v" C
7 n ~* f& U& X% y( a( o
$ ^. T y2 U' l* d3 c2 I/ w' e2 B) V图2.2 使能串口中断并设置优先级
" \/ B9 p) ^7 J& a" K0 P. o6 P8 ^; j/ r0 b6 G2 R a
前两步都是设置串口中断的设置,按照常规来设置就可以了,重点了如何使能 USE_HAL_UART_REGISTER_CALLBACKS 这个宏,这个宏在 Project Manager 中的 Advanced Settings 右边的 Register Callback 中,选择 UART 为 ENABLE ,注意不是 USART,别选错了。* y' H$ I* F3 i3 b) `
当然也可以在生成的项目中搜索 USE_HAL_UART_REGISTER_CALLBACKS 手工改成1,但是假如后续又通过STM32CubeMX修改配置的话,再生成代码,会将这里的配置覆盖过去,如果没注意这个点的就很容易出错,建议还是在这里修改比较稳妥一些。: C0 }- }# [$ \1 S H r& @& w/ |
5 s% ~# h/ O0 ?2 B3 C; ]
5 l' L- j+ I! {& B' E: e
0 m: h' f, \1 E) {' h5 g9 f代码生成之后,默认是没有打开串口中断的,通过 HAL_UART_RegisterCallback() 自定义回调函数,再开启中断就完成了我们的目的。
) Y1 P8 {$ I2 J. p4 l! u( b我们可以专门在一个.c文件来自定义接收完成的回调函数并打开中断,在main函数中调用这个初始化函数就可以了。这样就能和其他串口解耦,移植起来就很方便了。比如我需要指定一个串口来使用 Letter Shell( github上的开源项目,我的文章中也有这个教程),那么我就写一个 shell_port.c 文件,仅仅需要通过宏来指定使用shell 的串口句柄,其他的都不需要修改,直接添加这个 c文件,在main函数中使用 userShellInit() 来初始化,就能直接使用这个扩展模块了,相比于其他的方式,这个移植过程可以说是相当简便省事了(其他的形式需要在 usart.c 中定义串口接收的数组,再定义接收的个数,再在中断中移植自己的功能,再在main函数中打开串口中断,每建立一个新的项目都需要这么做,尤其时间长了之后,要配置串口中断的函数的时候就容易忘记函数名称,真的很烦)。' f( ?0 \6 p8 _. {
! K6 c; f* {3 C, t' Q F. |7 iHAL_UART_RegisterCallback()' j; y. S1 e% Q5 V: F
这个函数的第一个参数是 huart ,要配置的串口句柄。
# ]1 o r( {. d, U; a这个函数的第二个参数是 CallbackID ,不同的外设所支持的回调函数ID不同,对于串口来说,HAL_UART_CallbackIDTypeDef 列出了所支持的各个功能ID,我们要在串口接收完成后调用我们自定义的ID,使用的就是 HAL_UART_RX_COMPLETE_CB_ID 。/ g: n1 _, V' {1 V
这个函数的第三个参数是 pCallback ,我们自定义的回调函数,但是这个函数有要求,参照 pUART_CallbackTypeDef 的定义。返回应该是void,并且需要一个参数 UART_HandleTypeDef *huart。. B' }% z$ C" o& I, ?
1 W" ?% @+ N" m) h1 {& Z+ Q0 w8 Dtypedef void (*pUART_CallbackTypeDef)(UART_HandleTypeDef *huart); // pointer to an UART callback function5 H* H3 X7 `4 b6 Q3 Q
6 S7 ~6 P7 v: h2 O9 z注:这里设置的是每接收一个字节就触发中断,但是HAL库本身处理串口中断标志位后,会关闭中断,所以我们就需要 HAL_UART_Receive_IT 再次开启中断。, {7 j0 ]4 i; S) }- T
. _1 }, x" x3 p* M/ c& x2 W
- #include "shell_port.h"* l) \) x0 ~' k+ A! v4 A
- #include "usart.h"' l3 @& U- y( w- u
7 Y2 G: q0 E$ Y0 J7 q8 a" O- Shell shell;2 w4 X2 D) C" y8 s$ L( v3 R
- char shellBuffer[512];/ b& n4 `* e% g% q. [
- % ]" ?; y2 ?: `( V! Y! m7 `
- #define userShellhuart huart2 //shell 使用到的串口句柄
k5 x) M# T6 l- E - #define SHELL_UART_REC_LEN_ONCE 1 //串口单次接收的个数/ B" J' ], @, {- i
- uint8_t uartRecBuffer[SHELL_UART_REC_LEN_ONCE]; //串口接收的buffer
, d( o- j: y" v: [ - ; C2 o. Y0 V2 D- W Q& D
8 X5 I( Y& }! j5 @& ?- /**
5 O0 I! `& q( l0 y% W - * @brief 用户shell写, F4 C1 c- f* x# j% }2 V
- *
7 X) ]0 `0 v Y - * @param data 数据! K& R; @4 n! [
- */
# s3 i8 h) M8 d) J1 k+ p - void userShellWrite(char data)% Z/ s% [" v$ A) M. n/ S
- {' o- R: H! n( X
- HAL_UART_Transmit(&userShellhuart,(uint8_t *)&data, 1,1000);
8 B4 t* _' i- L - }
+ O0 }! P. Y8 {) o8 Y9 z* }9 I
" E e! R' g% U% ~8 y; a0 g/ h- 7 M5 G) X, X. M7 n4 B5 [" _
- /**
+ ?7 n4 B9 C. F9 d - * @brief 用户shell读8 r2 T% F& v& x. x
- * $ I& |: n0 e; T
- * @param data 数据
( @% h% [1 J. l% G - * @return char 状态' ^ R8 U! t" t9 H6 u" T
- */% r0 f, g$ q7 k- [/ G, o. `. @- I
- signed char userShellRead(char *data)/ h+ N) L/ F2 K ]
- {3 y( j F1 N: o+ r
- if(HAL_UART_Receive(&userShellhuart,(uint8_t *)data, 1, 0) == HAL_OK)
- P4 A& {3 O7 g* g& t1 I; ~; j4 z! A - {/ G) d- N- m. M( p9 Y
- return 0;: ~3 K; c) g* \0 ~1 I/ _$ ^; d
- }
4 u7 }& t' a5 R' B. [4 S1 R+ i - else
. y8 K, y) e# p7 x* v$ R* j/ Z' e - {( K3 B) G$ o& I5 [) q" m
- return -1; u) D( k0 q( ?5 U& P
- }# l, }0 S+ r4 D# \2 E7 g
- }/ A- [; d3 k& z$ `! U; Y' W
! h& n3 A G7 P- /**4 z$ I4 Z0 N) s# Y% I$ J
- * @brief 自定义函数串口接收完成中断 RxCpltCallback. z0 t$ u2 g$ E0 g* x6 K4 |
- */
2 G! \5 p K! Y& \ o9 Q, Z. x - void ShellRxCpltCallback(UART_HandleTypeDef *huart), ]8 \4 x8 q5 n, l9 P
- {) U6 a# o G' |3 d3 N
- shellHandler(&shell, uartRecBuffer[0]); //命令行处理函数$ H# A; L' m% O' Y
- HAL_UART_Receive_IT(huart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE);//使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
. y. ~2 T6 Q; K1 J0 Z+ n8 J3 V - }. }# d! ^( f* A8 v& f
4 m* `% }' O$ b# J% i# X2 A4 Z: v- /**- y) n d7 R7 A" M" O5 l
- * @brief 注册串口接收中断到自己的自定义函数# K7 w. y0 k; W
- */
8 }$ B% _+ z% w1 O - void ShellReceiveCallbackRemap(void)7 W) i+ l! B6 ~0 S
- {8 g6 ~1 |( N2 U; t
- HAL_UART_RegisterCallback(&userShellhuart,HAL_UART_RX_COMPLETE_CB_ID,ShellRxCpltCallback); //注册串口接收中断到自己的自定义函数2 o! {0 A% l& T7 _! b- l8 T& q
- HAL_UART_Receive_IT(&userShellhuart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE); //使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
! J/ ] k* z$ D3 e! K+ \1 M( H - }
4 w% c5 R0 `, w" k - 1 r* i' x' E2 e: n7 z4 |+ j
- / u7 n6 }& k$ [) `- X6 b1 [
- /**
7 n% w1 q& B/ g2 X: O - * @brief 用户shell初始化# A# j H% V9 M& G2 S
- * 1 p* L l3 ~" s Q: | G
- */4 ^" t! V- K0 Y% v8 e# p
- void userShellInit(void): p, z" F6 O# R
- {" Q. y7 Y% Z5 D& C: i
- shell.write = userShellWrite; //指定串口写入的函数
- T( K: ^; h, f2 H9 u& ] B - shell.read = userShellRead; //指定串口读取的函数/ M, Z$ n" D# e' {6 @
- ShellReceiveCallbackRemap(); //注册串口接收中断到自己的自定义函数$ M" o# D# w4 y' s7 O: `3 V4 }
- shellInit(&shell, shellBuffer, 512);
5 \# o( W. l2 i( i: O- `/ _9 p0 G
5 B1 V9 O0 s( t. P- }
复制代码
/ p3 j- \) h! o8 ]+ `————————————————: {5 Q( Q7 O7 f8 [ }2 R2 n
转载:fafuwxm7 o% p# | N: _# {
* p* s" i* v7 C8 e
) M9 m6 D; w: m
|