熟悉STM32的都知道ST官方提供了非常方便好用的库函数供用户使用,多数人都使用过STM32标准外设库,STM32Cube库(即HAL库),这个LL库是什么鬼,却从来没听说过。 好吧,我承认这个名字是我自己XJB取的。。。。。。。。
一、 初识LL 库最近william hill官网
发的STM32L476RGNucleo开发板到手了,准备学习玩耍,当然第一步就是下载资料,于是我下载STM32L4Cube 1.1.0版本,打开逐个查看,
好像和以前一样的,没什么特别嘛,于是准备开始开发。。。
3
|
|
|
|
熟悉HAL库的都知道,该库的文件几乎都是以stm32xxx_hal_xxx.h/.c命名的,为了和以前的标准库有个区分,上图中那些是什么鬼?????
|
|
|
|
|
前辈说,遇到问题赶紧查手册,于是我果断打开STM32L4Cube库的说明手册(UM1884):
原来这个东西叫做LowLayer APIs,作为英文渣渣表示实在不习惯洋里洋气的高大上名字,于是擅自把他叫做【STM32LL库】了(不服的你咬我啊)。
|
|
|
|
|
从这里看好像是说这个东东比HAL库更接近硬件,到底什么鬼,还不清楚。但是以前好像没见过这个东西啊,就算是STM32L4Cube的1.0.0版本中都没有。看看Cube发行历史:
[size=0.83em]image4.png (42.87 KB, 下载次数: 0)
下载附件
[color=rgb(153, 153, 153) !important]2015-11-1 18:48 上传
|
|
|
|
|
原来LL库是在1.1.0版本才加上的,大概意思就是: 1. LL APIs是寄存器级的编程,嗯,也就是我们常说的直接操作寄存器吧。 2. LL APIs适用于xxx等一大堆外设 3. LL APIs函数全部定义为staticinline函数,放在对应的头文件中,用户使用需要包含相关头文件 4. 参考这两个文档 看看LL库文件在Cube库中的位置,有20多个文件,全部以stm32l4xx_ll_xxx.h命名: STM32Cube_FW_L4_V1.1.0DriversSTM32L4xx_HAL_DriverInc
[size=0.83em]image5.png (51.15 KB, 下载次数: 0)
下载附件
[color=rgb(153, 153, 153) !important]2015-11-1 18:49 上传
STM32L4是面向低功耗市场的,同时不失高性能,功耗和性能往往是两个矛盾的东西,ST在硬件设计上想了各种办法来实现兼顾低功耗高性能(例如各种低功耗模式,LP外设等),而在软件层面,程序也讲求效率, LL库全是直接操作寄存器,直接操作寄存器往往效率较高,而且函数定义为内联函数,调用函数时不是堆栈调用,而是直接把函数的代码嵌入到调用的地方,利于提高代码相率,我想这也是ST在STM32L4系列中推出这个直接操作寄存器的LL库的原因之一吧。
|
|
|
|
|
二、 怎么使用LL库
英文渣渣就不翻译了,反正大概就是说 LL库更接近硬件层,对需要复杂上层协议栈的外设不适用,直接操作寄存器等等一大堆,到这里,可以看到它的使用方法: 1. 独立使用,该库完全独立实现,可以完全抛开HAL库,只用LL库编程完成。 2. 混合使用,和HAL库结合使用。 本人就常在编程的时候库函数和寄存器操作混合,所以觉得混合使用应该是不错的方式。 最后一段还说到该库不需要额外的内存资源来存储程序状态,数据指针等东西,所有的操作都通过直接修改外设的寄存器来完成。 下面是手册中对各个LL文件的描述:
|
|
|
|
|
[size=0.83em]image7.png (72.21 KB, 下载次数: 0)
下载附件
[color=rgb(153, 153, 153) !important]2015-11-1 18:54 上传
就是讲LL库由5部分组成:每个外设对应一个头文件组成一部分,以及系统相关的bus,cortex,utils,system四个部分。
|
|
|
|
|
前面提到,要使用LL库,需要包含对应头文件,各头文件之间有如下关系:
|
|
|
|
|
看来,我们编程的时候只需要#include某外设的头文件,就可以使用LL库了,但是同时,系统启动文件,初始化文件等一系列不能少,具体讲就是: stm32l4xx.h stm32l476xx.h system_stm32l4xx.h system_stm32l4xx.c startup_stm32l476xx.s
这几个文件,这在标准库,Cube库都不曾变过的铁律。
顺便分享一下STM32L4Cube 库 1.1.0版本和1.1.1版本的Pack 百度网盘下载地址:http://pan.baidu.com/s/1c0wjL5m
一楼二楼是具体使用方法。
|
|
|
|
|
三、 开发环境搭建—— 新建STM32LL库工程模板要开发首先要搭建开发环境,也就是简历所谓的工程模板。由于独立使用LL库的情况下不能使用Cube来建立工程,所以需要手动建立,这里的建工程方法和以前使用标准库建工程模板比较类似: 1. 准备相关文件: 新建一个文件夹用来放所有的文件 把需要用到的相关文件全部复制过来: 首先是上一节说到的几个必不可少的文件 stm32l4xx.h,stm32l476xx.h, system_stm32l4xx.h,system_stm32l4xx.c, startup_stm32l476xx.s 这些文件都在STM32Cube_FW_L4_V1.1.0DriversCMSISDeviceSTSTM32L4xx文件夹下,为了方便查找,我保持了和库文件中相同的文件夹结构。如果闲麻烦,可以把STM32Cube_FW_L4_V1.1.0DriversCMSIS整个文件夹复制过来,该文件夹下的其他文件如Include文件夹里面的也需要用到。 然后把所有LL库的文件复制过来,
|
|
|
|
|
STM32Cube_FW_L4_V1.1.0DriversSTM32L4xx_HAL_DriverInc文件夹下面所有stm32l4xx_ll_xxx.h命名的文件,也可以整个文件夹复制。
其实主要就是这两个文件夹下面相关的一些文件复制过来就好。当然一股脑全部粘贴过去也没什么影响,就是工程大一点(henduo)。 最后建立Inc,Src,MDK-ARM文件夹放头文件、源文件和工程文件,,stm32l4xx_it.h/.c这两个文件其实也可以不要,因为我们可以把中断函数处理函数放在任何地方都可以,main.c/main.h自己新建或者复制个模板。 所有文件,文件夹名字都可以随便取,这里只是为了保持和库、Cube建立的工程文件结构保持一致,所以取这些名字。
|
|
|
|
|
2. 好了,准备工作做好了,开始建工程 打开Keil MDK,新建一个工程选择型号STM32L4RG,在此之前需要确保已经安装了Keil STM32L4的Pack,不然新建工程的时候找不到对应的型号。 然后往工程里添加刚刚准备好的相关文件:
只需添加*.c和*.s文件,*.h文件设置头文件包含路径就行,也可以加到工程中方便查看。 接下来就是最重要的部分,工程设置: 有几个主要地方需要设置,
|
|
|
|
|
2. 设置文件包含路径 ..Inc //自己写的头文件路径 ..DriversCMSISInclude //关于Cortex的一些头文件 ..DriversCMSISDeviceSTSTM32L4xxInclude // stm32l4xx.h,stm32l476xx.h等文件 ..DriversSTM32L4xx_HAL_DriverInc //LL库文件的路径
|
|
|
|
|
3. 设置所用调试仿真器,使用Nucleo板载ST-Link 4. [size=0.83em]image15.png (61.13 KB, 下载次数: 0)
下载附件
[color=rgb(153, 153, 153) !important]2015-11-2 12:50 上传
差不多就这些了,如果芯片型号选择正确,pack安装正确,其他的工程已经默认设置好,编译一下无错误无警告,下面就是真正的开发,写代码了。
|
|
|
|
|
四、 LL库 第一个程序——点亮LED
首先点亮LED来验证下工程设置,代码编写是否正确。
1. 包含头文件 在main.c中
[color=rgb(51, 102, 153) !important]复制代码
在main.h中 - #include "stm32l4xx_ll_bus.h"
- #include "stm32l4xx_ll_rcc.h"
- #include "stm32l4xx_ll_system.h"
- #include "stm32l4xx_ll_utils.h"
- #include "stm32l4xx_ll_gpio.h"
- #include "stm32l4xx_ll_exti.h"
[color=rgb(51, 102, 153) !important]复制代码
包含需要使用的相关模块,要用那些就包含哪些,不知道用哪几个就全部包含进去。
|
|
|
|
|
2. 配置时钟 用这个LL库开发STM32跟用标准外设库很像,很类似,只不过封装层次更低。首先第一步要配置时钟,因为启动文件里没有默认配置,这和STM32F1早期的标准外设库一样,不过较高版本和后来的系列就可以不用了(如果使用默认配置的话),启动文件里默认配置了时钟。怎么配置这里就不细说了,看代码应该都能懂。 - __STATIC_INLINE void SystemClock_Config(void)
- {
- LL_FLASH_SetLatency(LL_FLASH_LATENCY_4);
- LL_RCC_HSI_Enable();
- while(LL_RCC_HSI_IsReady() != 1)
- {
- };
- LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_1, 10, LL_RCC_PLLR_DIV_2);
- LL_RCC_PLL_Enable();
- LL_RCC_PLL_EnableDomain_SYS();
- while(LL_RCC_PLL_IsReady() != 1) ;
- LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
- LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
- while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ;
- LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
- LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
- LL_Init1msTick(80000000);
- LL_SetSystemCoreClock(80000000);
- }
[color=rgb(51, 102, 153) !important]复制代码
函数声明为__STATIC_INLINE(即staticinline)是因为该库所有函数都是__STATIC_INLINE,前面提到这样做不需要额外的内存,因此这样最是为了保持这一点。
|
|
|
|
|
楼主,请教一个问题。从标准库下的程序移植到这个LL库和HAL库的找不到对应的库函数文件,有没有推荐的文档学习一下。
|
|
|
|
|
另外,你提到HAL和LL库可以同时使用,请问这两个库在一个工程里是include就行吗?我最近搞了一下HAL库,和标准库相比结构成员定义都发生了变化,移植之前的程序很麻烦。而且库有些函数也不见了。怎么解。
|
|
|
|
|