一文搞懂STM32开发用的HAL库

发布者:喜悦的38号最新更新时间:2024-01-29 来源: elecfans关键字:STM32  开发  HAL库 手机看文章 扫描二维码
随时随地手机看文章

前言


  相比较早几年使用标准库开发来讲,最近几年HAL库的使用是越来越多,那么我们开发应当使用哪一种呢,本文着重介绍常用的几种开发方式及相互之间的区别,白猫也好、黑猫也好,抓到耗子就是好猫。



STM32三种开发方式


  通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发。


  网上关于标准库、HAL库的描述相信是数不胜数。可是一个对于很多刚入门的朋友还是没法很直观的去真正了解这些不同开发发方式彼此之间的区别,所以笔者想以一种非常直白的方式,用自己的理解去将这些东西表述出来,如果有描述的不对的地方或者是不同意见的也可以大家提出。


1、直接配置寄存器


  不少先学了51的朋友可能会知道,会有一小部分人或是教程是通过汇编语言直接操作寄存器实现功能的,这种方法到了STM32就变得不太容易行得通了,因为STM32的寄存器数量是51单片机的十数倍,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,此时直接操作寄存器就变得非常的费力了。但还是会有很小一部分人,喜欢去直接操作寄存器,因为这样更接近原理,知其然也知其所以然。


2、标准库


  上面也提到了,STM32有非常多的寄存器,而导致了开发困难,所以为此ST公司就为每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的。在这些 .c .h文件中,包括一些常用量的宏定义,把一些外设也通过结构体变量封装起来,如GPIO口时钟等。所以我们只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。也是目前最多人使用的方式,也是学习STM32接触最多的一种开发方式,我也就不多阐述了。


3、HAL库


  HAL库是ST公司目前主力推的开发方式,全称就是Hardware Abstraction Layer(抽象印象层)。库如其名,很抽象,一眼看上去不太容易知道他的作用是什么。   


它的出现比标准库要晚,但其实和标准库一样,都是为了节省程序开发的时期,而且HAL库尤其的有效,如果说标准库把实现功能需要配置的寄存器集成了,那么HAL库的一些函数甚至可以做到某些特定功能的集成。也就是说,同样的功能,标准库可能要用几句话,HAL库只需用一句话就够了。   



并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相通外设,意思也就是不能无中生有,例如F7比F3要多几个定时器,不能明明没有这个定时器却非要配置,但其实这种情况不多,绝大多数都可以直接复制粘贴。是而且使用ST公司研发的STMcube软件,可以通过图形化的配置功能,直接生成整个使用HAL库的工程文件,可以说是方便至极,但是方便的同时也造成了它执行效率的低下,在各种论坛帖子真的是被吐槽的数不胜数。


STM32 HAL库与标准库的区别


1、句柄


  句柄(handle),有多种意义,其中第一种是指程序设计,第二种是指Windows编程。现在大部分都是指程序设计/程序开发这类。


第一种解释:句柄是一种特殊的智能指针 。当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。


第二种解释:整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息,但是句柄不是指针,程序不能利用句柄来直接阅读文件中的信息。如果句柄不在I/O文件中,它是毫无用处的。句柄是Windows用来标志应用程序中建立的或是使用的唯一整数,Windows大量使用了句柄来标识对象。


STM32的标准库中,句柄是一种特殊的指针,通常指向结构体!   在STM32的标准库中,假设我们要初始化一个外设(这里以USART为例),我们首先要初始化他们的各个寄存器。在标准库中,这些操作都是利用固件库结构体变量+固件库Init函数实现的:


USART_InitTypeDef USART_InitStructure;



USART_InitStructure.USART_BaudRate = bound;//串口波特率

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位

USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位

USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式



USART_Init(USART3, &USART_InitStructure); //初始化串口1

可以看到,要初始化一个串口,需要:


1、对六个位置进行赋值


2、然后引用Init函数


  USART_InitStructure并不是一个全局结构体变量,而是只在函数内部的局部变量,初始化完成之后,USART_InitStructure就失去了作用。而在HAL库中,同样是USART初始化结构体变量,我们要定义为全局变量。


UART_HandleTypeDef UART1_Handler;

结构体成员


typedef struct

