STM32 标准外设SPL库、硬件抽象层HAL库、低层LL库区别?

news2024/11/26 16:41:59

1、STM32 之一 HAL库、标准外设库、LL库_ZC·Shou的博客-CSDN博客_ll库(仔细阅读)

2、STM32标准外设库、 HAL库、LL库 - King先生 - 博客园  

3、STM32 之 HAL库_戈 扬的博客(仔细阅读)

4、STM32 LL 为什么比 HAL 高效?_strongerHuang的博客-CSDN博客

5、标准库和HAL库到底有什么不同?怎么选? - 知乎

6、从代码层面分析STM32 标准库和HAL库的差异_嵌入式@hxydj的博客-CSDN博客_hal库和寄存器版本区别

7、代码分析【STM32】入门(九):HAL库学习_郭老二的博客-CSDN博客_stm32 hal库

ST 为开发者提供了非常方便的开发库。到目前为止,有

标准外设(SPL 库)、

硬件抽象层HAL 库、

低层LL 库 三种。

前两者都是常用的库,后面的 LL 库是 ST 最近才添加,随 HAL 源码包一起提供,目前支持的芯片也偏少。各库如下所示: 

在这里插入图片描述 在这里插入图片描述

 串口通信实验包含的文件区别:

如上左边标准库、右边HAL:

具体代码区别:

主函数

 标准库

 
 
  1. #include "led.h"

  2. #include "delay.h"

  3. #include "key.h"

  4. #include "sys.h"

  5. #include "usart.h"

  6. int main(void)

  7. {

  8. u16 t;

  9. u16 len;

  10. u16 times=0;

  11. delay_init(); //延时函数初始化

  12. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

  13. uart_init(115200); //串口初始化为115200

  14. LED_Init(); //LED端口初始化

  15. KEY_Init(); //初始化与按键连接的硬件接口

  16. while(1)

  17. {

  18. if(USART_RX_STA&0x8000)

  19. {

  20. len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度

  21. printf("\r\n您发送的消息为:\r\n\r\n");

  22. for(t=0;t<len;t++)

  23. {

  24. USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据

  25. while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束

  26. }

  27. printf("\r\n\r\n");//插入换行

  28. USART_RX_STA=0;

  29. }else

  30. {

  31. times++;

  32. if(times%5000==0)

  33. {

  34. printf("\r\n精英STM32开发板 串口实验\r\n");

  35. printf("正点原子@ALIENTEK\r\n\r\n");

  36. }

  37. if(times%200==0)printf("请输入数据,以回车键结束\n");

  38. if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.

  39. delay_ms(10);

  40. }

  41. }

  42. }

HAL:

 
  1. #include "sys.h"

  2. #include "delay.h"

  3. #include "usart.h"

  4. #include "led.h"

  5. #include "key.h"

  6. int main(void)

  7. {

  8. u8 len;

  9. u16 times=0;

  10. HAL_Init(); //初始化HAL库

  11. Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

  12. delay_init(72); //初始化延时函数

  13. uart_init(115200); //初始化串口

  14. LED_Init(); //初始化LED

  15. KEY_Init(); //初始化按键

  16. while(1)

  17. {

  18. if(USART_RX_STA&0x8000)

  19. {

  20. len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度

  21. printf("\r\n您发送的消息为:\r\n");

  22. HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据

  23. while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET); //等待发送结束

  24. printf("\r\n\r\n");//插入换行

  25. USART_RX_STA=0;

  26. }else

  27. {

  28. times++;

  29. if(times%5000==0)

  30. {

  31. printf("\r\nALIENTEK 精英STM32开发板 串口实验\r\n");

  32. printf("正点原子@ALIENTEK\r\n\r\n\r\n");

  33. }

  34. if(times%200==0)printf("请输入数据,以回车键结束\r\n");

  35. if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.

  36. delay_ms(10);

  37. }

  38. }

  39. }


 

STM32的三种开发方式

通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发。网上关于标准库、HAL库的描述相信是数不胜数。可是一个对于很多刚入门的朋友还是没法很直观的去真正了解这些不同开发发方式彼此之间的区别,所以笔者想以一种非常直白的方式,用自己的理解去将这些东西表述出来,如果有描述的不对的地方或者是不同意见的也可以大家提出。

