STM32之HAL库详解及手动移植

发布者:JoyfulMelody最新更新时间:2019-07-23 来源: eefocus关键字:STM32  HAL库  手动移植 手机看文章 扫描二维码
随时随地手机看文章

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.h:stm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置


stm32f2xx_hal_conf.h :该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中。


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


库文件:

stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API

stm32f2xx_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 ().

注意:


目前LL库是和HAL库捆绑发布的,所以在HAL库源码中,还有一些名为 stm32f2xx_ll_ppp的源码文件,这些文件就是新增的LL库文件。

使用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的中断处理函数中被调用


绝大多数用户代码均在以上三大回调函数中实现。

HAL库结构中,在每次初始化前(尤其是在多次调用初始化前),先调用对应的反初始化(DeInit)函数是非常有必要的。某些外设多次初始化时不调用返回会导致初始化失败。

完成回调函数有多中,例如串口的完成回调函数有HAL_UART_TxCpltCallback 和 HAL_UART_TxHalfCpltCallback等

(用户代码的第三大部分:对于上面第二点和第三点的各种回调函数的处理)

在实际使用中,发现HAL仍有不少问题,例如在使用USB时,其库配置存在问题

HAL库移植使用

基本步骤

复制stm32f2xx_hal_msp_template.c,参照该模板,依次实现用到的外设的HAL_PPP_MspInit()和 HAL_PPP_MspDeInit。

复制stm32f2xx_hal_conf_template.h,用户可以在此文件中自由裁剪,配置HAL库。

在使用HAL库时,必须先调用函数:HAL_StatusTypeDef HAL_Init(void)(该函数在stm32f2xx_hal.c中定义,也就意味着第一点中,必须首先实现HAL_MspInit(void)和HAL_MspDeInit(void))

HAL库与STD库不同,HAL库使用RCC中的函数来配置系统时钟,用户需要单独写时钟配置函数(STD库默认在system_stm32f2xx.c中)

关于中断,HAL提供了中断处理函数,只需要调用HAL提供的中断处理函数。用户自己的代码,不建议先写到中断中,而应该写到HAL提供的回调函数中。


对于每一个外设,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库编写程序(针对某个外设)的基本结构(以串口为例)如下:


配置外设句柄 例如,建立UartConfig.c,在其中定义串口句柄 UART_HandleTypeDef huart;,接着使用初始化句柄(HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart))

编写Msp 例如,建立UartMsp.c,在其中实现void HAL_UART_MspInit(UART_HandleTypeDef* huart) 和 void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)

实现对应的回调函数 例如,建立UartCallBack.c,在其中实现上文所说明的三大回调函数中的完成回调函数和错误回调函数

参考文档

ST - Description of STM32F4 HAL and LL drivers.pdf

ST - en.stm32_embedded_software_offering.pdf

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

关键字:STM32  HAL库  手动移植 引用地址:STM32之HAL库详解及手动移植

上一篇:创建第一个stm32项目工程常见错误警告
下一篇:STM32G0开发笔记-1-开发环境搭建之STM32CubeMX

推荐阅读最新更新时间:2024-11-13 16:17

