nrf52840 gpiote如何配置中断输入_[STM32]HAL库下GPIO按键中断与去抖问题分析(分析源码解决问题)...

自上篇文章STM32 非阻塞HAL_UART_Receive IT解析与实际应用,具体总结了HAL库下套娃函数中如何看清库函数的脉络,更细致的认识调用的过程,以解决潜在的问题。又又又遇到了新的问题(GPIO按键中断),感觉网络上和各种资料都没解释清楚的情况下。我又去扒了扒HAL库函数源码,提供不一样解决按键抖动的问题(个人认为除了那些通过配置具体寄存器的外;是从HAL库提供函数的基础上解决的)有别于那种在Callback返回函数中添加延迟和判断具体是否按下按键的方法(个人实际操作感觉这种方法效果也不好)。而是在HAL_GPIO库函数上的HAL_GPIO_EXTI_IRQHandler处理中断函数解决按键抖动带来的问题,是更根上解决问题的方法。

前言

Yume-知乎文章​www.zhihu.com

在这里,还是以我一贯的风格——不重复造轮子。具体如何配置STM32CubeMX初始化、生成工程目录之类的问题。包括关于STM32的Cortex-M3处理器的中断细节都不会在本文章中赘述,想了解的朋友可以自行去翻看“Cortex-M3 权威指南”、“Cortex-M3 技术手册”等相关技术文档,都有中文的翻译版本。本文的目的只在如何具体分析问题,解决问题。

1 STM32CubeMX中GPIO配置(基于正点原子的Stm32F1-Nano板)

像如何配置RCC、配置相关的时钟树、配置中断NVIC、配置中断输入等基础配置,生成工程文件的问题。这里就省略了,有需要的朋友可以参考其他网络上资料。重点就看看关于GPIO配置,便于后面分析讲解代码。

v2-ef2d0fdcbae0db4b53da3c8927613770_b.png

其中,PC0、PC1、PC2为LED灯,当按键触发中断时反转。PC8、PC9、PD2为共地按键(低电平有效),所以设置GPIO为Pull-up。按键的GPIO mode有两个为上升沿触发(Rising edge)和一个下降沿触发(Falling edge),这里其实上升沿或下降沿都没啥特别大的关系。

2 HAL库的GPIO中断响应过程

在初始化相关中断响应函数后,HAL库是如何进行中断响应的。其实和上篇文章

Yume:STM32 非阻塞HAL_UART_Receive_IT解析与实际应用​zhuanlan.zhihu.com

有很多相似的地方,可以借助上篇文章来理解。

同样的,GPIO的入口函数是什么呢?这里先引用“Cortex-M3权威指南”的一张图来简单说明Cortex-M3处理器的中断问题

v2-8b7fb07c971fccde6d974c58875428b4_b.jpg

可以看出Cortex-M3处理器有16个外部I/O中断,分别对应[0:15]端口上。也就是说每个中断端口号(0-15)可以在任一Port(A-G)上,然后通过选择器去决定中断端口采用哪个Port。当然这部分在STM32CubeMX上很容易就能配置并初始化好。自然就会有对应的Handler函数。那具体是啥,我们看看生成的工程文件中“Src”文件夹里的“stm32f1xx_it.c”中断文件中有这么一段注释

v2-91b24b927976442500dce3e37b50398c_b.jpg

大致意思就是:STM32F1xx外部中断处理(Handlers)在“stratup file”中提及到了。有了这信息,再去看看在工程文件的根目录中“startup_stm32f103xb.s”文件(基于Nano版上的处理器)虽然里面是汇编语言,但通过查找“IRQHandler”可以看到

v2-ffe14fa7aadc58656c7a8fe054464b6f_b.jpg

v2-ed483ef1d26ae49d25872fe40e32983e_b.png

v2-48484f2820a4d5df30869f95ba0a1db4_b.png

这7个就是外部I/O中断的入口函数(可以看出[0:4]端口是独立的入口,而[5:9]、[10-15]分别共用一个入口)。再看看“stm32f1xx_it.c”文件也能看到对应的函数定义

v2-f5cdca72af75d86a29c965253fff2d89_b.jpg

可以看到进入对应的I/O中断入口函数后就会传输GPIO_Pin口(用户配置的中断端口[0:15])到“HAL_GPIO_EXTI_IRQHandler()”中。那我们再看看这个IRQHandle函数

v2-2e9bd9d060de53ec1fc9929d7e132e2d_b.jpg

描述也很清晰地表明了这个函数功能是EXTI中断回应。那具体发生了什么呢?在函数里调用了“__HAL_GPIO_EXTI_GET_IT()”和“__HAL_GPIO_EXTI_CLEAR_IT()”后就调用“HAL_GPIO_EXTI_Callback”用户处理函数,有了解上篇文章

Yume:STM32 非阻塞HAL_UART_Receive_IT解析与实际应用

