2023年11月25日发(作者:)

STM32HAL

1 STM32的三种开发⽅式

通常新⼿在⼊门STM32的时候,⾸先都要先选择⼀种要⽤的开发⽅式,不同的开发⽅式会导致你编程的架构是完全不⼀样的。⼀般⼤多数

都会选⽤标准库和HAL库,⽽极少部分⼈会通过直接配置寄存器进⾏开发。⽹上关于标准库、HAL库的描述相信是数不胜数。可是⼀个对

于很多刚⼊门的朋友还是没法很直观的去真正了解这些不同开发发⽅式彼此之间的区别,所以笔者想以⼀种⾮常直⽩的⽅式,⽤⾃⼰的理解

去将这些东西表述出来,如果有描述的不对的地⽅或者是不同意见的也可以⼤家提出。

⼀、直接配置寄存器

不少先学了51的朋友可能会知道,会有⼀⼩部分⼈或是教程是通过汇编语⾔直接操作寄存器实现功能的,这种⽅法到了STM32就变得不太

容易⾏得通了,因为STM32的寄存器数量是51单⽚机的⼗数倍,如此多的寄存器根本⽆法全部记忆,开发时需要经常的翻查芯⽚的数据⼿

2.根据芯⽚选择所需固件

版本是向下兼容的,可以直接选择最新版。但如果觉得最新版太⼤,可以阅读下⾯的Main Changes.能够⽀持你⽬前的芯⽚就好。

⽂件会被下载到如下位置,建议更改此⽬录,不要选在C盘

⽤户⼿册就在Drivers⽂件夹下⾯。

3 STM32 HAL库与标准库的区别_浅谈句柄、MSP函数、Callback函数

3.1.句柄

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

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

使⽤句柄。

第⼆种解释:整个Windows编程的基础。⼀个句柄是指使⽤的⼀个唯⼀的整数值,即⼀个4字节(64位程序中为8字节)长的数值,来标

识应⽤程序中的不同对象和同类中的不同的实例,诸如,⼀个窗⼝,按钮,图标,滚动条,输出设备,控件或者⽂件等。应⽤程序能够

通过句柄访问相应的对象的信息,但是句柄不是指针,程序不能利⽤句柄来直接阅读⽂件中的信息。如果句柄不在I/O⽂件中,它是毫

⽆⽤处的。 句柄是Windows⽤来标志应⽤程序中建⽴的或是使⽤的唯⼀整数,Windows⼤量使⽤了句柄来标识对象。

STM32的标准库中,句柄是⼀种特殊的指针,通常指向结构体

USART_InitTypeDef USART_InitStructure;

USART__BaudRate = bound;//串⼝波特率

USART__WordLength = USART_WordLength_8b;//字长为8位数据格式

USART__StopBits = USART_StopBits_1;//⼀个停⽌位

USART__Parity = USART_Parity_No;//⽆奇偶校验位

USART__HardwareFlowControl = USART_HardwareFlowControl_None;//⽆硬件数据流控制

USART__Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式

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

可以看到,要初始化⼀个串⼝,需要:

1、对六个位置进⾏赋值,

2、然后引⽤Init函数,

USART_InitStructure并不是⼀个全局结构体变量,⽽是只在函数内部的局部变量,初始化完成之后,USART_InitStructure就失去了作

⽤。

⽽在HAL库中,同样是USART初始化结构体变量,我们要定义为全局变量

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);

⼊⼝参数仅仅需要⼀个串⼝句柄,这样有能看出句柄的⽅便。

ck函数

类似于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才会执⾏⼀次Callback函数:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

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

接下来对于HAL库的源码⽂件进⾏⼀下说明,HAL库⽂件名均以stm32f4xx_hal开头,后⾯加上_外设或者模块名(如:

stm32f4xx_hal_adc.c):

4、库⽂件:

/**

* @brief ADC handle Structure definition

*/

typedef struct

{

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

ADC_InitTypeDef Init; /*!< ADC required parameters */

__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);

基本步骤

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))