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

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

[复制链接]
STMCU小助手 发布时间:2020-12-28 10:06
STM32Cube HAL库中断处理机制,回调函数实现原理
STM32Cube HAL出来六七年了,还是有很多初学者没有适应,今天就分享一个读者问到的关于中断处理的问题。
很多人都知道STM32CubeMX这套工具的一个目的:减少开发者对STM32底层驱动的开发时间,把重心放在应用代码上。
但是,STM32CubeMX只是生成了底层驱动的初始化代码。所以,我们还需要掌握:应用层代码如何调用HAL库函数(API接口),以及HAL库中断处理机制等相关知识。
HAL库牵涉的内容较多,下面简单描述一下HAL库中断处理,以及相关的回调函数。
1  HAL库中断处理机制
之前使用标准外设库开发时,中断程序(函数)由我们自己实现。
而HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理,如下图:
1.1.jpg
其它大部分外设(TIM、SPI、CAN...)中断都类似,HAL进行统一处理。
也就是说,HAL已经帮我们把中断处理函数写好了,我们只需要调用相应函数来编写应用程序就行了。
HAL_xxx_IRQHandler里面做了哪些处理? 我们以STM32F1的HAL_UART_IRQHandler为例:
  1. <p class="MsoNormal"><span lang="EN-US">void HAL_UART_IRQHandler(UART_HandleTypeDef
  2. *huart)<o:p></o:p></span></p>

  3. <p class="MsoNormal"><span lang="EN-US">{<o:p></o:p></span></p>

  4. <p class="MsoNormal"><span lang="EN-US">  
  5. uint32_t isrflags   =
  6. READ_REG(huart->Instance->SR);<o:p></o:p></span></p>

  7. <p class="MsoNormal"><span lang="EN-US">  
  8. uint32_t cr1its     =
  9. READ_REG(huart->Instance->CR1);<o:p></o:p></span></p>

  10. <p class="MsoNormal"><span lang="EN-US">  
  11. uint32_t cr3its     =
  12. READ_REG(huart->Instance->CR3);<o:p></o:p></span></p>

  13. <p class="MsoNormal"><span lang="EN-US">  
  14. uint32_t errorflags = 0x00U;<o:p></o:p></span></p>

  15. <p class="MsoNormal"><span lang="EN-US">  
  16. uint32_t dmarequest = 0x00U;<o:p></o:p></span></p>

  17. <p class="MsoNormal"><span lang="EN-US"> </span></p>

  18. <p class="MsoNormal"><span lang="EN-US">  /*
  19. If no error occurs */<o:p></o:p></span></p>

  20. <p class="MsoNormal"><span lang="EN-US">
  21. errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE |
  22. USART_SR_ORE | USART_SR_NE));<o:p></o:p></span></p>

  23. <p class="MsoNormal"><span lang="EN-US">
  24. if(errorflags == RESET)<o:p></o:p></span></p>

  25. <p class="MsoNormal"><span lang="EN-US">  {<o:p></o:p></span></p>

  26. <p class="MsoNormal"><span lang="EN-US">   
  27. /* UART in mode Receiver
  28. -------------------------------------------------*/<o:p></o:p></span></p>

  29. <p class="MsoNormal"><span lang="EN-US">   
  30. if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its &
  31. USART_CR1_RXNEIE) != RESET))<o:p></o:p></span></p>

  32. <p class="MsoNormal"><span lang="EN-US">    {<o:p></o:p></span></p>

  33. <p class="MsoNormal"><span lang="EN-US">     
  34. UART_Receive_IT(huart);<o:p></o:p></span></p>

  35. <p class="MsoNormal"><span lang="EN-US">     
  36. return;<o:p></o:p></span></p>

  37. <p class="MsoNormal"><span lang="EN-US">    }<o:p></o:p></span></p>

  38. <p class="MsoNormal"><span lang="EN-US">  }<o:p></o:p></span></p>

  39. <p class="MsoNormal"><span lang="EN-US"> </span></p>

  40. <p class="MsoNormal"><span lang="EN-US">  /*
  41. If some errors occur */<o:p></o:p></span></p>

  42. <p class="MsoNormal"><span lang="EN-US">
  43. if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) !=
  44. RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))<o:p></o:p></span></p>

  45. <p class="MsoNormal"><span lang="EN-US">  {<o:p></o:p></span></p>

  46. <p class="MsoNormal"><span lang="EN-US">  /*<o:p></o:p></span></p>

  47. <p class="MsoNormal"><span lang="EN-US">  ·<o:p></o:p></span></p>

  48. <p class="MsoNormal"><span lang="EN-US">  ·</span>删减了部分代码<span lang="EN-US"><o:p></o:p></span></p>

  49. <p class="MsoNormal"><span lang="EN-US">  ·<o:p></o:p></span></p>

  50. <p class="MsoNormal"><span lang="EN-US">  */<o:p></o:p></span></p>

  51. <p class="MsoNormal"><span lang="EN-US">  }
  52. /* End if some error occurs */<o:p></o:p></span></p>

  53. <p class="MsoNormal"><span lang="EN-US"> </span></p>

  54. <p class="MsoNormal"><span lang="EN-US">  /*
  55. UART in mode Transmitter ------------------------------------------------*/<o:p></o:p></span></p>

  56. <p class="MsoNormal"><span lang="EN-US">
  57. if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its &
  58. USART_CR1_TXEIE) != RESET))<o:p></o:p></span></p>

  59. <p class="MsoNormal"><span lang="EN-US">  {<o:p></o:p></span></p>

  60. <p class="MsoNormal"><span lang="EN-US">   
  61. UART_Transmit_IT(huart);<o:p></o:p></span></p>

  62. <p class="MsoNormal"><span lang="EN-US">   
  63. return;<o:p></o:p></span></p>

  64. <p class="MsoNormal"><span lang="EN-US">  }<o:p></o:p></span></p>

  65. <p class="MsoNormal"><span lang="EN-US">  /*
  66. UART in mode Transmitter end --------------------------------------------*/<o:p></o:p></span></p>

  67. <p class="MsoNormal"><span lang="EN-US">
  68. if(((isrflags & USART_SR_TC) != RESET) && ((cr1its &
  69. USART_CR1_TCIE) != RESET))<o:p></o:p></span></p>

  70. <p class="MsoNormal"><span lang="EN-US">  {<o:p></o:p></span></p>

  71. <p class="MsoNormal"><span lang="EN-US">   
  72. UART_EndTransmit_IT(huart);<o:p></o:p></span></p>

  73. <p class="MsoNormal"><span lang="EN-US">   
  74. return;<o:p></o:p></span></p>

  75. <p class="MsoNormal"><span lang="EN-US">  }<o:p></o:p></span></p>

  76. <p class="MsoNormal"><span lang="EN-US">}</span></p>
