STM32 HAL库头文件三胞胎stm32f4xx.h---f407.h---f4xx_hal.h解析(区别和联系)

总体一览

一、文件stm32f4xx.h 解析(简短不看版:最强顶级头文件)

这个文件是个头文件,它又包含了两个头文件(通过在 Keil 魔法棒工具定义两个宏 STM32F407xx 和 USE_HAL_DRIVER 开关)。
#include "stm32f407.h" //某一特定F4型号芯片寄存器定义
#include "stm32f4xx_hal.h"//HAL库函数(HAL库编程的API总集合)
 
然后在编程的时候,就只要包含stm32f4xx.h就可以了。
(这样一来,一样的程序不需要修改文件本身,只需要修改编译器定义的宏,就可以在所有 F4 系列芯片正确运行,因此这个文件也被称为“顶级头文件”。
 

因为不同的芯片寄存器映射不同,从而按道理程序也不同,但通过上述的映射,

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"
  */
翻译为中文
*该文件是程序员在 C 源码中使用的唯一的包含文件(通常在 main.c 当中)
*该文件包含了以下内容:
*-配置部分,允许选择:
*-目标应用程序中使用的 STM32F4XX 型号。
*-通过预编译宏:#define USE_HAL_DRIVER 来确定程序是寄存器编程方式,还是HAL库编程方式。
 
 
程序的主要架构:
第一部分:定义了芯片型号的宏STM32F4 、STM32F407xx(这部分推荐在编译器中定义,以防止一直修改这个文件)
#if !defined  (STM32F4)
    #define STM32F4
#endif /* STM32F4 */

#if !defined (STM32F407xx)
    #define STM32F407xx
#endif
 
第二部分:定义 USE_HAL_DRIVER(这部分也推荐在编译器中定义,以防止一直修改这个文件)
#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;
}

 

posted @ 2023-10-23 19:53  FBshark  阅读(1462)  评论(0编辑  收藏  举报