STM32作为大学生群体中“家喻户晓”的嵌入式MCU,每年吸引着无数大学生步入嵌入式行业,成为多少学生上手使用的第一块MCU芯片。
有很多第一次接触使用STM32的学生在Bilibili等平台选择网课教程的时候,总是会碰到一个问题:“学HAL库好”还是“学标准库好”。
这个问题一直是争论要点,话题不断。本期博主来分享一下我的见解,内容纯主观。
1、标准库和HAL库的区别
首先我们来讲一下标准库和HAL之间的差别,标准库是ST公司早期推出的外设封装库,它的设计逻辑非常接近底层寄存器,本质上就是把寄存器的操作进行了函数化命名,让开发者不再需要对着数据手册里的十六进制地址去写代码。
总有人在说:HAL库代码冗长,运行效率低下云云,下面让我们来比较一下二者的区别。
其核心结构是:定义GPIO句柄,配置引脚信息,引脚初始化。
其核心函数本质上是对寄存器的快速调用。标准库就像是给你提供了一套对应硬件零件的精密工具箱,虽然每个零件的作用都很清晰,但如果你想从STM32F1系列跳槽到F4系列,你会发现工具箱里的扳手规格全变了,必须重新熟悉新芯片的寄存器定义,这也就是我们常说的可移植性较差。
HAL库是硬件抽象层,它是ST为了解决芯片碎片化而推出的现代方案,它的逻辑不再是简单的寄存器映射,而是通过统一的API接口屏蔽了不同型号芯片之间的差异。
同样的以STM32F103的GPIO设定为例,HAL库的写法同标准库结构一样。
其核心函数一样是对寄存器方法的封装,只不过添加了一个if判断用户的输入方案,把标准库中的GPIO_SetBits和GPIO_ReSetBits封装成了同一个函数。
看到这里大家是不是觉得二者区别不大?流程也一样,核心函数也没什么区别?让我们把视角转入复杂功能实现。
在串口发送函数中,标准库的核心代码:USART_SendData仅接收一个参数:Data,操作也很简单,就是把Data的内容送入DR寄存器发送。
但是写入数据后,程序不能立即发送下一个字符。只有当SR标志位置1时,才表示上一个数据已经从数据寄存器转移到了移位寄存器,此时数据寄存器才有空间接收新的数据。
因此在标准库中发送串口函数需要自己去构建填入、循环判断、填入的过程。
STM32HAL库中的串口发送核心函数如图,虽然看上去很多一大段很冗长的样子,实际上拆解它的步骤:
if (huart->gState == HAL_UART_STATE_READY)
第一步,判断串口是否处在空闲状态。
/* Process Locked */__HAL_LOCK(huart);huart->ErrorCode = HAL_UART_ERROR_NONE;huart->gState = HAL_UART_STATE_BUSY_TX;/* Init tickstart for timeout managment */tickstart = HAL_GetTick();
锁定串口状态,获取当前时间。
while (huart->TxXferCount > 0U){huart->TxXferCount--;if (huart->Init.WordLength == UART_WORDLENGTH_9B){if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK){return HAL_TIMEOUT;}tmp = (uint16_t *) pData;huart->Instance->DR = (*tmp & (uint16_t)0x01FF);if (huart->Init.Parity == UART_PARITY_NONE){pData += 2U;}else{pData += 1U;}}else{if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK){return HAL_TIMEOUT;}huart->Instance->DR = (*pData++ & (uint8_t)0xFF);}}
这一段的逻辑就三点:发送、做数据移位,还有超时判断。
HAL库不仅仅是实现简单的发送,它实际上是构建了一套完整的通信管理流程,是工程师长年累月积累的所总结的安全的串口通信使用经验,在每一处可能导致系统崩溃的环节都加装了保险丝。
所以回到这个论点,HAL库所谓的冗长,其实是把方方面面都考虑了进去,超时保险,竞争保险,考虑了多个因素,配置了一套最安全的串口发送函数。
而标准库的发送函数,本质上就是把数据写入发送寄存器,缺乏了一系列的保护机制。在标准库的逻辑下,开发者必须自己充当设计者,手动编写大量的 while循环去盯着标志位,并在应用层自建互斥锁和计时器来防止程序跑飞。
先不提多少人能用HAL库真正的吃透MCU的性能,榨干到HAL库的安全机制显得冗余,到了那个程度不应该追求DMA的使用和直接编程寄存器的操作嘛?为什么要用标准库来说HAL库的运行低下呢?
其次ST还有LL库,LL库是替代标准库用来解决HAL库所谓的臃肿问题,在保持HAL库那种“跨系列接口统一”的同时,通过大量使用内联函数和直接寄存器读写来提高运行效率。
如图是定义在ll_uart.h中的内联函数,直接写在.h 头文件里的内联函数中。编译时,这些代码会直接嵌入到调用处。
所以标准库的性能优势真的很明显嘛?
2、守旧派和维新派
很多人吐槽HAL库不够底层,学HAL库学不到什么东西,都是CubeMX点点点。
首先,HAL库是HAL库,CubeMX是CubeMX,只是CubeMX支持HAL库不支持,不能光反对CubeMX就去反对。
而且CubeMX类编程是未来主流呀,各大国产厂商都陆陆续续推出了类似CubeMX的配置工具。以前一个工程师换一款国产芯片,可能需要花一周时间去啃几百页的数据手册,手动配置时钟树和初始化寄存器,中间只要有一个位配错,系统就跑不起来。
CubeMX工具的推出大大的节省了开发时间,提高开发效率本身就是好事,不能因为开发效率的提高而去反对吧。
就像如今AI的时代,总有人觉得靠AI什么都是虚的,坚决的反对AI;不应该是二者相结合,提高开发效率的同时能够做更多的事情学习到更多的新知识和嘛?
人和动物的区别就是人学会了使用工具。
3、早就落后的标准库
标准库已经过时了,这句话不是说说的。ST早在数年之前就已经停止了对标准库的维护,并且只支持ST的F0、F1、F3、F4和L1系列,这意味着你想使用ST其他的芯片不得不去学习HAL库,不得不使用HAL库编程。
在ST系列的25个大系列中,仅有6个系列支持标准库,其他系列均不支持标准库。如果一开始学习的是标准库,这代表着尝试使用ST更先进芯片的时候需要重新去理解HAL库。
4、大学生的需求究竟是什么?
回到开头,很多工程师似乎都不知道学生究竟需要什么,尤其是对于一个大一大二刚接触STM32的学生来说,究竟是他们认为的需要提高代码运行效率、国内公司很多用国产芯片这些原因重要。
还是快速去掌握这块芯片、无痛开发、更广的社区资源去完成课程设计、竞赛作品、项目目标重要,反倒是多少同学因为标准库的晦涩难懂而弃坑,总不能身边的同学都获奖、保研、拿奖学金的时候你对着他炫耀:“我懂底层,你不懂!”
我一直觉得工作有工作该学的东西,学生有学生该学的东西;我不觉得简历上一句坚持使用标准库开发能给你的简历贴多少金,但是我知道作为一名大学生,学习HAL库能保持自己的学习热忱,快速实现自己的开发目标。在大学的课余时间能有更多的精力去接触更多的新知识,新事物。同样的对AI等能大幅度提高生产力的工具,要学会的是去接纳,去利用AI提高自己,而不是去一味的反对;学会HAL库同时也多学习学习底层才是硬道理,而不是一味死板的坚持古法编程、坚持“祖宗之法不可变”。
欢迎大家在评论区留言探讨
1274