一、直接配置寄存器
不少先学了51的朋友可能会知道,会有一小部分人或是教程是通过汇编语言直接操作寄存器实现功能的,这种方法到了STM32就变得不太容易行得通了,因为STM32的寄存器数量是51单片机的十数倍,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,此时直接操作寄存器就变得非常的费力了。但还是会有很小一部分人,喜欢去直接操作寄存器,因为这样更接近原理,知其然也知其所以然。

二、标准(外设)SPL库
上面也提到了,STM32有非常多的寄存器,而导致了开发困难,所以为此ST公司就为每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的。在这些 .c .h文件中,包括一些常用量的宏定义,把一些外设也通过结构体变量封装起来,如GPIO口时钟等。所以我们只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。也是目前最多人使用的方式,也是学习STM32接触最多的一种开发方式,我也就不多阐述了。

三、HAL库
HAL库是ST公司目前主力推的开发方式,全称就是Hardware Abstraction Layer(硬件抽象层)。库如其名,很抽象,一眼看上去不太容易知道他的作用是什么。它的出现比标准库要晚,但其实和标准库一样,都是为了节省程序开发的时期,而且HAL库尤其的有效,如果说标准库把实现功能需要配置的寄存器集成了,那么HAL库的一些函数甚至可以做到某些特定功能的集成。也就是说,同样的功能,标准库可能要用几句话,HAL库只需用一句话就够了。并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相通外设,意思也就是不能无中生有,例如F7比F3要多几个定时器,不能明明没有这个定时器却非要配置,但其实这种情况不多,绝大多数都可以直接复制粘贴。是而且使用ST公司研发的STMcube软件,可以通过图形化的配置功能,直接生成整个使用HAL库的工程文件,可以说是方便至极,但是方便的同时也造成了它执行效率的低下,在各种论坛帖子真的是被吐槽的数不胜数。

个人理解:hal统一了define规范和函数规范,让移植只需要改几个参数即可

在这里插入图片描述

STM32中HAL库与标准库的区别

1.HAL库就是封装的比较猛,移植性比较强,标准库就是将寄存器封装好,移植性没有HAL好。
2.HAL库可以用ST的软件CUBE生成初始化工程。

3.HAL库最方便的就是可以用CUBEMX自动生成代码,动态的调用资源,不会出现地城配置上的冲突。
4.其实就是两种库的区别,Hal库更加全面一点,目前STM32官方也在主推Hal库,目前STMcubemx软件可以直接生成HAL库代码,非常方便编程,易移植。
5.HAL库是ST近年来推出的新库,可以直接在CubeMX下生成例程,并且各个不同型号的STM32之间的函数差异也减少了。
6.HAL库是现在ST主推的库,标准库现在已经不更新了。HAL库做了更深的封装,可以很方便的移植在F0/F1/F3/F4/F7的各个系列的芯片上。
7.HAL库通用型强,但是效率稍微低一点,标准库效率高。
8.本质上是一样的,就是配置寄存器,只是HAL库将应用层与驱动层分的比较明确。
9.HAL库设计进一步降低了API对硬件的依赖性,它借鉴了OS中驱动程序的思路,使得API的通用性更强。 能使用ST的CubeMX图形化界面来生成软件框架,它和CubeMX生成的软件代码完全兼容。减少了程序员的负担,同时代码也更规范。 至于与原库函数之间的使用差异, 关键还是要掌握内核及外设的工作原理,如果熟悉了硬件的工作原理,这些库函数还是很好用的。
10.HAL库似乎是为初学者而制定,但这也许是STM32的未来所在。
11.HAL和STD库最大的区别是移植性上的区别,HAL相当于在标准库上在加以封装了。增强了移植性,STD库是在寄存器的基础上封装了一次。12.hal库封装的更想arduino,移植性强,相对效率就低一些,在时间就是金钱的现在,hal库无疑是很好的选择。
13.HAL库和标准库都是对寄存器操作的封装,但是这些库的函数不同在HAL库每个.c文件的开头会介绍这个库里面包含哪些函数,这些函数的用途,可以留意一下。
14.标准库是STM32最早推出的库,应用非常广泛,但是比较新的F7和H7等系列已经不支持了。
HAL库是官方主推的库,目前支持所有系列,相对效率没有标准库高,但是各个系列之间的兼容性很好,而且能够配合STM32CubeMX进行使用。由于官方现在不在更新和支持标准库了,所有精力都放在了HAL库和LL库上了,所以建议今后学习和应用还是以HAL库和LL库为主要对象。
15.HAL库和标准库的最大区别就是减少了不同系列器件之间的库函数层差异,并且可以直接用cubemx生成。
16.HAL移植性比较强,可以通过cubemx生成代码,不过效率比较低标准库更像寄存器的操作,感觉更符合对寄存器使用的理解。
17.HAL库移植性比较好,操作比较简单。标准库移植性没那么方便,操作比较复杂,直接对寄存器的操作。
18.HAL库的封装比较多,大部分都是面向对象的设计,移植比较简单。
19.HAL的优点就是用API的设计,十分方便移植,而且操作也简单。
20.HAL的移植性是最好的,但是标准库不太方便移植,所以ST出了一个HAL库。
————————————————
版权声明:本文为CSDN博主「宇豪@」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44403143/article/details/117650293

