• 2018.1.19 更新其中的错误部分

HAL库结构

  说到 STM32 的 HAL 库,就不得不提 STM32CubeMX,其作为一个可视化的配置工具,对于开发者来说,确实大大节省了开发时间。STM32CubeMX 就是以 HAL 库为基础的,且目前仅支持 HAL 库及 LL 库!首先看一下,官方给出的 HAL 库的包含结构:

  • stm32f2xx.h 主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义,接着,其会根据定义的芯片信号包含具体的芯片型号的头文件:
#if defined(STM32F205xx)#include "stm32f205xx.h"
#elif defined(STM32F215xx)#include "stm32f215xx.h"
#elif defined(STM32F207xx)#include "stm32f207xx.h"
#elif defined(STM32F217xx)#include "stm32f217xx.h"
#else#error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"
#endif

紧接着,其会包含 stm32f2xx_hal.h

  • stm32f2xx_hal.hstm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置
  • stm32f2xx_hal_conf.h :该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中。

  接下来对于HAL库的源码文件进行一下说明,HAL库文件名均以stm32f2xx_hal开头,后面加上_外设或者模块名(如:stm32f2xx_hal_adc.c):

库文件:stm32f2xx_hal_ppp.c/.h         // 主要的外设或者模块的驱动源文件,包含了该外设的通用APIstm32f2xx_hal_ppp_ex.c/.h        // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。stm32f2xx_hal.c/.h              // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API其他库文件
用户级别文件:stm32f2xx_hal_msp_template.c   // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。stm32f2xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用system_stm32f2xx.c               // 此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。 **它不在启动时配置系统时钟(与标准库相反)**。 时钟的配置在用户文件中使用HAL API来完成。startup_stm32f2xx.s                // 芯片启动文件,主要包含堆栈定义,终端向量表等stm32f2xx_it.c/.h               // 中断处理函数的相关实现main.c/.h                        //

  根据HAL库的命名规则,其API可以分为以下三大类:

  • 初始化/反初始化函数: HAL_PPP_Init(), HAL_PPP_DeInit()
  • IO 操作函数: HAL_PPP_Read(), HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
  • 控制函数: HAL_PPP_Set (), HAL_PPP_Get ().
  • 状态和错误: HAL_PPP_GetState (), HAL_PPP_GetError ().

注意:

  1. 目前 LL 库是和 HAL 库捆绑发布的,所以在 HAL 库源码中,还有一些名为 stm32f2xx_ll_ppp 的源码文件,这些文件就是新增的LL库文件。
  2. 使用 CubeMX 生产项目时,可以选择LL库

HAL 库最大的特点就是对底层进行了抽象。在此结构下,用户代码的处理主要分为三部分:

  • 处理外设句柄(实现用户功能)
  • 处理MSP
  • 处理各种回调函数

外设句柄定义

  用户代码的第一大部分:对于外设句柄的处理。 HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef的结构体,其中ppp就是每个外设的名字。*所有的函数都是工作在ppp_HandleTypeDef指针之下。
  1. 多实例支持:每个外设/模块实例都有自己的句柄。 因此,实例资源是独立的
  2. 外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。
下面,以 ADC 为例

/** * @brief  ADC handle Structure definition*/
typedef struct
{ADC_TypeDef                   *Instance;                   /*!< Register base address */ADC_InitTypeDef               Init;                        /*!< ADC required parameters */__IO uint32_t                 NbrOfCurrentConversionRank;  /*!< ADC number of current conversion rank */DMA_HandleTypeDef             *DMA_Handle;                 /*!< Pointer DMA Handler */HAL_LockTypeDef               Lock;                        /*!< ADC locking object */__IO uint32_t                 State;                       /*!< ADC communication state */__IO uint32_t                 ErrorCode;                   /*!< ADC Error code */
}ADC_HandleTypeDef;

  从上面的定义可以看出,ADC_HandleTypeDef中包含了ADC可能出现的所有定义,对于用户想要使用ADC只要定义一个ADC_HandleTypeDef的变量,给每个变量赋好值,对应的外设就抽象完了。接下来就是具体使用了。
  当然,对于那些共享型外设或者说系统外设来说,他们不需要进行以上这样的抽象,这些部分与原来的标准外设库函数基本一样。 例如以下外设:
  - GPIO
  - SYSTICK
  - NVIC
  - RCC
  - FLASH
以 GPIO 为例,对于 HAL_GPIO_Init() 函数,其只需要 GPIO 地址以及其初始化参数即可。