基于STM32的血氧仪开源设计方案
一、简介 设计一款基于STM32的血氧仪,用于测量人体血氧饱和度和心率,并将测量结果显示在LCD屏幕上。 本 产品 由STM32F103C8T6 单片机 最小系统+MAX30102 传感器 +LCD显示模块+蜂鸣器模块组成。 1.选择合适的传感器模块,如MAX30102,用于采集红光和 红外 线 信号 ,并通过单片机I IC 总线读取。 2.使用STM32 微控制器 作为主控 芯片 ,配置相应的 时钟 源和分频系数,开启需要使用的外设时钟,包括GPIO口、 ADC 、LCD等。 3.根据传感器模块和LCD屏幕的 接口 要求,进行相应的GPIO口配置和LCD初始化操作。 二、功能需求 采集功能:能够采集被测者的
[单片机]
基于<font color='red'>STM32</font>的血氧仪开源设计方案
STM32单片机使用ST-LINK调试程序时不能设断点
我使用的是STM32单片机,昨天使用ST-LINK调试程序时,发现有些语句不能设断点。程序编译时OK的,没有错误,将程序烧录入单片机后程序看着也能正常运行,但是点击keil中debug按钮后程序进入调试状态,程序并没有停留在main函数的第一条语句,而是停留在别处。main函数第一条语句不能设置断点。 经过在网上查阅资料,发现可能是由于keil软件代码优化的问题。经过测试,发现果然是代码优化的问题。关于以上问题的解决方法:点击keil中“options for target”- 点击 C/C++ ,修改其中的 Optimization 一栏,选择“Level 0”即可。重新按keil中debug按钮进入调试状态,语
[单片机]
再造STM32---第十七部分:USART—串口通讯
本章参考资料:《STM32F4xx 中文参考手册》 USART 章节。 学习本章时,配合《STM32F4xx 中文参考手册》 USART 章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。 特别说明, 本书内容是以 STM32F42xxx 系列控制器资源讲解。 17.1 串口通讯协议简介: 物理层: 规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬 件部分。 协议层: 协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。其实就是软件部分。 1-RS232标准: RS232标准串口通讯结构图: 1、 RS232标准串口主要用于工业设备直接通信 2、电平转换芯片一般有M
[单片机]
再造STM32---第十七部分:USART—串口通讯
初学STM32及GPIO入门之流水灯
一.准备工作---建立一个keil工程 STEP1. 打开keil,选择project-new uvision project,建立一个LED工程 STEP2. 在工程文件保存的路径下新建以下文件夹,在官网上或其他渠道下载STM32的3.5版库,将inc 和scr文件夹放入fwlib中。将startup_stm32f10x_hd.s放入asm文件夹中。将库文件放入cmsis中。 STEP3. 在keil中分别新建 【startcode】【user】【fwlib】【cmsis】四个组,再按照下图所示将C文件加入组中。这里要注意的是组里面添加的都是汇编文件和C文件,头文件不需要添加。 STEP4. 为了更好的理清各项文
[单片机]
初学<font color='red'>STM32</font>及GPIO入门之流水灯
意法正与AWS和微软合作,帮助STM32U5实现更安全的IoT开发
ST(意法半导体)正在加强与亚马逊 (AWS) 和微软的合作,以不断扩展物联网领域。在 AWS 方面,ST提供了一个参考,可以更轻松、更安全地将 IoT (物联网)设备(STM32U5)连接到 AWS 云。同时,意法半导体与微软合作,致力于加强新兴物联网应用的安全性。 与AWS 合作 ST与 AWS 的合作结合了 ST 的 STM32U5 超低功耗微控制器 (MCU)、FreeRTOS 开源实时操作系统和 Arm 的嵌入式系统可信固件 (TF-M)。该参考实现是在 ST 的 B-U585I-IOT02A 探索套件上实现的,该套件用于配备 STM32U5 MCU 的物联网节点,其中包含丰富的功能,包括 USB、Wi-Fi 和
[单片机]
74HC595 for STM32 源代码【worldsing笔记】
74HC595是硅结构的CMOS器件, 兼容低电压TTL电路,遵守JEDEC标准。 74HC595是具有8位移位寄存器和一个存储器,三态输出功能。 移位寄存器和存储器是分别的时钟。 数据在SHcp(移位寄存器时钟输入)的上升沿输入到移位寄存器中,在STcp(存储器时钟输入)的上升沿输入到存储寄存器中去。如果两个时钟连在一起,则移位寄存器总是比存储寄存器早一个脉冲。 移位寄存器有一个串行移位输入(Ds),和一个串行输出(Q7’),和一个异步的低电平复位,存储寄存器有一个并行8位的,具备三态的总线输出,当使能OE时(为低电平),存储寄存器的数据输出到总线。 8位串行输入/输出或者并行输出移位寄存器,具有高阻关断状态。三态。 将
[单片机]
74HC595 for <font color='red'>STM32</font> 源代码【worldsing笔记】
STM32中用到的Cortex-M3寄存器说明
在STM32中用到了Cortex-M3定义的三组寄存器,有关这三组寄存器的说明不在STM32的技术手册中,需要参考ARM公司发布的Cortex-M3 Technical Reference Manual (r2p0)。 在STM32的固件库中定义了三个结构体与这三个寄存器组相对应,这三个结构体与ARM手册中寄存器的对应关系如下: 一、NVIC寄存器组 STM32的固件库中有如下定义: typedef struct { vu32 ISER ; u32 RESERVED0 ; vu32 ICER ; u32 RSERVED1 ; vu32 ISPR ; u32 RESERVED2 ;
[单片机]
【从智能锁谈STM32安全技术】之 - 安全固件安装
在第六季内容中,将介绍安全固件安装 SFI (Secure Firmware Installation)的原理及解决方案。 安全固件安装的本质是,我们是否能相信工厂的制造过程是安全的过程。如果我们相信工厂是可靠的,相信它总是按照我们的指示,例如,我们希望它不要泄露固件代码,我们希望它不要过生产,他们都能一一办到,那么安全固件安装是没有用武之地。 然而,总有一些工厂,或者工厂里的某些员工 ,他们为了一时利益,可能会将研发设计公司辛苦开发的源代码(通常是厂商的二进制固件代码),直接泄露给其他第三方。同时他们也有可能将委托生产的产品过生产。即,如果我们希望他试着生产的 1000 台 ,结果他们也许会生产 2000 台。1000
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved