STM32Cube HAL庫中斷處理機制,回調函數實現原理

2021-01-07 酷扯兒

本文轉載自【微信公眾號:strongerHuang,ID:strongerHuang】經微信公眾號授權轉載,如需轉載與原文作者聯繫

STM32Cube HAL出來六七年了,還是有很多初學者沒有適應,現在就分享一個讀者問到的關於中斷處理的問題。

很多人都知道STM32CubeMX這套工具的一個目的:減少開發者對STM32底層驅動的開發時間,把重心放在應用代碼上。

但是,STM32CubeMX只是生成了底層驅動的初始化代碼。所以,我們還需要掌握:應用層代碼如何調用HAL庫函數(API接口),以及HAL庫中斷處理機制等相關知識。

HAL庫牽涉的內容較多,下面簡單描述一下HAL庫中斷處理,以及相關的回調函數。

1HAL庫中斷處理機制

之前使用標準外設庫開發時,中斷程序(函數)由我們自己實現。

而HAL庫的中斷處理函數是按照HAL處理機制來實現,如USART1,統一由HAL_UART_IRQHandler來進行處理,如下圖:

其它大部分外設(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庫編寫應用代碼不是問題。

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.

相關焦點

  • 「STM32CubeMonitor」 拍了拍你
    變量可以根據可執行文件導入,也可以直接根據變量地址進行手動添加,還可以對採集的變量進行各種後處理運算,設置採樣觸發條件等。STM32CubeMonitor採用圖形化的編程界面,整個編程過程通過拖拽的方式就可以完成,無需寫代碼。有大量可選的圖形化組件(儀錶盤,柱狀圖,點線圖……)可用。
  • 【STM32CUBEMX】 I2C Slave 實現
    實際上,在這次硬體 I2C 調試之前,其實我已經通過 IO 模擬的方式實現了 I2C,但速率僅能實現 Standard-mode(up to 100 kbit/s)。對於 Fast-mode(up to 400 kbit/s),IO 模擬方式簡直是無能為力。同時,由於 IO 模擬 I2C 時並沒有充分的考慮架構,最終的實現結果是功能單一,客戶滿意度不好。
  • Camera2 / HAL3 架構了解下
    mDeviceHandler.post(mCallOnUnconfigured); }}2.2.3 Runtime通過 Binder 機制,我們遠端調用了 connectDevice 方法(在 C++ 中稱為函數,但說成方法可能更順口一些),這個方法實現在 CameraService 類中。
  • Camera2/HAL3 框架分析 程式設計師Android
    mDeviceHandler.post(mCallOnUnconfigured); }}2.2.3 Runtime通過 Binder 機制,我們遠端調用了 connectDevice 方法(在 C++ 中稱為函數,但說成方法可能更順口一些),這個方法實現在 CameraService 類中。
  • STM32F103C8T6驅動ov2640拍照串口傳輸到上位機
    而且STM32F103系列單片機就能夠輕鬆驅動(前提是SRAM足夠大)。於是決定使用性價比比較高的STM32F103C8T6這款單片機來驅動,這款單片機單單晶片的話只需8元人民幣,而且性能還可以,48腳,RAM20KB,FLASH64K,主頻72M,算是性價比較高的了。系統連接圖如下所示:
  • 標準庫函數與基於HAL庫函數
    開始學習51的時候有的人會直接選擇進行對單片機的寄存器進行操作,可以很好的理解單片機的工作原理,對於代碼的編寫也會後很好的理解,但是在stm32的時候操作寄存器就不太行了,因為stm32的寄存器是51的數十倍,如此多的寄存器根本無法全部記憶,開發時需要經常的翻查晶片的數據手冊,此時直接操作寄存器就變得非常的費力了。
  • 基於STM32F7通過cube軟體配置:讀寫QSPI接口64M flash和64M PSRAM參考教程
    基於STM32F7通過cube
  • STM32啟動文件初探之startup_stm32f10x_hd.s(Reset_Handler函數)
    該文件主要實現目的:設置初始SP設置初始PC=Reset_Handler設置向量表入口地址,並初始化向量表調用SystemInit,把系統時鐘配製成72M,SystemInit在庫文件system_stm32f10.c定義調轉到標號_main,最終來到C程序文件
  • STM32F429的定時器的使用方法
    用固件庫可以少一些錯誤,畢竟我們是做應用的,首先要學會使用CPU來實現我們要的功能。工程目錄如下:這裡使用最新的Keil MDK V5.17版本的,STM32F4XX的器件庫等還需要進一步下載。這裡使用的是自帶的ST-Link下載程序,當然使用Jlink也是可以的。
  • 如何實現STM32F407單片機的ADC轉換
    用到的引腳是PA3也就是ADC1的通道3 1、ADC的主要參數 a、解析度----stm32f407的解析度有6位、8位、10位、12位,參考電壓如果是3.3 那麼最小解析度就是3.3/4095。 b、轉換時間----stm32f407的最高允許頻率是36M,最快轉換時間 = 3+12個周期 =0.71us。
  • STM32中使用HAL庫重定向printf()函數
    函數添加頭文件#include "stdio.h"添加函數// 重定向函數1int fputc(int ch,FILE *f){    uint8_t temp[1]={ch};    HAL_UART_Transmit
  • JS異步編程,回調函數與promise
    它由社區最早提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了ES6之前,JavaScript中異步編程分為3類:DOM事件(如onclick)、網絡請求(如ajax)、定時器(setTimeout/setInterval)。他們均使用回調函數來進行異步調用。當回調函數中嵌套了回調函數,甚至是多層回調時,編碼就不夠直觀了。而使用Promise就能通過同步的編碼方式實現異步調用。
  • STM32Cube_FW_F0_V1.10.0 官方庫的I2C 調試
    又來寫I2C通信了......真是換一個新庫就要花時間重新調試一下,這次用的是最新的官方庫STM32Cube_FW_F0_V1.10.0 ,開發平臺用的也是新的STM32CubeIDE。
  • stm32CubeMx CAN 發送數據
    關鍵字:stm32CubeMx  CAN  發送數據 編輯前言:本系列教程將 對應外設原理,HAL庫與STM32CubeMX結合在一起講解,使您可以更快速的學會各個模塊的使用所用工具:1、晶片: STM32F407ZET6/ STM32F103ZET62、STM32CubeMx軟體3、IDE: MDK-Keil軟體4、STM32F1xx/STM32F4xxHAL庫知識概括:通過本篇博客您將學到:ACD工作原理STM32CubeMX創建ADC例程HAL
  • STM32教程(三)STM32 HAL固件庫介紹及使用建議!
    HAL庫是ST公司為STM32的MCU最新推出的抽象層嵌入式軟體,為更方便的實現跨STM32產品的最大可移植性。HAL庫的推出,可以說ST也慢慢的拋棄了原來的標準固件庫,這也使得很多老用戶不滿。但是HAL庫推出的同時,也加入了很多第三方的中間件,有RTOS,USB,TCP / IP和圖形等等。
  • C語言函數指針之回調函數
    如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。2 為什麼要用回調函數?
  • 還在用回調函數?快來學習怎麼將回調函數轉為成Promise吧!
    本文只是簡單的以實例講解如何將回調函數轉化為ES6中的Promise,並不會深入分析回調的優缺點,以及Promise和async/await的原理,如果你想了解這些,請關注我。我將在以後的文章中詳細講解這些知識點。
  • Xposed 實現原理分析
    (Hook 是一種函數鉤子技術,能夠對函數進行接管,從而修改函數的返回值,改變函數的原始意圖)本文將基於 Xposed 最新的開原始碼對 Xposed 的實現原理進行分析。通過了解 Xposed 的實現原理可以學到在 Android 平臺上對於 Java 層代碼的一種 Hook 機制的實現,同時複習 Android 系統的啟動原理以及增加對於 Android ART 虛擬機運行原理的了解。Xposed 使用方法在對 Xposed 進行分析之前,先回顧一下 Xposed 基本 API 的使用。
  • 三個案例帶你了解python回調函數
    回調函數: 把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。 通俗理解就是: 把一個函數作為參數傳給另一個函數,第一個函數稱為回調函數。這個被傳入的參數其實是函數指針,即指向一個函數的指針(地址)。