hal库开启中断关中断_[STM32]HAL库下GPIO按键中断与去抖问题分析(分析源码解决问题)...
前言
Yume-知乎文章?www.zhihu.com在這里,還是以我一貫的風格——不重復造輪子。具體如何配置STM32CubeMX初始化、生成工程目錄之類的問題。包括關于STM32的Cortex-M3處理器的中斷細節都不會在本文章中贅述,想了解的朋友可以自行去翻看“Cortex-M3 權威指南”、“Cortex-M3 技術手冊”等相關技術文檔,都有中文的翻譯版本。本文的目的只在如何具體分析問題,解決問題。
1 STM32CubeMX中GPIO配置(基于正點原子的Stm32F1-Nano板)
像如何配置RCC、配置相關的時鐘樹、配置中斷NVIC、配置中斷輸入等基礎配置,生成工程文件的問題。這里就省略了,有需要的朋友可以參考其他網絡上資料。重點就看看關于GPIO配置,便于后面分析講解代碼。
其中,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處理器的中斷問題
可以看出Cortex-M3處理器有16個外部I/O中斷,分別對應[0:15]端口上。也就是說每個中斷端口號(0-15)可以在任一Port(A-G)上,然后通過選擇器去決定中斷端口采用哪個Port。當然這部分在STM32CubeMX上很容易就能配置并初始化好。自然就會有對應的Handler函數。那具體是啥,我們看看生成的工程文件中“Src”文件夾里的“stm32f1xx_it.c”中斷文件中有這么一段注釋
大致意思就是:STM32F1xx外部中斷處理(Handlers)在“stratup file”中提及到了。有了這信息,再去看看在工程文件的根目錄中“startup_stm32f103xb.s”文件(基于Nano版上的處理器)雖然里面是匯編語言,但通過查找“IRQHandler”可以看到
這7個就是外部I/O中斷的入口函數(可以看出[0:4]端口是獨立的入口,而[5:9]、[10-15]分別共用一個入口)。再看看“stm32f1xx_it.c”文件也能看到對應的函數定義
可以看到進入對應的I/O中斷入口函數后就會傳輸GPIO_Pin口(用戶配置的中斷端口[0:15])到“HAL_GPIO_EXTI_IRQHandler()”中。那我們再看看這個IRQHandle函數
描述也很清晰地表明了這個函數功能是EXTI中斷回應。那具體發生了什么呢?在函數里調用了“__HAL_GPIO_EXTI_GET_IT()”和“__HAL_GPIO_EXTI_CLEAR_IT()”后就調用“HAL_GPIO_EXTI_Callback”用戶處理函數,有了解上篇文章
Yume:STM32 非阻塞HAL_UART_Receive_IT解析與實際應用
,應該已經清楚怎么回事了,這里就不贅述了。但關鍵問題是前面兩個調用又是什么?
可以看到,這兩并不是函數,而是一個宏。是干嘛的呢?大概可以看出是檢查到底是哪個I/O端口觸發中斷的。所以這里就可以理解為啥[5:9]和[10:15]可以共用入口函數也不會出問題了。原因就在這里可以檢查。
那再細致點的去看這兩個宏定義,不難發現第一個是獲取中斷的標志位和確認中斷是否產生,由誰產生。第二個則是清除標志位。通過查看“Cortex-M3 技術文檔”的相關寄存器,也能更好地理解上面說的過程。
那現在應該很清楚發生了什么事了。發生中斷響應時,通過入口函數“EXITx_IRQHandler”進入到“HAL_GPIO_EXTI_IRQHandler()”確認中斷端口后,重置對應中斷的標志位,進入到“HAL_GPIO_EXTI_Callback”用戶處理函數中去。那我們只需要在用戶處理函數中用switch語句選擇不同I/O中斷端口GPIO_Pin([0:15])對應不同中斷處理任務就行了。例:
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);”
然后再用戶處理函數中返回前添加延遲。就能比較好的解決按鍵抖動帶來可能重復進入中斷的問題。(雖然在我摧殘下,還是有可能出現問題,但感覺基本上是能達到預期的,畢竟鼠標都可能因為微動問題出現雙擊呢)
想說的話
網絡上有人批判HAL庫效率、為非EE專業設計的,將所有東西都抽象了等一系列問題。但我想說的是,對于大多數普通用戶或相關工作者。處理效率固然重要,但開發效率、移植效率也是需要實際考察的。會想起剛接觸C51用匯編操作寄存器能時,我也會有點批判高級程序語言的效率各種不如匯編,不如匯編一步一步清晰。但問題是現在微處理器計算性能、寄存器數量也是遠遠超過以前。面對復雜的項目時,需要操作32位甚至64位總線寬度時,你還能處理各種復雜的關系么。
雖然我有時也覺得怎么一個中斷都能套娃般弄得如此復雜,明明感覺有更簡便的方法。但也意識到,之所以會弄這么復雜,一方面是為了提高開發者的開發效率,方便移植,另一方面也減少因為手誤配置錯寄存器,導致不可預知的后果。隨著項目復雜度的上升,人為出錯的概率也會上升,而用庫相比去配置寄存器,出錯的概率我認為是更低的。更別說在抽象后高層級的去思考能更好的完成某件任務的邏輯,而不是苦與寄存器相互如何作用的問題發愁。
總的來說,如果認為HAL庫犧牲效率,甚至出現感覺不符合預期的Bug出現時無法像配置寄存器類似的方法排錯。那我感覺可能就只是片面的去看HAL庫函數就是黑盒,實際上這些函數都是能去追溯到具體的寄存器,在追溯的過程中慢慢的可能也能找到Bug的原因,在針對實際場景做修改也不是不可以的。
也許很多地方我表達的不專業,還請多多包涵。
Yume:STM32 非阻塞HAL_UART_Receive_IT解析與實際應用?zhuanlan.zhihu.com
總結
以上是生活随笔為你收集整理的hal库开启中断关中断_[STM32]HAL库下GPIO按键中断与去抖问题分析(分析源码解决问题)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开根号的笔算算法图解_机器学习KNN算法
- 下一篇: gdb 编译make: *** [all