MCU的替换就意味着程序的移植。对一个传感器而言,替换MCU时,其实替换的是底层,而上层的逻辑是保持一致的。只要保证底层能脱离MCU的寄存器那么程序移植起来就非常容易。

HAL库是st公司为了更方便地进行stm32之间的移植而开发的库,通用性很强,在不同的两款stm32芯片之间的移植基本上不需要修改。之前使用的标准库都是基于寄存器的操作。寄存器版本使用较麻烦,每个设置都要去查看芯片datasheet,好处是可以让你熟悉芯片的寄存器配置。库函数是基于寄存器版本进行二次封装后推出的,它的优势就是寄存器版本的劣势,方便了使用,不再需要手动去配置寄存器,使用更方便了。但是它的劣势就是HAL的优势,就是每次修改MCU功能,都需要手动去修改功能,而且自己修改也不能保证正确性,程序代码在不同MCU之间的移植性不强。

HAL是Hardware Abstraction Layer的缩写,中文名称是:硬件抽象层,HAL库工程一般使用Cube软件来生成工程。HAL库是ST公司为STM32的MCU最新推出的抽象层嵌入式软件,更方便的实现跨STM32产品的最大可移植性。优势就是不需要开发工程师再关注所用MCU型号,只需要专注所以要的功能软件开发工作。而且是未来主推的方向,正在不断的推出更新。HAL库推出的同时,也加入了很多第三方的中间件,有RTOS,USB,TCP / IP和图形等等。和标准库对比起来,STM32的HAL库更加的抽象,ST最终的目的是要实现在STM32系列MCU之间无缝移植,甚至在其他MCU也能实现快速移植。

HAL 库相对标准库更加复杂更加繁琐,但是HAL库能够适应不同的st芯片的应用,所以应用能力更加广泛,但是是直接和硬件联系的,所以比起标准库来说,更难上手。

文章下方附学习资源,自助领取。

摘要:通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发。网上关于标准库、HAL库的描述相信是数不胜数。可是一个对于很多刚入门的朋友还是没法很直观的去真正了解这些不同开发发方式彼此之间的区别,所以笔者想以一种非常直白的方式,用自己的理解去将这些东西表述出来,如果有描述的不对的地方或者是不同意见的也可以大家提出。

一、配置寄存器

不少先学了51的朋友可能会知道,会有一小部分人或是教程是通过汇编语言直接操作寄存器实现功能的,这种方法到了STM32就变得不太容易行得通了,因为STM32的寄存器数量是51单片机的十数倍,如此多的寄存器根本无法全部记忆,开发时需要经常的翻查芯片的数据手册,此时直接操作寄存器就变得非常的费力了。但还是会有很小一部分人,喜欢去直接操作寄存器,因为这样更接近原理,知其然也知其所以然。

二、标准库

