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

STM32Cube HAL库中断处理机制

[复制链接]
gaosmile 发布时间:2021-2-19 11:28
STM32Cube HAL出来六七年了,还是有很多初学者没有适应,今天就分享一个读者问到的关于中断处理的问题。
0 ^: }2 @0 q; o
很多人都知道STM32CubeMX这套工具的一个目的:减少开发者对STM32底层驱动的开发时间,把重心放在应用代码上
7 x2 @6 |2 v8 s) G
* a" y; ]( ?' K7 b- W3 _
但是,STM32CubeMX只是生成了底层驱动的初始化代码。所以,我们还需要掌握:应用层代码如何调用HAL库函数(API接口),以及HAL库中断处理机制等相关知识。
6 e, T0 w1 E: i
HAL库牵涉的内容较多,下面简单描述一下HAL库中断处理,以及相关的回调函数。

, E/ v8 O. G, h
1HAL库中断处理机制

之前使用标准外设库开发时,中断程序(函数)由我们自己实现。


* Y2 A( Y& M; y# u  l1 N+ v
而HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理,如下图:
微信图片_20210219112745.jpg

6 L) a! X5 z% u- [. Z4 V( Y2 L

其它大部分外设(TIM、SPI、CAN...)中断都类似,HAL进行统一处理。
, q& n+ n! y" ]$ a# _, [- W( u+ M- M+ h( g
也就是说,HAL已经帮我们把中断处理函数写好了,我们只需要调用相应函数来编写应用程序就行了。
+ D; G: x' N* c- Z$ m
HAL_xxx_IRQHandler里面做了哪些处理? 我们以STM32F1的HAL_UART_IRQHandler为例:
  1. & J) B6 U0 e3 ~5 m
  2. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
    / V  T6 R4 r4 ^# A8 Z8 C- [' [8 H; b
  3. {7 s* q3 t. S# z( a
  4.    uint32_t isrflags   = READ_REG(huart->Instance->SR);
    $ g; }, o1 N6 V  ~2 X9 J8 @
  5.    uint32_t cr1its     = READ_REG(huart->Instance->CR1);
    . r# k# W- M4 [
  6.    uint32_t cr3its     = READ_REG(huart->Instance->CR3);
      Q. P, i7 `+ L' _# D
  7.    uint32_t errorflags = 0x00U;& F2 c9 P+ Q& p8 n: y8 q( D
  8.    uint32_t dmarequest = 0x00U;' W9 t) o1 a! c+ c' C5 G, K, R

  9. - c9 `( o% i9 |# M3 [# q
  10.   /* If no error occurs */
    $ x. X- I6 P& b: a" A: {$ @
  11.   errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));, D  R# ]5 |! ?& D* z8 L' I: Q! n
  12.   if(errorflags == RESET)
      G& J" b4 h8 p  ~# B
  13.   {* m: p7 @$ W% B- n4 w
  14.     /* UART in mode Receiver -------------------------------------------------*/
    " }8 o. ]/ r, f6 O1 F
  15.     if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))" t4 B  C# D( D; r# q
  16.     {$ ]/ ^7 v% h' n, \% G4 _, y
  17.       UART_Receive_IT(huart);4 o* F; k  g% m- d
  18.       return;
    9 r3 J) N1 |9 p3 E
  19.     }* I% r4 [: _$ j$ o% c  N: J5 h5 O
  20.   }
    3 b2 c" m. v5 B& a: g

  21. ) w6 B4 V, ?7 ?# E& w) s' I* c
  22.   /* If some errors occur */
    ) r4 x! @# W7 S7 A
  23.   if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))' V. ^" z  R5 j4 B# {) {5 Y4 W
  24.   {
    9 X- `  I, r7 f6 S: g% Q6 p; _
  25.   /*) A! @5 ^& ^2 c2 w7 n7 t" E, H
  26.   ·
    - N% l5 d0 t, r2 k2 T# M4 U
  27.   ·删减了部分代码
    : q% y/ }' X" ^9 f0 x# w: o
  28.   ·- y& U/ z( p. [2 a( ?
  29.   */( b9 }- j$ t$ \2 x  S- ~" _/ |, \
  30.   } /* End if some error occurs */6 l$ P  U3 j- I/ |+ \" F

  31. 5 {/ w- O* v' d: ^$ m( ?' G
  32.   /* UART in mode Transmitter ------------------------------------------------*/
    - P* W5 A5 @6 s0 O4 A6 d
  33.   if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))0 g8 `2 ?1 U4 t1 Q7 ]$ |
  34.   {
    . T+ d% a' _6 c' c4 E
  35.     UART_Transmit_IT(huart);
    7 X6 h. c. B" v5 Z0 T' m
  36.     return;) J. q& o: O. ?
  37.   }
    6 F$ ^3 L7 c, h- _5 P
  38.   /* UART in mode Transmitter end --------------------------------------------*/
    : R3 |# r+ F& V3 A' k2 A& @9 C
  39.   if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))( _" r% n! g9 u2 m3 F! P/ \3 k, ]8 p
  40.   {1 E! W9 Y! Q' u5 M" v
  41.     UART_EndTransmit_IT(huart);9 {' H2 x: H' C$ A# {. z9 Z# A7 R
  42.     return;
      h- N# k/ W& H: w7 y
  43.   }
      B% i2 K* _4 _; N
  44. }
复制代码

8 X4 D& V8 |* L


2 \' f# h* V* T9 v: l" W$ ^

其实,大家认真看一下代码应该能明白,这些和我们编写的中断处理函数是不是有类似之处?
' Y8 k3 O* ]& I+ @
这是无非就是接收中断、发送中断、错误中断等一系列处理。只是这里又进行了再次封装,比如接收中断UART_Receive_IT
/ @# L5 T0 y/ s) C2 A# |( k: X- O9 E
当然,这个UART_Receive_IT接收中断实现方式又可能存在不同。像F0、F1...就是直接调用这个接收中断函数来进一步处理。
. h& i0 S$ |- R. A/ v1 T- Z" T
像L0、G0...是通过执行指针函数RxISR来进一步处理。G0的接收中断处理为:huart->RxISR(huart);
& R# D: Z# ~  J! s) k

  1. 0 t0 W/ k; h) `& d! g, q5 d
  2. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart), e: R* M$ b6 M- W/ x3 h- }5 V
  3. {& L' n6 \8 i8 k. m
  4.   //删除了前面代码/ e$ r' X, ^/ d1 C# @- A4 o
  5.   /* If no error occurs */* i. a! I$ J2 `/ m, p- x1 F% Q
  6.   errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));
    ; v. o$ I( i# l. Q  e. B$ e$ U
  7.   if (errorflags == 0U)
    / g1 D: x( I1 M: u5 g
  8.   {* F: K" o4 ]3 V# P
  9.     /* UART in mode Receiver ---------------------------------------------------*/) H* J1 |9 ?: }6 W, j$ L! K
  10.     if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
    5 I$ }, s3 h# I! k0 e* Q
  11.         && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)7 D2 y. ^" e/ \. L
  12.             || ((cr3its & USART_CR3_RXFTIE) != 0U)))+ K8 d2 Y  W7 \/ D( T
  13.     {1 ]* E! C& y8 Y
  14.       if (huart->RxISR != NULL)+ M, \$ b% \) j" ^7 Y7 a$ J
  15.       {
      u6 w1 n& t7 k8 d5 f" f% n
  16.         huart->RxISR(huart);, [" x: w* P! t- ?7 X$ h
  17.       }
    & ^" c. J% I8 t7 v$ O6 f* g
  18.       return;
    1 A+ X. _1 S2 f& W& l; R) S; o
  19.     }6 J3 E5 m2 s) R3 Y
  20.   }) G! X& C1 U' I
  21.   //删除了后面代码
    3 N; u1 G, O0 L# b0 Y
  22. }