{

   USART_TypeDef                 *Instance;        /*!< UART registers base address        */

   UART_InitTypeDef              Init;             /*!< UART communication parameters      */

uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */

uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */

uint16_t                      TxXferCount;      /*!< UART Tx Transfer Counter           */

uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */

uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */

uint16_t                      RxXferCount;      /*!< UART Rx Transfer Counter           */

   DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */

   DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */

   HAL_LockTypeDef               Lock;             /*!< Locking object                     */

   __IO HAL_UART_StateTypeDef    State;            /*!< UART communication state           */

   __IO uint32_t                 ErrorCode;        /*!< UART Error code                    */

}UART_HandleTypeDef;

我们发现,与标准库不同的是,该成员不仅:

1、包含了之前标准库就有的六个成员(波特率,数据格式等),


2、还包含过采样、(发送或接收的)数据缓存、数据指针、串口 DMA 相关的变量、各种标志位等等要在整个项目流程中都要设置的各个成员。


该 UART1_Handler就被称为串口的句柄,它被贯穿整个USART收发的流程,比如开启中断:


HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);

比如后面要讲到的MSP与Callback回调函数:


void HAL_UART_MspInit(UART_HandleTypeDef *huart);

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

在这些函数中,只需要调用初始化时定义的句柄UART1_Handler就好。


2、MSP函数


MSP: MCU Specific Package 单片机的具体方案 MSP是指和MCU相关的初始化,引用一下正点原子的解释,个人觉得说的很明白:   



我们要初始化一个串口,首先要设置和 MCU 无关的东西,例如波特率,奇偶校验,停止位等,这些参数设置和 MCU 没有任何关系,可以使用 STM32F1,也可以是 STM32F2/F3/F4/F7上的串口。而一个串口设备它需要一个 MCU 来承载,例如用 STM32F4 来做承载,PA9 做为发送,PA10 做为接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置这两个引脚。所以 HAL驱动方式的初始化流程就是:


HAL_USART_Init()—>HAL_USART_MspInit() ,先初始化与 MCU无关的串口协议,再初始化与 MCU 相关的串口引脚。


在 STM32 的 HAL 驱动中HAL_PPP_MspInit()作为回调,被 HAL_PPP_Init()函数所调用。当我们需要移植程序到 STM32F1平台的时候,我们只需要修改 HAL_PPP_MspInit 函数内容而不需要修改 HAL_PPP_Init 入口参数内容。


  在HAL库中,几乎每初始化一个外设就需要设置该外设与单片机之间的联系,比如IO口,是否复用等等,可见,HAL库相对于标准库多了MSP函数之后,移植性非常强,但与此同时却增加了代码量和代码的嵌套层级。可以说各有利弊。 同样,MSP函数又可以配合句柄,达到非常强的移植性:


void HAL_UART_MspInit(UART_HandleTypeDef *huart);

3、Callback函数


  类似于MSP函数,个人认为Callback函数主要帮助用户应用层的代码编写。   还是以USART为例,在标准库中,串口中断了以后,我们要先在中断中判断是否是接收中断,然后读出数据,顺便清除中断标志位,然后再是对数据的处理,这样如果我们在一个中断函数中写这么多代码,就会显得很混乱:


void USART3_IRQHandler(void)                 //串口1中断服务程序

{

 u8 Res;

if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

 {

  Res =USART_ReceiveData(USART3); //读取接收到的数据

/*数据处理区*/

  }      

     } 

}

而在HAL库中,进入串口中断后,直接由HAL库中断函数进行托管:


void USART1_IRQHandler(void)                 

 HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数

 /***************省略无关代码****************/ 

}

  HAL_UART_IRQHandler这个函数完成了判断是哪个中断(接收?发送?或者其他?),然后读出数据,保存至缓存区,顺便清除中断标志位等等操作。   



比如我提前设置了,串口每接收五个字节,我就要对这五个字节进行处理。在一开始我定义了一个串口接收缓存区:

/*HAL库使用的串口接收缓冲,处理逻辑由HAL库控制,接收完这个数组就会调用HAL_UART_RxCpltCallback进行处理这个数组*/

/*RXBUFFERSIZE=5*/

u8 aRxBuffer[RXBUFFERSIZE];

在初始化中,我在句柄里设置好了缓存区的地址,缓存大小(五个字节)


/*该代码在HAL_UART_Receive_IT函数中,初始化时会引用*/

    huart->pRxBuffPtr = pData;//aRxBuffer

    huart->RxXferSize = Size;//RXBUFFERSIZE

    huart->RxXferCount = Size;//RXBUFFERSIZE

  则在接收数据中,每接收完五个字节,HAL_UART_IRQHandler才会执行一次Callback函数:


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

  在这个Callback回调函数中,我们只需要对这接收到的五个字节(保存在aRxBuffer[]中)进行处理就好了,完全不用再去手动清除标志位等操作。   



所以说Callback函数是一个应用层代码的函数,我们在一开始只设置句柄里面的各个参数,然后就等着HAL库把自己安排好的代码送到手中就可以了~   




综上,就是HAL库的三个与标准库不同的地方之个人见解。个人觉得从这三个小点就可以看出HAL库的可移植性之强大,并且用户可以完全不去理会底层各个寄存器的操作,代码也更有逻辑性。但与此带来的是复杂的代码量,极慢的编译速度,略微低下的效率。看怎么取舍了。

STM32 HAL库结构


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

ca53c4e725a3a355b1af15c3cdb68801_wKgaomToJeaAF7i4AAM4lXd5CsY638.png

1、stm32f4xx.h主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义,接着,其会根据定义的芯片信号包含具体的芯片型号的头文件:


#if defined(STM32F405xx)

#include "stm32f405xx.h"

#elif defined(STM32F415xx)

#include "stm32f415xx.h"

#elif defined(STM32F407xx)

#include "stm32f407xx.h"

#elif defined(STM32F417xx)

#include "stm32f417xx.h"

#else

#error "Please select first the target STM32F4xx device used in your application (in stm32f2xx.h file)"

#endif

紧接着,其会包含stm32f4xx_hal.h。

2、stm32f4xx_hal.h:stm32f4xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置


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


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


4、库文件:stm32f4xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API stm32f4xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。 stm32f4xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API


5、其他库文件 用户级别文件: stm32f4xx_hal_msp_template.c // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。 stm32f4xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用 system_stm32f4xx.c // 此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。它不在启动时配置系统时钟(与标准库相反)。时钟的配置在用户文件中使用HAL API来完成。startup_stm32f4xx.s // 芯片启动文件,主要包含堆栈定义,终端向量表等 stm32f4xx_it.c/.h // 中断处理函数的相关实现


6 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


处理各种回调函数


相关知识如下:


1、外设句柄定义


  用户代码的第一大部分:对于外设句柄的处理。HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef的结构体,其中ppp就是每个外设的名字。*所有的函数都是工作在ppp_HandleTypeDef指针之下。


多实例支持:每个外设/模块实例都有自己的句柄。因此,实例资源是独立的


下面,以ADC为例


外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。


/** 

 * @brief  ADC handle Structure definition

 */

typedef struct