上面也提到了,STM32有非常多的寄存器,而导致了开发困难,所以为此ST公司就为每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx.....之类的。在这些.c .h文件中,包括一些常用量的宏定义,把一些外设也通过结构体变量封装起来,如GPIO口时钟等。所以我们只需要配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能。也是目前最多人使用的方式,也是学习STM32接触最多的一种开发方式,我也就不多阐述了。

三、HAL库

HAL库是ST公司目前主力推的开发方式,全称就是Hardware Abstraction Layer(抽象印象层)。库如其名,很抽象,一眼看上去不太容易知道他的作用是什么。它的出现比标准库要晚,但其实和标准库一样,都是为了节省程序开发的时期,而且HAL库尤其的有效,如果说标准库把实现功能需要配置的寄存器集成了,那么HAL库的一些函数甚至可以做到某些特定功能的集成。也就是说,同样的功能,标准库可能要用几句话,HAL库只需用一句话就够了。并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相通外设,意思也就是不能无中生有,例如F7比F3要多几个定时器,不能明明没有这个定时器却非要配置,但其实这种情况不多,绝大多数都可以直接复制粘贴。是而且使用ST公司研发的STMcube软件,可以通过图形化的配置功能,直接生成整个使用HAL库的工程文件,可以说是方便至极,但是方便的同时也造成了它执行效率的低下,在各种论坛帖子真的是被吐槽的数不胜数。

四、总结

综合上面说的,其实笔者还是强烈推荐HAL库的,理由有二:第一、 F7系列开始 ST公司就已近开始停止更新标准库,也就是F7开始包括F7已经不能用标准库了,公司对于主打HAL库的目的已经非常明显了。第二、追求更方便、追求模块化向来是世界的潮流,更方便的HAL库一定会迅速发展,低效的短板迟早会被硬件高度集成化所弥补

当然啦,不能只学习HAL库,底层的原理必需是要懂的,这是每个学有所成的人都公认的事实,HAL库也不是万能的,结合对底层的理解相信一定会让你的开发水准大大提高。

五、STM32 HAL库与标准库的区别

1.句柄

在STM32的标准库中,假设我们要初始化一个外设(这里以USART为例) 我们首先要初始化他们的各个寄存器。在标准库中,这些操作都是利用固件库结构体变量+固件库Init函数实现的:

 
  1. USART_InitTypeDef USART_InitStructure;

  2. USART_InitStructure.USART_BaudRate = bound;//串口波特率

  3. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式

  4. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位

  5. USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位

  6. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制

  7. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式

  8. USART_Init(USART3, &USART_InitStructure); //初始化串口1

可以看到,要初始化一个串口,需要对六个位置进行赋值,然后引用Init函数,并且USART_InitStructure并不是一个全局结构体变量,而是只在函数内部的局部变量,初始化完成之后,USART_InitStructure就失去了作用。

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

UART_HandleTypeDef UART1_Handler;

右键查看结构体成员

 
  1. typedef struct

  2. {

  3. USART_TypeDef *Instance; /*!< UART registers base address */

  4. UART_InitTypeDef Init; /*!< UART communication parameters */

  5. uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */

  6. uint16_t TxXferSize; /*!< UART Tx Transfer size */

  7. uint16_t TxXferCount; /*!< UART Tx Transfer Counter */

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

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

  10. uint16_t RxXferCount; /*!< UART Rx Transfer Counter */

  11. DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */

  12. DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */

  13. HAL_LockTypeDef Lock; /*!< Locking object */

  14. __IO HAL_UART_StateTypeDef State; /*!< UART communication state */

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

  16. }UART_HandleTypeDef;

我们发现,与标准库不同的是,该成员不仅包含了之前标准库就有的六个成员(波特率,数据格式等),还包含过采样、(发送或接收的)数据缓存、数据指针、串口 DMA 相关的变量、各种标志位等等要在整个项目流程中都要设置的各个成员。

该UART1_Handler就被称为串口的句柄 它被贯穿整个USART收发的流程,比如开启中断:

HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);

比如后面要讲到的MSPCallback回调函数:

 
  1. void HAL_UART_MspInit(UART_HandleTypeDef *huart);

  2. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

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

