熟悉HAL库的都知道,该库的文件几乎都是以stm32xxx_hal_xxx.h/.c命名的,为了和以前的标准库有个区分,上图中那些是什么鬼?????
前辈说,遇到问题赶紧查手册,于是我果断打开STM32L4Cube库的说明手册(UM1884):
原来LL库是在1.1.0版本才加上的,大概意思就是:
1. LL APIs是寄存器级的编程,嗯,也就是我们常说的直接操作寄存器吧。
2. LL APIs适用于xxx等一大堆外设
3. LL APIs函数全部定义为sta
ticinline函数,放在对应的头文件中,用户使用需要包含相关头文件
4. 参考这两个文档
看看LL库文件在Cube库中的位置,有20多个文件,全部以stm32l4xx_ll_xxx.h命名:
STM32Cube_FW_L4_V1.1.0DriversSTM32L4xx_HAL_DriverInc
三、 开发环境搭建—— 新建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文件设置头文件包含路径就行,也可以加到工程中方便查看。
接下来就是最重要的部分,工程设置:
有几个主要地方需要设置,
1. 定义使用的芯片型号 STM32L4xx
2. 设置文件包含路径
..Inc //自己写的头文件路径
..DriversCMSISInclude //关于Cortex的一些头文件
..DriversCMSISDeviceSTSTM32L4xxInclude // stm32l4xx.h,stm32l476xx.h等文件
..DriversSTM32L4xx_HAL_DriverInc //LL库文件的路径
3. 设置所用调试
仿真器,使用Nucleo板载ST-Link
4.
差不多就这些了,如果芯片型号选择正确,pack安装正确,其他的工程已经默认设置好,编译一下无错误无警告,下面就是真正的开发,写代码了。
趁中午休息把开发环境搭建好,晚上回去写两个简单例子,这篇帖子就算是完结了!
四、 LL库 第一个程序——点亮LED
首先点亮LED来验证下工程设置,代码编写是否正确。
1. 包含头文件
在main.c中
#include "main.h"
在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"
包含需要使用的相关模块,要用那些就包含哪些,不知道用哪几个就全部包含进去。
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);
- }
函数声明为__STATIC_INLINE(即staticinline)是因为该库所有函数都是__STATIC_INLINE,前面提到这样做不需要额外的内存,因此这样最是为了保持这一点。
3. 将LED对应引脚PA5配置为推挽输出模式
- __STATIC_INLINE void Configure_GPIO(void)
- {
- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
- LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT);
- LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_5, LL_GPIO_OUTPUT_PUSHPULL);
- LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_5, LL_GPIO_SPEED_LOW);
- LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_5, LL_GPIO_PULL_NO);
- }
4. PA5输出高电平,点亮LED
LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_5);
5. 最后需要在主函数中依次调用上诉函数,编译下载运行即可看到Nulceo上的绿色LED被成功点亮了。
- int main(void)
- {
- SystemClock_Config();
- Configure_GPIO();
- LL_GPIO_SetOutputPin(GPIOA, LL_GPIO_PIN_5);
- while (1)
- { ;
- }
- }
/*碎碎念:虽然已经不知道这是第几百还是第几千次做点灯实验,依然还是不厌其烦啊,LED不愧是被称为
单片机届的”Hello World!”*/
五、 添加其他程序功能
会了第一个程序,其他的就按照手册讲的写就行了,再举个简单的例子,利用按键中断模式来控制LED的闪烁频率。
只说下大概步奏,不详细说明:将按键连接的引脚配置为中断输入模式,开启中断,配置中断线,写中断服务函数来处理中断。
- __STATIC_INLINE void Configure_EXTI(void)
- {
- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
- LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
- LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_INPUT);
- LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_13, LL_GPIO_PULL_NO);
- NVIC_EnableIRQ(EXTI15_10_IRQn);
- NVIC_SetPriority(EXTI15_10_IRQn, 0);
- LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTC, LL_SYSCFG_EXTI_LINE13 );
- LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_13);
- LL_EXTI_EnableFallingTrig_0_31(LL_EXTI_LINE_13);
- }
在主函数调用Configure_EXTI()并添加代码:
- while (1)
- {
- LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
- if(i == 0)
- {
- LL_mDelay(100);
- }
- else if(i == 1)
- {
- LL_mDelay(500);
- }
- else
- {
- LL_mDelay(1000);
- }
- }
实现功能:每按一次按键,LED改变一次闪烁频率,间隔0.1s,0.5s,1s三种不同频率循环闪烁。
最后,这个LL库总体给人感觉是使用方法跟标准外设库很像,但也有不同,前面也有提到,它全是直接操作寄存器,程序效率一定程度上会比较高,但使用起来有时候不一定那么方便,可移植性也不好,所以把它和HAL库结合起来使用是最好的方式,在程序要求效率的地方就可以使用这些函数,而其他地方使用HAL库,这也是我们常常使用的方法,只不过以前是使用HAL库,需要的地方我们自己直接写操作寄存器,而现在可以换成使用这个【LL库】了。
上个IAR工程模板: