HAL库的框架结构
1 初始HAL库
1.1 CMSIS简介
基于Cortex系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核,不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的Cortex微控制器软件 的兼容性问题,ARM与芯片厂商建立了CMSIS标准(Cortex MicroController Software Interface Standard)。
Cortex微控制器软件接口标准(Cortex Microcontroller Software Interface Standard)是ARM和一些编译器厂家以及半导体厂家共同遵循的一套标准,是由ARM提出,专门针对CORTEX-M系列的标准。在该标准的约定下,ARM和芯片厂商会提供一些通用的API接口来访问CORTEX内核以及一些专用外设,以减少更换芯片以及开发工具等移植工作所带来的金钱以及时间上的消耗。
以下是ARM官网关于CMSIS的架构图
可见,CMSIS层位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层,可以为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,这对软件的移植是有极大的好处的。STM32的库,就是按照CMSIS标准建立的。
1.2 HAL库简介
ST 为了方便用户开发 STM32芯片开发提供了三种库:
- 标准外设库 :Standard Peripheral Libraries F0/F1/F3/F2/F4/L1目前已停止维护。
- HAL库:Hardware Abstraction Layer 全系列兼容,ST目前主推的库,具有强兼容性、易移植性,但效率较低。
- LL库:Low Layer 全系列兼容,与HAL库捆绑发布,轻量级、效率高,但不匹配部分复杂外设。
三种库比较:
1.3 MCU固件包
STM32微控制器各个产品系列都有独立的MCU固件包,HAL库属于MCU固件包的一部分。
MCU固件包文件结构:
- Drivers:文件夹下是官方的CMSISI库,HAL库,板载外设驱动。
- CMSIS:由ARM公司提供的Cortex微控制器软件接口标准,包括Cortex内核寄存器定义、启动文件等。
- STM32F1xx_HAL_Driver:STM32微控制器片内外设的HAL库驱动文件stm32f1xx_hal_ppp.h,stm32f1xx_hal_ppp.c。
- BSP:基于HAL库开发的官方开发板的板级支持包,提供指示灯、按键等外围电路的驱动程序,用于适配ST官方的开发板(可参考)。
- Middleware:中间件,包含ST官方的STemWin、 STM32_Audio、STM32_USB_Device_Library、STM32_USB_Host_Library;也有第三方的fatfs文件系统等等。
- Projects:文件夹下是用驱动库写的针对官方发行demo板的例子和工程模板。
- Templates:各集成开发环境的工程模板。
- Examples:针对MCU片内外设的简单例程,如GPIO、UART等。
- Applications:针对单个中间件的应用例程。
- Demonstations:针对多个中间件的综合应用例程。
- Utilities:实用的公用组件比如LCD_LOG实用液晶打印调试信息。
1.4 HAL库框架结构
HAL库文件夹结构
Src(Source):外设驱动源码,Inc(Include):外设驱动源码头文件。
HAL库文件介绍:
- stm32f1xx_hal.c, stm32f1xx_hal.h
HAL库初始化、系统滴答、HAL库延时等相关函数 - stm32f1xx_hal_conf.h
HAL库的用户配置文件,用于裁剪HAL库、配置晶振参数等 - stm32f1xx_hal_def.h
包含HAL库通用的枚举类型数据和宏定义 - stm32f1xx_hal_cortex.h, stm32f1xx_hal_cortex.c
内核通用函数定义和声明,如NVIC、MPU、系统软复位、Systick等,其实主要是对core_cm3.h 文件的相关函数再次封装。 - stm32f1xx_hal_ppp.c, stm32f1xx_hal_ppp.h
某任意外设驱动源码,PPP表示任意外设 - stm32f1xx_hal_ppp_ex.c, stm32f1xx_hal_ppp_ex.h
主要是存放外设的扩展(特殊)功能的驱动源码,PPP表示任意外设 - stm32f1xx_II_ppp.c, stm32f1xx_II_ppp.h
LL库驱动源码,在部分STM32F1xx_hal_ppp.c 或stm32f1xx_hal_ppp_ex.c中会被调用
HAL库API函数和变量命名规则:
- stm32f1xx_hal_ppp .(c/h)
- 函数名:HAL_PPP_Function
- 外设句柄:PPP_HandleTypeDef
- 外设工作参数初始化结构体:PPP_InitTypeDef
- 配置参数结构体:PPP_ YyyyConfTypeDef
- stm32f1xx_hal_ppp_ex .(c/h)
- 函数名:HAL_PPPEx_Function
- 外设句柄:无
- 外设工作参数初始化结构体:PPP_InitTypeDef
- 配置参数结构体:PPP_ YyyyConfTypeDef
- 初始化/反初始化函数:
- HAL_PPP_Init(),HAL_PPP_DeInit()
- 外设读写函数:
- HAL_PPP_Read(),
- HAL_PPP_Write(),
- HAL_PPP_Transmit(),
- HAL_PPP_Receive()
- 控制函数:
- HAL_PPP_Set (),
- HAL_PPP_Get ()
- 状态和错误:
- HAL_PPP_GetState (),HAL_PPP_GetError ()
HAL库对寄存器位操作的相关宏定义:
- __HAL_PPP_ENABLE_IT( HANDLE , INTERRUPT )
使能外设中断 - __HAL_PPP_DISABLE_IT( HANDLE , INTERRUPT )
禁用外设中断 - __HAL_PPP_GET_IT ( HANDLE , __ INTERRUPT __)
获取外设某一中断源 - __HAL_PPP_CLEAR_IT ( HANDLE , __ INTERRUPT __)
清除外设中断 - __HAL_PPP_GET_FLAG ( HANDLE , FLAG )
获取外设的状态标记 - __HAL_PPP_CLEAR_FLAG ( HANDLE , FLAG )
清除外设的状态标记 - __HAL_PPP_ENABLE( HANDLE )
使能某一外设 - __HAL_PPP_DISABLE( HANDLE )
禁用某一外设 - __HAL_PPP_XXXX ( HANDLE , PARAM )
针对外设的特殊操作 - _HAL_PPP_GET IT_SOURCE ( HANDLE , __INTERRUPT __)
检查外设的中断源
HAL库回调函数:
- HAL_PPP_MspInit() / _MspDeInit()
举例: HAL_USART_MspInit() 会被HAL_PPP_Init() 函数调用,该函数主要用于存放外设使用到的GPIO、CLOCK、NVIC、DMA等初始化代码。 - HAL_PPP_ProcessCpltCallback
举例: HAL_USART_TxCpltCallback由外设中断或DMA中断调用,HAL库中断公共处理函数已经实现对中断标记位读取、判断和清除操作,用户只需要专注于中断逻辑功能的实现即可。 - HAL_PPP_ErrorCallback
举例: HAL_USART_ErrorCallback外设或DMA中断中发生的错误,用于发生错误处理。
此类函数通常被_weak修饰(弱函数),允许用户重新定义该函数。
1.5 HAL库的设计思想
HAL库借鉴 面向对象的设计思想 :
开发方式的特点:
- 屏蔽底层硬件 :编程者只需要了解库函数中相关接口函数的功能,并按照要求传入参数,利用返回值完成操作即可,不需要过多了解底层硬件。
- 提高开发效率 :开发难度较小,开发周期较短,后期的维护升级、以及硬件平台的移植等工作量较小。
- 程序执行效率 :由于考虑了程序的稳健性、扩充性和可移植性,程序代码比较繁琐和臃肿,执行效率较低。
用户代码的处理主要分为三部分:
- 处理外设句柄(实现用户功能)
- 处理MSP
- 处理各种回调函数
设计思想:从抽象到重载
1.6 以USART为例说明HAL库的抽象结构
①外设句柄的定义:
HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef
的结构体,其中ppp就是每个外设的名字。所有的函数都是工作在ppp_HandleTypeDef
指针之下。
例如,使用USART2时,可以定义USART初始化结构体变量(全局变量)huart2
。huart2
就被称为串口的句柄,它被贯穿整个USART收发的流程。
UART_HandleTypeDef huart2;
②外设句柄数据类型的组成:
③串口初始化数据类型的组成:
④串口初始化过程:
抽象—串口初始化函数MX_USART2_UART_Init
: 将与MCU无关的通信参数存入句柄结构 +使用HAL_UART_Init
执行串口初始化操作,将句柄参数写入寄存器。
HAL_UART_Init` 干了哪些事?
调用 HAL_UART_MspInit
* 修改状态忙* 配置寄存器* 清除标志位
承载—与MCU相关的初始化函数HAL_UART_MspInit
: 时钟初始化 + 引脚初始化
⑤串口中断处理过程:
HAL_UART_Receive_IT
:开启中断,在中断方式下接收一定数量的数据。USART2_IRQHandler
:串口2的中断服务程序,调用串口中断通用处理函数HAL_UART_IRQHandler
。HAL_UART_IRQHandler
:在函数 HAL_UART_IRQHandler
内部通过判断中断类型是否为接收完成中断,确定是否调用UART_Receive_IT
。
函数UART_Receive_IT
的作用是把每次中断接收到的字符保存在串口句柄的缓存指针pRxBuffPtr
中,同时每次接收一个字符,其计数器 RxXferCount
减 1,直到接收完成 RxXferSize
个字符之后 RxXferCount
设置为0,同时调用接收完成回调函数 HAL_UART_RxCpltCallback
进行处理。HAL_UART_RxCpltCallback
:函数由串口中断通用处理函数UART_Receive_IT
调用,完成所有串口的接收中断任务处理,函数内部需要根据串口句柄的实例来判断是哪一个串口产生的接收中断,函数由用户根据具体的处理任务编写。