全网最全STM32(HAL的知识总结)

ST为开发者提供了非常方便的开发库:标准外设库(SPL库)、HAL库(硬件抽象层)和LL库(底层)。但是前一个ST的老库已经停止更新,后两个是ST现在推广的开发库。

与标准外设库相比,STM32Cube HAL库表现出更高的抽象集成水平。HAL API集中了所有外设的通用功能,因此很容易定义一套通用的用户友好的API函数接口,从而可以很容易地从一个STM32产品移植到另一个不同的STM32系列产品。HAL库是未来ST主推的库,ST的新芯片没有STD库,比如F7系列。目前HAL库已经支持STM32产品。

通过文字描述,我们可以知道HAL库的几点:1。最大的便携性。2.提供一套一致的中间件组件,如RTOS、USB、TCP/IP和图形。3.通用的用户友好的API函数接口。4.ST的新芯片没有标准库。5.HAL库已经支持STM32产品。

网友认为,“HAL我觉得很优秀,就是SPI接收速度太慢,没有DMA就吃不下。”

通常初学者在开始STM32的时候,首先要选择一个开发方法来使用。不同的开发方法会导致你的编程架构完全不同。一般会使用标准库和HAL库,很少有人会直接配置寄存器进行开发。网上关于标准库和HAL库的描述数不胜数。

但是对于很多初学者来说,还是不能真正直观的理解这些不同开发方式的区别,所以我想用一种非常直白的方式,用自己的理解来表达这些东西。如有不正确的描述或不同意见,也可提出。

01、直接配置寄存器

很多先学过51的朋友可能都知道,会有少部分人或者教程通过汇编语言直接操作寄存器来实现其功能。这种方法到了STM32就不好用了,因为STM32的寄存器数量是51单片机的十倍,所以很多寄存器根本记不住。开发时,需要经常查阅芯片的数据手册。这时候直接操作寄存器就变得非常费力。但是还是会有少部分人喜欢直接操作寄存器,因为这样更接近原理,他们自己也知道为什么会知道。

02、标准库

如上所述,STM32的寄存器很多,导致开发困难。所以ST公司给每个芯片都编了一个库文件,就是工程文件里的stm32F1xx…等等。这些。c .h文件包括一些常用量的宏定义,一些外设也是用结构变量封装的,比如GPIO口时钟。所以我们只需要配置结构的可变成员就可以修改外设的配置寄存器,从而选择不同的功能。目前也是应用最广泛的方式,也是学习STM32的一种开发方式,就不细说了。

03、哈尔图书馆

HAL库是ST公司目前主推的开发方式,全称是硬件抽象层(抽象印象层)。顾名思义,Ku非常抽象,不容易一眼就知道他的角色是什么。它的产生比标准库晚,但其实和标准库一样,都是为了节省程序开发的时间,HAL库尤其有效。如果标准库集成了实现功能需要配置的寄存器,HAL库的一些功能甚至可以集成一些特定的功能。

也就是说,对于同一个函数,标准库可能需要用几个字,而HAL库只需要一句话。而且HAL库也很好的解决了程序移植的问题。不同类型的stm32芯片有不同的标准库。比如在F4上开发的程序,移植到F3上就不能通用。在使用HAL库时,只要使用互联外设,程序基本上可以完全复制粘贴。注意是环环相扣的外设,也就是说不能无中生有。

比如F7比F3多了几个定时器,没有这个定时器你是无法配置的,但其实这种情况并不多,大部分都是可以直接复制粘贴的。而且ST公司开发的STMcube软件,通过图形化的配置功能,可以直接使用HAL库生成整个工程文件,可以说是极其方便,但是方便也造成了其执行效率低下,各种论坛的帖子不计其数。

HAL库和标准固件库的区别

在STM32的开发中,我们可以操作寄存器:

GPIOF-》BSRR=0x 00000001;//这个是STM32F1系列的。

这种方法当然是可以的,但是这种方法的缺点是你需要掌握每个寄存器的用法,这样你才能正确的使用STM32。对于STM32级别的MCU来说,很难记住上百个寄存器。于是ST(意法半导体)推出了官方标准固件库,封装了这些寄存器的底层操作,并提供了一套完整的接口(API)供开发者调用。大多数情况下,不需要知道操作哪个寄存器,只需要知道调用哪个函数就可以了。

比如上面的控制BRR寄存器实现了级别控制,官方库封装了一个函数:

void GPIO _ reset bits(GPIO _ TypeDef * GPIO x,uint 16 _ t GPIO _ Pin){ GPIO x-》BRR=GPIO _ Pin;}

这时,你不需要直接操作BRR寄存器,只需要知道如何使用函数GPIO_ResetBits()。在你对外设的工作原理有了一定的了解之后,就可以去标准库函数了。基本上函数名就能告诉你这个函数的作用是什么,怎么用,开发起来方便很多。

标准库自问世以来,受到了工程师们的高度赞扬,现在许多工程师和公司仍在使用标准库函数对其进行开发。但ST官方不再更新STM32标准固件库,而是推一个新的固件库:HAL库。

比如上面的控制BSRRL寄存器实现了电平控制,官方的HAL库封装了一个函数:

void Hal _ GPIO _ write Pin(GPIO _ TypeDef * GPIOx,uint16_t GPIO_Pin,GPIO _ PinStatePinState){ assert _ param(IS _ GPIO _ Pin(GPIO _ Pin));assert _ param(IS _ GPIO _ PIN _ ACTION(PinState));如果(PinState!=GPIO _ PIN _ RESET){ GPIOx-》BSRR=GPIO _ PIN;} else { GPIOx-》BSRR=(uint 32 _ t)GPIO _ Pin《16;}}

这个时候不需要直接操作BSRRL寄存器,只需要知道如何使用函数HAL_GPIO_WritePin就可以了。

标准固件库和HAL库一样,都是固件库函数。ST官方硬件抽象层设计的软件功能包由程序、数据结构和宏组成,包含了STM32所有外设的性能特征。这些固件库为开发人员的底层硬件提供了中间API。通过使用固件库,开发人员可以轻松应用每个外设,而无需掌握底层细节。

HAL库和标准库本质上是一样的,都提供底层的硬件操作API,使用上也差不多。对标准库有过基本了解的同学可以轻松使用HAL库。近几年ST官方推广HAL库的原因是HAL的结构更容易集成STM32Cube,STM32CubeMX是近几年ST极力推荐的程序生成和开发工具。所以近几年新出的STM32芯片,ST只直接提供HAL库。

在ST的官方声明中,哈尔库是大势所趋。st最新研发的芯片中,只有HAL库没有标准库。虽然标准库和HAL库都是操作外设的功能,但是标准库已经正式停止更新,在STM32创建和初始化的时候,也就是CubMX软件产生代码的时候,标准库是不能被CubMX软件代码生成和使用的。

项目和初始化代码是自动生成的,本项目和初始化代码使用的库都是基于HAL库。STM32CubeMX是一个图形化工具,也是C代码生成器的配置和初始化。HAL库(硬件抽象层软件库)配合STM32CubeMX使用。

基本配置

工程创造

通过内核芯片的选择,创建相应的工程文件。

配置时钟系统、引脚和基本功能。

配置时钟系统我们首先考虑的是:我们需要什么样的时钟系统,而不是如何配置时钟系统。

配置SWD程序刻录界面,使用ST-Link刻录下载。

项目管理设置推荐如图配置,实现更快的编译和更简洁的文件系统。

点击“生成代码”生成Keil项目文件。

如果您已经安装了编译环境MDK,请单击直接打开项目。

使用GPIO

开发环境设置好之后,就可以开始STM32的开发了。下面是使用GPIO让LED灯每400 ms闪烁一次的例程。

打开STM32CubeMX新项目,选择STM32F103ZET6芯片。选择外部高速晶体振荡器(HSE)。

根据下位机主控管脚分布图和原理图选择LED管脚。

PD7是LED1输出控制引脚,选择GPIO_OUTPUT模式。

单击时钟配置,将系统时钟配置为最大速度72M。

点击配置-》GPIO配置引脚。LED引脚配置为低速推挽输出模式,既不上拉也不下拉(即不配置默认模式)。

点击生成报告,软件会提示新建一个项目,输入项目名称,选择保存项目的路径。选择IDE MDK-ARM V5。

在代码生成器中,找到生成文件框,并检查生成的外设a初始化为一对'c/。' h '每个IP文件。外设被初始化为独立的C文件和头文件。