2.MSP函数

MCU Specific Package单片机的具体方案

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

3年嵌入式物联网学习资源整理分享:C语言、Linux开发、数据结构;软件开发,STM32单片机、ARM硬件开发、物联网通信开发、综合项目开发教程资料;笔试面试真题。点击下方插件

点我领取​s.pdb2.com/l/CMIsoKcnATFIF4M

3.Callback函数

类似于MSP函数,个人认为Callback函数主要帮助用户应用层的代码编写。还是以USART为例,在标准库中,串口中断了以后,我们要先在中断中判断是否是接收中断,然后读出数据,顺便清除中断标志位,然后再是对数据的处理,这样如果我们在一个中断函数中写这么多代码,就会显得很混乱:

 
  1. void USART3_IRQHandler(void) //串口1中断服务程序

  2. {

  3. u8 Res;

  4. if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)

  5. {

  6. Res =USART_ReceiveData(USART3); //读取接收到的数据

  7. /*数据处理区*/

  8. }

  9. }

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

 
  1. void USART1_IRQHandler(void)

  2. {

  3. HAL_UART_IRQHandler(&UART1_Handler); //调用HAL库中断处理公用函数

  4. /***************省略无关代码****************/

  5. }

HAL_UART_IRQHandler这个函数完成了判断是哪个中断(接收?发送?或者其他?),然后读出数据,保存至缓存区,顺便清除中断标志位等等操作。比如我提前设置了,串口每接收五个字节,我就要对这五个字节进行处理。在一开始我定义了一个串口接收缓存区:

 
  1. /*HAL库使用的串口接收缓冲,处理逻辑由HAL库控制,接收完这个数组就会调用HAL_UART_RxCpltCallback进行处理这个数组*/

  2. /*RXBUFFERSIZE=5*/

  3. u8 aRxBuffer[RXBUFFERSIZE];

在初始化中,我在句柄里设置好了缓存区的地址,缓存大小(五个字节)

 
  1. /*该代码在HAL_UART_Receive_IT函数中,初始化时会引用*/

  2. huart->pRxBuffPtr = pData;//aRxBuffer

  3. huart->RxXferSize = Size;//RXBUFFERSIZE

  4. huart->RxXferCount = Size;//RXBUFFERSIZE

则在接收数据中,每接收完五个字节,HAL_UART_IRQHandler才会执行一次Callback函数:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

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

所以说Callback函数是一个应用层代码的函数,我们在一开始只设置句柄里面的各个参数,然后就等着HAL库把自己安排好的代码送到手中就可以了~

综上,就是HAL库的三个与标准库不同的地方之个人见解。

个人觉得从这三个小点就可以看出HAL库的可移植性之强大,并且用户可以完全不去理会底层各个寄存器的操作,代码也更有逻辑性。但与此带来的是复杂的代码量,极慢的编译速度,略微低下的效率。看怎么取舍了

五、HAL库结构

说到STM32 的HAL库,就不得不提STM32CubeMX,其作为一个可视化的配置工具,对于开发者来说,确实大大节省了开发时间。STM32CubeMX 就是以 HAL 库为基础的,且目前仅支持 HAL 库及 LL 库!首先看一下,官方给出的 HAL 库的包含结构:

stm32f2xx.h 主要包含STM32同系列芯片的不同具体型号的定义,是否使用HAL库等的定义,接着,其会根据定义的芯片信号包含具体的芯片型号的头文件:

 
  1. #if defined(STM32F205xx)

  2. #include "stm32f205xx.h"

  3. #elif defined(STM32F215xx)

  4. #include "stm32f215xx.h"

  5. #elif defined(STM32F207xx)

  6. #include "stm32f207xx.h"

  7. #elif defined(STM32F217xx)

  8. #include "stm32f217xx.h"

  9. #else

  10. #error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"

  11. #endif

紧接着,其会包含 stm32f2xx_hal.h。

  • stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置
  • stm32f2xx_hal_conf.h :该文件是一个用户级别的配置文件,用来实现对HAL库的裁剪,其位于用户文件目录,不要放在库目录中。

