打印
[其他ST产品]

STM32 HAL库组成概述

[复制链接]
1194|60
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
什么是HAL(Hardware Abstraction Layer)?
from 百度百科:

硬件抽象层是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化。它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,可在多种平台上进行移植。从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件测试工作的并行进行成为可能。

编程方式发展史:在没有库函数编程时,通常采用配置寄存器的方式进行片上外设的配置,这种通过操作寄存器的编程方式直接且直观,但是要求编程人员可以清晰地**相关寄存器的名称以及要求大量的位运算,再编程时要翻阅datasheet进行查阅,操作繁杂于是库函数编程应运而生,研发人员将寄存器进行二次封装,将一些外设的寄存器配置封装为结构体,用户在使用时直接更改结构体变量就可以完成对底层寄存器的配置,这就是标准外设库(Standard Peripheral Libraries),但是ST公司给出的标准外设库对每一款芯片都不同,在代码移植上很困难,于是HAL库应运而生,HAL库可以更好地确保跨STM32产品的最大可移植性。该库提供了一整套一致的中间件组件,如RTOS,USB,TCP / IP和图形等。相比标准外设库,HAL库表现出更高的抽象整合水平,HAL API集中关注各外设的公共函数功能,这样便于定义一套通用的用户友好的API函数接口,从而可以轻松实现从一个STM32产品移植到另一个不同的STM32系列产品。

ST公司针对不同外设构造了不同的数据类型以及操作这些数据类型的接口函数,开发人员只需要用这些数据类型定义对应的变量,并写入对应的配置参数,然后调用初始化函数完成外设的初始化、调用控制函数控制外设的行为、调用状态函数判断外设的状态。

使用特权

评论回复
沙发
结合国际经验|  楼主 | 2023-8-27 16:25 | 只看该作者
(二)HAL库文件结构

HAL库文件可以分为以下两大类
库文件:存放在Drivers/STM32Fxxx_HAL_Driver文件夹下

stm32f2xx_hal_ppp.c/.h(ppp指外设名称,例如uart/gpio/i2c等等)
主要的外设或者模块的驱动源文件,包含了该外设的通用API

使用特权

评论回复
板凳
结合国际经验|  楼主 | 2023-8-27 16:25 | 只看该作者
stm32f2xx_hal_ppp_ex.c/.h(ppp指外设名称,例如uart/gpio/i2c等等)
外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp(外设名称)中的通用API。

使用特权

评论回复
地板
结合国际经验|  楼主 | 2023-8-27 16:25 | 只看该作者
stm32f2xx_hal.c/.h
此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API其他库文件

使用特权

评论回复
5
结合国际经验|  楼主 | 2023-8-27 16:25 | 只看该作者
用户级别文件:大部分存放在Application/User/Core文件夹下

stm32f2xx_hal_msp.c
只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。

使用特权

评论回复
6
结合国际经验|  楼主 | 2023-8-27 16:25 | 只看该作者
system_stm32f2xx.c(存放在Drivers/CMSIS文件夹下)
此文件主要包含SystemInit()函数,该函数在刚复位及跳到main之前的启动过程中被调用。它不再启动时配置系统时钟(与标准库相反)。时钟的配置在用户文件中使用HAL API来完成。

使用特权

评论回复
7
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
startup_stm32f2xx.s(存放在Application/MDK-ARM文件夹下)
芯片启动文件,主要包含堆栈定义,中断向量表等。

使用特权

评论回复
8
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
stm32f2xx_it.c/.h
中断服务函数的相关实现。

使用特权

评论回复
9
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
ppp.c/h
外设初始化以及相关实现

使用特权

评论回复
10
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
main.c/.h
主函数

使用特权

评论回复
11
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
(三)HAL库用户代码处理

句柄 HandleTypedef
HAL库将外设全部封装成了一个ppp_HandleTypedef(ppp为外设名称),例如:
UART_HandleTypedef—>串口句柄

