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

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库到底有什么不同?怎么选?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文档或书籍扫描为 PDF:ScanPapyrus Crack

ScanPapyrus 可让您快速轻松地将文档或书籍扫描为 PDF&#xff0c;批处理模式使扫描过程快速高效&#xff0c;自动处理书籍并将其拆分为单独的页面 用于快速扫描文档、书籍或打印照片的扫描仪软件 快速扫描文档 使用此扫描仪软件&#xff0c;您无需在扫描仪和计算机之间来回移动…

架构LNMP

目录 1.安装Nginx服务 2.安装 MySQL 服务 3.安装配置 PHP 解析环境 4.部署 Discuz&#xff01;社区论坛 Web 应用 1.安装Nginx服务 实验准备 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装依赖包 yum -y install pcre-devel zlib-devel gcc…

【Python】Selenium自动化测试框架

设计思路 本文整理归纳以往的工作中用到的东西&#xff0c;现汇总成基础测试框架提供分享。 框架采用python3 selenium3 PO yaml ddt unittest等技术编写成基础测试框架&#xff0c;能适应日常测试工作需要。 1、使用Page Object模式将页面定位和业务操作分开&#xff0…

Gilisoft Video Editor——迈出剪辑的第一步

今天博主分享的是又一款剪辑软件——视频剪辑手&#xff08;GiliSoft Video Editor&#xff09;&#xff0c;对剪辑视频感兴趣的小伙伴千万不要错过。这是一款专门用于视频剪辑的软件&#xff0c;功能比较简单&#xff0c;相比于专业的pr是比不了的&#xff0c;但是制作一些简单…

C/C++ 编程规范总结

目录 前言 一、编程规范的作用 二、规范的三种形式 三、规范的内容 1. 基本原则 原则1-1 原则1-2 原则1-3 原则1-4 原则1-5 原则1-6 原则1-7 2. 布局 规则2-1-1 规则2-1-2 规则2-1-3 规则2-1-4 规则2-1-5 规则2-1-6 规则2-2-1 规则2-2-2 规则2-2-3 建议2…

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

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

pWnOS v2.0

该靶机绑定了静态IP地址 10.10.10.100&#xff0c;所以这里需要修改我们的网络配置&#xff01;整个网段修改为10.10.10.0/24 信息收集 主机存活探测 arp-scan -l 端口信息探测 nmap -sT --min-rate 10000 -p- 10.10.10.100 &#xff08;只开放了22 80端口&#xff09; 服务…

2023-12-10 LeetCode每日一题(爬楼梯)

2023-12-10每日一题 一、题目编号 70. 爬楼梯二、题目链接 点击跳转到题目位置 三、题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 示例 2&#xff1a; 提…

【Python】手把手教你用tkinter设计图书管理登录UI界面(三)

上一篇&#xff1a;【Python】手把手教你用tkinter设计图书管理登录UI界面&#xff08;二&#xff09;-CSDN博客 下一篇&#xff1a; 紧接上一篇文章&#xff0c;继续完善项目功能&#xff1a;用户登录。由于老王的注册部分有亿点点复杂&#xff0c;还没完成&#xff0c;但是…

期末速成数据库极简版【分支循环函数】(4)

目录 全局变量&局部变量 局部变量定义declare 局部变量赋值select 局部变量赋值select 【1】分支结构IF 【2】分支结构CASE 简单CASE语句 搜索CASE语句 【3】循环结构While 【4】系统函数 常用字符串函数 时间函数 【5】自定义函数—标量函数 函数创建 函…

oops-framework框架 之 Excel转Json

引擎&#xff1a; CocosCreator 3.8.0 环境&#xff1a; Mac Gitee: oops-plugin-excel-to-json 注&#xff1a; 作者dgflash的oops-framework框架QQ群&#xff1a; 628575875 配置 作者dgflash在oops-framework的框架中&#xff0c;提供了关于Excel数据表转换为Json和TypeSc…

typora中显示除号的问题

问题 在latex中“除号&#xff08; \div &#xff09;” 通常用 \div。但在typora中写数学公式时&#xff0c;却发现 “除号” 如果使用 \div 并没有显示为 “ \div ”&#xff0c;而是 “ ∇ ⋅ \nabla \cdot ∇⋅ ”。 原因 typora中&#xff0c;\div 显示为 ∇ ⋅ \…

Html转PDF,前端JS实现Html页面导出PDF(html2canvas+jspdf)

Html转PDF&#xff0c;前端JS实现Html页面导出PDF&#xff08;html2canvasjspdf&#xff09; 文章目录 Html转PDF&#xff0c;前端JS实现Html页面导出PDF&#xff08;html2canvasjspdf&#xff09;一、背景介绍二、疑问三、所使用技术html2canvasjspdf 四、展示开始1、效果展示…

Java第21章网络通信

网络程序设计基础 网络程序设计编写的是与其他计算机进行通信的程序。Java 已经将网络程序所需要的元素封 装成不同的类&#xff0c;用户只要创建这些类的对象&#xff0c;使用相应的方法&#xff0c;即使不具备有关的网络支持&#xff0c;也可 以编写出高质量的网络…

pyinstaller 常用命令参数

PyInstaller是一个用于将Python程序打包成独立的可执行文件的工具。它可以将Python代码和所有依赖的库、资源文件等打包成一个单独的可执行文件&#xff0c;方便在不安装Python解释器的环境中运行。PyInstaller提供了许多参数&#xff0c;用于配置打包过程和生成的可执行文件的…

NSS [NSSCTF 2022 Spring Recruit]babyphp

NSS [NSSCTF 2022 Spring Recruit]babyphp 考点&#xff1a;PHP特性 开局源码直接裸奔 <?php highlight_file(__FILE__); include_once(flag.php);if(isset($_POST[a])&&!preg_match(/[0-9]/,$_POST[a])&&intval($_POST[a])){if(isset($_POST[b1])&&…

java--Date、SimpleDateFormat时间类,JDK8之前的

1.Date 代表的是日期和时间 2.SimpleDateFormat 代表简单日期格式化&#xff0c;可以用来把日期对象、时间毫秒值格式化成我们想要的形式。 3.时间格式常见符号 4.SimpleDateFormat解析字符串时间成为日期对象

Redis之IO多路复用模型

Redis之IO多路复用模型 多路复用要解决的问题 解决同步阻塞IO模型下大量线程创建导致资源的浪费问题 同步阻塞IO模式的特点就是用一个进程来处理一个网络连接(一个用户请求)&#xff0c;比如一段典型的示例代码如下。 直接调用 recv 函数从一个 socket 上读取数据。 int main…

语义分割 简介及数据集简介

参考文章 MS COCO数据集介绍以及pycocotools简单使用-CSDN博客

Linux7安装tomcat9.0.83教程

1.下载tomcat.tar.gz包 地址&#xff1a;Apache Tomcat - Apache Tomcat 9 Software Downloads 2.将包上传到linux服并解压 cd /home/local # 跳转到上传包的目录 tar -zxvf apache-tomcat-9.0.83.tar.gz # 解压包 mv apache-tomcat-9.0.83 tomcat # 重命名目录为tomcat cp …