接下来对于HAL库的源码文件进行一下说明,HAL库文件名均以stm32f2xx_hal开头,后面加上_外设或者模块名(如:stm32f2xx_hal_adc.c):

 
  1. 库文件:

  2. stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件,包含了该外设的通用API

  3. stm32f2xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式,则该文件中的特殊API将覆盖_ppp中的通用API。

  4. stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API

  5. 其他库文件

  6. 用户级别文件:

  7. stm32f2xx_hal_msp_template.c // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化(主程序和回调函数)。使用者复制到自己目录下使用模板。

  8. stm32f2xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用

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

  10. startup_stm32f2xx.s // 芯片启动文件,主要包含堆栈定义,终端向量表等

  11. stm32f2xx_it.c/.h // 中断处理函数的相关实现

  12. 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 处理各种回调函数 外设句柄定义 用户代码的第一大部分:对于外设句柄的处理。HAL库在结构上,对每个外设抽象成了一个称为ppp_HandleTypeDef的结构体,其中ppp就是每个外设的名字。*所有的函数都是工作在ppp_HandleTypeDef指针之下。

  1. 多实例支持:每个外设/模块实例都有自己的句柄。因此,实例资源是独立的
  2. 外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。

下面,以ADC为例

 
  1. /**

  2. * @brief ADC handle Structure definition

  3. */

  4. typedef struct

  5. {

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

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

  8. __IO uint32_t NbrOfCurrentConversionRank; /*!< ADC number of current conversion rank */

  9. DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */

  10. HAL_LockTypeDef Lock; /*!< ADC locking object */

  11. __IO uint32_t State; /*!< ADC communication state */

  12. __IO uint32_t ErrorCode; /*!< ADC Error code */

  13. }ADC_HandleTypeDef;

从上面的定义可以看出,ADC_HandleTypeDef中包含了ADC可能出现的所有定义,对于用户想要使用ADC只要定义一个ADC_HandleTypeDef的变量,给每个变量赋好值,对应的外设就抽象完了。接下来就是具体使用了。

当然,对于那些共享型外设或者说系统外设来说,他们不需要进行以上这样的抽象,这些部分与原来的标准外设库函数基本一样。例如以下外设:

  • GPIO
  • SYSTICK
  • NVIC
  • RCC
  • FLASH

以GPIO 为例,对于HAL_GPIO_Init()函数,其只需要GPIO地址以及其初始化参数即可。

1. 三种编程方式

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

 
  1. HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);

  2. HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);

  3. HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);

  4. HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);

  5. HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);

  6. HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* 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):清除一个指定的外设的标志状态
  • __HAL_PPP_ENABLE(HANDLE) :使能外设
  • __HAL_PPP_DISABLE(HANDLE) :失能外设
  • __HAL_PPP_XXXX (HANDLE, PARAM) :指定外设的宏定义
  • _HAL_PPP_GET IT_SOURCE (HANDLE, __ INTERRUPT __)检查中断源

2. 三大回调函数

在 HAL 库的源码中,到处可见一些以__weak开头的函数,而且这些函数,有些已经被实现了,比如:

 
  1. __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)

  2. {

  3. /*Configure the SysTick to have interrupt in 1ms time basis*/

  4. HAL_SYSTICK_Config(SystemCoreClock/1000U);

  5. /*Configure the SysTick IRQ priority */

  6. HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U);

  7. /* Return function status */

  8. return HAL_OK;

  9. }

有些则没有被实现,例如:

 
  1. __weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)

  2. {

  3. /* Prevent unused argument(s) compilation warning */

  4. UNUSED(hspi);

  5. /* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file

  6. */

  7. }

