完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第16章 STM32H7必備的HAL庫API(重要)
本章教程為大家講解制作一個STM32H7的例子所需的最基本API函數,對於一些常用的API函數,一定要熟練掌握這些函數都是實現了什么功能,不常用的函數有個了解即可,用到的時候再去學。
16.1 初學者重要提示
16.2 那些是必備的API
16.3 源文件stm32h7xx_hal.c
16.4 stm32h7xx_hal_rcc.c
16.5 stm32h7xx_hal_cortex.c
16.6 總結
16.1 初學者重要提示
- 對於一些常用的函數,大家一定要熟練的掌握都實現了什么功能,比如HAL_Init,HAL_RCC_OscConfig,HAL_RCC_ClockConfig等。最好的辦法是把這些函數的源碼讀一遍。
16.2 那些是必備的API
這里我們通過一個簡單的初始化流程來了解STM32H7的工程模板所必備的庫文件和API:
第1步:系統上電復位,進入啟動文件startup_stm32h743xx.s,在這個文件里面執行復位中斷服務程序。
- 在復位中斷服務程序里面執行函數SystemInit,此函數在文件system_stm32h7xx.c里面。
- 之后是調用編譯器封裝好的函數,比如用於MDK的啟動文件是調用__main,最終進入到main函數。
第2步:進入到main函數就可以開始用戶應用程序編程了。在這個函數里面要做幾個重要的初始化,依次是:
- MPU初始化,需要用到庫文件stm32h7xx_hal_cortex.c和stm32h7xx_hal_cortex.h。
- Cache初始化,需要用到core_cm7.h文件。
- HAL庫初始化函數HAL_Init,需要用到文件stm32h7xx_hal.c。
- 系統時鍾初始化,需要用到庫文件stm32h7xx_hal_rcc.c。
前面的兩步完成后,就可以開始做用戶需要的按鍵、串口等方面的初始化和應用代碼的實現了。這里把我們需要學習的幾個庫文件整理出來,依次有:
- startup_stm32h743xx.s
- system_stm32h7xx.c
- stm32h7xx_hal.c
- stm32h7xx_hal_cortex.c
- stm32h7xx_hal_rcc.c
- core_cm7.h
其中startup_stm32h743xx.s和system_stm32h7xx.c已經在第13章為大家講解過,這里不再贅述。而MPU和Cache涉及到的文件core_cm7.h在第23和24章為大家講解。本章教程重點為大家講解文件stm32h7xx_hal.c、stm32h7xx_hal_cortex.c和sm32h7xx_hal_rcc.c。
16.3 源文件stm32h7xx_hal.c(重要)
這個文件比較雜,像基准電壓大小配置,EXTI配置,IO補償配置等都在這個文件里面設置。學習這個文件注意事項:
- HAL庫中各個外設驅動里面的延遲實現是基於此文件提供的時間基准,而這個時間基准既可以使用滴答定時器實現也可以使用通用的定時器實現,默認情況下是用的滴答定時器。
- 函數HAL_Init里面會調用時間基准初始化函數HAL_InitTick,而調用函數HAL_RCC_ClockConfig也會調用時間基准初始化函數HAL_InitTick。
- 如果在中斷服務程序里面調用延遲函數HAL_Delay要特別注意,因為這個函數的時間基准是基於滴答定時器或者其他通用定時器實現,實現方式是滴答定時器或者其他通用定時器里面做了個變量計數。如此一來,結果是顯而易見的,如果其他中斷服務程序調用了此函數,且中斷優先級高於滴答定時器,會導致滴答定時器中斷服務程序一直得不到執行,從而卡死在里面。所以滴答定時器的中斷優先級一定要比它們高。
16.3.1 函數HAL_Init
函數原型:
HAL_StatusTypeDef HAL_Init(void) { /* 設置中斷優先級分組 */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); /* 使用滴答定時器做為默認時基,配置為1ms滴答,另外系統上電后默認使用的HIS時鍾 */ if(HAL_InitTick(TICK_INT_PRIORITY) != HAL_OK) { return HAL_ERROR; } /* 初始化底層硬件 */ HAL_MspInit(); /* 返回函數狀態 */ return HAL_OK; }
函數描述:
此函數用於初始化HAL庫,此函數主要實現如下功能:
- 設置NVIC優先級分組是4。
- 設置滴答定時器的每1ms中斷一次。
- HAL庫不像之前的標准庫,在系統啟動函數SystemInit里面做了RCC初始化,HAL庫是沒有做的,所以進入到main函數后,系統還在用內部高速時鍾HSI,對於H7來說,HSI主頻是64MHz。
- 函數HAL_Init里面調用的HAL_MspInit一般在文件stm32h7xx_hal_msp.c里面做具體實現,主要用於底層初始化。當前此函數也在文件stm32h7xx_hal.c里面,只是做了弱定義。
函數參數:
- 返回值,返回HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示忙,正在使用中。
注意事項:
- 必須在main函數里面優先調用此函數。
- 用戶務必保證每1ms一次滴答中斷。
- 關於優先級分組的設置可以看第21章節。
使用舉例:
此函數的使用比較簡單,上電后優先調用即可。
16.3.2 函數HAL_DeInit
函數原型:
HAL_StatusTypeDef HAL_DeInit(void) { /* 復位所有外設 */__set_PRIMASK __HAL_RCC_AHB3_FORCE_RESET(); __HAL_RCC_AHB3_RELEASE_RESET(); /* 省略未寫 */ __HAL_RCC_APB4_FORCE_RESET(); __HAL_RCC_APB4_RELEASE_RESET(); /* 復位底層硬件初始化 */ HAL_MspDeInit(); /* 返回值 */ return HAL_OK; }
函數描述:
此函數用於復位HAL庫和滴答時鍾。
- 復位了AHB1,2,3,4的時鍾以及APB1L,APB1H,APB2,3,4的時鍾。
- 函數HAL_DeInit里面調用的HAL_MspDeInit一般在文件stm32h7xx_hal_msp.c里面做具體實現,主要用於底層初始化,跟函數HAL_Init里面調用的HAL_MspInit是一對。當前此函數也在文件stm32h7xx_hal.c里面,只是做了弱定義。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
16.3.3 函數HAL_InitTick
函數原型:
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { /* Configure the SysTick to have interrupt in 1ms time basis*/ if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U) { return HAL_ERROR; } /* Configure the SysTick IRQ priority */ if (TickPriority < (1UL << __NVIC_PRIO_BITS)) { HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U); uwTickPrio = TickPriority; } else { return HAL_ERROR; } /* Return function status */ return HAL_OK; }
函數描述:
此函數用於初始化滴答時鍾,此函數相關問題如下:
- 此函數有個前綴__weak ,表示弱定義,用戶可以重定義。
- 此函數用於初始化滴答時鍾1ms中斷一次,並且為滴答中斷配置一個用戶指定的優先級。
- 此函數由HAL_Init調用,或者任何其它地方調用函數HAL_RCC_ClockConfig配置RCC的時候也會調用HAL_InitTick。
- 調用基於此函數實現的HAL_Delay要特別注意,因為這個函數的時間基准是基於滴答定時器或者其他通用定時器實現,實現方式是滴答定時器或者其他通用定時器里面做了個變量計數。如此一來,結果是顯而易見的,如果其他中斷服務程序調用了此函數,且中斷優先級高於滴答定時器,會導致滴答定時器中斷服務程序一直得不到執行,從而卡死在里面。所以滴答定時器的中斷優先級一定要比它們高。
函數參數:
- 形參TickPriority用於設置滴答定時器優先級。
- 返回值,返回HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示忙,正在使用中。
使用舉例:
此函數由HAL_Init調用,無需用戶操作,除非需要重定義。
16.3.4 Systick的相關函數
調用了函數HAL_Init后,Systick相關的函數就可以使用了。這些函數如下:
函數原型:
__weak void HAL_IncTick(void) __weak uint32_t HAL_GetTick(void) uint32_t HAL_GetTickPrio(void) HAL_StatusTypeDef HAL_SetTickFreq(HAL_TickFreqTypeDef Freq) HAL_TickFreqTypeDef HAL_GetTickFreq(void) __weak void HAL_Delay(uint32_t Delay) __weak void HAL_SuspendTick(void) __weak void HAL_ResumeTick(void)
函數描述:
這些函數就比較簡單了,下面把這些函數實現的功能做個簡單的說明:
- 函數HAL_IncTick在滴答定時器中斷里面被調用,實現一個簡單的計數功能,因為一般滴答定時器中斷都是配置的1ms,所以計數全局變量uwTick每毫秒加1。
- 函數HAL_GetTick用於獲取全局變量uwTick當前的計數。
- 函數HAL_GetTickPrio用於獲取滴答時鍾優先級。
- 函數HAL_SetTickFreq和HAL_GetTickFreq是一對,前者用於設置滴答中斷頻率,后再用於獲取滴答中斷頻率。
- 函數HAL_Delay用於阻塞式延遲,默認單位是ms。
- 函數HAL_SuspendTick和HAL_ResumeTick是一對,前者用於掛起滴答定時器,后者用於恢復。
注意事項:
- 函數有個前綴__weak ,表示弱定義,用戶可以重定義。
使用舉例:
這些函數都比較簡單,這里就不舉例了。需要的時候,直接調用即可。
16.3.5 函數HAL_SYSCFG_VREFBUF_VoltageScalingConfig
函數原型:
void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling)
函數描述:
此函數用於配置STM32H7內部電壓基准大小。
- 當形參VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE0時
輸出基准是2.048 V,條件是VDDA >= 2.4V。
- 當形參VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE1時
輸出基准是2.5 V,條件是VDDA >= 2.8V。
- 當形參VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE2時
輸出基准是1.5 V,條件是VDDA >= 1.8V。
- 當形參VoltageScaling = SYSCFG_VREFBUF_VOLTAGE_SCALE3時
輸出基准是1.8 V,條件是VDDA >= 2.1V。
16.3.6 H7自帶電壓基准相關函數
函數原型:
void HAL_SYSCFG_VREFBUF_VoltageScalingConfig(uint32_t VoltageScaling) void HAL_SYSCFG_VREFBUF_TrimmingConfig(uint32_t TrimmingValue) HAL_StatusTypeDef HAL_SYSCFG_EnableVREFBUF(void) void HAL_SYSCFG_DisableVREFBUF(void)
函數描述:
- 函數HAL_SYSCFG_VREFBUF_VoltageScalingConfig
此函數用於配置STM32H7內部電壓基准是否在芯片內部與VREF+引腳接通。
- 形參為SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE時,表示導通。
- 形參為SYSCFG_VREFBUF_HIGH_IMPEDANCE_ENABLE時,表示高阻,即不導通。
- 函數HAL_SYSCFG_VREFBUF_TrimmingConfig
此函數用於內部電壓基准的校准調節。
- 函數HAL_SYSCFG_EnableVREFBUF和HAL_SYSCFG_DisableVREFBUF是一對,分別用於內部電壓參考基准的禁止和使能。
16.3.7 函數HAL_SYSCFG_AnalogSwitchConfig
函數原型:
void HAL_SYSCFG_AnalogSwitchConfig(uint32_t SYSCFG_AnalogSwitch , uint32_t SYSCFG_SwitchState )
函數描述:
- 引腳PA0,PA1,PC2,PC3用於ADC時,還有一組對應的可選引腳PA0_C,PA1_C,PC2_C和PC3_C。此函數的作用就是切換可選引腳。關於這個問題的詳情可看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87707 。
16.3.8 BOOST的使能和禁止(用於ADC)
函數原型:
void HAL_SYSCFG_EnableBOOST(void) void HAL_SYSCFG_DisableBOOST(void)
函數描述:
- 這兩個函數用於使能或者禁止Booster。如果使能了booster的話,在供電電壓低於2.7V時,可以減少模擬開關總的諧波失真,這樣的話,模擬開關的性能跟正常供電電壓時的全范圍測量一樣。
16.3.9 函數HAL_SYSCFG_CM7BootAddConfig
函數原型:
void HAL_SYSCFG_CM7BootAddConfig(uint32_t BootRegister, uint32_t BootAddress)
函數描述:
用於配置BOOT = 0或者BOOT = 1時的啟動地址,詳情可以看第13章的13.4小節。
16.3.10 IO補償相關函數
函數原型:
void HAL_EnableCompensationCell(void) void HAL_DisableCompensationCell(void) void HAL_SYSCFG_EnableIOSpeedOptimize(void) void HAL_SYSCFG_DisableIOSpeedOptimize(void) void HAL_SYSCFG_CompensationCodeSelect(uint32_t SYSCFG_CompCode) void HAL_SYSCFG_CompensationCodeConfig(uint32_t SYSCFG_PMOSCode, uint32_t SYSCFG_NMOSCode )
函數描述:
- 函數HAL_EnableCompensationCell和HAL_DisableCompensationCell
分別用於使能或者禁止IO補償,只有在供電電壓范圍是2.4V到3.6V之間時,使用此功能才有意義。
- 函數HAL_SYSCFG_EnableIOSpeedOptimize和HAL_SYSCFG_DisableIOSpeedOptimize
分別用於優化IO速度或者禁止優化,不過僅在供電電壓低於2.5V時可用,高於2.5V是不可以使用的,另外使用這個功能的前提是用戶使能了PRODUCT_BELOW_25V(是可選字節配置選項里面的一個bit)。
- 函數HAL_SYSCFG_CompensationCodeSelect
IO補償單元的選擇,函數形參可以是SYSCFG_CELL_CODE,即寄存器SYSCFG_CCVR,也可以是SYSCFG_REGISTER_CODE,即寄存器SYSCFG_CCCR。
- 函數HAL_SYSCFG_CompensationCodeConfig
用於設置GPIO內部構造中NMOS和PMOS的補償值,兩個形參的范圍都是0-15。根據用戶調用函數HAL_SYSCFG_CompensationCodeSelect選擇的寄存器,這里僅有一個形參的設置是有效的。
16.3.11 低功耗和EXTI相關函數
低功耗和EXTI相關的函數暫時不做講解,后面章節用到時候再說明。
16.4 源文件stm32h7xx_hal_rcc.c
這個文件主要是實現內部和外部時鍾(HSE、HSI、LSE、CSI、LSI、HSI48、PLL、CSS、MCO)以及總線時鍾(SYSCLK、AHB3、 AHB1、AHB2、AHB4、APB3、APB1L、APB1H、APB2、 APB4)的配置。
學習這個文件注意事項:
- 系統上電復位后,通過內部高速時鍾HSI運行(主頻64MHz),Flash工作在0等待周期,所有外設除了SRAM、Flash、JTAG 和 PWR,時鍾都是關閉的。這里特別注意SRAM,官方手冊里面說的太籠統,這個SRAM到底是指的哪個SRAM。關於這個問題,詳情看此貼:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87784 。
- AHB和APB總線無分頻,所有掛載這兩類總線上的外設都是以HSI頻率運行。
- 所有的GPIO都是模擬模式,除了JTAG相關的幾個引腳。
- 系統上電復位后,用戶需要完成以下工作:
- 選擇用於驅動系統時鍾的時鍾源。
- 配置系統時鍾頻率和Flash設置。
- 配置分頻器。
- 使能外設時鍾。
- 配置外設時鍾源,部分外設的時鍾可以不來自系統時鍾,此時通過配置寄存器RCC_D1CCIPR、RCC_D2CCIP1R、RCC_D2CCIP2R 和 RCC_D3CCIPR 實現。
RCC局限性:
使能了外設時鍾后,不能立即操作對應的寄存器,要加延遲。不同外設延遲不同:
- 如果是AHB的外設,使能了時鍾后,需要等待2個AHB時鍾周期才可以操作這個外設的寄存器。
- 如果是APB的外設,使能了時鍾后,需要等待2個APB時鍾周期才可以操作這個外設的寄存器。
當前HAL庫的解決方案是在使能了外設時鍾后,再搞一個讀操作,算是當做延遲用。
比如下面使能GPIOA的時鍾:
#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \ __IO uint32_t tmpreg; \ SET_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);\ /* Delay after an RCC peripheral clock enabling */ \ tmpreg = READ_BIT(RCC->AHB4ENR, RCC_AHB4ENR_GPIOAEN);\ UNUSED(tmpreg); \ } while(0)
- 關於時鍾方面的知識點補充
1、正確理解HCLK1,2,3,4以及PCLK1,2,3,4對應哪些總線的時鍾,下面一張圖可以說明問題:

由上圖可以得出:
HCLK1,2,3,4對應的是AHB總線AHB1,AHB2,AHB3和AHB4時鍾。
PCLK1、2、3、4對應的是APB總線APB1,APB2,APB3和APB4時鍾。
2、內部和外部時鍾配置:
- HSI (high-speed internal)
高速內部RC振盪器,可以直接或者通過PLL倍頻后做系統時鍾源。缺點是精度差些,即使經過校准。
- CSI (Low-power internal oscillator)
低功耗內部RC振盪器,作用同HSI,不過主要用於低功耗。
- LSI (low-speed internal)
低速內部時鍾,主要用於獨立看門狗和RTC的時鍾源。
- HSE (high-speed external)
高速外部晶振,可接4 - 48MHz的晶振,可以直接或者通過PLL倍頻后做系統時鍾源,也可以做RTC的是時鍾源。
- LSE (low-speed external)
低速外部晶振,主要用於RTC。
- CSS (Clock security system)
時鍾安全系統,一旦使能后,如果HSE啟動失敗(不管是直接作為系統時鍾源還是通過PLL輸出后做系統時鍾源),系統時鍾將切換到HSI。如果使能了中斷的話,將進入不可屏蔽中斷NMI。
- MCO1 (micro controller clock output)
可以在PA8引腳輸出HSI, LSE, HSE, PLL1(PLL1_Q)或HSI48 時鍾。
- MCO2 (micro controller clock output)
可以在PC9引腳輸出HSE, PLL2(PLL2_P), SYSCLK, LSI, CSI或PLL1(PLL1_P) 時鍾。
- PLL鎖相環,時鍾輸入來自HSI , HSE 或者CSI
主鎖相環PLL1,用於給CPU和部分外設提供時鍾。
專用鎖相環PLL2和PLL3,用於給部分外設提供時鍾。
3、System, AHB 和 APB總線時鍾配置:
- 系統總線時鍾源可以來自CSI,HIS,HSE 或 PLL。
- AHB總線時鍾是通過系統時鍾分頻產生的,用於給AXI,AHB1,2,3,4和APB1,2,3,4九個總線上的外設提供時鍾。另外用戶可以通過函數HAL_RCC_GetSysClockFreq獲取系統時鍾。
- 所有外設時鍾都可以通過系統時鍾獲取,並且部分外設還支持多個時鍾源,比如下面串口1,6
16.4.1 函數HAL_RCC_DeInit
函數原型:
void HAL_RCC_DeInit(void) { /* Set HSION bit */ SET_BIT(RCC->CR, RCC_CR_HSION); /* Reset CFGR register */ CLEAR_REG(RCC->CFGR); /* 省略未寫 */ /* Reset HSEBYP bit */ CLEAR_BIT(RCC->CR, RCC_CR_HSEBYP); /* Disable all interrupts */ CLEAR_REG(RCC->CICR); }
函數描述:
此函數用於RCC復位函數,主要實現如下功能:
- HSI 打開作為系統時鍾。
- HSE, PLL1, PLL2 和 PLL3 關閉。
- AHB, APB 總線無分頻 。
- CSS, MCO1 和 MCO2 關閉。
- 所有中斷關閉。
注意事項:
- 此函數不會修改外設時鍾,LSI、LSE和RTC時鍾。
使用舉例:
此函數的使用比較簡單,需要調用的時候直接調用即可。
16.4.2 函數HAL_RCC_OscConfig
函數原型:
函數描述:
通過上面函數原型,我們可以一目了然的看出此函數的作用,配置了HSE、HSI、CSI、LSI、HSI48、LSE和PLL。
函數參數:
- 函數的形參是RCC_OscInitTypeDef類型結構體變量,這個結構體的定義如下(主要是HSE、HSI、CSI、LSI、HSI48、LSE和PLL的配置變量):
/** * @brief RCC Internal/External Oscillator (HSE, HSI, CSI, LSE and LSI) configuration structure definition */ typedef struct { uint32_t OscillatorType; uint32_t HSEState; uint32_t LSEState; uint32_t HSIState; uint32_t HSICalibrationValue; uint32_t LSIState; uint32_t HSI48State; uint32_t CSIState; uint32_t CSICalibrationValue; RCC_PLLInitTypeDef PLL; }RCC_OscInitTypeDef;
注意事項:
- LSE Bypass 切換到 LSE On 或者 LSE On切換到 LSE Bypass都不支持,用需要先關閉LSE,然后才可以切換到LSE Bypass 或者LSE On。
- HSE Bypass 切換到 HSE On 或者 LSE On切換到 LSE Bypass都不支持,用需要先關閉HSE,然后才可以切換到HSE Bypass 或者HSE On。
使用舉例:
RCC_OscInitTypeDef RCC_OscInitStruct; HAL_StatusTypeDef ret = HAL_OK; /* 使能HSE,並選擇HSE作為PLL時鍾源 */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_OFF; RCC_OscInitStruct.CSIState = RCC_CSI_OFF; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 5; RCC_OscInitStruct.PLL.PLLN = 160; RCC_OscInitStruct.PLL.PLLP = 2; RCC_OscInitStruct.PLL.PLLR = 2; RCC_OscInitStruct.PLL.PLLQ = 4; RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE; RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2; ret = HAL_RCC_OscConfig(&RCC_OscInitStruct); if(ret != HAL_OK) { Error_Handler(__FILE__, __LINE__); }
16.4.3 函數HAL_RCC_ClockConfig
函數原型:
函數描述:
通過上面函數原型,我們可以一目了然的看出此函數的作用,配置了CPU、AHB和APB的所有總線時鍾,即HCLK,SYSTICK,D1PCLK1,PCLK1,PCLK2和D3PCLK1時鍾。這里的D1PCLK1其實就是PCLK3,而D3PCLK1是PCLK4。
函數參數:
- 第1個參數是RCC_ClkInitTypeDef類型的結構體變量。
- 第2個參數是Flash的延遲設置。
- 返回值,返回HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示忙,正在使用中。
注意事項:
- 此函數會更新全局變量SystemCoreClock的主頻值,並且會再次調用函數HAL_InitTick更新系統滴答時鍾,這點要特別注意。
- 系統上電復位或者從停機、待機模式喚醒后,使用的是HSI作為系統時鍾。以防使用HSE直接或者通過PLL輸出后做系統時鍾時失敗(如果使能了CSS)。
- 目標時鍾就緒后才可以從當前的時鍾源往這個目標時鍾源切換,如果目標時鍾源沒有就緒,就會等待直到時鍾源就緒才可以切換。
- 根據設備的供電范圍,必須正確設置D1CPRE[3:0]位的范圍,防止超過允許的最大頻率。
使用舉例:
RCC_ClkInitTypeDef RCC_ClkInitStruct; HAL_StatusTypeDef ret = HAL_OK; /* 選擇PLL的輸出作為系統時鍾 配置RCC_CLOCKTYPE_SYSCLK系統時鍾 配置RCC_CLOCKTYPE_HCLK 時鍾,對應AHB1,AHB2,AHB3和AHB4總線 配置RCC_CLOCKTYPE_PCLK1時鍾,對應APB1總線 配置RCC_CLOCKTYPE_PCLK2時鍾,對應APB2總線 配置RCC_CLOCKTYPE_D1PCLK1時鍾,對應APB3總線 配置RCC_CLOCKTYPE_D3PCLK1時鍾,對應APB4總線 */ RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \ RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1); RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2; RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2; RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2; /* 此函數會更新SystemCoreClock,並重新配置HAL_InitTick */ ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4); if(ret != HAL_OK) { Error_Handler(__FILE__, __LINE__); }
16.4.4 函數HAL_RCC_MCOConfig
函數原型:
void HAL_RCC_MCOConfig(uint32_t RCC_MCOx, uint32_t RCC_MCOSource, uint32_t RCC_MCODiv) { GPIO_InitTypeDef GPIO_InitStruct; /* Check the parameters */ assert_param(IS_RCC_MCO(RCC_MCOx)); assert_param(IS_RCC_MCODIV(RCC_MCODiv)); /* RCC_MCO1 */ if(RCC_MCOx == RCC_MCO1) { /* 省略未寫 */ } else { /* 省略未寫 */ } }
函數描述:
此函數的作用是配置MCO1(PA8引腳)和MCO2(PC9引腳)的時鍾輸出以及選擇的時鍾源,通過下面的截圖可以很好的說明此函數的作用:
函數參數:
- 第1個形參用於選擇輸出的引腳,可選擇RCC_MCO1(PA8引腳)或者RCC_MCO2(PC9引腳)。
- 第2個形參用於選擇輸出的時鍾源,MCO1可選擇的時鍾源如下:
- RCC_MCO1SOURCE_HSI
- RCC_MCO1SOURCE_LSE
- RCC_MCO1SOURCE_HSE
- RCC_MCO1SOURCE_PLL1QCLK
- RCC_MCO1SOURCE_HSI48
MCO1可選擇的時鍾源如下:
- RCC_MCO2SOURCE_SYSCLK
- RCC_MCO2SOURCE_PLL2PCLK
- RCC_MCO2SOURCE_HSE
- RCC_MCO2SOURCE_PLLCLK
- RCC_MCO2SOURCE_CSICLK
- RCC_MCO2SOURCE_LSICLK
- 第3個參數用於設置輸出分頻,范圍從RCC_MCODIV_1到RCC_MCODIV_15。
使用舉例:
此函數的使用比較簡單,需要在MCO引腳輸出時鍾的時候,直接調用即可。
16.4.5 RCC相關函數
函數原型:
void HAL_RCC_EnableCSS(void) uint32_t HAL_RCC_GetSysClockFreq(void) uint32_t HAL_RCC_GetHCLKFreq(void) uint32_t HAL_RCC_GetPCLK1Freq(void) uint32_t HAL_RCC_GetPCLK2Freq(void) void HAL_RCC_GetOscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct) void HAL_RCC_GetClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t *pFLatency)
剩下的這些函數都比較簡單了,我們這里就不做講解了。
16.5 源文件stm32h7xx_hal_cortex.c
這個庫文件主要功能是NVIC,MPU和Systick的配置。此文件有個臃腫的地方,里面的API其實就是將ARM的CMSIS庫各種API重新封裝了一遍。這么做的好處是保證了HAL的API都是以字母HAL開頭。
更多NVIC相關知識需要大家看第21章節。
更多SysTick相關知識需要大家看第22章節。
更多MPU相關知識需要大家看第23章節。
16.5.1 NVIC相關函數
函數原型:
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup) void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority) void HAL_NVIC_EnableIRQ(IRQn_Type IRQn) void HAL_NVIC_DisableIRQ(IRQn_Type IRQn) void HAL_NVIC_SystemReset(void) uint32_t HAL_NVIC_GetPriorityGrouping(void) void HAL_NVIC_GetPriority(IRQn_Type IRQn, uint32_t PriorityGroup, uint32_t *pPreemptPriority, uint32_t *pSubPriority) void HAL_NVIC_SetPendingIRQ(IRQn_Type IRQn) uint32_t HAL_NVIC_GetPendingIRQ(IRQn_Type IRQn) void HAL_NVIC_ClearPendingIRQ(IRQn_Type IRQn) uint32_t HAL_NVIC_GetActive(IRQn_Type IRQn)
這些函數的操作就比較容易了,這里就不做講解了,用到時調用即可。
16.5.2 SysTick相關函數
函數原型:
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb) void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource) void HAL_SYSTICK_IRQHandler(void) __weak void HAL_SYSTICK_Callback(void)
這些函數的操作就比較容易了,這里就不做講解了,用到時調用即可。
16.5.3 函數HAL_MPU_ConfigRegion
函數原型:
void HAL_MPU_ConfigRegion(MPU_Region_InitTypeDef *MPU_Init) { /* 部分省略未寫 */ /* Set the Region number */ MPU->RNR = MPU_Init->Number; if ((MPU_Init->Enable) != RESET) { /* 部分省略未寫 */ MPU->RBAR = MPU_Init->BaseAddress; MPU->RASR = ((uint32_t)MPU_Init->DisableExec << MPU_RASR_XN_Pos) | ((uint32_t)MPU_Init->AccessPermission << MPU_RASR_AP_Pos) | ((uint32_t)MPU_Init->TypeExtField << MPU_RASR_TEX_Pos) | ((uint32_t)MPU_Init->IsShareable << MPU_RASR_S_Pos) | ((uint32_t)MPU_Init->IsCacheable << MPU_RASR_C_Pos) | ((uint32_t)MPU_Init->IsBufferable << MPU_RASR_B_Pos) | ((uint32_t)MPU_Init->SubRegionDisable << MPU_RASR_SRD_Pos) | ((uint32_t)MPU_Init->Size << MPU_RASR_SIZE_Pos) | ((uint32_t)MPU_Init->Enable << MPU_RASR_ENABLE_Pos); } else { MPU->RBAR = 0x00; MPU->RASR = 0x00; } }
此函數在本教程第23章有專門的講解。
16.6 總結
本章節就為大家講解這么多,對於一些常用的函數,望大家務必要掌握。