单击生成代码。单击“打开项目”打开项目。这里我们配置工程外设的初始化。

点按“构建”按钮,然后稍等片刻。构建选项信息框将不会输出任何错误和警告。将以下代码添加到main函数中

延迟水平翻转功能,以便LED灯可以开始闪烁。

再次点按“构建”按钮,然后稍等片刻。构建选项信息框将不会输出任何错误和警告。代码烧录。

现在有两种烧录程序,一种是用ST-LINK工具烧录,另一种是用串口1和上位机通讯直接烧录。书写工具使用mcuisp。

软件可以自己上网搜索,下载配置如下。

选择串口后,就可以开始下载了。

HAL库固件库安装和用户手册

1.首先设置相关固件库,让Cube自动在线下载。选择更新程序设置。

2.根据芯片选择,所需固件版本来自向后兼容,可以直接选择最新版本。但如果你觉得最新版本太大,可以看看下面的主要变化。能支持你现在的芯片就不错了。

是的,只需点击立即安装。这个过程可能有点长。建议直接下载到当地官网再安装。

该文件将被下载到以下位置。建议更改该目录,而不是在驱动器C上选择它!

查找帮助手册

3.找用户帮助手册,进入固件所在的文件夹,里面有很多内容。

例如,官方开发板计划

每种模式下都有相应的功能。

用户手册在驱动程序文件夹下。

STM32 HAL库和标准库3354的区别谈句柄、MSP函数和回调函数

01手柄

Handle有很多含义,第一个是指编程,第二个是指Windows编程。现在大多指编程/编程。

第一个解释:Handle是一个特殊的智能指针。当应用程序想要引用其他系统(如数据库和操作系统)管理的内存块或对象时,它需要使用句柄。第二个解释:整个Windows编程的基础。句柄是指一个唯一的整数值,即一个4字节(64位程序中为8字节)长的值,用于标识应用程序中的不同对象和同类的不同实例。

例如窗口、按钮、图标、滚动条、输出设备、控件或文件等。应用程序可以通过句柄访问相应对象的信息,但是句柄不是指针,程序不能使用句柄直接读取文件中的信息。如果句柄不在I/O文件中,它就没有用。Handle是Windows用来标记应用程序的建立或使用的唯一整数,Windows大量使用handle来标识对象。

在STM32的标准库中,句柄是一个特殊的指针,通常指向结构!

在STM32的标准库中,假设我们要初始化一个外设(这里以USART为例),首先要初始化它们的寄存器。在标准库中,这些操作通过使用固件库结构变量的固件库初始化函数来实现:

USART _ init typedef USART _ init structure;

USART_InitStructure。USART _ BaudRate=bound//串行波特率usart _ init structure . usart _ word length=usart _ word length _ 8b;//8位数据格式usart _ initstructure。usart _ stop bits=usart _ stop bits _ 1;//a停止位usart _ init structure . usart _ parity=usart _ parity _ no;//没有奇偶校验位usart _ init structure . usart _ hardware flow control=usart _ hardware flow control _ none;//没有硬件数据流控制usart _ init结构。usart _ mode=usart _ mode _ rx | usart _ mode _ tx;//收发器模式

USART_Init(USART3,USART _ Init structure);//初始化串行端口1

如您所见,要初始化串行端口,您需要:

1、为六个位置赋值,

2、然后引用Init函数,

USART_InitStructure不是全局结构变量,只是函数内部的局部变量。初始化完成后,USART_InitStructure就失去了作用。

在HAL库中,初始化结构变量的也是USART,我们想把它定义为全局变量。

UART _ handle typedef UART 1 _ Handler;

TypeDef struct { USART _ TypeDef * Instance;UART _ Init typedef Init;uint 8 _ t * pTxBuffPtr;uint 16 _ t TxXferSize;uint 16 _ t TxXferCount;uint 8 _ t * prxbufptr;uint 16 _ t rxfersize;uint 16 _ t rxfercount;DMA _ handle typedef * hdm atx;DMA _ Handle typedef * HD Marx;HAL _ Lock typedef Lock;__IO HAL_UART_StateTypeDef状态;__IO uint32_t错误代码》;} UART _ handle typedef;

我们发现,与标准库不同,这个成员不仅:

