你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32小技巧(1) HAL自定义串口中断回调 模块化代码进行解耦

[复制链接]
STMCU小助手 发布时间:2022-8-23 16:13
前言# ~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. /**1 A3 N7 T5 J) K- h) x
  2.   * @brief This function handles USART2 global interrupt.
    . o7 f- ^2 V% `: I
  3.   */3 E* V5 r3 x% m
  4. void USART2_IRQHandler(void)2 p% ]' Z; c1 a* A" e' b" i6 E4 g
  5. {
    / }' h! `1 C! x* J
  6.   /* USER CODE BEGIN USART2_IRQn 0 */& {6 B( Y8 Z  h! s7 B8 J) T) K- q4 ]
  7. ) y- X( f4 R' ]
  8.   /* USER CODE END USART2_IRQn 0 */; {, Q& H" {; n0 z
  9.   HAL_UART_IRQHandler(&huart2);
    ; Y( @( i( @' \1 T. t" L" B2 @6 ~
  10.   /* USER CODE BEGIN USART2_IRQn 1 */, O+ s: Q0 N) e: s2 \! c

  11. , ~; `0 P3 ~6 _2 W& N( z$ A: W8 Q
  12.   /* USER CODE END USART2_IRQn 1 */( l" ^- Y4 ?, @. G" p
  13. }
复制代码
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
  1. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)+ S2 ~9 u1 L7 R
  2. {
    5 |8 B7 _9 |' m
  3.   uint32_t isrflags   = READ_REG(huart->Instance->SR);; R$ y+ @! ^) s3 K8 q
  4.   uint32_t cr1its     = READ_REG(huart->Instance->CR1);
    2 E: H6 I8 w: k
  5.   uint32_t cr3its     = READ_REG(huart->Instance->CR3);
    . @1 w# L, W' J/ M1 l
  6.   uint32_t errorflags = 0x00U;
    0 g$ T- c6 Y- z9 u6 ]5 p8 a
  7.   uint32_t dmarequest = 0x00U;
    5 R) T) C, `9 r, w
  8. 2 j7 U1 N9 `5 p3 Y$ n
  9.   /* 如果没有错误发生 */: E3 L8 s3 U8 J$ U9 ~
  10.   errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));; J7 d5 t+ H  p" g2 n
  11.   if (errorflags == RESET)% F% I( C, v% z4 j) V
  12.   {' }; p8 M1 S5 S5 ^
  13.     /* UART处于接收模式 -------------------------------------------------*/- L  B* D& m, P8 \' N/ R5 R! O# l  I
  14.     if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))' W4 T$ d- u! b0 T# V1 q4 F
  15.     {# e# i  n& O' D8 c  B
  16.       UART_Receive_IT(huart);! o* I3 p+ [; \
  17.       return;# ?& e; i6 ]5 o  |: K( B$ @( b
  18.     }
    . E6 V/ \0 f, J4 c
  19.   }
    9 P, m  V8 u$ \; U- F" D

  20.   S$ j3 x; k8 |% D4 b% Y
  21.   /* 如果发生一些错误 */8 h% g* y$ a' Z* o
  22.   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
  23.   {
    ' k8 l& Q) P6 G% X
  24.     /* UART parity error interrupt occurred ----------------------------------*/
    # S. G% @( ~: e- ?) i
  25.     if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))! F: g( \; j7 h* T
  26.     {/ V3 l/ s2 r5 b
  27.       huart->ErrorCode |= HAL_UART_ERROR_PE;' E- `, ~5 t! d3 z& p6 @4 N% H
  28.     }
    ! ^) F. T6 Q. v/ G8 L6 h

  29. % Y, V, o! A; Q$ r! s* N
  30.     /* UART noise error interrupt occurred -----------------------------------*/9 @' N* Y3 \; f5 R
  31.     if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    1 G* |7 d" g: D3 J3 L& V
  32.     {& H* ?- e* F/ Y0 l2 k( P8 z5 r
  33.       huart->ErrorCode |= HAL_UART_ERROR_NE;
    . x' L1 p0 g% k+ u* X  d5 Y
  34.     }
    3 R5 |2 D: H- s" }8 `
  35. ( }4 S! u( G( m, N7 w! q) `- [
  36.     /* UART frame error interrupt occurred -----------------------------------*/
    , [' Z' a. V7 i* S7 S' [9 s
  37.     if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)). \+ F0 B" C! ?
  38.     {
    1 l0 z% r9 ?- c6 y1 Z* M
  39.       huart->ErrorCode |= HAL_UART_ERROR_FE;
    . T3 P+ o" @" B9 k2 x
  40.     }, l: W( I# m& X8 }% q4 b; h0 }& B
  41. ; i, I: c( s9 l/ J% T
  42.     /* UART Over-Run interrupt occurred --------------------------------------*/
    6 n' [& X$ [: |' r
  43.     if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET))). f/ X4 p* o* P
  44.     {. k" ?1 W4 y- p8 k) {
  45.       huart->ErrorCode |= HAL_UART_ERROR_ORE;3 I+ \( d: w: T9 U$ J/ H
  46.     }
    & {* Y* K/ Y3 L3 m2 o% t$ F
  47. 4 v, \6 J/ C) B% `4 i" h
  48.     /* Call UART Error Call back function if need be --------------------------*/
    6 A0 s; o7 @( _3 @/ V
  49.     if (huart->ErrorCode != HAL_UART_ERROR_NONE)" u; S1 b. U/ j. ]
  50.     {
    2 [, U" D( i# l: G$ g* I) v& M
  51.       /* UART in mode Receiver -----------------------------------------------*/
    0 y3 x+ }8 j/ Y) _& `- {
  52.       if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)); N2 N0 ]0 L. X+ b$ s& Z; j3 _
  53.       {" [# Q& r" y1 A  g+ j, u
  54.         UART_Receive_IT(huart);0 q! `  r5 f6 v9 L) ~
  55.       }
    4 ~6 _1 D" x) D/ W

  56. + v) H9 c5 i. w1 V
  57.       /* If Overrun error occurs, or if any error occurs in DMA mode reception,
    + J4 R3 d. z. E6 S0 J
  58.          consider error as blocking */- w1 ?, [' M' Z. e' ]# j4 X
  59.       dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);5 P6 N' _- Z) w" V- t" i# v/ u9 z
  60.       if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)% N6 {1 L  B( C0 G
  61.       {
    # ^& L: b$ O- G% z8 T* V# _
  62.         /* Blocking error : transfer is aborted
    0 r* S  E! g9 T8 w# q
  63.            Set the UART state ready to be able to start again the process,
    # ^9 ?) x+ F; C+ b: C! ]% s0 B
  64.            Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
    " I, }' B, f3 I
  65.         UART_EndRxTransfer(huart);$ O- p. I$ f; _! }6 n4 G8 h& A
  66. * I5 h  `5 q7 y
  67.         /* Disable the UART DMA Rx request if enabled */7 ~2 c2 `1 J/ h" O; ?5 g
  68.         if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
    ' ]3 K* j' Q7 J" t! o  O: \! V% [
  69.         {
    # D1 ^/ T$ K3 z, _, @
  70.           CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);$ o) m/ }# x- x* @8 |% l& t
  71. ' [$ W4 M3 e9 @  r
  72.           /* Abort the UART DMA Rx channel */
    - n& i" v6 u. r1 r3 ?
  73.           if (huart->hdmarx != NULL)
    6 K  M0 v/ y$ T6 O/ f6 Y
  74.           {
    , S9 T+ D+ S9 w, H/ ?( S* t$ v% L
  75.             /* Set the UART DMA Abort callback :
    0 e$ D7 R% j- u" i, N$ n1 T7 R
  76.                will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
    % B; S" Q+ M- J6 x2 a2 n% L
  77.             huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;3 I) l$ W* o& z3 w* _/ L
  78.             if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
    ) @7 }$ U; u$ h
  79.             {; b# s. A9 ^# Q* b/ K7 U
  80.               /* Call Directly XferAbortCallback function in case of error */1 [: K% f/ Q. h* e# K# n
  81.               huart->hdmarx->XferAbortCallback(huart->hdmarx);" w- \' l% p: f9 h9 A
  82.             }: P$ [9 w& M/ a) ~$ J
  83.           }
    1 M6 l! X$ P1 {; w, K0 o6 |3 k
  84.           else
      G# @. M2 S$ |5 G; Z
  85.           {
      `/ L) e9 m5 _6 `( H
  86.             /* Call user error callback */
    8 U2 f% m/ a0 f2 X6 M) n
  87. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    , U2 j5 \5 n% s( b
  88.             /*Call registered error callback*/, Y5 i1 }2 z) R# w' p, C
  89.             huart->ErrorCallback(huart);7 q9 [" @. F0 k
  90. #else
    9 j& _4 L( Q$ g) c3 k6 U  i% o
  91.             /*Call legacy weak error callback*/7 x& O" D3 E7 S. v' D' _# \
  92.             HAL_UART_ErrorCallback(huart);
    + H9 I: u" q, c( P  `+ H$ \
  93. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */2 ]- Z* j0 a4 T0 Y9 Q2 C8 b9 i
  94.           }
      p: t( c, d: i
  95.         }" }( p7 N5 M* o
  96.         else
    ; F. b; @# y5 v: d. q& ?( C
  97.         {
    , N. s8 l$ f4 o0 H' m; z% _
  98.           /* Call user error callback *// t+ Z5 V  a5 v5 G1 i4 W" R
  99. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    0 f( J; G' F: I' Y5 r# J4 J( E3 ]
  100.           /*Call registered error callback*/. x  D+ {' L7 C! X6 E: r
  101.           huart->ErrorCallback(huart);9 u2 C1 b; K$ P& }0 r! h; e
  102. #else
    " f4 v1 k& {! S
  103.           /*Call legacy weak error callback*/; _% S/ h& D; ^
  104.           HAL_UART_ErrorCallback(huart);
      z+ H0 l% b: \; ]4 d) x
  105. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */- R2 G4 p0 F5 U( q
  106.         }
    9 K5 e$ o( g3 F4 `" |; [6 x% d
  107.       }. N2 s6 H4 Z! }. W6 h
  108.       else, V$ w: \7 s9 l, I& q& J, q. I
  109.       {  ]* M8 j5 [  |7 G6 |6 e
  110.         /* Non Blocking error : transfer could go on.  e6 Y6 c' S; S/ e9 X7 B  ?
  111.            Error is notified to user through user error callback */
    + W5 _- V9 ?" f  G. q+ e$ t
  112. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)2 X1 @! n  b/ Y# Y1 I* Q
  113.         /*Call registered error callback*/
    7 Z5 }( u0 T6 z. i& V
  114.         huart->ErrorCallback(huart);0 Z# T( W% ^8 E  u
  115. #else
    3 Q, }2 W$ q7 ?2 J
  116.         /*Call legacy weak error callback*/
    # `% v  u5 x+ z' w
  117.         HAL_UART_ErrorCallback(huart);3 P% x+ Y9 ~. K# b/ s
  118. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */9 I% d# y) h: {' v1 U6 U$ @
  119. ( a7 y9 C# h8 h- S+ B$ l
  120.         huart->ErrorCode = HAL_UART_ERROR_NONE;
    $ w+ {. u7 Q' N1 `. H: d+ V
  121.       }' K/ ?5 l( t# B2 P1 r/ X
  122.     }9 M9 L. c+ s$ h& G
  123.     return;
    2 A) F! e1 k* ]! J
  124.   } /* 如果发生错误则结束 */
    7 K5 p8 {7 @, ^/ I8 ?; f6 X
  125. + m: e: |2 O( L/ ?
  126.   /* UART 处于发送模式,准备在非阻塞模式下发送大量数据。 ---------------------------*/* l! H/ h5 L. T1 Y
  127.   if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
    & ^# O. f2 _$ H8 c; D& m1 ^
  128.   {
    ! v& \! ^/ w9 D) Z* M
  129.     UART_Transmit_IT(huart);# J0 f% g  u  d( S5 F% t: W  z
  130.     return;
    9 {, z4 m% e; B, ^( Z# w
  131.   }# z1 T" L; h2 O

  132. 5 A" T- d5 b& R2 n$ w  k/ G
  133.   /*  UART 处于发送结束模式,在非阻塞模式下结束传输。 -------------------------------*/8 h/ M) M. |$ V) O% O8 l( o, _
  134.   if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
    ( |/ y4 B* `  N7 q" C4 c
  135.   {
    ) ]+ U) ?2 o+ d# o* b/ k) m
  136.     UART_EndTransmit_IT(huart);
    ) E: z6 L, L; S# X$ Z
  137.     return;* V4 c  p5 ~6 u! t5 j
  138.   }5 c% S. v& L% s7 \/ |: j  q7 B. M) J  Y
  139. }
复制代码
* 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 ?) `
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
    & [7 H( n# B/ w1 U% l
  2. {
    % D$ _( g; b- K% ~
  3.   uint16_t *tmp;. |1 s  x/ B9 L; Y
  4. # G1 `. P6 z0 T  r1 w6 u( i! G
  5.   /* 检查接收进程是否正在进行 */: S6 _3 P' _4 V
  6.   if (huart->RxState == HAL_UART_STATE_BUSY_RX)
    : d+ N+ l6 K) g1 V/ O
  7.   {
    2 }: w( c4 f: l* N' {4 ~
  8.     if (huart->Init.WordLength == UART_WORDLENGTH_9B)
    3 h1 k- G9 _; P! A) v/ r( s" h
  9.     {
    + R9 t  [7 S7 K& T! _) ?
  10.       tmp = (uint16_t *) huart->pRxBuffPtr;
    8 J, u# }0 e+ B+ P0 W  b7 P
  11.       if (huart->Init.Parity == UART_PARITY_NONE)
    5 H9 s4 B0 s* S
  12.       {  W9 n3 j) d; S# N+ S
  13.         *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
    ; @$ {9 g3 w7 S% E
  14.         huart->pRxBuffPtr += 2U;, k! @5 V' f/ ]/ @4 J% X7 ?
  15.       }# v$ f& M0 ?8 H3 Z) R: e. [0 v& s
  16.       else
    ; [  ~: C  G$ n9 n& a9 \
  17.       {
    ) q7 \9 k( V- K) T% o% u
  18.         *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
    / n' q( b! O/ H* R2 {5 b
  19.         huart->pRxBuffPtr += 1U;/ [, K/ Z) [" a! R* ?
  20.       }" ^: C* L1 z5 A0 W4 G4 r
  21.     }; o% l- l  _3 ]6 R1 {
  22.     else
    6 u1 c7 a! ], \# q( v0 h3 [
  23.     {
    3 _/ }# c& S, X# X( B, w
  24.       if (huart->Init.Parity == UART_PARITY_NONE)
    4 \0 D5 J# k1 H' F. ?4 B4 _. p; x
  25.       {
    " w0 a' b( Q% n/ h+ |* }
  26.         *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);& _$ {2 ]" i1 N
  27.       }5 t7 }! f3 [  f/ ?- Z. K
  28.       else
    ; i1 V) e7 M2 o
  29.       {
    $ ^8 m! Z  `: Z; e
  30.         *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      u# O+ U+ j; L& ~
  31.       }( K( p. L$ I" n% I" K' T
  32.     }. x. t  A% W+ S
  33. " x8 p) u# o1 v1 ^  T7 d6 l
  34.     if (--huart->RxXferCount == 0U)
    1 F& S9 \) t0 K4 `* X& C
  35.     {: k4 j3 D) L2 a5 k+ o
  36.       /* 禁止UART数据寄存器不为空的中断 */
    . d8 C3 I; t9 D3 [
  37.       __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
    * a4 o; L' ^  b+ W4 o

  38. 8 ^; `3 S) U- |; t
  39.       /* 禁用UART奇偶校验错误中断 */& m+ F7 D1 |/ D/ `& A# E2 }
  40.       __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
    5 c5 l  L- C/ R
  41. 6 @5 m9 j& D. b; e" @
  42.       /* 禁用UART错误中断:(帧错误,噪声错误,溢出错误) */4 x- }' L, }' _' }; x9 m$ t3 R
  43.       __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);5 q0 ?( C& T* p! w% Q' J5 x
  44. 6 Z5 X* i4 Y. L. E' I- O
  45.       /* Rx进程完成,将huart-> RxState还原为Ready */; [$ w1 }: B; x& I: L+ S
  46.       huart->RxState = HAL_UART_STATE_READY;- W) i2 m$ J; D# E

  47. ( V+ ]8 C. s7 h# G6 @, }; F
  48. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)9 w# y" A1 H  D
  49.       /* 调用注册的Rx完成回调函数 */
    # E( O# `0 y5 S: D
  50.       huart->RxCpltCallback(huart);, b& U$ X  {$ b/ v# M# R" j# `
  51. #else2 c6 O: F% M) U5 v5 I8 ]- Z
  52.       /*调用旧版弱Rx完成回调函数*/
    9 u) |3 o# {0 h* `
  53.       HAL_UART_RxCpltCallback(huart);
    3 Y$ X' w+ e- }+ B$ _
  54. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
    ; w3 l% e" v9 i
  55. & K: J" h0 [* b2 J: C+ b7 J- ]: z- N
  56.       return HAL_OK;
    0 r1 G" y' C& e4 Z* W, S, t
  57.     }
      H* t1 ]1 F% K' Y1 s
  58.     return HAL_OK;5 o- e7 Z3 I3 ~6 ^: w8 a
  59.   }' t: B8 E# V7 c; N: u
  60.   else
    ' \& F  ]% N* \& Q- W: C5 K
  61.   {1 E- H- R* ]! R1 s0 Q
  62.     return HAL_BUSY;
    8 n9 r% ^' F( B& Q
  63.   }
    . Q0 X/ e) c4 H, \8 O7 I8 e" |
  64. }