复制代码

其实,大家认真看一下代码应该能明白,这些和我们编写的中断处理函数是不是有类似之处?
这是无非就是接收中断、发送中断、错误中断等一系列处理。只是这里又进行了再次封装,比如接收中断UART_Receive_IT。
当然,这个UART_Receive_IT接收中断实现方式又可能存在不同。像F0、F1...就是直接调用这个接收中断函数来进一步处理。
像L0、G0...是通过执行指针函数RxISR来进一步处理。G0的接收中断处理为:huart->RxISR(huart);
  1. <p class="MsoNormal"><span lang="EN-US">void HAL_UART_IRQHandler(UART_HandleTypeDef
  2. *huart)<o:p></o:p></span></p>

  3. <p class="MsoNormal"><span lang="EN-US">{<o:p></o:p></span></p>

  4. <p class="MsoNormal"><span lang="EN-US">  //</span>删除了前面代码<span lang="EN-US"><o:p></o:p></span></p>

  5. <p class="MsoNormal"><span lang="EN-US">  /*
  6. If no error occurs */<o:p></o:p></span></p>

  7. <p class="MsoNormal"><span lang="EN-US">
  8. errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE |
  9. USART_ISR_ORE | USART_ISR_NE));<o:p></o:p></span></p>

  10. <p class="MsoNormal"><span lang="EN-US">  if
  11. (errorflags == 0U)<o:p></o:p></span></p>

  12. <p class="MsoNormal"><span lang="EN-US">  {<o:p></o:p></span></p>

  13. <p class="MsoNormal"><span lang="EN-US">   
  14. /* UART in mode Receiver
  15. ---------------------------------------------------*/<o:p></o:p></span></p>

  16. <p class="MsoNormal"><span lang="EN-US">   
  17. if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)<o:p></o:p></span></p>

  18. <p class="MsoNormal"><span lang="EN-US">      
  19. && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)<o:p></o:p></span></p>

  20. <p class="MsoNormal"><span lang="EN-US">           
  21. || ((cr3its & USART_CR3_RXFTIE) != 0U)))<o:p></o:p></span></p>

  22. <p class="MsoNormal"><span lang="EN-US">    {<o:p></o:p></span></p>

  23. <p class="MsoNormal"><span lang="EN-US">     
  24. if (huart->RxISR != NULL)<o:p></o:p></span></p>

  25. <p class="MsoNormal"><span lang="EN-US">     
  26. {<o:p></o:p></span></p>

  27. <p class="MsoNormal"><span lang="EN-US">      
  28. huart->RxISR(huart);<o:p></o:p></span></p>

  29. <p class="MsoNormal"><span lang="EN-US">     
  30. }<o:p></o:p></span></p>

  31. <p class="MsoNormal"><span lang="EN-US">     
  32. return;<o:p></o:p></span></p>

  33. <p class="MsoNormal"><span lang="EN-US">    }<o:p></o:p></span></p>

  34. <p class="MsoNormal"><span lang="EN-US">  }<o:p></o:p></span></p>

  35. <p class="MsoNormal"><span lang="EN-US">  //</span>删除了后面代码<span lang="EN-US"><o:p></o:p></span></p>

  36. <p class="MsoNormal"><span lang="EN-US">}</span></p>
复制代码
看了上面USART中断处理的函数,大家有没有得到什么启发?
其实,HAL库里面处理机制基本一致,只是实现方式上有所不同。
如果你摸清楚了HAL库基本原理,相信阅读HAL库源码,或者使用HAL库编写应用代码不是问题。
2  回调函数实现原理
在HAL库中存在大量类似HAL_XXX_XXXCallback这样的函数,这些都是回调函数。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
---来自百度百科
HAL库中断处理使用了较多的回调函数,还是拿UART接收中断来举例说明。
初始化配置好UART中断接收,如果有中断请求,就会执行回调函数HAL_UART_RxCpltCallback。
看上面回调函数的定义,通过特定条件调用『回调函数』,这里触发的条件就是中断。
3  扩展说明
这里也简单说几点:
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.
------------END ------------
文章出处: strongerHuang

收藏 评论0 发布时间:2020-12-28 10:06

举报

0个回答

所属标签

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