【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 文件也不会对程序运行产生任何影响。

posted @ 2023-01-19 20:58  FBshark  阅读(1917)  评论(0编辑  收藏  举报