1、包含六个成员(波特率、数据格式等。)中,并且2、还包含过采样、数据缓存(发送或接收)、数据指针、串行DMA相关变量、各种标志位等。应在整个项目过程中设置。UART1_Handler被称为串口的句柄,它贯穿于整个USART发送和接收过程,比如启动中断:

HAL _ UART _ Receive _ IT(UART 1 _ Handler,(u8 *)aRxBuffer,RXBUFFERSIZE);

比如后面要讲的MSP和回调函数:

void HAL _ UART _ MSP init(UART _ handle typedef * huart);void HAL _ UART _ RxCpltCallback(UART _ handle typedef * huart);

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

02MSP功能

MSP的具体方案:MCU专用封装MCU

MSP是指与MCU相关的初始化。引用守时atom的解释,我个人认为很清楚:要初始化一个串口,首先要设置与MCU无关的东西,比如波特率、奇偶校验、停止位等。这些参数设置与MCU无关。您可以使用STM32F1或STM32F2/F3/F4/F7上的串行端口。而一个串口设备需要一个MCU来承载,比如STM32F4承载,PA9发送,PA10接收。MSP是初始化STM32F4的PA9和PA10,并配置这两个管脚。所以HAL驱动模式的初始化过程是:

Hal _ usart _ init()——Hal _ usart _ MSP init()先初始化与MCU无关的串口协议,再初始化与MCU相关的串口管脚。

STM32的HAL驱动中的HAL_PPP_Init()函数调用HAL _ PPP _ MSPInit()作为回调。当我们需要将程序移植到STM32F1平台时,只需要修改HAL_PPP_MspInit函数的内容,而不需要修改HAL_PPP_Init入口参数的内容。在HAL库中,几乎每初始化一个外设,都需要设置外设与单片机的连接,比如IO口,是否复用等。可以看出,HAL库与标准库相比具有很强的可移植性,但同时增加了代码量和代码的嵌套层次。可以说各有利弊。

类似地,MSP函数可以与句柄协作以实现非常强的可移植性:

void HAL _ UART _ MSP init(UART _ handle typedef * huart);

entry参数只需要一个串口句柄,所以我们可以看到句柄的便利性。

03回调功能

类似于MSP函数,个人认为回调函数主要是帮助用户在应用层写代码。

以USART为例。在标准库中,串口中断后,我们要先判断它是否在接收中断,然后顺便读出数据,清除中断标志,然后再处理数据,这样如果我们在一个中断函数中写这么多代码,会很混乱:

voiusart 3 _ IRQ handler(void)//串口1中断服务程序{ u8 Resif(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET) //接收中断(接收的数据必须以0x0d0x0a结束){ Res=USART _ Receive data(USART 3);//读取接收到的数据}}

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

