前言# ~2 j7 ]1 I# F" u: n- u/ g
用STM32CubeMx生成初始化配置代码是十分方便的,但是在处理多个项目的时候,就会发现,自动生成的中断函数调用的都是同一个回调函数,在这个回调函数中又要根据不同的句柄来处理,不同的项目外设又有所区别,还是要花点时间来进行移植。
Z3 ?' B' w0 a比如说,我在项目A中使用到了串口1、串口2、串口3,但是在项目B中,我只使用到了串口2,并且在项目B中,串口2的功能是项目A的串口1的功能,在这种情况下,我就需要再定义一次 HAL_UART_RxCpltCallback() ,然后移植对应的功能。当然也可以重新 define 一个宏,来对应所使用的串口,这样移植上也会快许多。但是假如,后面新来了一个需求,需要实现项目C的串口3的功能,原本只要添加对应的头文件,模块处理这些外设是最简便的,但是这时候会发现,项目C中也定义了HAL_UART_RxCpltCallback(),这就需要人工去移植这部分的代码了。5 g; a6 v% W; `0 c6 B4 R B
而我就在想,项目C的串口3是已经实现的模块,且这个模块是单独的.c文件和.h文件,并且不和其他的串口耦合在一起,这样的轮子造起车子来就快了许多了。但是找遍全网,会发现,关于HAL库中串口中断的教程都是基本的实现,没有更深层次的解释了,在学习MEMS库的过程中,看到官方的代码,在使用按钮触发外部中断的时候,竟然可以自定义外部中断的回调函数,进行一番学习之后,终于掌握了自定义中断回调的方法,不仅限于串口中断函数的回调,其他的外设都是相类似的,都是可以自定义中断的回调函数。
# e1 R+ K0 j# }( v: |1 F高手可以直接跳过第一节的内容,直接跳到第二节看如何进行操作。" X1 n" Q. e# l. z0 T5 j$ r( @
一、HAL库的中断实现, }& Q8 y0 m% Z
中断的原理网上到处都有,也可以参考正点原子这种开源的教程。3 J' ?' ^& Y2 D8 ]# o
我们正常在STM32CubeMX配置好中断后,通过 GENERATE CODE 生成代码,在 stm32f7xx_it.c 中,就会自动生成中断的处理函数,比如 USART2_IRQHandler,可以看到串口2的全局中断仅仅调用了 HAL_UART_IRQHandler 函数,该函数用于处理UART中断请求。, T* S0 t: c; N$ [3 S
: x4 m& P( o2 P5 s3 z l. M, B- /**1 A3 N7 T5 J) K- h) x
- * @brief This function handles USART2 global interrupt.
. o7 f- ^2 V% `: I - */3 E* V5 r3 x% m
- void USART2_IRQHandler(void)2 p% ]' Z; c1 a* A" e' b" i6 E4 g
- {
/ }' h! `1 C! x* J - /* USER CODE BEGIN USART2_IRQn 0 */& {6 B( Y8 Z h! s7 B8 J) T) K- q4 ]
- ) y- X( f4 R' ]
- /* USER CODE END USART2_IRQn 0 */; {, Q& H" {; n0 z
- HAL_UART_IRQHandler(&huart2);
; Y( @( i( @' \1 T. t" L" B2 @6 ~ - /* USER CODE BEGIN USART2_IRQn 1 */, O+ s: Q0 N) e: s2 \! c
, ~; `0 P3 ~6 _2 W& N( z$ A: W8 Q- /* USER CODE END USART2_IRQn 1 */( l" ^- Y4 ?, @. G" p
- }
复制代码 9 M6 {% I7 j2 O/ U
我们进入 HAL_UART_IRQHandler 看看里面具体的实现。这个实现虽然很长,但是逻辑比较简单。首先判断是否有错误产生,没有错误就会调用 UART_Receive_IT,用于在非阻塞模式下接收大量数据。如果在接收的过程中有错误发生,那么就会进行标志位的处理,再下面一点就可以看到 USE_HAL_UART_REGISTER_CALLBACKS 这个宏定义,这就是我们自定义中断函数的关键了。如果我们定义了 USE_HAL_UART_REGISTER_CALLBACKS,那么我们通过注册自定义的中断函数后,在这里 huart->ErrorCallback(huart) 就可以直接跳转到我们的自定义的接收异常函数了,而不是 HAL_UART_ErrorCallback(huart) 这个HAL通用的接收异常处理函数。
$ E7 V( f. x; a# U5 |注:F7的板子在公司,写这个教程的时候手头只有 NUCLEO-L152RE 的开发板,使用的库是 STM32Cube_FW_L1_V1.10.2。 STM32CubeMx生成的代码有所区别,但原理上是一样的。F767ZI 是通过 huart->RxISR(huart) 这个方式来调用中断处理函数的,最终调用的是 UART_RxISR_8BIT 这样子的函数。当然后面推出了新的固件版本,也可能导致具体的实现有所区别。; `6 p, P7 c2 Q! A
+ z1 K9 ?/ x; Q2 N- void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)+ S2 ~9 u1 L7 R
- {
5 |8 B7 _9 |' m - uint32_t isrflags = READ_REG(huart->Instance->SR);; R$ y+ @! ^) s3 K8 q
- uint32_t cr1its = READ_REG(huart->Instance->CR1);
2 E: H6 I8 w: k - uint32_t cr3its = READ_REG(huart->Instance->CR3);
. @1 w# L, W' J/ M1 l - uint32_t errorflags = 0x00U;
0 g$ T- c6 Y- z9 u6 ]5 p8 a - uint32_t dmarequest = 0x00U;
5 R) T) C, `9 r, w - 2 j7 U1 N9 `5 p3 Y$ n
- /* 如果没有错误发生 */: E3 L8 s3 U8 J$ U9 ~
- errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));; J7 d5 t+ H p" g2 n
- if (errorflags == RESET)% F% I( C, v% z4 j) V
- {' }; p8 M1 S5 S5 ^
- /* UART处于接收模式 -------------------------------------------------*/- L B* D& m, P8 \' N/ R5 R! O# l I
- if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))' W4 T$ d- u! b0 T# V1 q4 F
- {# e# i n& O' D8 c B
- UART_Receive_IT(huart);! o* I3 p+ [; \
- return;# ?& e; i6 ]5 o |: K( B$ @( b
- }
. E6 V/ \0 f, J4 c - }
9 P, m V8 u$ \; U- F" D
S$ j3 x; k8 |% D4 b% Y- /* 如果发生一些错误 */8 h% g* y$ a' Z* o
- if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))7 [& q8 K" W* L0 g7 ^4 ]2 r, J
- {
' k8 l& Q) P6 G% X - /* UART parity error interrupt occurred ----------------------------------*/
# S. G% @( ~: e- ?) i - if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))! F: g( \; j7 h* T
- {/ V3 l/ s2 r5 b
- huart->ErrorCode |= HAL_UART_ERROR_PE;' E- `, ~5 t! d3 z& p6 @4 N% H
- }
! ^) F. T6 Q. v/ G8 L6 h
% Y, V, o! A; Q$ r! s* N- /* UART noise error interrupt occurred -----------------------------------*/9 @' N* Y3 \; f5 R
- if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
1 G* |7 d" g: D3 J3 L& V - {& H* ?- e* F/ Y0 l2 k( P8 z5 r
- huart->ErrorCode |= HAL_UART_ERROR_NE;
. x' L1 p0 g% k+ u* X d5 Y - }
3 R5 |2 D: H- s" }8 ` - ( }4 S! u( G( m, N7 w! q) `- [
- /* UART frame error interrupt occurred -----------------------------------*/
, [' Z' a. V7 i* S7 S' [9 s - if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)). \+ F0 B" C! ?
- {
1 l0 z% r9 ?- c6 y1 Z* M - huart->ErrorCode |= HAL_UART_ERROR_FE;
. T3 P+ o" @" B9 k2 x - }, l: W( I# m& X8 }% q4 b; h0 }& B
- ; i, I: c( s9 l/ J% T
- /* UART Over-Run interrupt occurred --------------------------------------*/
6 n' [& X$ [: |' r - if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET))). f/ X4 p* o* P
- {. k" ?1 W4 y- p8 k) {
- huart->ErrorCode |= HAL_UART_ERROR_ORE;3 I+ \( d: w: T9 U$ J/ H
- }
& {* Y* K/ Y3 L3 m2 o% t$ F - 4 v, \6 J/ C) B% `4 i" h
- /* Call UART Error Call back function if need be --------------------------*/
6 A0 s; o7 @( _3 @/ V - if (huart->ErrorCode != HAL_UART_ERROR_NONE)" u; S1 b. U/ j. ]
- {
2 [, U" D( i# l: G$ g* I) v& M - /* UART in mode Receiver -----------------------------------------------*/
0 y3 x+ }8 j/ Y) _& `- { - if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)); N2 N0 ]0 L. X+ b$ s& Z; j3 _
- {" [# Q& r" y1 A g+ j, u
- UART_Receive_IT(huart);0 q! ` r5 f6 v9 L) ~
- }
4 ~6 _1 D" x) D/ W
+ v) H9 c5 i. w1 V- /* If Overrun error occurs, or if any error occurs in DMA mode reception,
+ J4 R3 d. z. E6 S0 J - consider error as blocking */- w1 ?, [' M' Z. e' ]# j4 X
- dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);5 P6 N' _- Z) w" V- t" i# v/ u9 z
- if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)% N6 {1 L B( C0 G
- {
# ^& L: b$ O- G% z8 T* V# _ - /* Blocking error : transfer is aborted
0 r* S E! g9 T8 w# q - Set the UART state ready to be able to start again the process,
# ^9 ?) x+ F; C+ b: C! ]% s0 B - Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
" I, }' B, f3 I - UART_EndRxTransfer(huart);$ O- p. I$ f; _! }6 n4 G8 h& A
- * I5 h `5 q7 y
- /* Disable the UART DMA Rx request if enabled */7 ~2 c2 `1 J/ h" O; ?5 g
- if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
' ]3 K* j' Q7 J" t! o O: \! V% [ - {
# D1 ^/ T$ K3 z, _, @ - CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);$ o) m/ }# x- x* @8 |% l& t
- ' [$ W4 M3 e9 @ r
- /* Abort the UART DMA Rx channel */
- n& i" v6 u. r1 r3 ? - if (huart->hdmarx != NULL)
6 K M0 v/ y$ T6 O/ f6 Y - {
, S9 T+ D+ S9 w, H/ ?( S* t$ v% L - /* Set the UART DMA Abort callback :
0 e$ D7 R% j- u" i, N$ n1 T7 R - will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
% B; S" Q+ M- J6 x2 a2 n% L - huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;3 I) l$ W* o& z3 w* _/ L
- if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
) @7 }$ U; u$ h - {; b# s. A9 ^# Q* b/ K7 U
- /* Call Directly XferAbortCallback function in case of error */1 [: K% f/ Q. h* e# K# n
- huart->hdmarx->XferAbortCallback(huart->hdmarx);" w- \' l% p: f9 h9 A
- }: P$ [9 w& M/ a) ~$ J
- }
1 M6 l! X$ P1 {; w, K0 o6 |3 k - else
G# @. M2 S$ |5 G; Z - {
`/ L) e9 m5 _6 `( H - /* Call user error callback */
8 U2 f% m/ a0 f2 X6 M) n - #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
, U2 j5 \5 n% s( b - /*Call registered error callback*/, Y5 i1 }2 z) R# w' p, C
- huart->ErrorCallback(huart);7 q9 [" @. F0 k
- #else
9 j& _4 L( Q$ g) c3 k6 U i% o - /*Call legacy weak error callback*/7 x& O" D3 E7 S. v' D' _# \
- HAL_UART_ErrorCallback(huart);
+ H9 I: u" q, c( P `+ H$ \ - #endif /* USE_HAL_UART_REGISTER_CALLBACKS */2 ]- Z* j0 a4 T0 Y9 Q2 C8 b9 i
- }
p: t( c, d: i - }" }( p7 N5 M* o
- else
; F. b; @# y5 v: d. q& ?( C - {
, N. s8 l$ f4 o0 H' m; z% _ - /* Call user error callback *// t+ Z5 V a5 v5 G1 i4 W" R
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
0 f( J; G' F: I' Y5 r# J4 J( E3 ] - /*Call registered error callback*/. x D+ {' L7 C! X6 E: r
- huart->ErrorCallback(huart);9 u2 C1 b; K$ P& }0 r! h; e
- #else
" f4 v1 k& {! S - /*Call legacy weak error callback*/; _% S/ h& D; ^
- HAL_UART_ErrorCallback(huart);
z+ H0 l% b: \; ]4 d) x - #endif /* USE_HAL_UART_REGISTER_CALLBACKS */- R2 G4 p0 F5 U( q
- }
9 K5 e$ o( g3 F4 `" |; [6 x% d - }. N2 s6 H4 Z! }. W6 h
- else, V$ w: \7 s9 l, I& q& J, q. I
- { ]* M8 j5 [ |7 G6 |6 e
- /* Non Blocking error : transfer could go on. e6 Y6 c' S; S/ e9 X7 B ?
- Error is notified to user through user error callback */
+ W5 _- V9 ?" f G. q+ e$ t - #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)2 X1 @! n b/ Y# Y1 I* Q
- /*Call registered error callback*/
7 Z5 }( u0 T6 z. i& V - huart->ErrorCallback(huart);0 Z# T( W% ^8 E u
- #else
3 Q, }2 W$ q7 ?2 J - /*Call legacy weak error callback*/
# `% v u5 x+ z' w - HAL_UART_ErrorCallback(huart);3 P% x+ Y9 ~. K# b/ s
- #endif /* USE_HAL_UART_REGISTER_CALLBACKS */9 I% d# y) h: {' v1 U6 U$ @
- ( a7 y9 C# h8 h- S+ B$ l
- huart->ErrorCode = HAL_UART_ERROR_NONE;
$ w+ {. u7 Q' N1 `. H: d+ V - }' K/ ?5 l( t# B2 P1 r/ X
- }9 M9 L. c+ s$ h& G
- return;
2 A) F! e1 k* ]! J - } /* 如果发生错误则结束 */
7 K5 p8 {7 @, ^/ I8 ?; f6 X - + m: e: |2 O( L/ ?
- /* UART 处于发送模式,准备在非阻塞模式下发送大量数据。 ---------------------------*/* l! H/ h5 L. T1 Y
- if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
& ^# O. f2 _$ H8 c; D& m1 ^ - {
! v& \! ^/ w9 D) Z* M - UART_Transmit_IT(huart);# J0 f% g u d( S5 F% t: W z
- return;
9 {, z4 m% e; B, ^( Z# w - }# z1 T" L; h2 O
5 A" T- d5 b& R2 n$ w k/ G- /* UART 处于发送结束模式,在非阻塞模式下结束传输。 -------------------------------*/8 h/ M) M. |$ V) O% O8 l( o, _
- if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
( |/ y4 B* ` N7 q" C4 c - {
) ]+ U) ?2 o+ d# o* b/ k) m - UART_EndTransmit_IT(huart);
) E: z6 L, L; S# X$ Z - return;* V4 c p5 ~6 u! t5 j
- }5 c% S. v& L% s7 \/ |: j q7 B. M) J Y
- }
复制代码 * r' ^, k* Z! s' p; p" D8 b( ?
然后我们看看没有接收错误发生时,调用的函数 UART_Receive_IT(huart)。这个首先根据数据位的长度来进行接收数据,(9bit和少于9bit的处理是不一样的),串口数据就保存在 huart->pRxBuffPtr,每接收一个数据,那么 huart->RxXferCount 就减一,直至为0,这时候就会触发接收完成的中断了。至于需要接收几个字节来触发中断,就取决于初始化时候的设置了。
7 s- q- g' n; O4 ?' t1 O0 qhuart->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,这个函数会将所有的回调函数初始化为其默认值。
1 B0 O2 k7 j [6 B0 N$ M+ A注:接收完成后,这个会自动将关闭中断,接收完指定的字节之后,还需要继续接收的话就需要再次打开中断了。
4 c% F$ j4 t) I9 l& {# P) P, t& g2 W1 \3 {8 U9 ?) `
- static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
& [7 H( n# B/ w1 U% l - {
% D$ _( g; b- K% ~ - uint16_t *tmp;. |1 s x/ B9 L; Y
- # G1 `. P6 z0 T r1 w6 u( i! G
- /* 检查接收进程是否正在进行 */: S6 _3 P' _4 V
- if (huart->RxState == HAL_UART_STATE_BUSY_RX)
: d+ N+ l6 K) g1 V/ O - {
2 }: w( c4 f: l* N' {4 ~ - if (huart->Init.WordLength == UART_WORDLENGTH_9B)
3 h1 k- G9 _; P! A) v/ r( s" h - {
+ R9 t [7 S7 K& T! _) ? - tmp = (uint16_t *) huart->pRxBuffPtr;
8 J, u# }0 e+ B+ P0 W b7 P - if (huart->Init.Parity == UART_PARITY_NONE)
5 H9 s4 B0 s* S - { W9 n3 j) d; S# N+ S
- *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
; @$ {9 g3 w7 S% E - huart->pRxBuffPtr += 2U;, k! @5 V' f/ ]/ @4 J% X7 ?
- }# v$ f& M0 ?8 H3 Z) R: e. [0 v& s
- else
; [ ~: C G$ n9 n& a9 \ - {
) q7 \9 k( V- K) T% o% u - *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
/ n' q( b! O/ H* R2 {5 b - huart->pRxBuffPtr += 1U;/ [, K/ Z) [" a! R* ?
- }" ^: C* L1 z5 A0 W4 G4 r
- }; o% l- l _3 ]6 R1 {
- else
6 u1 c7 a! ], \# q( v0 h3 [ - {
3 _/ }# c& S, X# X( B, w - if (huart->Init.Parity == UART_PARITY_NONE)
4 \0 D5 J# k1 H' F. ?4 B4 _. p; x - {
" w0 a' b( Q% n/ h+ |* } - *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);& _$ {2 ]" i1 N
- }5 t7 }! f3 [ f/ ?- Z. K
- else
; i1 V) e7 M2 o - {
$ ^8 m! Z `: Z; e - *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
u# O+ U+ j; L& ~ - }( K( p. L$ I" n% I" K' T
- }. x. t A% W+ S
- " x8 p) u# o1 v1 ^ T7 d6 l
- if (--huart->RxXferCount == 0U)
1 F& S9 \) t0 K4 `* X& C - {: k4 j3 D) L2 a5 k+ o
- /* 禁止UART数据寄存器不为空的中断 */
. d8 C3 I; t9 D3 [ - __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
* a4 o; L' ^ b+ W4 o
8 ^; `3 S) U- |; t- /* 禁用UART奇偶校验错误中断 */& m+ F7 D1 |/ D/ `& A# E2 }
- __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
5 c5 l L- C/ R - 6 @5 m9 j& D. b; e" @
- /* 禁用UART错误中断:(帧错误,噪声错误,溢出错误) */4 x- }' L, }' _' }; x9 m$ t3 R
- __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);5 q0 ?( C& T* p! w% Q' J5 x
- 6 Z5 X* i4 Y. L. E' I- O
- /* Rx进程完成,将huart-> RxState还原为Ready */; [$ w1 }: B; x& I: L+ S
- huart->RxState = HAL_UART_STATE_READY;- W) i2 m$ J; D# E
( V+ ]8 C. s7 h# G6 @, }; F- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)9 w# y" A1 H D
- /* 调用注册的Rx完成回调函数 */
# E( O# `0 y5 S: D - huart->RxCpltCallback(huart);, b& U$ X {$ b/ v# M# R" j# `
- #else2 c6 O: F% M) U5 v5 I8 ]- Z
- /*调用旧版弱Rx完成回调函数*/
9 u) |3 o# {0 h* ` - HAL_UART_RxCpltCallback(huart);
3 Y$ X' w+ e- }+ B$ _ - #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
; w3 l% e" v9 i - & K: J" h0 [* b2 J: C+ b7 J- ]: z- N
- return HAL_OK;
0 r1 G" y' C& e4 Z* W, S, t - }
H* t1 ]1 F% K' Y1 s - return HAL_OK;5 o- e7 Z3 I3 ~6 ^: w8 a
- }' t: B8 E# V7 c; N: u
- else
' \& F ]% N* \& Q- W: C5 K - {1 E- H- R* ]! R1 s0 Q
- return HAL_BUSY;
8 n9 r% ^' F( B& Q - }
. Q0 X/ e) c4 H, \8 O7 I8 e" | - }
复制代码
Y. U( ?/ N5 y, Z; V }二、使用方法
8 I& |$ l3 R9 z- k n8 _7 y5 E原理性的代码解析已经在前面一章节解析了,这一章节就是讲要如何实现,其他的都不难,重点是要知道 USE_HAL_UART_REGISTER_CALLBACKS 这个宏,其他的不懂的地方就可以根据这个线索来一步一步的追踪了。7 @+ {) u8 c0 O6 m
$ \6 Q/ r0 d d. X
! A, p6 e X A% r) _4 L
: c2 j4 k6 J& u/ C0 m图2.1 设置串口参数( D# ]; {9 n/ i7 V P! s
$ C1 a$ Z+ ~" V- W/ Z
- x5 W# O, w6 e% s
0 @& H5 k$ r7 m. a9 a7 q' X
图2.2 使能串口中断并设置优先级2 v0 b7 a, L! I1 b8 F, R) l3 V
9 x$ X+ |: F! F; u
前两步都是设置串口中断的设置,按照常规来设置就可以了,重点了如何使能 USE_HAL_UART_REGISTER_CALLBACKS 这个宏,这个宏在 Project Manager 中的 Advanced Settings 右边的 Register Callback 中,选择 UART 为 ENABLE ,注意不是 USART,别选错了。
0 J) q/ j; O" J1 C) V% w) ~: {当然也可以在生成的项目中搜索 USE_HAL_UART_REGISTER_CALLBACKS 手工改成1,但是假如后续又通过STM32CubeMX修改配置的话,再生成代码,会将这里的配置覆盖过去,如果没注意这个点的就很容易出错,建议还是在这里修改比较稳妥一些。
. m4 B" ]) |1 v6 I0 B& q0 r7 L+ g: m; e" w% s0 x2 b! e
5 G, D# w& Z, z* R0 `) l. Y
. T" A. z# G" u代码生成之后,默认是没有打开串口中断的,通过 HAL_UART_RegisterCallback() 自定义回调函数,再开启中断就完成了我们的目的。
% s3 d& M: X& }) \5 _我们可以专门在一个.c文件来自定义接收完成的回调函数并打开中断,在main函数中调用这个初始化函数就可以了。这样就能和其他串口解耦,移植起来就很方便了。比如我需要指定一个串口来使用 Letter Shell( github上的开源项目,我的文章中也有这个教程),那么我就写一个 shell_port.c 文件,仅仅需要通过宏来指定使用shell 的串口句柄,其他的都不需要修改,直接添加这个 c文件,在main函数中使用 userShellInit() 来初始化,就能直接使用这个扩展模块了,相比于其他的方式,这个移植过程可以说是相当简便省事了(其他的形式需要在 usart.c 中定义串口接收的数组,再定义接收的个数,再在中断中移植自己的功能,再在main函数中打开串口中断,每建立一个新的项目都需要这么做,尤其时间长了之后,要配置串口中断的函数的时候就容易忘记函数名称,真的很烦)。
2 S. Y1 d2 \8 a2 E: D" v! M( H% z. v; e2 x; g* `
HAL_UART_RegisterCallback()6 n9 v2 u8 q* b" H6 H) g1 P
这个函数的第一个参数是 huart ,要配置的串口句柄。
+ \) }- q4 p$ I7 R' g) X这个函数的第二个参数是 CallbackID ,不同的外设所支持的回调函数ID不同,对于串口来说,HAL_UART_CallbackIDTypeDef 列出了所支持的各个功能ID,我们要在串口接收完成后调用我们自定义的ID,使用的就是 HAL_UART_RX_COMPLETE_CB_ID 。" q; B' O4 ]2 y. U! p
这个函数的第三个参数是 pCallback ,我们自定义的回调函数,但是这个函数有要求,参照 pUART_CallbackTypeDef 的定义。返回应该是void,并且需要一个参数 UART_HandleTypeDef *huart。) k; {7 y1 z+ b2 ^. ^
$ w8 H! H% Q/ q7 Z+ W4 a
typedef void (*pUART_CallbackTypeDef)(UART_HandleTypeDef *huart); // pointer to an UART callback function
8 T0 v7 @0 J$ P, d: A! ]4 j1 N2 h2 X" i3 E0 c) e1 B7 V/ d5 U
注:这里设置的是每接收一个字节就触发中断,但是HAL库本身处理串口中断标志位后,会关闭中断,所以我们就需要 HAL_UART_Receive_IT 再次开启中断。
, X$ g* ]) `0 m. s! Y% Q \9 i& I7 W$ \& c0 C9 C. Q: Q0 u7 T9 Y, m# M
- #include "shell_port.h"8 c; |! p$ k* x. c/ a% I' M
- #include "usart.h" C# w D9 @1 c( Y1 E9 ^* p
- " h- k" G9 Y( q# V
- Shell shell;
; K1 w4 L, m* R7 N$ P - char shellBuffer[512];
7 p/ w& k' f2 \1 F - ' V+ V" C2 h3 B( A: H9 Z' e% m9 B8 t
- #define userShellhuart huart2 //shell 使用到的串口句柄( E# D8 y3 I$ R# j
- #define SHELL_UART_REC_LEN_ONCE 1 //串口单次接收的个数
4 N* R1 m; a4 d2 S! K/ B! k+ T - uint8_t uartRecBuffer[SHELL_UART_REC_LEN_ONCE]; //串口接收的buffer5 k) w: `! ~7 [" H& H; F
- / a2 k- |9 J* ^. v
, m" t. C$ r+ p9 H& a' b3 }: r- /**
: M3 y# @6 k* _% Z - * @brief 用户shell写# b* R$ q9 y+ t& z
- *
) g9 f4 @$ L' \: U3 Y - * @param data 数据
* l% B& E" d+ l, D - */
% z. f2 A' X8 i, V" E - void userShellWrite(char data)# j& c; L D8 R3 x0 ~! O
- {
+ Y& m' }3 t0 i/ q, F0 Q4 Y - HAL_UART_Transmit(&userShellhuart,(uint8_t *)&data, 1,1000);1 |+ Z. ?& c1 p/ |& H
- }
7 f( |1 k7 d8 E" m1 m8 W8 ^
) Y: M3 x; w" r9 P; r# \- $ i( y- A, b: S+ E
- /**
8 _/ l" N2 {# \, X1 `& _ - * @brief 用户shell读
6 M: K) t4 X( @9 x( u7 s - * 4 i" o; O: e5 X- p
- * @param data 数据
( p$ B3 B/ P1 _+ V, q/ ~( e# Y9 ? - * @return char 状态
! ~$ ?0 X9 ~5 M3 [5 B5 T - */5 U. ]: M( T O0 t
- signed char userShellRead(char *data)" W8 X$ k4 {7 `' X
- {
( t$ g! h0 o' `! _5 ? - if(HAL_UART_Receive(&userShellhuart,(uint8_t *)data, 1, 0) == HAL_OK)
5 {" [' n; o8 ~ - {" K4 Y2 ?2 {: U: ?# A9 h5 J
- return 0;
' q8 F# I; Y T4 N; _5 w ?" l" t - }' |1 _1 m, k+ B2 {! I+ M& v3 @
- else
: D$ X$ P4 O* i5 ~6 g0 G1 J4 o) H( d - {9 ~( {. [" \: g* X; W, o
- return -1;
7 v! ~' u- [2 _) p. f - }; t9 }& J, a7 R" ]3 q
- }
3 d Z8 Y+ u# J6 o! g$ n7 K q - . v' H! I. C" b7 o7 H0 y
- /*** H9 \" G4 g5 j
- * @brief 自定义函数串口接收完成中断 RxCpltCallback ?( e( N$ i9 z" L7 S
- */, a" ]& B1 O8 l
- void ShellRxCpltCallback(UART_HandleTypeDef *huart)
4 L' v& X2 j s, w$ X$ L - {
: a! s, P5 X( `8 o - shellHandler(&shell, uartRecBuffer[0]); //命令行处理函数
O3 i5 E# X' B; K( S ]' Y - HAL_UART_Receive_IT(huart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE);//使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量. ?# J5 o% d" ]( K# u
- }9 V* z3 O, J s
6 W& R' N) q; r2 r. ]- /**0 G( g S7 z' \8 ^
- * @brief 注册串口接收中断到自己的自定义函数
7 c$ [5 C1 ~3 c* J3 r9 m - */
9 m$ s, ]' B5 r - void ShellReceiveCallbackRemap(void)7 i6 K2 W9 ]) m$ m
- {
7 q/ b/ Z9 ~) a2 c6 |* W - HAL_UART_RegisterCallback(&userShellhuart,HAL_UART_RX_COMPLETE_CB_ID,ShellRxCpltCallback); //注册串口接收中断到自己的自定义函数
0 v- T2 h; y7 @; M) ]4 L1 n - HAL_UART_Receive_IT(&userShellhuart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE); //使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量) _. v& R& y1 C& Q% q1 c$ y7 ~
- }
! b' b7 t# d' F9 \
- P& H3 Z9 ^" f& t) |- : e/ P% R' z1 l2 g4 I
- /**3 P, M( y8 b9 m: ]) }7 w2 M6 v
- * @brief 用户shell初始化
' a- w1 k' Q/ O& u- k& g - *
) n2 r2 e" g3 I- @9 q - */3 ~- n; a) }. C' I/ C! F+ }7 [
- void userShellInit(void)2 I% k: Z# E8 ]% g4 K& M
- {
2 V) s" }+ a) I0 Y1 ~* [, j - shell.write = userShellWrite; //指定串口写入的函数
/ K+ g0 f) `: d* b$ f, R+ R7 d - shell.read = userShellRead; //指定串口读取的函数
$ ^. Y0 C( }5 N U9 r - ShellReceiveCallbackRemap(); //注册串口接收中断到自己的自定义函数
7 W+ T, n- l! m; m: S* ^9 O# z - shellInit(&shell, shellBuffer, 512);# T/ y. B/ i8 O1 D/ Q, f
- : C2 g1 M& [. `7 F# n+ @
- }
复制代码 2 S, V$ e2 n3 L3 P$ ?4 u/ J* |- z
————————————————4 Y' l G( r; o8 g# p8 K/ S
转载:fafuwxm
1 p- Y0 q3 G! |" W4 k& y0 ?4 |2 G
; c& |( S# Y8 A- U
. y* `" |+ e+ Z |