由于学习STM32的过程中逐渐接触了标准外设库(SPL)和HAL库,而这两者库存在着明显差异。于是此次通过对串口通信的SPL库与HAL库的STM32编码差异上来分析这两者的不同。
一、 简介
ST为开发者提供了非常方便的开发库。到目前为止,有标准外设库(STD库)、HAL库、LL库 三种。本文主要是针对标准外设库(STD库)、HAL库进行对比分析。
1.1 标准外设库
STM32标准外设库(Standard Peripherals Library,简称SPL)之前的版本也称固件函数库或简称固件库,是一个固件函数包,它由程序、数据结构和宏组成,包括了微控制器所有外设的性能特征。该函数库还包括每一个外设的驱动描述和应用实例,为开发者访问底层硬件提供了一个中间API,通过使用固件函数库,无需深入掌握底层硬件细节,开发者就可以轻松应用每一个外设。因此,使用固态函数库可以大大减少用户的程序编写时间,进而降低开发成本。每个外设驱动都由一组函数组成,这组函数覆盖了该外设所有功能。每个器件的开发都由一个通用API (application programming interface 应用编程界面)驱动,API对该驱动程序的结构,函数和参数名称都进行了标准化。STM32固件库详解
但是,标准外设库也是针对某一系列芯片而言的,没有可移植性。
1.2 HAL库
HAL库是ST公司目前主力推的开发方式,全称就是Hardware Abstraction Layer(抽象印象层)。库如其名,很抽象,一眼看上去不太容易知道他的作用是什么。它的出现比标准库要晚,但其实和标准库一样,都是为了节省程序开发的时期,而且HAL库尤其的有效,如果说标准库把实现功能需要配置的寄存器集成了,那么HAL库的一些函数甚至可以做到某些特定功能的集成。也就是说,同样的功能,标准库可能要用几句话,HAL库只需用一句话就够了。并且HAL库也很好的解决了程序移植的问题,不同型号的stm32芯片它的标准库是不一样的,例如在F4上开发的程序移植到F3上是不能通用的,而使用HAL库,只要使用的是相通的外设,程序基本可以完全复制粘贴,注意是相通外设,意思也就是不能无中生有,例如F7比F3要多几个定时器,不能明明没有这个定时器却非要配置,但其实这种情况不多,绝大多数都可以直接复制粘贴。是而且使用ST公司研发的STMcube软件,可以通过图形化的配置功能,直接生成整个使用HAL库的工程文件,可以说是方便至极,但是方便的同时也造成了它执行效率的低下,在各种论坛帖子真的是被吐槽的数不胜数。STM32 之 HAL库
1.3 性能比较
下图是官方给出的几个常用功能:GPIO 翻转、TIM PWM 输出、ADC DMA 数据采集和 DMA M2M ,使用不同库开发出来的运行结果,可供大家参考对比,做出符合自己开发项目需要的选择。
STM32使用HAL库、标准库、LL库和寄存器操作的性能对比
代码效率与移植性成反比的规律是明显的。但与 Cube HAL 相比, Cube LL 的效率优势还是很明显的,几乎和直
接写寄存器的效率相差无几。
而且目前 STM32cubeMX 已经开始支持直接生成使用 Cube LL 的工程,对于以后追求效率的开
发应用人员来说,非常值得推荐给大家使用。
二、 源代码
主要是在串口通信上进行源代码分析。
- 基于标准库的串口通信
int main(void)
{
u16 t;
u16 len;
u16 times=0;
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_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\n精英STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0){LED0=!LED0; LED1=!LED1;}//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
- 基于HAL库的串口通信
int main(void)
{
u8 len;
u16 times=0;
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_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
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_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK 精英STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
以上代码均来源正点原子。
三、 编码差异
- 标准库向串口输出的代码
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
- HAL库向串口输出的代码
HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000); //发送接收到的数据
可以明显看出该部分内容下,HAL库的代码量明显少于SPL库。而且一个大工程中,使用HAL库将明显减少了开发时间。
四、 总结
通过两种库对串口通信的编码方式差异可以简单看出,HAL库使用的代码量更少,但之前性能分析中HAL库却相比较于SPL库有着较大的ROM占用量,同时执行代码效率降低。
然而较高的可移植性,和适配STMcube软件成为了ST公司的主推的库。
综合上述,学习HAL库有较好的未来机会,但单片机的学习并不是简简单单的库函数学习,更多还是对底层知识的理解。
推荐文献阅读:
STM32教程(三)STM32 HAL固件库介绍及使用
STM32HAL库与标准库的区别
STM32标准库与HAL库比较
五、参考资料
[1]STM32 之 HAL库
[2]STM32固件库详解
[3]STM32使用HAL库、标准库、LL库和寄存器操作的性能对比