STM32 HAL库头文件三胞胎stm32f4xx.h---f407.h---f4xx_hal.h解析(区别和联系)
总体一览
一、文件stm32f4xx.h 解析(简短不看版:最强顶级头文件)
#include "stm32f407.h" //某一特定F4型号芯片寄存器定义
#include "stm32f4xx_hal.h"//HAL库函数(HAL库编程的API总集合)
因为不同的芯片寄存器映射不同,从而按道理程序也不同,但通过上述的映射,
F4 系列芯片的程序,都可以以 stm32f4xx.h 作为头文件。(无论是什么型号,是F407 还是 F429,是VET还是IGT)
相当于对芯片型号进行了抽象化。
详细介绍:
首先在文件开头,官方简介如下:
/*
* The file is the unique include file that the application programmer
* is using in the C source code, usually in main.c. This file contains:
* - Configuration section that allows to select:
* - The STM32F4xx device used in the target application
* - To use or not the peripheral抯 drivers in application code(i.e.
* code will be based on direct access to peripheral抯 registers
* rather than drivers API), this option is controlled by
* "#define USE_HAL_DRIVER"
*/
#if !defined (STM32F4)
#define STM32F4
#endif /* STM32F4 */
#if !defined (STM32F407xx)
#define STM32F407xx
#endif
#if !defined (USE_HAL_DRIVER)
/**
* @brief 如果想要寄存器编程,将其注释掉
*/
#define USE_HAL_DRIVER
#endif /* USE_HAL_DRIVER */
#if defined(STM32F405xx)
#include "stm32f405xx.h"
#elif defined(STM32F407xx)
#include "stm32f407xx.h" //假设第一部分定义了STM32F407xx,那么该文件包含 stm32f407xx.h 这个头文件
#elif ....
#include ...
#endif
第四部分:由上面第二部分的宏定义(USE_HAL_DRIVER),包含具体芯片的HAL库头文件
#if defined (USE_HAL_DRIVER)
#include "stm32f4xx_hal.h"
#endif /* USE_HAL_DRIVER */
第五部分:定义各种状态(用作各种函数的返回参数)
//Flag 和 中断标志: RESET 和 SET
typedef enum
{
RESET = 0U,
SET = !RESET
} FlagStatus, ITStatus;
//使能标志: DISABLE 和 ENABLE
typedef enum
{
DISABLE = 0U,
ENABLE = !DISABLE
} FunctionalState;
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
//错误标志: SUCCESS 和 ERROR
typedef enum
{
SUCCESS = 0U,
ERROR = !SUCCESS
} ErrorStatus;
第六部分:定义寄存器编程的相关读写操作
#define SET_BIT(REG, BIT) ((REG) |= (BIT)) //将寄存器REG与BIT掩码或,从而置1
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))//将寄存器REG和BIT掩码与非,从而置0
....
#define CLEAR_REG(REG) ((REG) = (0x0)) //清零
二、文件stm32f407.h 解析——特定芯片的寄存器映射文件
首先在文件开头,官方简介如下:
/*
* This file contains:
* - Data structures and the address mapping for all peripherals
* - peripherals registers declarations and bits definition
* - Macros to access peripheral’s registers hardware
*/
翻译为中文就是:
*此文件包含:
*-所有外围设备的数据结构和地址映射
*-外设寄存器声明和位定义
*-访问外设寄存器硬件的宏
简而言之,stm32f407.h 就是寄存器映射文件。包含这个头文件,便可以进行寄存器编程。
除此之外,还包括内核编程API、基础数据结构定义和中断号。因此是stm32芯片整体最基础、最必要的文件。
这个文件包括:
0. 包含cortex-m3.h, stdint.h 的头文件。
//stm32f407.h 包含了core_cm4.h,也就是说stm32f407.h既包含了核心,也包含片上外设.
//另外,也包含了标准库stdint.h,其中有 uint32_t, uint16_t 等最基础的定义。
//从这个意义上说——stm32f407.h 是stm32芯片整体最基础、最必要的文件。
#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */
#include "system_stm32f4xx.h"
#include <stdint.h>
1. 片上外设的数据结构和寄存器映射,以GPIO为例:
typedef struct
{
__IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */
__IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */
__IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */
__IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
__IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */
__IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */
__IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */
__IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */
__IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */
} GPIO_TypeDef;
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
2. 读写外设寄存器的相关掩码 ,以 GPIO_MODER 寄存器为例:这些在进行寄存器编程的时候很有用。
/******************************************************************************/
/****************** Bits definition for GPIO_MODER register *****************/
#define GPIO_MODER_MODER0_Pos (0U)
#define GPIO_MODER_MODER0_Msk (0x3UL << GPIO_MODER_MODER0_Pos) /*!< 0x00000003 */
#define GPIO_MODER_MODER0 GPIO_MODER_MODER0_Msk
#define GPIO_MODER_MODER0_0 (0x1UL << GPIO_MODER_MODER0_Pos) /*!< 0x00000001 */
#define GPIO_MODER_MODER0_1 (0x2UL << GPIO_MODER_MODER0_Pos) /*!< 0x00000002 */
3. 中断号:记得在标准库编程时,NVIC的中断结构体初始化需要查看这里。
typedef enum
{
/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/
PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers **********************************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
...
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
....
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
}IRQn_Type;
三、文件stm32f4xx_hal.h 解析——HAL 库驱动总集合
第一部分:包含"stm32f4xx_hal_conf.h",这一行虽小,但是确是核心内容。它包含了所有外设的HAL驱动声明。
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal_conf.h"
stm32f4xx_hal_conf.h 总管所有外设的 HAL 驱动,然后被 stm32f4xx_hal.h 包含。(stm32f4xx_hal.h 也算是头文件的头文件。)
因此HAL库编程的时候所需要做的就是:
1. 根据所需要的外设,裁剪stm32f4xx_hal_conf.h
2. 然后包含 stm32f4xx_hal.h 文件。(当然包含 stm32f4xx.h 也可,因为 stm32f4xx_hal.h也被 stm32f4xx.h 包含了)
第二部分:导出的3个变量:
/* Exported variables --------------------------------------------------------*/
extern __IO uint32_t uwTick;
extern uint32_t uwTickPrio;
extern HAL_TickFreqTypeDef uwTickFreq;
第三部分:导出的若干函数:分为初始化函数和外设控制函数两类。
这一部分是 HAL 库区别于 STM32 标准库关键所在,精华是 HAL_Init() 函数,体现了 HAL库的特点。
HAL_Init() 用于实现:
- 1----设置 FLASH预取、命令缓存、数据缓存
- 2----设置NVIC优先级分组为NVIC_PRIORITYGROUP_4(避免了标准库分组混乱的情况)
- 3----设置 Systick 为时基,周期为1ms。并提供延时函数(避免了标准库需要自己设置延时)
/* Exported functions --------------------------------------------------------*/
/* Initialization and Configuration functions ******************************///共5个
HAL_StatusTypeDef HAL_Init(void);
HAL_StatusTypeDef HAL_DeInit(void);
void HAL_MspInit(void);
void HAL_MspDeInit(void);
HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority);
/* Peripheral Control functions ************************************************///共23个
void HAL_IncTick(void);
void HAL_Delay(uint32_t Delay);
uint32_t HAL_GetTick(void);
uint32_t HAL_GetTickPrio(void);
。。。。(省略)
第二、三部分导出的函数在 stm32f4xx_hal.c 中明确定义:
其中第二部分的变量:
__IO uint32_t uwTick;
uint32_t uwTickPrio = (1UL << __NVIC_PRIO_BITS); /* Invalid PRIO */
HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT; /* 1KHz */
第三部分的函数:
关于 HAL_Init():
/*
初始化和配置功能
[..]本节提供的功能允许:
(+)初始化闪存接口NVIC分配和初始时钟配置。它还会在需要超时时初始化系统,并在启用时初始化备份域。
(+)取消初始化HAL的公用部分。
(+)将时基源配置为具有1ms时基,并具有专用Tick中断优先级。
(++)SysTick计时器默认用作时基源,但用户最终可以实现其适当的时基源(例如通用计时器或其他时基),请记住,时基持续时间应保持1ms,因为PPP_TIMEOUT_VALUE是以毫秒为单位定义和处理的。
(++)时基配置函数(HAL_InitTick())在程序开始时由HAL_Init()重置后自动调用,或在时钟配置的任何时候由HAL_RCC_ClockConfig()调用。
(++)时基源被配置为以规则的时间间隔生成中断。如果从外围ISR进程调用HAL_Delay(),则必须小心,Tick中断线必须具有比外围中断更高的优先级(数值更低)。否则,调用方ISR进程将被阻止。
影响时基配置的(++)函数被声明为__weak,以便在用户文件中有其他实现的情况下进行重写。
*/
/**
*@brief此函数用于初始化HAL库;它一定是第一个
*要在主程序中执行的指令(在调用任何其他程序之前
*HAL功能),它执行以下操作:
*配置闪存预取、指令和数据缓存。
*将SysTick配置为每1毫秒生成一次中断,
*由HSI计时(在这个阶段,时钟还没有
*配置并且因此系统从内部HSI以16MHz运行)。
*将NVIC组优先级设置为4。
*调用用户文件中定义的HAL_MspInit()回调函数
*“stm32f4x_hal_msp.c”进行全局低级别硬件初始化
*
*@note SysTick用作HAL_Delay()函数的时基
*需要确保SysTick时基始终设置为1毫秒
*以具有正确的HAL操作。
*@retval HAL状态
*/
HAL_StatusTypeDef HAL_Init(void)
{
/* Configure Flash prefetch, Instruction cache, Data cache */
__HAL_FLASH_INSTRUCTION_CACHE_ENABLE();
__HAL_FLASH_DATA_CACHE_ENABLE();
__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); //1----设置 FLASH预取、命令缓存、数据缓存
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); //2------设置NVIC主分组:为NVIC_PRIORITYGROUP_4
/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
HAL_InitTick(TICK_INT_PRIORITY); //3-----设置 Systick 为时基,周期为1ms
HAL_MspInit();/* Init the low level hardware */ //4------用于用户初始化外设
return HAL_OK;/* Return function status */
}
关于HAL_InitTick()
/**
*@brief此函数配置时基的来源。
*时间源配置为具有1ms时基,并具有专用的Tick中断优先级。
*@note: 此函数在之后的程序开始时自动调用
*通过HAL_Init()重置或在时钟通过HAL_RCC_ClockConfig()重新配置时的任何时间重置。
*@note: 在默认实现中,SysTick计时器是时基的来源。
*它用于以规则的时间间隔生成中断。
*如果从外围ISR进程调用HAL_Delay(),则必须小心,
*SysTick中断必须具有更高的优先级(数值更低)
*而不是外围中断。否则,调用方ISR进程将被阻止。
*函数被声明为__weak,以便在用户文件中有其他实现的情况下被覆盖。
*@param TickPriority Tick中断优先级。
*@retval HAL状态
*/
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* 配置 SysTick,周期为 1ms */
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{ return HAL_ERROR;
}
/* 配置 SYsTick 的中断等级, 由形参 TickPriority 指定*/
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{ return HAL_ERROR;}
return HAL_OK;
}
关于 HAL_Delay()函数:
/*
===============================================================================
#####HAL控制功能#####
===============================================================================
[..]本节提供的功能允许:
(+)以毫秒为单位提供刻度值
(+)以毫秒为单位提供阻塞延迟 //这个应该最常用: HAL_Delay
(+)暂停时基源中断
(+)恢复时基源中断
(+)获取HAL API驱动程序版本
(+)获取设备标识符
(+)获取设备版本标识符
(+)在SLEEP模式期间启用/禁用调试模块
(+)在STOP模式下启用/禁用调试模块
(+)在STANDBY模式下启用/禁用调试模块
*/
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
/* Add a freq to guarantee minimum wait */
if (wait < HAL_MAX_DELAY)
{
wait += (uint32_t)(uwTickFreq);
}
while((HAL_GetTick() - tickstart) < wait) {;}//可以看到是通过死循环实现的
}
/**
*@note: 在默认实现中,SysTick计时器是时基的来源。用于以规则的时间间隔生成中断。
* 当HAL_SuspendTick(该函数)被调用时,SysTick中断将被禁用,因此Tick增量暂停。
*/
__weak void HAL_SuspendTick(void)
{
/* Disable SysTick Interrupt */
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
}