三种编程方式

  HAL库对所有的函数模型也进行了统一。在HAL库中,支持三种编程模式:轮询模式、中断模式、DMA模式(如果外设支持)。其分别对应如下三种类型的函数(以ADC为例):

HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);

  其中,带_IT的表示工作在中断模式下;带_DMA的工作在DMA模式下(注意:DMA模式下也是开中断的);什么都没带的就是轮询模式(没有开启中断的)。至于使用者使用何种方式,就看自己的选择了。
  此外,新的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 库的源码中,到处可见一些以__weak开头的函数,而且这些函数,有些已经被实现了,比如:

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{/*Configure the SysTick to have interrupt in 1ms time basis*/HAL_SYSTICK_Config(SystemCoreClock/1000U);/*Configure the SysTick IRQ priority */HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);/* Return function status */return HAL_OK;
}

有些则没有被实现,例如:

__weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{/* Prevent unused argument(s) compilation warning */UNUSED(hspi);/* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file*/
}

所有带有__weak关键字的函数表示,就可以由用户自己来实现。如果出现了同名函数,且不带__weak关键字,那么连接器就会采用外部实现的同名函数。通常来说,HAL库负责整个处理和MCU外设的处理逻辑,并将必要部分以回调函数的形式给出到用户,用户只需要在对应的回调函数中做修改即可。 HAL 库包含如下三种用户级别回调函数(PPP为外设名):
  1. 外设系统级初始化/解除初始化回调函数(用户代码的第二大部分:对于MSP的处理):HAL_PPP_MspInit()HAL_PPP_MspDeInit** 例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函数中被调用,用来初始化底层相关的设备(GPIOs, clock, DMA, interrupt)

  2. 处理完成回调函数:HAL_PPP_ProcessCpltCallback*(Process指具体某种处理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)。当外设或者DMA工作完成后时,触发中断,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用

  3. 错误处理回调函数:HAL_PPP_ErrorCallback例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)**。当外设或者DMA出现错误时,触发终端,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用

  1. 绝大多数用户代码均在以上三大回调函数中实现。
  2. HAL库结构中,在每次初始化前(尤其是在多次调用初始化前),先调用对应的反初始化(DeInit)函数是非常有必要的。某些外设多次初始化时不调用返回会导致初始化失败。
  3. 完成回调函数有多中,例如串口的完成回调函数有HAL_UART_TxCpltCallback 和 HAL_UART_TxHalfCpltCallback
  4. (用户代码的第三大部分:对于上面第二点和第三点的各种回调函数的处理)
  5. 在实际使用中,发现HAL仍有不少问题,例如在使用USB时,其库配置存在问题

HAL库移植使用

基本步骤

  1. 复制stm32f2xx_hal_msp_template.c,参照该模板,依次实现用到的外设的HAL_PPP_MspInit()HAL_PPP_MspDeInit
  2. 复制stm32f2xx_hal_conf_template.h,用户可以在此文件中自由裁剪,配置HAL库。
  3. 在使用HAL库时,必须先调用函数:HAL_StatusTypeDef HAL_Init(void)(该函数在stm32f2xx_hal.c中定义,也就意味着第一点中,必须首先实现HAL_MspInit(void)HAL_MspDeInit(void)
  4. HAL库与STD库不同,HAL库使用RCC中的函数来配置系统时钟,用户需要单独写时钟配置函数(STD库默认在system_stm32f2xx.c中)
  5. 关于中断,HAL提供了中断处理函数,只需要调用HAL提供的中断处理函数。用户自己的代码,不建议先写到中断中,而应该写到HAL提供的回调函数中。
  6. 对于每一个外设,HAL都提供了回调函数,回调函数用来实现用户自己的代码。整个调用结构由HAL库自己完成。例如:Uart中,HAL提供了void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);函数,用户只需要触发中断后,用户只需要调用该函数即可,同时,自己的代码写在对应的回调函数中即可!如下:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);

使用了哪种就用哪个回调函数即可!

基本结构

  综上所述,使用HAL库编写程序(针对某个外设)的基本结构(以串口为例)如下:

  1. 配置外设句柄 例如,建立UartConfig.c,在其中定义串口句柄 UART_HandleTypeDef huart;,接着使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart))
  2. 编写Msp 例如,建立UartMsp.c,在其中实现void HAL_UART_MspInit(UART_HandleTypeDef* huart) 和 void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
  3. 实现对应的回调函数 例如,建立UartCallBack.c,在其中实现上文所说明的三大回调函数中的完成回调函数错误回调函数

至此,HAL库的总体结构就介绍完了!具体的每个文件的详细说明,官方源码注释很详细!