{

 ADC_TypeDef                   *Instance;                   /*!< Register base address */

[1] [2]
关键字:STM32  开发  HAL库 引用地址:一文搞懂STM32开发用的HAL库

上一篇:STM32的PWM实验
下一篇:串口屏与stm32串口通信

推荐阅读最新更新时间:2024-11-13 22:45

AVR开发 Arduino方法(七) 嵌入式操作系统FreeRTOS
FreeRTOS可以提供任务管理,队列管理,中断管理,资源管理和内存管理等功能,由于占用资源少,它可以运行在Arduino UNO R3开发板上。 你可以在 https://github.com/greiman/FreeRTOS-Arduino 上下载到它,将下载到的FreeRTOS-Arduino-master.zip解压,并将/FreeRTOS-Arduino-master/libraries文件夹下的内容全部复制到Arduino IDE安装目录下的libraries文件夹里就可以使用了。下面是它提供的frBlink示例: 1 // frBlink.ino 2 #include FreeRTOS_AVR.h 3
[单片机]
Socionext携手大连东显 共同开发车载图像领域
SoC设计与应用技术领导厂商Socionext Inc.(以下“公司”)宣布与大连东显电子有限公司(以下“DED”)共同合作,实现基于Socionext高性能图像处理SoC SC1810系列产品运行BlackBerry QNX软件开发平台7.0 SC1810系列产品是Socionext最新款图像处理SoC。该系列产品除了支持多个摄像头和显示器外,还内置有高性能计算机视觉加速OpenVX规范,为仪表盘、抬头显示(HUD),3D环视系统、车载信息娱乐系统(IVI)、高级驾驶辅助系统等多种汽车人机界面应用提供了多种解决方案,是图像处理基础的理想设备。 BlackBerry QNX软件开发平台致力于构筑可靠、可扩展和高性能应用,应
[汽车电子]
STM32串口实时接收数据与所提前定义的比较,并作出相应的操作
//typedef const uint16_t uc16; /*! Read Only */ uc16 BUF ={162,170,178,187,197,206,216,227,238,249}; u16 value;//可以是串口实时接收到的一些数据 void main(void) { for(i=0;i 10;i++) { if(value BUF ) { /* 这里可以写一些操作 */ } } }
[单片机]
安通林与Net4Things合作,未来将共同开发联网汽车
日前,有海外媒体报道,西班牙零部件供应商安通林集团(GrupoAntolin)与移动出行及数据驱动服务提供商Net4Things签署战略协议,未来将共同开发联网汽车。 据悉,借助Net4Things公司开发的GlobalMobilityPlatform,安通林集团希望可以开发出基于数据分析的互联性和驾驶员服务的新解决方案,并探索其商业潜力。而安通林集团是国际上汽车内饰市场制造商,并且是车顶衬板基材全球供应商。同时,该公司还开发/ 测试 /制造内部零件,例如高架和软装饰,门和硬装饰,照明,以及驾驶舱和控制台。 互联性可以使乘员与车辆、周围环境以及其他车辆进行连接及交换数据,是彻底改变汽车内饰的因素之一。作为其集团战略的一部分
[汽车电子]
STM32库函数开发&按键库函数开发源码篇
LED流水灯 led.h #ifndef __LED_H #define __LED_H #include stm32f4xx.h void Led_Init(void); void Led1_Init(void); void Led2_Init(void); void Led3_Init(void); void Led4_Init(void); #endif typedef struct { uint32_t GPIO_Pin; /* 指定要配置的GPIO引脚 */ GPIOMode_TypeDef GPIO_Mode; /* 指定选定接点的操作模式。*/
[单片机]
一种基于ZigBee和STM32的室内智能照明系统的设计
  在人们的传统意识中,照明系统仅以照明为目的。传统的照明系统中主要的控制方式有手动控制方式和自动控制方式。其中手动控制方式简单、有效,但是过于依赖人工操作,并且控制相对分散,不能有效管理;自动控制方式主要是由时钟元件、光电元件或两者组合的方式来实现对照明设备的控制,这种控制方式减少了对人员的依赖性,管理相对集中,实现了照明控制的自动化,但却不能对照明系统进行调光控制。   此外,随着生活水平的不断提高,人们对日常生活的无线化、网络化、智能化、节能化的需求越来越强烈,以上两种传统的照明控制系统已经无法满足人们对日常生活品质的需求。基于上述原因提出了一种基于ZigBee和STM32的室内智能照明系统的设计。   1 系统总体
[单片机]
一种基于ZigBee和<font color='red'>STM32</font>的室内智能照明系统的设计
Ubuntu下安装Stm32的Eclipse的开发环境(1)
在最起初的时候,我刚刚接触linux上单片机的开发,最喜欢的就是 eclipse + arm-plug-in + arm-none-eabi 的开发环境,因为这是在Linux上最接近于windows下keil、IAR等IDE的开发方式,然而那是由于对eclipse亦或是makefile等编译过程中的工具的不甚了解,很多时候会遇到一些莫名的错误,也导致了那是觉得这样的环境很鬼畜,现在看来多是一些很浅显的问题,直到最近我再一次的尝试了eclipse开发环境的搭建和使用,我才很顺利的完成了程序的配置。现在也把这个方法推荐给大家,不过在使用这个方式前,还是建议大家先把之前的文章内容看懂了,否则很多时候遇到问题都会不知道怎么解决。 安装
[单片机]
STM32启动文件选择说明
startup_stm32f10x_cl.s 互联型的器件,STM32F105xx,STM32F107xx startup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx,STM32F103xx startup_stm32f10x_hd_vl.s 大容量的STM32F100xx startup_stm32f10x_ld.s 小容量的STM32F101xx,STM32F102xx,STM32F103xx startup_stm32f10x_ld_vl.s 小容量的STM32F100xx startup_stm32f10x_md.s 中容量的STM32F101xx,STM32F102xx,STM3
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习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