STM32Cube HAL库中断处理机制,以及回调函数实现原理

置顶/星标公众,不错过每一条消息!

640?wx_fmt=jpeg

    最近有较多关于STM32Cube HAL的问题,侧面反应了使用STM32CubeMX的人不少。所以,最近可能会重点写这方面内容。

1写在前面

很多人都知道STM32CubeMX这套工具的一个目的:减少开发者对STM32底层驱动的开发时间,把重心放在应用代码上


但是,STM32CubeMX只是生成了底层驱动的初始化代码。所以,我们还需要掌握:应用层代码如何调用HAL库函数接口,以及HAL库中断处理机制等相关知识。


HAL库牵涉的内容较多,本文拿HAL库中断处理来讲解,以及相关的回调函数。


2

HAL库中断处理机制

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


而HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理如下图:

640?wx_fmt=png

其它大部分外设(TIM、SPI、CAN...)中断都类似,HAL进行统一处理。

也就是说,HAL已经帮我们把中断处理函数写好了,我们只需要调用相应函数来编写应用程序就行了。


HAL_xxx_IRQHandler里面做了哪些处理? 我们以STM32F1的HAL_UART_IRQHandler为例:

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)	
{	
   uint32_t isrflags   = READ_REG(huart->Instance->SR);	
   uint32_t cr1its     = READ_REG(huart->Instance->CR1);	
   uint32_t cr3its     = READ_REG(huart->Instance->CR3);	
   uint32_t errorflags = 0x00U;	
   uint32_t dmarequest = 0x00U;	

	
  /* If no error occurs */	
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));	
  if(errorflags == RESET)	
  {	
    /* UART in mode Receiver -------------------------------------------------*/	
    if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))	
    {	
      UART_Receive_IT(huart);	
      return;	
    }	
  }	

	
  /* If some errors occur */	
  if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))	
  {	
  /*	
  ·	
  ·删减了部分代码	
  ·	
  */	
  } /* End if some error occurs */	

	
  /* UART in mode Transmitter ------------------------------------------------*/	
  if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))	
  {	
    UART_Transmit_IT(huart);	
    return;	
  }	
  /* UART in mode Transmitter end --------------------------------------------*/	
  if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))	
  {	
    UART_EndTransmit_IT(huart);	
    return;	
  }	
}

其实,大家认真看一下代码应该能明白,这些和我们编写的中断处理函数是不是有类似之处?


这是无非就是接收中断、发送中断、错误中断等一系列处理。只是这里又进行了再次封装,比如接收中断UART_Receive_IT


当然,这个UART_Receive_IT接收中断实现方式又可能存在不同。像F0、F1...就是直接调用这个接收中断函数来进一步处理。


像L0、G0...是通过执行指针函数RxISR来进一步处理。G0的接收中断处理为:huart->RxISR(huart);

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)	
{	
  //删除了前面代码	
  /* If no error occurs */	
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE));	
  if (errorflags == 0U)	
  {	
    /* UART in mode Receiver ---------------------------------------------------*/	
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)	
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)	
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))	
    {	
      if (huart->RxISR != NULL)	
      {	
        huart->RxISR(huart);	
      }	
      return;	
    }	
  }	
  //删除了后面代码	
}

看了上面USART中断处理的函数,大家有没有得到什么启发?


其实,HAL库里面处理机制基本一致,只是实现方式上有所不同


如果你摸清楚了HAL库基本原理,相信阅读HAL库,或者使用HAL库编写应用代码不是问题。

3

回调函数实现原理

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


回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

---来自百度百科


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


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


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

4

扩展说明

这里也简单说几点:


1.初学者想直接使用HAL不是不行,需要有一定C语言功底

针对大部分初学者来说,是不建议直接上手HAL。但是,有部分C功底较好的,还是建议直接上手。


2.学HAL,建议参看官网例程

很多人不知道如何找资源,我不止一次强调,官方的才是最好。在HAL库中Projects目录下就有很多例程Examples。

3.我们追求效率,可以HAL库源码

如果你想修改HAL库源码,允许修改少部分。如果要大量修改,还是别折腾了。


4.实际项目需做一定修改

STM32CubeMX仅仅是生成初始化代码和工程,你实际项目中一般都有自己的软件架构。

特别是项目越大,软件架构就需要更加规范。


比如:生成的gpio.c文件名,你需要修改成bsp_gpio.c.


再比如:函数MX_USART2_UART_Init改成MX_DEBUG_UART_Init.



推荐阅读:

1.STM32低功耗时引脚配置

2.HAL库中do{...} while(0U)宏定义的作用和意义

5最后

我的知乎:strongerHuang

我的网站:www.strongerhuang.com

若觉得文章对你有帮助,随手点“好看、转发分享,也是我继续更新的动力。

扫描下面二维码、关注公众号,在底部菜单中查看更多精彩内容!

640?wx_fmt=jpeg

长按识别图中二维码关注

STM32Cube中,用户代码中断回调函数可以写在一个文件中。这种做法的好处在于集中管理和组织用户代码,提高代码的可读性和可维护性。 首先,我们需要在该文件中包含必要的头文件,例如stm32fxx_hal.h和stm32fxx_hal_gpio.h等,以便使用相关的函数和宏定义。 然后,我们可以定义全局变量或宏来存储中断回调函数所需的信息。例如,我们可以定义一个全局变量来表示某个外部中断引脚的状态。 接下来,我们可以编写中断回调函数。这些函数将根据特定的中断事件进行触发和执行相应的操作。例如,如果是外部中断回调函数,我们可以在函数内部读取外部中断引脚的状态并根据其状态执行对应的操作。我们可以使用STM32Cube提供的API函数来处理中断,例如HAL_GPIO_EXTI_Callback()函数。 除了中断回调函数外,我们还可以在这个文件中编写其他的相关函数,以便在中断回调函数中调用。这样做能够更好地组织代码,并使得代码更加可读和易于维护。 最后,我们需要在main函数中注册这些中断回调函数。在该文件中,我们可以找到一个类似于“HAL_NVIC_SetPriority”的函数来设置中断的优先级,并使用“HAL_NVIC_EnableIRQ”函数来使能中断。 总的来说,将STM32Cube用户代码中断回调函数写在一个文件里可以更好地组织和管理代码,提高代码的可读性和可维护性,并简化中断配置的过程。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

strongerHuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值