复制代码
( m6 q0 w+ E: A
. ~; k7 \/ d: G: I
看了上面USART中断处理的函数,大家有没有得到什么启发?
( J# y5 L( s9 ^
其实,HAL库里面处理机制基本一致,只是实现方式上有所不同
, m! |$ C" J% J' \) p$ Q
如果你摸清楚了HAL库基本原理,相信阅读HAL库源码,或者使用HAL库编写应用代码不是问题。
  G4 J6 w: u- r
2回调函数实现原理

在HAL库中存在大量类似HAL_XXX_XXXCallback这样的函数,这些都是回调函数


( j! i6 A, I  O* u! t0 l# b4 o

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。9 J# Z% A; T& p# ^* O! p7 y6 }
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

---来自百度百科1 F3 S! v6 ]3 c/ x5 X- L2 x

- g7 Z* _1 `1 f' R' W: Y) Z

HAL库中断处理使用了较多的回调函数,还是拿UART接收中断来举例说明。

3 N5 j' [* |( u1 w* ?6 @

初始化配置好UART中断接收,如果有中断请求,就会执行回调函数HAL_UART_RxCpltCallback

" S; T' G8 E/ k1 Q! K

看上面回调函数的定义,通过特定条件调用『回调函数』,这里触发的条件就是中断。

: J5 \8 L2 H) U
3
扩展说明

这里也简单说几点:

  S9 z3 V; ]% i& u, q# j2 O
1.初学者想直接使用HAL不是不行,需要有一定C语言功底
针对大部分初学者来说,是不建议直接上手HAL。但是,有部分C功底较好的,还是建议直接上手。
6 Y! u, g6 p( ]
2.学HAL,建议参看官网例程
很多人不知道如何找资源,我不止一次强调,官方的才是最好。在HAL库中Projects目录下就有很多例程Examples。

5 [# {4 E& p- B  L% j
3.我们追求效率,可以HAL库源码" q" b8 p& D# |: S3 {
如果你想修改HAL库源码,允许修改少部分。如果要大量修改,还是别折腾了。

9 \! T' A; w/ o* K; J9 B
4.实际项目需做一定修改
STM32CubeMX仅仅是生成初始化代码和工程,你实际项目中一般都有自己的软件架构。
特别是项目越大,软件架构就需要更加规范。

2 S' ]$ _" r8 E/ t: t
比如:生成的gpio.c文件名,你需要修改成bsp_gpio.c.

2 o# i! t; V3 G: v8 ^
再比如:函数MX_USART2_UART_Init改成MX_DEBUG_UART_Init.

- m+ C  [' {) I0 h2 P8 r+ f3 Q% |( q; J  y2 H. m- ^6 B
收藏 1 评论0 发布时间:2021-2-19 11:28

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版