,应该已经清楚怎么回事了,这里就不赘述了。但关键问题是前面两个调用又是什么?

v2-0de38dba4f3c0ac8167592344acd409a_b.jpg

可以看到,这两并不是函数,而是一个宏。是干嘛的呢?大概可以看出是检查到底是哪个I/O端口触发中断的。所以这里就可以理解为啥[5:9]和[10:15]可以共用入口函数也不会出问题了。原因就在这里可以检查。

那再细致点的去看这两个宏定义,不难发现第一个是获取中断的标志位和确认中断是否产生,由谁产生。第二个则是清除标志位。通过查看“Cortex-M3 技术文档”的相关寄存器,也能更好地理解上面说的过程。

v2-e1b3ada1787ea1ecdd45284c69e8ba47_b.jpg

那现在应该很清楚发生了什么事了。发生中断响应时,通过入口函数“EXITx_IRQHandler”进入到“HAL_GPIO_EXTI_IRQHandler()”确认中断端口后,重置对应中断的标志位,进入到“HAL_GPIO_EXTI_Callback用户处理函数中去。那我们只需要在用户处理函数中用switch语句选择不同I/O中断端口GPIO_Pin([0:15])对应不同中断处理任务就行了。例:

v2-2f6fe4eadbbf4359177acaf42f04eca7_b.jpg

3 按键抖动问题分析与解决方案

那如果单纯在用户函数中类似上例中这么写,肯定会发现很严重的问题。发现按下去触发中断时可能会产生两次任务(预期是按下去就触发一次任务)。因为按键时会有抖动的,导致进入了两次中断,相信这不用我来解释为何按键抖动会引发这类的问题。

解决方案大家应该也清楚,要么通过计算添加合适大小电容消除抖动产生的影响,那另一种方案,也是最常见的方案就是添加去抖延迟。在网上也有很多类似的教程,甚至也是针对STM32的。会发现,他们都是在用户处理函数“HAL_GPIO_EXTI_Callback中添加延迟,然后读取确认按键。那问题来了,通过上面一步一步分析HAL库的I/O中断处理过程,就知道在用户处理函数之前的“HAL_GPIO_EXTI_IRQHandler()”确认中断端口中就已经将中断标志位消除了(在用户处理函数之前),意味着抖动仍然能触发中断。然后再通过阅读相关文档,发现STM32中断是依靠向量表机制,也就是说只要触发了中断,一般情况下总是要去响应和清除相应的中断标志位。所以我认为在用户处理函数这么做可能可以解决问题,但以我个人经验,效果并不是很好,原因就是解决问题的方法不太对。

个人认为更正确的做法是在清除标志位之前延迟等待抖动消失,防止因抖动在此将中断标志位置为有效。即需要修改HAL库(Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_gpio.c)中的“HAL_GPIO_EXTI_IRQHandler”函数。如图,在“__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);”之前添加延迟“HAL_Delay((uint32_t)20);”

v2-1dd7fdc7cf9e0aa7d5a6c7a609f20361_b.jpg

然后再用户处理函数中返回前添加延迟。就能比较好的解决按键抖动带来可能重复进入中断的问题。(虽然在我摧残下,还是有可能出现问题,但感觉基本上是能达到预期的,毕竟鼠标都可能因为微动问题出现双击呢)


想说的话

网络上有人批判HAL库效率、为非EE专业设计的,将所有东西都抽象了等一系列问题。但我想说的是,对于大多数普通用户或相关工作者。处理效率固然重要,但开发效率、移植效率也是需要实际考察的。会想起刚接触C51用汇编操作寄存器能时,我也会有点批判高级程序语言的效率各种不如汇编,不如汇编一步一步清晰。但问题是现在微处理器计算性能、寄存器数量也是远远超过以前。面对复杂的项目时,需要操作32位甚至64位总线宽度时,你还能处理各种复杂的关系么。

虽然我有时也觉得怎么一个中断都能套娃般弄得如此复杂,明明感觉有更简便的方法。但也意识到,之所以会弄这么复杂,一方面是为了提高开发者的开发效率,方便移植,另一方面也减少因为手误配置错寄存器,导致不可预知的后果。随着项目复杂度的上升,人为出错的概率也会上升,而用库相比去配置寄存器,出错的概率我认为是更低的。更别说在抽象后高层级的去思考能更好的完成某件任务的逻辑,而不是苦与寄存器相互如何作用的问题发愁。

总的来说,如果认为HAL库牺牲效率,甚至出现感觉不符合预期的Bug出现时无法像配置寄存器类似的方法排错。那我感觉可能就只是片面的去看HAL库函数就是黑盒,实际上这些函数都是能去追溯到具体的寄存器,在追溯的过程中慢慢的可能也能找到Bug的原因,在针对实际场景做修改也不是不可以的。

也许很多地方我表达的不专业,还请多多包涵。


Yume:STM32 非阻塞HAL_UART_Receive_IT解析与实际应用​zhuanlan.zhihu.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值