void usart 1 _ IRQ Handler(void){ HAL _ UART _ IRQ Handler(UART 1 _ Handler);//调用HAL库中断处理实用函数/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

HAL_UART_IRQHandler该函数完成对哪个中断的判断(接收?发送?还是别的?),然后读出数据,保存到缓冲区,顺便清除中断标志等等。比如我事先设置好,串口每接收到5个字节,我就要处理这5个字节。一开始,我定义了一个串口接收缓冲区:

* rx buffer size=5 */u 8 ARX buffer[rx buffer size];

在初始化期间,我在句柄中设置了缓冲区的地址和缓存大小(五个字节)。

*/huart-》pRxBuffPtr=pData;//ARX buffer huart-》rxfersize=Size;//RXBUFFERSIZE huart-》rxfercount=Size;//RXBUFFERSIZE

在接收到的数据中,HAL_UART_IRQHandler将每接收5个字节执行一次回调函数:

void HAL _ UART _ RxCpltCallback(UART _ handle typedef * huart);

在这个回调回调函数中,我们只需要处理接收到的五个字节(存储在arxbuffer [])就可以了,根本不用手动清除标志位。所以回调函数是应用层代码的一个函数。我们一开始只在句柄里设置参数,然后等着HAL库把我们整理好的代码送到我们手里~

综上所述,关于HAL库和标准库的三个区别,是我个人的看法。个人认为,从这三个小点可以看出,HAL库的可移植性很强,用户可以完全忽略底层寄存器的操作,代码更有逻辑性。但带来的是代码量复杂,编译速度极慢,效率略低。看怎么选。

0stm32hal库结构

说到STM32的HAL库,就不得不提STM32CubeMX,它作为一个可视化的配置工具,确实为开发者节省了开发时间。STM32CubeMX基于HAL库,目前只支持HAL库和LL库!先来看看官方给出的HAL库的包含结构:

4.1 STM32f4xx.h主要包括同系列stm32芯片不同具体型号的定义,是否使用HAL库等。然后,它将根据定义的芯片信号包含特定芯片型号的头文件:

# if defined(STM 32 f 405 xx)# include"STM 32 f 405 xx . h"# elif defined(STM 32 f 415 xx)# include"STM 32 f 415 xx . h"# elif defined(STM 32 f 407 xx)# include"STM 32 f 407 xx . h"# elif defined(STM 32 f 417 xx)# include"STM 32 f 417 xx . h"# else # error"请先选择目标STM 32 f 4x

然后,它将包含stm32f4xx_hal.h

4.2 STM 32 f 4xx _ hal . h:STM 32 f 4xx _ HAL . c/h主要实现HAL库初始化、系统tick相关函数、CPU调试模式配置。

4.3 stm32f4xx _ HAL _ conf.h:该文件是用户级配置文件,用于剪切HAL库。它位于用户文件目录中,而不是库目录中。

接下来解释HAL库的源文件。HAL库的文件名都是以stm32f4xx_hal开头,后面加上_外设或模块名(如stm32f4xx_hal_adc.c):

4.4图书馆文件

Stm32f4xx_hal_ppp.c/。h //主外设或模块的驱动源文件包含外设的通用API。

Stm32f4xx_hal_ppp_ex.c/。h //外围设备或模块驱动程序的扩展文件。这组文件包含特定型号或系列芯片的特殊API。而且如果在具体的芯片中有不同的实现,这个文件中的专用API会覆盖通用API in _ppp。

Stm32f4xx_hal.c/。h //该文件用于HAL初始化,包含DBGMCU、基于systick的重映射、延时等相关API。

4.5其他图书馆文件

用户级文件:

仅Stm32f4xx_hal_msp_template.c //。c没有。h .包含用户应用程序中使用的外设的MSP初始化和去初始化(主程序和回调函数)。用户复制到他们自己的目录来使用模板。

STM 32 f 4xx _ Hal _ conf _ template . h//用户级库配置文件模板。用户将其复制到自己的目录中使用。

System_stm32f4xx.c //该文件主要包含SystemInit()函数,该函数在启动过程中,就在复位和跳转到main之前被调用。它不在启动时配置系统时钟(与标准库相反)。时钟的配置通过使用用户文件中的HAL API来完成。

Startup_stm32f4xx.s //芯片启动文件,主要包括stm32f4xx_it.c/的堆栈定义、终端向量表等相关实现。h //中断处理功能。

4.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指针下工作。

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

2.外围进程相互通信:这个句柄用于管理进程例程之间的共享数据资源。接下来以ADC为例。

/* * * * @ brief ADC句柄结构定义*/TypeDef struct { ADC _ TypeDef *实例;ADC _ Init typedef Init;_ _ IO uint 32 _ t NbrOfCurrentConversionRank;DMA _ Handle typedef * DMA _ Handle;HAL_LockTypeDef锁;__IO uint32_t状态;__IO uint32_t错误代码;} ADC _ handle typedef;

从上面的定义可以看出,ADC_HandleTypeDef包含了ADC所有可能的定义。对于想要使用ADC的用户,只需要定义一个ADC_HandleTypeDef的变量,给每个变量赋值,对应的外设就会被抽象出来。接下来是具体使用。当然,对于那些共享外设或者系统外设来说,不需要上述的抽象,这些部分和原来的标准外设库函数基本相同。

比如以下外设:-GPIO-SYSTICK-NVIC-RCC-FLASH以GPIO为例。对于HAL_GPIO_Init()函数,它只需要GPIO的地址及其初始化参数。

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

HAL _ StatusTypeDef HAL _ ADC _ Start(ADC _ handle typedef * hadc);HAL _ StatusTypeDef HAL _ ADC _ Stop(ADC _ handle typedef * hadc);

HAL _ StatusTypeDef HAL _ ADC _ Start _ IT(ADC _ handle typedef * hadc);HAL _ StatusTypeDef HAL _ ADC _ Stop _ IT(ADC _ handle typedef * hadc);

HAL _ StatusTypeDef HAL _ ADC _ Start _ DMA(ADC _ handle typedef * hadc,uint32_t* pData,uint 32 _ t Length);HAL _ StatusTypeDef HAL _ ADC _ Stop _ DMA(ADC _ handle typedef * 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):清除

(3)三个回调函数。在HAL库的源代码中,到处都有一些以__weak开头的函数,并且其中一些函数已经实现,比如:

_ _ weak HAL _ StatusTypeDef HAL _ init tick(uint 32 _ t tick priority){HAL _ sy stick _ Config(system core clock/1000U);HAL _ NVIC _ set priority(sy stick _ IRQn,TickPriority,0U);Return HAL _ OK;}

有些没有实现,例如:

_ _ weak void HAL _ SPI _ TxCpltCallback(SPI _ handle typedef * hspi){UNUSED(hspi);}

所有带有__weak关键字的函数表示都可以由用户自己实现。如果具有相同名称的函数没有__weak关键字,连接器将采用外部实现的同名函数。一般来说,HAL库负责整个处理和MCU外设的处理逻辑,以回调函数的形式把需要的部分交给用户。用户只需要在相应的回调函数中进行修改。HAL库包含以下三个用户级回调函数(PPP是外设名):

1.外围系统级初始化/去初始化回调函数(用户代码第二部分:MSP的处理):HAL_PPP_MspInit()和HAL_PPP_MspDeInit**例如:_ _弱void HAL _ SPI _ MSP init(SPI _ handle typedef * HSPI)。在HAL_PPP_Init()函数中调用,以初始化底层相关设备(GPIOs、时钟、DMA、中断)

2.处理完成回调函数:Hal _ PPP _ ProcessCPLTCallback *(进程是指一种特定的处理,比如UART的Tx),例如:_ _弱void Hal _ SPI _ RxCPLTCallback(SPI _ handle typedef * HSPI)。当外设或DMA工作完成后,中断被触发,在外设中断处理函数或DMA中断处理函数中会调用回调函数。

3.错误处理回调函数:HAL_PPP_ErrorCallback例如:_ _ weak void HAL _ SPI _ error callback(SPI _ handle typedef * HSPI)* *。当外设或DMA发生错误时,终端被触发,回调函数将在外设或DMA的中断处理函数中被调用。

大多数用户代码都是在上述三个回调函数中实现的。

在HAL库结构中,每次初始化之前(尤其是多次初始化调用之前)调用对应的去初始化函数是非常必要的。某些外设多次初始化时,不调用return会导致初始化失败。回调函数有很多种,比如HAL_UART_TxCpltCallback和HAL_UART_TxHALfCpltCallback等。(用户代码第三部分:以上第二、三点中各种回调函数的处理)。在实际使用中发现Hal还有很多问题,比如使用USB时,其库配置有问题。

05HAL库的移植和使用

基本步骤

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 _ IRQ handler(Uart _ handle typedef * HUART);函数,用户只需要触发中断后,用户只需要调用这个函数,同时他的代码就可以写在相应的回调函数里了!如下所示:

void HAL _ UART _ TxCpltCallback(UART _ handle typedef * huart);void HAL _ UART _ TxHalfCpltCallback(UART _ handle typedef * huart);void HAL _ UART _ RxCpltCallback(UART _ handle typedef * huart);void HAL _ UART _ RxHalfCpltCallback(UART _ handle typedef * huart);void HAL _ UART _ error callback(UART _ handle typedef * huart);

用哪个回调函数就用哪个!

基本结构总结一下,使用HAL库编程(针对一个外设)的基本结构(以串口为例)如下:

1.配置外设句柄。比如建立UartConfig.c,其中定义了串口句柄UART _ HandleTypeDef huart然后使用初始化句柄(Hal _ status typedef Hal _ UART _ init(UART _ handle typedef huart))。

2.写Msp。比如设置UartMsp.c,实现void Hal _ UART _ MSP init(UART _ Handle Typedef Huart)和void Hal _ UART _ MSP Deinit(UART _ Handle Typedef * Huart)

3.实现相应的回调函数。比如建立UartCallBack.c,其中实现了上述三个回调函数中的完成回调函数和错误回调函数。

japan quarterly 日本季刊