1000元做网站,免费app软件下载网站,科技经济导刊官网,苏州无锡市住房和城乡建设局网站1、STM32 之一 HAL库、标准外设库、LL库_ZCShou的博客-CSDN博客_ll库#xff08;仔细阅读#xff09;
2、STM32标准外设库、 HAL库、LL库 - King先生 - 博客园
3、STM32 之 HAL库_戈 扬的博客#xff08;仔细阅读#xff09;
4、STM32 LL 为什么比 HAL 高效#xff1…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:
具体代码区别
主函数 标准库 #include led.h #include delay.h #include key.h #include sys.h #include usart.h int main(void) { u16 t; u16 len; u16 times0; delay_init(); //延时函数初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级2位响应优先级 uart_init(115200); //串口初始化为115200 LED_Init(); //LED端口初始化 KEY_Init(); //初始化与按键连接的硬件接口 while(1) { if(USART_RX_STA0x8000) { lenUSART_RX_STA0x3fff;//得到此次接收到的数据长度 printf(\r\n您发送的消息为:\r\n\r\n); for(t0;tlen;t) { USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!SET);//等待发送结束 } printf(\r\n\r\n);//插入换行 USART_RX_STA0; }else { times; if(times%50000) { printf(\r\n精英STM32开发板 串口实验\r\n); printf(正点原子ALIENTEK\r\n\r\n); } if(times%2000)printf(请输入数据,以回车键结束\n); if(times%300)LED0!LED0;//闪烁LED,提示系统正在运行. delay_ms(10); } } } HAL: #include sys.h #include delay.h #include usart.h #include led.h #include key.h int main(void) { u8 len; u16 times0; HAL_Init(); //初始化HAL库 Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M delay_init(72); //初始化延时函数 uart_init(115200); //初始化串口 LED_Init(); //初始化LED KEY_Init(); //初始化按键 while(1) { if(USART_RX_STA0x8000) { lenUSART_RX_STA0x3fff;//得到此次接收到的数据长度 printf(\r\n您发送的消息为:\r\n); HAL_UART_Transmit(UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据 while(__HAL_UART_GET_FLAG(UART1_Handler,UART_FLAG_TC)!SET); //等待发送结束 printf(\r\n\r\n);//插入换行 USART_RX_STA0; }else { times; if(times%50000) { printf(\r\nALIENTEK 精英STM32开发板 串口实验\r\n); printf(正点原子ALIENTEK\r\n\r\n\r\n); } if(times%2000)printf(请输入数据,以回车键结束\r\n); if(times%300)LED0!LED0;//闪烁LED,提示系统正在运行. delay_ms(10); } } } 1 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库推出的同时也加入了很多第三方的中间件有RTOSUSBTCP / 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函数实现的 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate bound;//串口波特率 USART_InitStructure.USART_WordLength USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, USART_InitStructure); //初始化串口1
可以看到要初始化一个串口需要对六个位置进行赋值然后引用Init函数并且USART_InitStructure并不是一个全局结构体变量而是只在函数内部的局部变量初始化完成之后USART_InitStructure就失去了作用。
而在HAL库中同样是USART初始化结构体变量我们要定义为全局变量。
UART_HandleTypeDef UART1_Handler;右键查看结构体成员 typedef struct { 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 */ uint16_t TxXferCount; /*! UART Tx Transfer Counter */ uint8_t *pRxBuffPtr; /*! Pointer to UART Rx transfer Buffer */ uint16_t RxXferSize; /*! UART Rx Transfer size */ uint16_t RxXferCount; /*! UART Rx Transfer Counter */ 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 State; /*! UART communication state */ __IO uint32_t ErrorCode; /*! UART Error code */ }UART_HandleTypeDef;
我们发现与标准库不同的是该成员不仅包含了之前标准库就有的六个成员波特率数据格式等还包含过采样、发送或接收的数据缓存、数据指针、串口 DMA 相关的变量、各种标志位等等要在整个项目流程中都要设置的各个成员。
该UART1_Handler就被称为串口的句柄 它被贯穿整个USART收发的流程比如开启中断
HAL_UART_Receive_IT(UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);比如后面要讲到的MSP与Callback回调函数 void HAL_UART_MspInit(UART_HandleTypeDef *huart); 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为例在标准库中串口中断了以后我们要先在中断中判断是否是接收中断然后读出数据顺便清除中断标志位然后再是对数据的处理这样如果我们在一个中断函数中写这么多代码就会显得很混乱 void USART3_IRQHandler(void) //串口1中断服务程序 { u8 Res; if(USART_GetITStatus(USART3, USART_IT_RXNE) ! RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res USART_ReceiveData(USART3); //读取接收到的数据 /*数据处理区*/ } }
而在HAL库中进入串口中断后直接由HAL库中断函数进行托管 void USART1_IRQHandler(void) { HAL_UART_IRQHandler(UART1_Handler); //调用HAL库中断处理公用函数 /***************省略无关代码****************/ }
HAL_UART_IRQHandler这个函数完成了判断是哪个中断接收发送或者其他然后读出数据保存至缓存区顺便清除中断标志位等等操作。比如我提前设置了串口每接收五个字节我就要对这五个字节进行处理。在一开始我定义了一个串口接收缓存区 /*HAL库使用的串口接收缓冲,处理逻辑由HAL库控制接收完这个数组就会调用HAL_UART_RxCpltCallback进行处理这个数组*/ /*RXBUFFERSIZE5*/ u8 aRxBuffer[RXBUFFERSIZE];
在初始化中我在句柄里设置好了缓存区的地址缓存大小五个字节 /*该代码在HAL_UART_Receive_IT函数中初始化时会引用*/ huart-pRxBuffPtr pData;//aRxBuffer huart-RxXferSize Size;//RXBUFFERSIZE 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库等的定义接着其会根据定义的芯片信号包含具体的芯片型号的头文件 #if defined(STM32F205xx) #include stm32f205xx.h #elif defined(STM32F215xx) #include stm32f215xx.h #elif defined(STM32F207xx) #include stm32f207xx.h #elif defined(STM32F217xx) #include stm32f217xx.h #else #error Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file) #endif
紧接着其会包含 stm32f2xx_hal.h。
stm32f2xx_hal.hstm32f2xx_hal.c/h 主要实现HAL库的初始化、系统滴答相关函数、及CPU的调试模式配置stm32f2xx_hal_conf.h 该文件是一个用户级别的配置文件用来实现对HAL库的裁剪其位于用户文件目录不要放在库目录中。
接下来对于HAL库的源码文件进行一下说明HAL库文件名均以stm32f2xx_hal开头后面加上_外设或者模块名如stm32f2xx_hal_adc.c 库文件 stm32f2xx_hal_ppp.c/.h // 主要的外设或者模块的驱动源文件包含了该外设的通用API stm32f2xx_hal_ppp_ex.c/.h // 外围设备或模块驱动程序的扩展文件。这组文件中包含特定型号或者系列的芯片的特殊API。以及如果该特定的芯片内部有不同的实现方式则该文件中的特殊API将覆盖_ppp中的通用API。 stm32f2xx_hal.c/.h // 此文件用于HAL初始化并且包含DBGMCU、重映射和基于systick的时间延迟等相关的API 其他库文件 用户级别文件 stm32f2xx_hal_msp_template.c // 只有.c没有.h。它包含用户应用程序中使用的外设的MSP初始化和反初始化主程序和回调函数。使用者复制到自己目录下使用模板。 stm32f2xx_hal_conf_template.h // 用户级别的库配置文件模板。使用者复制到自己目录下使用 system_stm32f2xx.c // 此文件主要包含SystemInit()函数该函数在刚复位及跳到main之前的启动过程中被调用。 **它不在启动时配置系统时钟与标准库相反**。 时钟的配置在用户文件中使用HAL API来完成。 startup_stm32f2xx.s // 芯片启动文件主要包含堆栈定义终端向量表等 stm32f2xx_it.c/.h // 中断处理函数的相关实现 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指针之下。
多实例支持每个外设/模块实例都有自己的句柄。因此实例资源是独立的外围进程相互通信该句柄用于管理进程例程之间的共享数据资源。
下面以ADC为例 /** * brief ADC handle Structure definition */ typedef struct { ADC_TypeDef *Instance; /*! Register base address */ ADC_InitTypeDef Init; /*! ADC required parameters */ __IO uint32_t NbrOfCurrentConversionRank; /*! ADC number of current conversion rank */ DMA_HandleTypeDef *DMA_Handle; /*! Pointer DMA Handler */ HAL_LockTypeDef Lock; /*! ADC locking object */ __IO uint32_t State; /*! ADC communication state */ __IO uint32_t ErrorCode; /*! ADC Error code */ }ADC_HandleTypeDef;
从上面的定义可以看出ADC_HandleTypeDef中包含了ADC可能出现的所有定义对于用户想要使用ADC只要定义一个ADC_HandleTypeDef的变量给每个变量赋好值对应的外设就抽象完了。接下来就是具体使用了。
当然对于那些共享型外设或者说系统外设来说他们不需要进行以上这样的抽象这些部分与原来的标准外设库函数基本一样。例如以下外设
GPIOSYSTICKNVICRCCFLASH
以GPIO 为例对于HAL_GPIO_Init()函数其只需要GPIO地址以及其初始化参数即可。
1. 三种编程方式
HAL库对所有的函数模型也进行了统一。在HAL库中支持三种编程模式轮询模式、中断模式、DMA模式如果外设支持。其分别对应如下三种类型的函数以ADC为例 HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc); HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc); HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc); HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length); 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开头的函数而且这些函数有些已经被实现了比如 __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { /*Configure the SysTick to have interrupt in 1ms time basis*/ HAL_SYSTICK_Config(SystemCoreClock/1000U); /*Configure the SysTick IRQ priority */ HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0U); /* Return function status */ return HAL_OK; }
有些则没有被实现例如 __weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { /* Prevent unused argument(s) compilation warning */ UNUSED(hspi); /* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file */ }
所有带有__weak关键字的函数表示就可以由用户自己来实现。如果出现了同名函数且不带__weak关键字那么连接器就会采用外部实现的同名函数。通常来说HAL库负责整个处理和MCU外设的处理逻辑并将必要部分以回调函数的形式给出到用户用户只需要在对应的回调函数中做修改即可。HAL 库包含如下三种用户级别回调函数(PPP为外设名)
外设系统级初始化/解除初始化回调函数用户代码的第二大部分对于MSP的处理HAL_PPP_MspInit()和HAL_PPP_MspDeInit 例如__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函数中被调用用来初始化底层相关的设备GPIOs, clock, DMA, interrupt处理完成回调函数HAL_PPP_ProcessCpltCallbackProcess指具体某种处理如UART的Tx例如__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)。当外设或者DMA工作完成后时触发中断该回调函数会在外设中断处理函数或者DMA的中断处理函数中被调用错误处理回调函数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库到底有什么不同怎么选