复制代码

  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
20210131202630176.png
! 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
2021013120265496.png - 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
20210131203021383.png 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
  1. #include "shell_port.h"8 c; |! p$ k* x. c/ a% I' M
  2. #include "usart.h"  C# w  D9 @1 c( Y1 E9 ^* p
  3. " h- k" G9 Y( q# V
  4. Shell shell;
    ; K1 w4 L, m* R7 N$ P
  5. char shellBuffer[512];
    7 p/ w& k' f2 \1 F
  6. ' V+ V" C2 h3 B( A: H9 Z' e% m9 B8 t
  7. #define userShellhuart                             huart2        //shell 使用到的串口句柄( E# D8 y3 I$ R# j
  8. #define SHELL_UART_REC_LEN_ONCE         1           //串口单次接收的个数
    4 N* R1 m; a4 d2 S! K/ B! k+ T
  9. uint8_t uartRecBuffer[SHELL_UART_REC_LEN_ONCE];                //串口接收的buffer5 k) w: `! ~7 [" H& H; F
  10. / a2 k- |9 J* ^. v

  11. , m" t. C$ r+ p9 H& a' b3 }: r
  12. /**
    : M3 y# @6 k* _% Z
  13. * @brief 用户shell写# b* R$ q9 y+ t& z
  14. *
    ) g9 f4 @$ L' \: U3 Y
  15. * @param data 数据
    * l% B& E" d+ l, D
  16. */
    % z. f2 A' X8 i, V" E
  17. void userShellWrite(char data)# j& c; L  D8 R3 x0 ~! O
  18. {
    + Y& m' }3 t0 i/ q, F0 Q4 Y
  19.   HAL_UART_Transmit(&userShellhuart,(uint8_t *)&data, 1,1000);1 |+ Z. ?& c1 p/ |& H
  20. }
    7 f( |1 k7 d8 E" m1 m8 W8 ^

  21. ) Y: M3 x; w" r9 P; r# \
  22. $ i( y- A, b: S+ E
  23. /**
    8 _/ l" N2 {# \, X1 `& _
  24. * @brief 用户shell读
    6 M: K) t4 X( @9 x( u7 s
  25. * 4 i" o; O: e5 X- p
  26. * @param data 数据
    ( p$ B3 B/ P1 _+ V, q/ ~( e# Y9 ?
  27. * @return char 状态
    ! ~$ ?0 X9 ~5 M3 [5 B5 T
  28. */5 U. ]: M( T  O0 t
  29. signed char userShellRead(char *data)" W8 X$ k4 {7 `' X
  30. {
    ( t$ g! h0 o' `! _5 ?
  31.   if(HAL_UART_Receive(&userShellhuart,(uint8_t *)data, 1, 0) == HAL_OK)
    5 {" [' n; o8 ~
  32.   {" K4 Y2 ?2 {: U: ?# A9 h5 J
  33.       return 0;
    ' q8 F# I; Y  T4 N; _5 w  ?" l" t
  34.   }' |1 _1 m, k+ B2 {! I+ M& v3 @
  35.   else
    : D$ X$ P4 O* i5 ~6 g0 G1 J4 o) H( d
  36.   {9 ~( {. [" \: g* X; W, o
  37.       return -1;
    7 v! ~' u- [2 _) p. f
  38.   }; t9 }& J, a7 R" ]3 q
  39. }
    3 d  Z8 Y+ u# J6 o! g$ n7 K  q
  40. . v' H! I. C" b7 o7 H0 y
  41. /*** H9 \" G4 g5 j
  42. * @brief 自定义函数串口接收完成中断 RxCpltCallback  ?( e( N$ i9 z" L7 S
  43. */, a" ]& B1 O8 l
  44. void ShellRxCpltCallback(UART_HandleTypeDef *huart)
    4 L' v& X2 j  s, w$ X$ L
  45. {
    : a! s, P5 X( `8 o
  46.   shellHandler(&shell, uartRecBuffer[0]);   //命令行处理函数
      O3 i5 E# X' B; K( S  ]' Y
  47.   HAL_UART_Receive_IT(huart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE);//使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量. ?# J5 o% d" ]( K# u
  48. }9 V* z3 O, J  s

  49. 6 W& R' N) q; r2 r. ]
  50. /**0 G( g  S7 z' \8 ^
  51. * @brief 注册串口接收中断到自己的自定义函数
    7 c$ [5 C1 ~3 c* J3 r9 m
  52. */
    9 m$ s, ]' B5 r
  53. void ShellReceiveCallbackRemap(void)7 i6 K2 W9 ]) m$ m
  54. {
    7 q/ b/ Z9 ~) a2 c6 |* W
  55.   HAL_UART_RegisterCallback(&userShellhuart,HAL_UART_RX_COMPLETE_CB_ID,ShellRxCpltCallback);    //注册串口接收中断到自己的自定义函数
    0 v- T2 h; y7 @; M) ]4 L1 n
  56.   HAL_UART_Receive_IT(&userShellhuart, (uint8_t *)uartRecBuffer, SHELL_UART_REC_LEN_ONCE);      //使能串口中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量) _. v& R& y1 C& Q% q1 c$ y7 ~
  57. }
    ! b' b7 t# d' F9 \

  58. - P& H3 Z9 ^" f& t) |
  59. : e/ P% R' z1 l2 g4 I
  60. /**3 P, M( y8 b9 m: ]) }7 w2 M6 v
  61. * @brief 用户shell初始化
    ' a- w1 k' Q/ O& u- k& g
  62. *
    ) n2 r2 e" g3 I- @9 q
  63. */3 ~- n; a) }. C' I/ C! F+ }7 [
  64. void userShellInit(void)2 I% k: Z# E8 ]% g4 K& M
  65. {
    2 V) s" }+ a) I0 Y1 ~* [, j
  66.   shell.write = userShellWrite;         //指定串口写入的函数
    / K+ g0 f) `: d* b$ f, R+ R7 d
  67.   shell.read = userShellRead;           //指定串口读取的函数
    $ ^. Y0 C( }5 N  U9 r
  68.   ShellReceiveCallbackRemap();          //注册串口接收中断到自己的自定义函数
    7 W+ T, n- l! m; m: S* ^9 O# z
  69.   shellInit(&shell, shellBuffer, 512);# T/ y. B/ i8 O1 D/ Q, f
  70. : C2 g1 M& [. `7 F# n+ @
  71. }
复制代码
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
赞 收藏 评论0 发布时间:2022-8-23 16:13

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版