使用特权

评论回复
12
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
下面截取stm32f1xx.hal.h文件中的UART_HandleTypedef作为例子:(ps:此处不对结构体中的变量做相应的解释,相关详细解释见 STM32 UART/USART 调试心得 )

使用特权

评论回复
13
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
typedef struct __UART_HandleTypeDef
{
  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              */

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

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

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

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

  __IO HAL_UART_RxTypeTypeDef ReceptionType;      /*!< Type of ongoing reception          */

  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    gState;           /*!< UART state information related to global Handle management
                                                       and also related to Tx operations.
                                                       This parameter can be a value of [url=home.php?mod=space&uid=144993]@ref[/url] HAL_UART_StateTypeDef */

  __IO HAL_UART_StateTypeDef    RxState;          /*!< UART state information related to Rx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */

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

使用特权

评论回复
14
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
从UART_HandleTypedef这个例子中可以看出,该结构体涵盖了UART与MCU有关的所有部分,如接收数据缓冲区指针等,这些变量都是与MCU有密切关系的。

使用特权

评论回复
15
结合国际经验|  楼主 | 2023-8-27 16:26 | 只看该作者
另外,HAL库ppp.c/h文件中的 MX_PPP_Init()函数中也有一个结构体变量,这里我们引入uart.c文件中的MX_USART2_UART_Init()作为例子,与上述UART_HandleTypedef中的结构体变量做对比。

使用特权

评论回复
16
结合国际经验|  楼主 | 2023-8-27 16:27 | 只看该作者
void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */
}

使用特权

评论回复
17
结合国际经验|  楼主 | 2023-8-27 16:27 | 只看该作者
不难发现,MX_USART2_UART_Init()中出现的结构体成员变量与MCU无关,只是规定了波特率、字长、停止位等通信协议,这些变量可在任何MCU上直接使用,这部分初始化称之为抽象初始化(相关HAL库初始化的解释说明见下文)

使用特权

评论回复
18
结合国际经验|  楼主 | 2023-8-27 16:27 | 只看该作者
MSP函数-单片机的具体解决方案(MCU Specific Package)
MSP主要指和MCU相关的初始化,HAL库中对外设的初始化主要有两部分:
1.抽象初始化:主要指与MCU无关的外设初始化,如上文MX_USART2_UART_Init(),主要是对通信协议做了初始化。
2.承载初始化:主要指与MCU有关的外设初始化,外设不仅需要通信协议这样的初始化,外设的功能实现还需要借助MCU来完成,故外设初始化中包含了MSP_Init(),其执行的功能是为外设分配管脚等,为外设功能的实现提供硬件条件。

使用特权

评论回复
19
结合国际经验|  楼主 | 2023-8-27 16:27 | 只看该作者
此处引入uart.c文件中的HAL_UART_MspInit(UART_HandleTypeDef*uartHandle)作为例子来说明HAL库的承载初始化

使用特权

评论回复
20
结合国际经验|  楼主 | 2023-8-27 16:27 | 只看该作者
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();         //USART 1的时钟使能

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PB6     ------> USART1_TX
    PB7     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6;       //USART1的引脚分配
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_AFIO_REMAP_USART1_ENABLE();

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
  else if(uartHandle->Instance==USART2)
  {
  /* USER CODE BEGIN USART2_MspInit 0 */

  /* USER CODE END USART2_MspInit 0 */
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART2 DMA Init */
    /* USART2_RX Init */
    hdma_usart2_rx.Instance = DMA1_Channel6;            // USART2 DMA初始化
    hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart2_rx.Init.Mode = DMA_CIRCULAR;
    hdma_usart2_rx.Init.Priority = DMA_PRIORITY_LOW;
    if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart2_rx);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  /* USER CODE BEGIN USART2_MspInit 1 */

  /* USER CODE END USART2_MspInit 1 */
  }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

692

帖子

1

粉丝