【HAL库&CubeMX】0-STM32中HAL库和标准库的区别
转载自:https://www.lmonkey.com/t/RwykY8bBX
一、首先,大致了解一下区别~
ST为开发者提供了非常方便的开发库。到目前为止,有标准外设库(STD库)、HAL库、LL库 三种。
其中标准库与HAL库最常用,LL库只是最近新添加的。
标准外设库(Standard Peripherals Library)是对STM32芯片的一个完整的封装,包括所有标准器件外设的器件驱动器。这应该是目前使用最多的ST库,几乎全部使用C语言实现。但是,标准外设库也是针对某一系列芯片而言的,没有可移植性。
HAL库与新增的LL库,都是ST公司提供的新标准库,包含在ST为新的标准库注册了一个新商标:STMCube™当中。LL库和HAL库两者相互独立,只不过LL库更底层。而且,部分HAL库会调用LL库(例如:USB驱动)。同样,LL库也会调用HAL库。
可以说HAL库就是用来取代之前的标准外设库的。相比标准外设库,STM32Cube HAL库表现出更高的抽象整合水平,HAL API集中关注各外设的公共函数功能,这样便于定义一套通用的用户友好的API函数接口,从而可以轻松实现从一个STM32产品移植到另一个不同的STM32系列产品。HAL库是ST未来主推的库,从前年开始ST新出的芯片已经没有STD库了,比如F7系列。现在,ST主推HAL库,目前,HAL库已经支持STM32全线产品。
新芯片现在只推出HAL库,HAL库与标准库比较,所用的API函数不一样,两者也相互独立,优势在于可以用cubeMX生成代码。(ST专门为其开发了配套的桌面软件STMCubeMX,开发者可以直接使用该软件进行可视化配置,大大节省开发时间。)
二. 如何从标准库转型使用HAL库呢?
步骤①、首先需要直到两者文件结构的不同。HAL库比标准库多出很多文件,另外,还有一些文件功能相同,但是名字已经改变。
步骤②、其次,要学会main() 一开始就调用 HAL_Init()函数,知道HAL_Init()函数的底层原理(干了什么事情)。需要延时的时候,可以使用 HAL_Delay(),需要配置NVIC的优先级分组的时候,再HAL_Init() 中进行配置。
步骤③、学会使用回调函数, 最多的回调函数为中断的回调函数,还有初始化的中断回调函数。并且理解这些回调函数什么时候会被调用。(往往这些函数不用用户调用,但是需要用户编写)。
以下进行分步讲解:
步骤①:了解HAL库文件架构和文件名称的区别
以前标准库编程的时候,调用一个 #include <stm32f10x.h>就可以了。HAL库之后,需要用新的头文件。
请看我的文章以理解 HAL 库的头文件包含关系:
《STM32 HAL库头文件三胞胎stm32f4xx.h---f407.h---f4xx_hal.h解析(区别和联系)》
同时,HAL库也 看不到 misc.c 和 stm32fxxx_systick.c 的身影了,因为已经有了新文件取代它们:stm32f4xx_hal_cortex.c
因此,当需要设置优先级级别(NVIC)和开启systick的时候,请为工程添加文件——stm32f4xx_hal_cortex.c,即:
标准库的misc.c+stm32fxxx_systick.c == HAL库的stm32f4xx_hal_cortex.c
步骤②: 学会调用 HAL_Init()函数
步骤③:学会使用回调函数
3-1: 中断回调:HAL库中断处理以及相关的回调函数 ——以分析USART1中断为例
转载自:http://news.eeworld.com.cn/mcu/ic655867.html?ipcity__1865=n4%2BxyDnD0D9Ai%3DDCDuADl1N3RwBQarY4D&alichlgref=http%3A%2F%2Fwww.eeworld.com.cn%2F
今天就分享一个读者问到的关于中断处理的问题。 很多人都知道STM32CubeMX这套工具的一个目的:减少开发者对STM32底层驱动的开发时间,把重心放在应用代码上。 但是,STM32CubeMX只是生成了底层驱动的初始化代码。所以,我们还需要掌握:应用层代码如何调用HAL库函数(API接口),以及HAL库中断处理机制等相关知识。 HAL库牵涉的内容较多,下面简单描述一下HAL库中断处理,以及相关的回调函数。
HAL库中断处理机制
之前使用标准外设库开发时,中断程序(函数)由我们自己实现。
而HAL库的中断处理函数是按照HAL处理机制来实现,如USART1,统一由HAL_UART_IRQHandler来进行处理,如下图:
其它大部分外设(TIM、SPI、CAN...)中断都类似,HAL进行统一处理。
也就是说,HAL已经帮我们把中断处理函数(的框架)写好了,我们只需要调用相应函数来编写应用程序就行了。 HAL_xxx_IRQHandler里面做了哪些处理?我们以STM32F1的HAL_UART_IRQHandler为例:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags = 0x00U;
uint32_t dmarequest = 0x00U;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if(errorflags == RESET)
{ /* UART in mode Receiver -------------------------------------------------*/
if(((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{ UART_Receive_IT(huart); return; } } /* If some errors occur */
if((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
{/*··删减了部分代码·*/ }
/* End if some error occurs */
/* UART in mode Transmitter ------------------------------------------------*/
if(((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{ UART_Transmit_IT(huart); return;}
/* UART in mode Transmitter end --------------------------------------------*/
if(((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{ UART_EndTransmit_IT(huart); return; }}
其实,大家认真看一下代码应该能明白,这些和我们编写的中断处理函数是不是有类似之处?
无非就是接收中断、判断中断、执行中断任务程序等一系列处理。只是这里又进行了再次封装,比如接收中断UART_Receive_IT、UART_Transmit_IT。这些函数需要用户自己编写。
当然,这个UART_Receive_IT接收中断实现方式又可能存在不同。像F0、F1...就是直接调用这个接收中断函数来进一步处理。 像L0、G0...是通过执行指针函数RxISR来进一步处理。G0的接收中断处理为:huart->RxISR(huart);
3-2: 初始化回调:以 HAL_SPI_Init(SPI_HandleTypeDef *hspi)函数为例:
其主要的工作如下:
1. 检查hspi->Init结构体各项参数。
2. 如果要求注册回调函数 宏USE_HAL_SPI_REGISTER_CALLBACKS == 1U
则注册回调函数(回调函数具体内容由用户编写)。
3. 执行SPI的底层初始化函数 HAL_SPI_MspInit(hspi);
(底层初始化函数具体内容由用户编写)。
注意!!!这个就是一个初始化类型的回调函数。它不需要用户调用,但是需要用户自己编写。
4. 将上述 Init 结构体的配置转换,再通过 WRITE_REG() 函数写到SPI配置寄存器里。
/**
* @brief Initialize the SPI according to the specified parameters
* in the SPI_InitTypeDef and initialize the associated handle.
* @param hspi pointer to a SPI_HandleTypeDef structure that contains
* the configuration information for SPI module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi)
{
/*检查输入参数*/
if (hspi == NULL) { return HAL_ERROR;}
/* Check the parameters */
assert_param(IS_SPI_ALL_INSTANCE(hspi->Instance));
assert_param(IS_SPI_MODE(hspi->Init.Mode));
assert_param(IS_SPI_DIRECTION(hspi->Init.Direction));
assert_param(IS_SPI_DATASIZE(hspi->Init.DataSize));
assert_param(IS_SPI_NSS(hspi->Init.NSS));
assert_param(IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRatePrescaler));
assert_param(IS_SPI_FIRST_BIT(hspi->Init.FirstBit));
/* TI mode is not supported on this device.
TIMode parameter is mandatory equal to SPI_TIMODE_DISABLE */
assert_param(IS_SPI_TIMODE(hspi->Init.TIMode));
if (hspi->Init.TIMode == SPI_TIMODE_DISABLE)
{
assert_param(IS_SPI_CPOL(hspi->Init.CLKPolarity));
assert_param(IS_SPI_CPHA(hspi->Init.CLKPhase));
if (hspi->Init.Mode == SPI_MODE_MASTER)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRatePrescaler));
}
else
{
/* Baudrate prescaler not use in Motoraola Slave mode. force to default value */
hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
}
}
else
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(hspi->Init.BaudRatePrescaler));
/* Force polarity and phase to TI protocaol requirements */
hspi->Init.CLKPolarity = SPI_POLARITY_LOW;
hspi->Init.CLKPhase = SPI_PHASE_1EDGE;
}
#if (USE_SPI_CRC != 0U)
assert_param(IS_SPI_CRC_CALCULATION(hspi->Init.CRCCalculation));
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
assert_param(IS_SPI_CRC_POLYNOMIAL(hspi->Init.CRCPolynomial));
}
#else
hspi->Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
#endif /* USE_SPI_CRC */
if (hspi->State == HAL_SPI_STATE_RESET)
{
/* Allocate lock resource and initialize it */
hspi->Lock = HAL_UNLOCKED;
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
/* Init the SPI Callback settings */
hspi->TxCpltCallback = HAL_SPI_TxCpltCallback; /* Legacy weak TxCpltCallback */
hspi->RxCpltCallback = HAL_SPI_RxCpltCallback; /* Legacy weak RxCpltCallback */
hspi->TxRxCpltCallback = HAL_SPI_TxRxCpltCallback; /* Legacy weak TxRxCpltCallback */
hspi->TxHalfCpltCallback = HAL_SPI_TxHalfCpltCallback; /* Legacy weak TxHalfCpltCallback */
hspi->RxHalfCpltCallback = HAL_SPI_RxHalfCpltCallback; /* Legacy weak RxHalfCpltCallback */
hspi->TxRxHalfCpltCallback = HAL_SPI_TxRxHalfCpltCallback; /* Legacy weak TxRxHalfCpltCallback */
hspi->ErrorCallback = HAL_SPI_ErrorCallback; /* Legacy weak ErrorCallback */
hspi->AbortCpltCallback = HAL_SPI_AbortCpltCallback; /* Legacy weak AbortCpltCallback */
if (hspi->MspInitCallback == NULL)
{
hspi->MspInitCallback = HAL_SPI_MspInit; /* Legacy weak MspInit */
}
/* Init the low level hardware : GPIO, CLOCK, NVIC... */
hspi->MspInitCallback(hspi);
#else
/* Init the low level hardware : GPIO, CLOCK, NVIC... */
HAL_SPI_MspInit(hspi);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
}
hspi->State = HAL_SPI_STATE_BUSY;
/* Disable the selected SPI peripheral */
__HAL_SPI_DISABLE(hspi);
/*----------------------- SPIx CR1 & CR2 Configuration ---------------------*/
/* Configure : SPI Mode, Communication Mode, Data size, Clock polarity and phase, NSS management,
Communication speed, First bit and CRC calculation state */
WRITE_REG(hspi->Instance->CR1, ((hspi->Init.Mode & (SPI_CR1_MSTR | SPI_CR1_SSI)) |
(hspi->Init.Direction & (SPI_CR1_RXONLY | SPI_CR1_BIDIMODE)) |
(hspi->Init.DataSize & SPI_CR1_DFF) |
(hspi->Init.CLKPolarity & SPI_CR1_CPOL) |
(hspi->Init.CLKPhase & SPI_CR1_CPHA) |
(hspi->Init.NSS & SPI_CR1_SSM) |
(hspi->Init.BaudRatePrescaler & SPI_CR1_BR_Msk) |
(hspi->Init.FirstBit & SPI_CR1_LSBFIRST) |
(hspi->Init.CRCCalculation & SPI_CR1_CRCEN)));
/* Configure : NSS management */
WRITE_REG(hspi->Instance->CR2, ((hspi->Init.NSS >> 16U) & SPI_CR2_SSOE));
#if (USE_SPI_CRC != 0U)
/*---------------------------- SPIx CRCPOLY Configuration ------------------*/
/* Configure : CRC Polynomial */
if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
{
WRITE_REG(hspi->Instance->CRCPR, (hspi->Init.CRCPolynomial & SPI_CRCPR_CRCPOLY_Msk));
}
#endif /* USE_SPI_CRC */
#if defined(SPI_I2SCFGR_I2SMOD)
/* Activate the SPI mode (Make sure that I2SMOD bit in I2SCFGR register is reset) */
CLEAR_BIT(hspi->Instance->I2SCFGR, SPI_I2SCFGR_I2SMOD);
#endif /* SPI_I2SCFGR_I2SMOD */
hspi->ErrorCode = HAL_SPI_ERROR_NONE;
hspi->State = HAL_SPI_STATE_READY;
return HAL_OK;
}
三、使用CubeMX 生成的一些文件
stm32l4xx_hal_msp.c 文件
MSP,全称为 MCU support package,函数名字中 带有 MspInit 的函数,它们的作用是进行 MCU 级别硬件初始化设置,并且它们通常会被上一层的初始化函数 所调用,这样做的目的是为了把 MCU 相关的硬件 初始化剥夺出来,方便用户代码在不同型号的 MCU 上移植。stm32l4xx_hal_msp.c 文件定义了两个函数 HAL_MspInit 和 HAL_MspDeInit。这两个函数分别被文件 stm32l4xx_hal.c 中的 HAL_Init 和 HAL_DeInit 所调用。HAL_MspInit 函数的主要作用是进行 MCU相关的硬件初始化操作。例如我们要初始化某些硬件,我们可以硬件相关的初始化配置 写在HAL_MspDeinit 函数中。这样的话,在系统启动后调用了 HAL_Init 之后,会自动调用硬件初始化函数。
实际上,我们在工程模板中直接删掉 stm32l4xx_hal_msp.c 文件也不会对程序运行产生任何影响。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升