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

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

[复制链接]
STMCU小助手 发布时间:2022-8-23 16:13
前言
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
  1. /**' O/ g- ^; L8 m) {
  2.   * @brief This function handles USART2 global interrupt.
    % g, i# A' M$ U
  3.   */
    " L1 E# N* }, l" a: J) F7 g3 [8 I
  4. void USART2_IRQHandler(void)
    3 O' t5 b3 T9 y4 K! G) O
  5. {" ?, u. y; L! U; }
  6.   /* USER CODE BEGIN USART2_IRQn 0 */6 l, I) D, L9 h6 x

  7. 0 E) V. k7 w( j) L
  8.   /* USER CODE END USART2_IRQn 0 */6 h. f, \/ f; ~6 O' z* L
  9.   HAL_UART_IRQHandler(&huart2);+ L' i( m4 C2 J9 j' V/ K) B2 g
  10.   /* USER CODE BEGIN USART2_IRQn 1 */
    5 O7 l" q. s8 R( z6 a! F! f. x
  11. ! ]5 _( N& ^0 c" d6 G7 _
  12.   /* USER CODE END USART2_IRQn 1 */  R8 o2 _) p' _1 |
  13. }
复制代码

' 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
  1. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)/ x5 D, l& g% }4 `5 ^- K
  2. {
    3 d% u3 h$ m( A! P+ V
  3.   uint32_t isrflags   = READ_REG(huart->Instance->SR);
    3 b5 P$ k( \: I, U. w7 y. F
  4.   uint32_t cr1its     = READ_REG(huart->Instance->CR1);; Q! i' T# s/ i8 H- ]( S
  5.   uint32_t cr3its     = READ_REG(huart->Instance->CR3);( R% e6 U' \8 M6 M/ c3 I
  6.   uint32_t errorflags = 0x00U;
    + v  j* w' U6 k4 @7 i
  7.   uint32_t dmarequest = 0x00U;
    * L5 k1 N8 H- A! j8 z. |; y& [
  8. $ D" t5 p3 f7 H3 g* k$ k. n
  9.   /* 如果没有错误发生 */% _, N$ K! y- M% z) g7 S) l" X- L
  10.   errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
    . O2 }7 v* F4 H+ w+ n" H
  11.   if (errorflags == RESET)
    & N0 G  x7 Y+ B
  12.   {" ^! d& i" `  k4 K
  13.     /* UART处于接收模式 -------------------------------------------------*/
    1 n- J( l* H7 L3 q
  14.     if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    1 [+ C/ `  S4 M! R
  15.     {
    : r! q/ [6 G( S( x
  16.       UART_Receive_IT(huart);1 Z5 k8 P6 w  y* h) \
  17.       return;
    " m+ |( g7 n' |* Z0 Z- f
  18.     }4 p' i9 L# c8 z+ d* A
  19.   }2 W+ B6 f, c4 q, r8 ~3 Y

  20. - N' U; @9 \; f, K! n( _
  21.   /* 如果发生一些错误 */" T  P1 j, W1 O; t9 c+ ^( c
  22.   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
  23.   {
    & N; H. b' b& v+ l3 W# O6 ]1 b  U
  24.     /* UART parity error interrupt occurred ----------------------------------*/
    3 z6 r/ c- A4 g
  25.     if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))) x# W. a8 u2 q9 `- d8 J
  26.     {* c( f2 W# h# U8 T2 z9 E
  27.       huart->ErrorCode |= HAL_UART_ERROR_PE;
    : \6 r. n# z$ z; g% }
  28.     }
    ' v5 f3 X/ b6 J1 r. v, {% R! F
  29. ! t  f. l; r$ K, E; [: L- V
  30.     /* UART noise error interrupt occurred -----------------------------------*/  M& ^7 M% o* j1 E& d
  31.     if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    7 j6 |. E0 w3 l, t
  32.     {1 K% M" t4 [( V% S0 n0 X
  33.       huart->ErrorCode |= HAL_UART_ERROR_NE;5 M3 g3 j( z' y5 x6 ^
  34.     }
    ) z4 r/ g- |0 r/ g9 p5 r1 |- ^

  35. ) G5 {( O& U! r3 ~! h" ~
  36.     /* UART frame error interrupt occurred -----------------------------------*/7 W7 Y+ r/ Z$ K/ j
  37.     if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))# v6 q' e  O; A$ _
  38.     {5 K, ~0 y) l; k5 o# Q# A/ |
  39.       huart->ErrorCode |= HAL_UART_ERROR_FE;  t8 ?7 t0 z' L
  40.     }
    4 b; {+ b- x+ x+ l! O" f. k/ s
  41. ! r& S0 v+ {5 Q8 Z+ k" |
  42.     /* UART Over-Run interrupt occurred --------------------------------------*/& Q( V& q9 n: E. J) U: T/ A
  43.     if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET)))5 h) B  f" ?+ x7 I. y
  44.     {  M  T  Q$ g& J+ P8 L+ U3 T
  45.       huart->ErrorCode |= HAL_UART_ERROR_ORE;
    : ~3 t* q; @5 P" T0 Y& ]$ }  i
  46.     }& V8 }% H3 a7 m7 }$ [. i, b
  47. : u6 ^% h/ A; C1 `( D
  48.     /* Call UART Error Call back function if need be --------------------------*/
    2 ]; M+ m2 s( @) p
  49.     if (huart->ErrorCode != HAL_UART_ERROR_NONE)
      A8 k6 i2 K. n/ p8 N- a, W
  50.     {6 a# M; r/ B  Y
  51.       /* UART in mode Receiver -----------------------------------------------*/
    8 m' U; u, j7 k! ^" ?
  52.       if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    ! M/ O1 Q+ U' z, ~1 Y
  53.       {4 V; J  u' T& ]1 |2 |! u
  54.         UART_Receive_IT(huart);( S( j0 o( K6 o9 h  W, Y, y
  55.       }
    " `+ Z6 q4 F  m3 p& t5 Y7 t

  56. 9 a, E5 [1 O& v# x: w5 C3 K' r7 K
  57.       /* If Overrun error occurs, or if any error occurs in DMA mode reception,  `. ]7 c7 J" C( e" E3 w
  58.          consider error as blocking */
    5 e* N# d, n2 E0 j6 b( Y
  59.       dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
    8 i7 r1 t- c1 S2 `; e
  60.       if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
    0 B  h9 I1 `1 D" G; X# U$ S$ M3 e
  61.       {
      L; m6 g' d+ t) k
  62.         /* Blocking error : transfer is aborted: X2 ?: V  t% `- i
  63.            Set the UART state ready to be able to start again the process,
    & X3 @- q# o9 ?2 a
  64.            Disable Rx Interrupts, and disable Rx DMA request, if ongoing */" \% u/ N+ r4 K7 \& ?0 s. ^1 S% x' O
  65.         UART_EndRxTransfer(huart);$ b* K( a" J# s$ k: S# _8 H9 c

  66. + R; \5 x. R+ g. B( ?
  67.         /* Disable the UART DMA Rx request if enabled */, n+ U+ z: ~9 R: z3 I
  68.         if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
    1 b* h) w- J9 ?. M
  69.         {
    6 s+ }( x* V9 U8 Q3 \! H6 a
  70.           CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);6 L/ x" R' K6 m% D! ^" b/ ^. P: o- G

  71. ; m) h' M' K1 y* T
  72.           /* Abort the UART DMA Rx channel */
    2 }7 I" ?6 c3 }7 v$ m, r9 I6 b
  73.           if (huart->hdmarx != NULL)
    / M& E" b% a9 @
  74.           {' ?2 f( `# v  b/ j( @/ T2 M
  75.             /* Set the UART DMA Abort callback :
    : I* T0 s4 a$ B. `+ O& i% M% k/ P
  76.                will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
    6 t# F3 k/ z6 b7 D0 ?
  77.             huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;) B+ t1 _1 j- Z5 {. O
  78.             if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
    " }% @; p0 l4 f6 b! ]3 K$ m7 h6 x
  79.             {
    ! y" R1 Y8 d1 E4 o1 m% l# Q! h# X
  80.               /* Call Directly XferAbortCallback function in case of error */
    2 f" u* ?# R$ d1 f7 t7 k5 ~
  81.               huart->hdmarx->XferAbortCallback(huart->hdmarx);
    " t! l1 o+ A6 \/ d3 [# l
  82.             }
    8 [/ A, g1 v# \* j& V; H, n
  83.           }/ w  H) t% b( _, t2 t2 K2 s; w
  84.           else! [3 {3 @* o# P3 b6 c0 e; z
  85.           {
    2 w) w7 a' `5 u. m( I! K
  86.             /* Call user error callback */
    : R4 G, R2 }8 s& }( `7 B& W' p+ V# c
  87. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)' E+ W' \# E' M6 c  a
  88.             /*Call registered error callback*/$ t! v0 W% Y- i  N, U  ]
  89.             huart->ErrorCallback(huart);' i' K* K: ?2 m+ K0 a! @5 ?- z3 K
  90. #else
    ) C; s4 }/ `! }& ?- q6 m2 X7 ?
  91.             /*Call legacy weak error callback*/9 o6 D. F% T8 H* Y
  92.             HAL_UART_ErrorCallback(huart);
    & W; W5 v: K8 i- D4 W& a
  93. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
      L/ F! G4 Z# T7 _. r( F- O: C* R
  94.           }
    ' e( C# S4 w/ ?% P3 ?, [
  95.         }
    * \0 h( h, V3 L1 N1 y* V* ~
  96.         else. c* O. @5 ~( d+ `9 h% {+ B) S
  97.         {
    ( Q, z3 g* p) e8 M- l
  98.           /* Call user error callback */# H* h4 A1 g. q" B# K+ ~. C
  99. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    7 i, Q8 j3 f- ]0 B
  100.           /*Call registered error callback*/: l4 w* p# K+ \5 M% c
  101.           huart->ErrorCallback(huart);/ K* R7 y  ~: Z9 F  l8 P
  102. #else
    ) w' a. n8 T  S4 d
  103.           /*Call legacy weak error callback*/
    1 u& _* f4 B( z  O8 I$ j
  104.           HAL_UART_ErrorCallback(huart);3 T6 Z, B/ r# [3 u- P
  105. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */6 Z' o5 r, V: ~
  106.         }
    & s' [- [, G# L  ]7 j
  107.       }" E6 i& r' N! z( ?
  108.       else
    5 c2 Q: Z1 a  ~6 l" I
  109.       {
    ; L6 E% S- F& `7 s
  110.         /* Non Blocking error : transfer could go on.
    / D$ Q/ S1 F& @7 g  G
  111.            Error is notified to user through user error callback */
    & f( m. Q1 D8 X7 H0 B9 i* ?. w8 J
  112. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    ) h2 x; V6 {* y% e8 C
  113.         /*Call registered error callback*/
    + t% b7 w' X9 |  n5 u8 I
  114.         huart->ErrorCallback(huart);& T% C" [. Z# A6 W8 U
  115. #else
    7 T2 c# H6 ^+ R4 E/ I* k
  116.         /*Call legacy weak error callback*/
    7 F; v, d1 V) U
  117.         HAL_UART_ErrorCallback(huart);
      [- L6 L! d& Q/ `* p
  118. #endif /* USE_HAL_UART_REGISTER_CALLBACKS *// @5 t& S8 [/ C1 x
  119. ! i  x+ A3 ^% u0 z  M) z- w1 g
  120.         huart->ErrorCode = HAL_UART_ERROR_NONE;4 x2 l  G" d( g1 E' M7 ]4 n$ {
  121.       }/ G  N  v8 X% u4 L* P/ n
  122.     }6 v* f3 }0 J8 F, ]
  123.     return;
      @. W& k( M! x' V4 d
  124.   } /* 如果发生错误则结束 */$ t2 O/ e1 a* L# K7 V6 B0 F9 x
  125. 7 r! i$ b. r  c8 M) d; |
  126.   /* UART 处于发送模式,准备在非阻塞模式下发送大量数据。 ---------------------------*/9 R! F$ u$ {8 K0 C9 L- o# V. x3 \
  127.   if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
    7 d/ i) ?+ f$ Y7 s
  128.   {/ v) ]8 f* B; q
  129.     UART_Transmit_IT(huart);/ ^& w5 w( m9 _" \- E
  130.     return;
    ' o2 e" n4 h/ S! E4 R7 r, J' Q
  131.   }5 i: z1 G" o9 k' }4 E5 x4 c

  132. 7 j7 s6 @& |) p$ a
  133.   /*  UART 处于发送结束模式,在非阻塞模式下结束传输。 -------------------------------*/$ n/ \  h. f9 H& c; t: }
  134.   if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))' H3 Z* ^5 l7 q* S" b
  135.   {+ j* p$ V, ?; f4 m, R% u( Q
  136.     UART_EndTransmit_IT(huart);7 p) c' F: z: p) l! _+ Z2 H
  137.     return;: N6 X2 U* @( ]- U3 B2 ^% e
  138.   }
    0 i! R. h0 x- Y9 [" A' l4 S2 D2 l: B
  139. }
复制代码
/ 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
  1. static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)/ d3 z/ B3 K7 \9 j* |+ X
  2. {
    . O6 T+ x6 `) ?7 T7 C% \
  3.   uint16_t *tmp;
    ! v% B$ W2 R* n1 I( d  J/ q) s" i
  4. , L1 q: M) m% _# M7 s7 r# X1 B0 D; @- @
  5.   /* 检查接收进程是否正在进行 */
    ) M$ y0 I$ A4 @7 }) }
  6.   if (huart->RxState == HAL_UART_STATE_BUSY_RX)+ F' G. k4 }7 @% d( r) |
  7.   {' u7 v+ P/ E' q5 b* `4 M
  8.     if (huart->Init.WordLength == UART_WORDLENGTH_9B)/ W. n* h' o: `% r8 G0 @
  9.     {
    8 B* Y/ d( Q3 R, W: Z8 Q- b* s& P
  10.       tmp = (uint16_t *) huart->pRxBuffPtr;
    ( M# G2 m$ h. l7 }; v
  11.       if (huart->Init.Parity == UART_PARITY_NONE)/ d( P1 C4 L: o/ U
  12.       {% d5 v9 B% @5 N8 ]! l% u
  13.         *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);& _* m: ^  E; Y; m. y
  14.         huart->pRxBuffPtr += 2U;3 X) G' y! A% |% C$ ^
  15.       }- M( Y% \* v* X% A! }( v8 t2 D8 t* a* J
  16.       else
    ! }/ \, y; R$ T
  17.       {
    6 @  q% Z- Y! c& E" j
  18.         *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
    : t6 q( ~- L- j3 A0 i3 e1 |! r" v
  19.         huart->pRxBuffPtr += 1U;
    ; P4 S4 \% ^$ b9 z: Z" P
  20.       }
    : _# y* _# X  R& w5 E7 |3 P
  21.     }
    7 H, r! U, Y+ x% x- G& B
  22.     else4 m  n4 m1 M( Y' s; }' d7 k5 T
  23.     {
    - b8 @% \  T# q( A( U* p" |
  24.       if (huart->Init.Parity == UART_PARITY_NONE)
    5 G- L; L4 k! k7 r! v  k
  25.       {7 X6 ?! x  u# d6 z2 F" t
  26.         *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);2 R$ r6 [# I( T2 p! i
  27.       }8 @. r+ x0 r9 l. [8 [
  28.       else) H* O9 s0 e5 D( T% p
  29.       {( Y- ^+ E+ P# H. [* I
  30.         *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
    . I( F  c. w# P- a5 I
  31.       }
    / J2 n+ K" L' i# I; ]$ S
  32.     }0 B( H3 G$ R7 f' O

  33. ) ]4 r6 m# p8 q& a. t' Q& f
  34.     if (--huart->RxXferCount == 0U)2 N; h& ?# @! [* b7 D
  35.     {3 x" E; Q9 q( o, L2 p4 f
  36.       /* 禁止UART数据寄存器不为空的中断 */
    $ Q& P& W+ o; G: \9 _+ X: W  _
  37.       __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);# L* B' ~1 U& ?+ H9 }

  38.   S( z- b# S3 |7 u& B1 U  N
  39.       /* 禁用UART奇偶校验错误中断 */
    ' c# c' z$ U. f
  40.       __HAL_UART_DISABLE_IT(huart, UART_IT_PE);
    ! U1 a7 l" |! D0 ?7 U6 A
  41. . ~: l0 v& o% V$ R% \
  42.       /* 禁用UART错误中断:(帧错误,噪声错误,溢出错误) */1 l0 ^- Z1 y" |9 ]1 n" d1 [
  43.       __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
    2 s4 d: m  g2 V8 F
  44. $ [( ^5 S* q5 [1 r% N5 L+ C
  45.       /* Rx进程完成,将huart-> RxState还原为Ready */+ e. p' n1 L5 A/ Q3 i
  46.       huart->RxState = HAL_UART_STATE_READY;! S. `5 V- q& D$ L
  47. * e  ~' Z4 b: I7 m0 \/ ^7 g. v
  48. #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    ; U" Z9 j1 Q( H  X2 B8 X
  49.       /* 调用注册的Rx完成回调函数 */
    ; i9 n$ n, b6 U# S, `
  50.       huart->RxCpltCallback(huart);. G3 k4 q; i! C2 x
  51. #else
    ( G  J3 Q$ @  u
  52.       /*调用旧版弱Rx完成回调函数*/
    ) R( Y! k6 S1 T' x# A
  53.       HAL_UART_RxCpltCallback(huart);
    : w* L/ v7 }) x6 U
  54. #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
    % V  `5 n( D# e* }9 F; D, s

  55. + t) |3 ]4 T. B& _0 v& v
  56.       return HAL_OK;; Z: v: s; k( q' \7 k0 I. [* y' [
  57.     }
    6 T! R/ y6 a0 H0 p$ Y
  58.     return HAL_OK;
    0 j! j- y6 f; `2 X
  59.   }
    ( f& F( c5 z" D6 k* e
  60.   else* R, a! P, F7 i4 s
  61.   {* ~8 o) i7 q( J( |+ K
  62.     return HAL_BUSY;. W% J2 C/ Y1 [7 I
  63.   }
    ' g$ @9 J5 s7 i5 T
  64. }
复制代码
# 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 20210131202630176.png
; 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
2021013120265496.png 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; ] 20210131203021383.png
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
  1. #include "shell_port.h"* l) \) x0 ~' k+ A! v4 A
  2. #include "usart.h"' l3 @& U- y( w- u

  3. 7 Y2 G: q0 E$ Y0 J7 q8 a" O
  4. Shell shell;2 w4 X2 D) C" y8 s$ L( v3 R
  5. char shellBuffer[512];/ b& n4 `* e% g% q. [
  6. % ]" ?; y2 ?: `( V! Y! m7 `
  7. #define userShellhuart                             huart2        //shell 使用到的串口句柄
      k5 x) M# T6 l- E
  8. #define SHELL_UART_REC_LEN_ONCE         1           //串口单次接收的个数/ B" J' ], @, {- i
  9. uint8_t uartRecBuffer[SHELL_UART_REC_LEN_ONCE];                //串口接收的buffer
    , d( o- j: y" v: [
  10. ; C2 o. Y0 V2 D- W  Q& D

  11. 8 X5 I( Y& }! j5 @& ?
  12. /**
    5 O0 I! `& q( l0 y% W
  13. * @brief 用户shell写, F4 C1 c- f* x# j% }2 V
  14. *
    7 X) ]0 `0 v  Y
  15. * @param data 数据! K& R; @4 n! [
  16. */
    # s3 i8 h) M8 d) J1 k+ p
  17. void userShellWrite(char data)% Z/ s% [" v$ A) M. n/ S
  18. {' o- R: H! n( X
  19.   HAL_UART_Transmit(&userShellhuart,(uint8_t *)&data, 1,1000);
    8 B4 t* _' i- L
  20. }
    + O0 }! P. Y8 {) o8 Y9 z* }9 I

  21. " E  e! R' g% U% ~8 y; a0 g/ h
  22. 7 M5 G) X, X. M7 n4 B5 [" _
  23. /**
    + ?7 n4 B9 C. F9 d
  24. * @brief 用户shell读8 r2 T% F& v& x. x
  25. * $ I& |: n0 e; T
  26. * @param data 数据
    ( @% h% [1 J. l% G
  27. * @return char 状态' ^  R8 U! t" t9 H6 u" T
  28. */% r0 f, g$ q7 k- [/ G, o. `. @- I
  29. signed char userShellRead(char *data)/ h+ N) L/ F2 K  ]
  30. {3 y( j  F1 N: o+ r
  31.   if(HAL_UART_Receive(&userShellhuart,(uint8_t *)data, 1, 0) == HAL_OK)
    - P4 A& {3 O7 g* g& t1 I; ~; j4 z! A
  32.   {/ G) d- N- m. M( p9 Y
  33.       return 0;: ~3 K; c) g* \0 ~1 I/ _$ ^; d
  34.   }
    4 u7 }& t' a5 R' B. [4 S1 R+ i
  35.   else
    . y8 K, y) e# p7 x* v$ R* j/ Z' e
  36.   {( K3 B) G$ o& I5 [) q" m
  37.       return -1;  u) D( k0 q( ?5 U& P
  38.   }# l, }0 S+ r4 D# \2 E7 g
  39. }/ A- [; d3 k& z$ `! U; Y' W

  40. ! h& n3 A  G7 P
  41. /**4 z$ I4 Z0 N) s# Y% I$ J
  42. * @brief 自定义函数串口接收完成中断 RxCpltCallback. z0 t$ u2 g$ E0 g* x6 K4 |
  43. */
    2 G! \5 p  K! Y& \  o9 Q, Z. x
  44. void ShellRxCpltCallback(UART_HandleTypeDef *huart), ]8 \4 x8 q5 n, l9 P
  45. {) U6 a# o  G' |3 d3 N
  46.   shellHandler(&shell, uartRecBuffer[0]);   //命令行处理函数$ H# A; L' m% O' Y
  47.   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
  48. }. }# d! ^( f* A8 v& f

  49. 4 m* `% }' O$ b# J% i# X2 A4 Z: v
  50. /**- y) n  d7 R7 A" M" O5 l
  51. * @brief 注册串口接收中断到自己的自定义函数# K7 w. y0 k; W
  52. */
    8 }$ B% _+ z% w1 O
  53. void ShellReceiveCallbackRemap(void)7 W) i+ l! B6 ~0 S
  54. {8 g6 ~1 |( N2 U; t
  55.   HAL_UART_RegisterCallback(&userShellhuart,HAL_UART_RX_COMPLETE_CB_ID,ShellRxCpltCallback);    //注册串口接收中断到自己的自定义函数2 o! {0 A% l& T7 _! b- l8 T& q
  56.   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
  57. }
    4 w% c5 R0 `, w" k
  58. 1 r* i' x' E2 e: n7 z4 |+ j
  59. / u7 n6 }& k$ [) `- X6 b1 [
  60. /**
    7 n% w1 q& B/ g2 X: O
  61. * @brief 用户shell初始化# A# j  H% V9 M& G2 S
  62. * 1 p* L  l3 ~" s  Q: |  G
  63. */4 ^" t! V- K0 Y% v8 e# p
  64. void userShellInit(void): p, z" F6 O# R
  65. {" Q. y7 Y% Z5 D& C: i
  66.   shell.write = userShellWrite;         //指定串口写入的函数
    - T( K: ^; h, f2 H9 u& ]  B
  67.   shell.read = userShellRead;           //指定串口读取的函数/ M, Z$ n" D# e' {6 @
  68.   ShellReceiveCallbackRemap();          //注册串口接收中断到自己的自定义函数$ M" o# D# w4 y' s7 O: `3 V4 }
  69.   shellInit(&shell, shellBuffer, 512);
    5 \# o( W. l2 i( i: O- `/ _9 p0 G

  70. 5 B1 V9 O0 s( t. P
  71. }
复制代码

/ 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
收藏 评论0 发布时间:2022-8-23 16:13

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版