所有带有__weak关键字的函数表示,就可以由用户自己来实现。如果出现了同名函数,且不带__weak关键字,那么连接器就会采用外部实现的同名函数。通常来说,HAL库负责整个处理和MCU外设的处理逻辑,并将必要部分以回调函数的形式给出到用户,用户只需要在对应的回调函数中做修改即可。HAL 库包含如下三种用户级别回调函数(PPP为外设名):

  1. 外设系统级初始化/解除初始化回调函数(用户代码的第二大部分:对于MSP的处理):HAL_PPP_MspInit()HAL_PPP_MspDeInit 例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函数中被调用,用来初始化底层相关的设备(GPIOs, clock, DMA, interrupt)
  2. 处理完成回调函数:HAL_PPP_ProcessCpltCallback(Process指具体某种处理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)。当外设或者DMA工作完成后时,触发中断,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用
  3. 错误处理回调函数:HAL_PPP_ErrorCallback例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)。当外设或者DMA出现错误时,触发终端,该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用。

参考文档及网文链接

ST-Description of STM32F4 HAL and LL drivers.pdf

ST-en.stm32_embedded_software_offering.pdf

https://mp.weixin.qq.com/s/GxvIvbxagSPvKuYSXcSVTw

转载自:嵌入式大杂烩 标准库和HAL库到底有什么不同?怎么选?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1301015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通信:mqtt学习网址

看这个网址&#xff1a;讲的很详细&#xff0c;后面补实战例子 第一章 - MQTT介绍 MQTT协议中文版 (gitbooks.io)https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

【Matlab算法】多维函数求解的基本概念

多维函数求解的基本概念 多维函数最优化问题最优化算法最优化问题的类型最优化算法的分类常用的多维函数求解方法结语 多维函数 多维函数是指定义在 R n \mathbb{R}^n Rn 上的函数&#xff0c;其中 n n n 是函数的维数。例如&#xff0c; f ( x , y ) x 2 y 2 f(x, y) x^…

DL Homework 10

习题6-1P 推导RNN反向传播算法BPTT. 习题6-2 推导公式(6.40)和公式(6.41)中的梯度 习题6-3 当使用公式(6.50)作为循环神经网络的状态更新公式时&#xff0c; 分析其可能存在梯度爆炸的原因并给出解决方法&#xff0e; 当然&#xff0c;因为我数学比较菜&#xff0c;我看了好半…

Vue3:表格单元格内容由:图标+具体内容 构成

一、背景 在Vue3项目中&#xff0c;想让单元格的内容是由 &#xff1a;图标具体内容组成的&#xff0c;类似以下效果&#xff1a; 二、图标 Element-Plus 可以在Element-Plus里面找是否有符合需求的图标iconfont 如果Element-Plus里面没有符合需求的&#xff0c;也可以在这…

掌握iText:轻松处理PDF文档-基础篇

关于iText iText是一个强大的PDF处理库&#xff0c;可以用于创建、读取和操作PDF文件。它支持PDF表单、加密和签署等操作&#xff0c;同时支持多种字体和编码。maven的中央仓库中的最新版本是5.X&#xff0c;且iText5不是完全免费的&#xff0c;但是基础能力是免费使用的&…

MyBatis 四大核心组件之 StatementHandler 源码解析

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

【设计模式--创建型--建造者模式】

建造者模式 建造者模式概述结构结果优缺点使用场景 将上述案例改为链式调用结果 建造者模式 概述 将一个复杂对象的构建与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 分离了部件的构建&#xff08;由Builder来负责&#xff09;和装配&#xff08;由Direct…

【HarmonyOS开发】拖拽动画的实现

动画的原理是在一个时间段内&#xff0c;多次改变UI外观&#xff0c;由于人眼会产生视觉暂留&#xff0c;所以最终看到的就是一个“连续”的动画。UI的一次改变称为一个动画帧&#xff0c;对应一次屏幕刷新&#xff0c;而决定动画流畅度的一个重要指标就是帧率FPS&#xff08;F…

[ 蓝桥杯Web真题 ]-冬奥大抽奖

目录 介绍 准备 目标 规定 思路 知识补充 解法参考 介绍 蓝桥云课庆冬奥需要举行一次抽奖活动&#xff0c;我们一起做一个页面提供给云课冬奥抽奖活动使用。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目录结构如下&#xff1a; ├──…

Qt设置类似于qq登录页面

头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QWindow> #include <QIcon> #include <QLabel> #include <QMovie> #include <QLineEdit> #include <QPushButton>QT_BEGIN_NAMESPACE namespace Ui { class…