参考文档

  • ST - Description of STM32F4 HAL and LL drivers.pdf
  • ST - en.stm32_embedded_software_offering.pdf

STM32 之二 HAL库详解 及 手动移植相关推荐

  1. STM32 HAL库详解 及 手动移植

    源: STM32 HAL库详解 及 手动移植

  2. STM32之HAL库详解 及 手动移植

    转载出处:http://blog.csdn.net/zcshoucsdn/article/details/55213616 HAL库结构   说到STM32的HAL库,就不得不提STM32CubeMX ...

  3. STM32 HAL库详解

    STM32 HAL库整体总结 STM32 之二 HAL库详解 及 手动移植 本篇博客是对以上参考资源的一个二次总结与整理. 1. HAL库文件结构 对于开发人员而言,首先要清楚 HAL 库的文件结构. ...

  4. stm32 IOT_基于STM32平台的cubeMX和HAL库详解

    课程简介: <朱有鹏老师单片机完全学习系列课程>总共5季,其中第1.2季是51单片机学习,第3.4.5季是STM32单片机与RTOS学习.整个课程时长约250小时,是一套零基础.全面系统. ...

  5. matlab中sinks,MATLAB Simulink模块库详解(二)Sinks篇

    MATLAB Simulink模块库详解(二)Sinks篇 Simulink模块库概述 1.Sources模块库,为仿真提供各种信号源 2.Sinks模块库,为仿真提供输出设备元件 3.Continu ...

  6. 爬虫入门之urllib库详解(二)

    爬虫入门之urllib库详解(二) 1 urllib模块 urllib模块是一个运用于URL的包 urllib.request用于访问和读取URLS urllib.error包括了所有urllib.r ...

  7. c语言标准库详解(十二):非局部跳转setjmp.h

    c语言标准库详解(十二):非局部跳转<setjmp.h> 概述 头文件<setjmp.h>中的声明提供了一种不同于通常的函数调用和返回顺序的方式,特别是,它允许立即从一个深层嵌 ...

  8. MATLAB Simulink模块库详解(二)Sinks篇

    MATLAB Simulink模块库详解(二)Sinks篇 Simulink模块库概述 1.Sources模块库,为仿真提供各种信号源 2.Sinks模块库,为仿真提供输出设备元件 3.Continu ...

  9. TFHE同态库详解(二)

    引言 关于库的下载与安装可以参考TFHE同态库的安装与简单使用 也可以自己直接下载TFHE同态库 上一部分TFHE同态库详解(一) 这一部分主要是介绍了该库中一些方法以及一些其他声明 src/incl ...

最新文章

  1. php开发之登录注册教程,PHP开发登录注册完整代码之注册PHP页面
  2. 万字长文带你看尽深度学习中的各种卷积网络
  3. 无法远程访问Mysql的解决方案
  4. 程序员离职后躲老家山洞 2 年,敲出 45 万行代码...
  5. java线程----生产者和消费者问题
  6. PHP 逆转字符串与逆转句子
  7. Docker添加或者更改容器的端口映射
  8. burpsuite小米手机抓包_使用burpsuite实现Android APP的HTTPS抓包
  9. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_03 过滤器_2_FileNameFilter过滤器的使用和Lambda表达式...
  10. sql server的标识种子列
  11. 误删除vmdk文件恢复办法
  12. 分享篇:第十届“泰迪杯”数据挖掘挑战赛-农田害虫图像识别(特等奖)一
  13. CTFd+CTFd-whale动态靶机环境搭建
  14. IoT黑板报:英特尔公布LTE通讯模组XMM 7560
  15. jsp醒目_40个醒目的精美插图网站[展示柜]
  16. 华为数通笔记-NAT
  17. Mann-Whitney非参数多组比较R计算方法
  18. Qt的基本语法及其使用(一)
  19. 华硕p6t7支持服务器cpu吗,宇宙最强X58 华硕P6T7WS工作站主板评测
  20. 学术会议论文查重吗_学术论文会论文查重吗?

热门文章

  1. Qt 字体字号和字体像素关系
  2. 2007年最杰出十大新锐站长评选-------关注!
  3. 三子棋(井字棋)的实现
  4. java智能卡操作系统_智能卡操作系统(COS),什么是智能卡操作系统(COS)
  5. GhSAD1基因调节棉花的冷应激反应
  6. 国产迪王耀全球(下)
  7. 微信IOT模块 用户手册(串口向微信发送消息,串口与微信通讯)
  8. flutter获取图片大小的Widget(网络图片大小,本地图片大小)
  9. 重磅!新职业技能证书来了,让你高薪就业
  10. iOS开发-常用的数学方法