python:五种算法(DBO、WOA、GWO、PSO、GA)求解23个测试函数(python代码)

一、五种算法简介 1、蜣螂优化算法DBO 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、粒子群优化算法PSO 5、遗传算法GA 二、5种算法求解23个函数 &#xff08;1&#xff09;23个函数简介 参考文献&#xff1a; [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…

RF射频干扰被动型红外传感器误判分析及整改事例

1.1 什么是红外传感 测量系统是以红外线为介质&#xff0c;探测可分成为光子和热探测器。 简洁原理就是利用产生的辐射与物质相互作用后呈现出来的物理效应就是它的基本原理。 1.2 红外按方式分类 &#xff08;1&#xff09;被动型红外&#xff1a;本身不会向外界辐射任何能量…

红队攻防实战之phpmyadmin-RCE集锦

世界上只有一种真正的英雄主义&#xff0c;那就是认清了生活的真相后&#xff0c;仍然热爱她 phpmyadmin远程代码执行漏洞 访问该页面&#xff0c;存在弱口令 爆破进入后发现该php版本以及phpmyadmin版本信息&#xff0c;该版本存在远程命令执行漏洞。 使用exp利用此漏洞&am…

建立个人学习观|地铁上的自习室

作者&#xff1a;向知 如果大家有机会来北京&#xff0c;可以来看看工作日早上八九点钟&#xff0c;15 号线从那座叫“顺义”的城市通向“望京”的地铁&#xff0c;你在那上面&#xff0c;能看到明明白白的&#xff0c;人们奔向梦想的模样。 一、地铁上的自习室 我在来北京之前…

RT-Thread学习笔记(六):RT_Thread系统死机日志定位

RT_Thread系统死机日志定位 一、RT_Thread系统死机日志定位二、Cortex-M3 / M4架构知识2.1 Cortex-M3 / M4架构概述2.2 寄存器用途 三、排查步骤 一、RT_Thread系统死机日志定位 RT-Thread 系统发生hardfault死机时&#xff0c;系统默认会打印出一系列寄存器状态帮助用户定位死…

[GPT]Andrej Karpathy微软Build大会GPT演讲(上)--GPT如何训练

前言 OpenAI的创始人之一,大神Andrej Karpthy刚在微软Build 2023开发者大会上做了专题演讲:State of GPT(GPT的现状)。 他详细介绍了如何从GPT基础模型一直训练出ChatGPT这样的助手模型(assistant model)。作者不曾在其他公开视频里看过类似的内容,这或许是OpenAI官方…

大数据技术7:基于StarRocks统一OALP实时数仓

前言&#xff1a; 大家对StarRocks 的了解可能不及 ClickHouse或者是远不及 ClickHouse 。但是大家可能听说过 Doris &#xff0c;而 StarRocks 实际上原名叫做 Doris DB &#xff0c;他相当于是一个加强版的也就是一个 Doris ,也就是说 Doris 所有的功能 StarRocks 都是有的&a…

2023/12/10总结

学习 WebSocket 一共四种方法&#xff0c;传递数据是要通过JSON格式传递 前端 onopen 在连接时 onmessage 收到消息时 通常携带参数 event &#xff0c;event.data 是消息 onerror 发生错误时 onclose 关闭连接时 发送消息 需要安装 vue-native-websocket 包 pnpm i vue-n…

【HarmonyOS开发】控件开发过程中,知识点记录

1、问题记录及解决方案 1.1 资源&#xff08;Icon&i18n&#xff09;问题 控件&#xff1a;只有一个JS文件&#xff0c;不会将任何资源型文件&#xff08;图片、字体、默认文字等&#xff09;打包到SO中。因此&#xff0c;当我们开发控件时&#xff0c;需要将需要使用到的资…

从零构建属于自己的GPT系列5:模型本地化部署(文本生成函数解读、模型本地化部署、文本生成文本网页展示、代码逐行解读)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Hugging Face 实战系列 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在PyCharm中进行 本篇文章配套的代码资源已经上传 从零构建属于自己的GPT系列1&#xff1a;数据预处理 从零构建属于自己的GPT系列2&#xff1a;模型训…