对于官方的STM32开发库,除了主推的HAL库之外还有一个LL(Low Layer)库,在下载的HAL开发包里面HAL库和LL库在一起共存着,LL库更接近硬件层,对于简单的外设可以直接使用,操作类似直接操作寄存器。LL库可以独立使用也可以和HAL一起混合使用,没有接触过的同学可以参考官方的例子,基本包含了各个外设的使用demo。记得前几年刚推出的时候用过一次后来也就没再玩过了,今天再再点个灯熟悉下LL库。 外设使用例子 LL库的特点是将很多外设的操作都封装成强制内联函数(static inline),这样的好处就是运行的效率更高,类似一种空间换时间的方案。 - __STATIC_INLINE void LL_AHB2_GRP1_EnableClock(uint32_t Periphs)
- {
- __IO uint32_t tmpreg;
- SET_BIT(RCC->AHB2ENR, Periphs);
- /* Delay after an RCC peripheral clock enabling */
- tmpreg = READ_BIT(RCC->AHB2ENR, Periphs);
- (void)tmpreg;
- }
复制代码除了大量的封装在外设头文件(stm32l4xx_ll_xx.h)中内联函数外,LL库对应的外设的模块驱动里面也提供了类似HAL库的初始化函数,但是内部实现还是调用的内联函数。比如下面的的IO口初始化函数 - ErrorStatus LL_GPIO_Init(GPIO_TypeDef *GPIOx, LL_GPIO_InitTypeDef *GPIO_InitStruct)
- {
- uint32_t pinpos = 0x00000000U;
- uint32_t currentpin = 0x00000000U;
- /* Check the parameters */
- assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
- assert_param(IS_LL_GPIO_PIN(GPIO_InitStruct->Pin));
- assert_param(IS_LL_GPIO_MODE(GPIO_InitStruct->Mode));
- assert_param(IS_LL_GPIO_PULL(GPIO_InitStruct->Pull));
- /* ------------------------- Configure the port pins ---------------- */
- /* Initialize pinpos on first pin set */
- pinpos = POSITION_VAL(GPIO_InitStruct->Pin);
- /* Configure the port pins */
- while (((GPIO_InitStruct->Pin) >> pinpos) != 0x00000000U)
- {
- /* Get current io position */
- currentpin = (GPIO_InitStruct->Pin) & (0x00000001U << pinpos);
- if (currentpin)
- {
- /* Pin Mode configuration */
- LL_GPIO_SetPinMode(GPIOx, currentpin, GPIO_InitStruct->Mode);
- if ((GPIO_InitStruct->Mode == LL_GPIO_MODE_OUTPUT) || (GPIO_InitStruct->Mode == LL_GPIO_MODE_ALTERNATE))
- {
- /* Check Speed mode parameters */
- assert_param(IS_LL_GPIO_SPEED(GPIO_InitStruct->Speed));
- /* Speed mode configuration */
- LL_GPIO_SetPinSpeed(GPIOx, currentpin, GPIO_InitStruct->Speed);
- }
- /* Pull-up Pull down resistor configuration*/
- LL_GPIO_SetPinPull(GPIOx, currentpin, GPIO_InitStruct->Pull);
- if (GPIO_InitStruct->Mode == LL_GPIO_MODE_ALTERNATE)
- {
- /* Check Alternate parameter */
- assert_param(IS_LL_GPIO_ALTERNATE(GPIO_InitStruct->Alternate));
- /* Speed mode configuration */
- if (POSITION_VAL(currentpin) < 0x00000008U)
- {
- LL_GPIO_SetAFPin_0_7(GPIOx, currentpin, GPIO_InitStruct->Alternate);
- }
- else
- {
- LL_GPIO_SetAFPin_8_15(GPIOx, currentpin, GPIO_InitStruct->Alternate);
- }
- }
- }
- pinpos++;
- }
- if ((GPIO_InitStruct->Mode == LL_GPIO_MODE_OUTPUT) || (GPIO_InitStruct->Mode == LL_GPIO_MODE_ALTERNATE))
- {
- /* Check Output mode parameters */
- assert_param(IS_LL_GPIO_OUTPUT_TYPE(GPIO_InitStruct->OutputType));
- /* Output mode configuration*/
- LL_GPIO_SetPinOutputType(GPIOx, GPIO_InitStruct->Pin, GPIO_InitStruct->OutputType);
- }
- return (SUCCESS);
- }
复制代码如果我们只使用LL库封装的内联函数的,只需要在工程里面加入一个 UTILS LL module driver.的C文件,这个文件主要是对应系统时钟的一些处理,其他的设置都可以使用头文件里面封装内联函数实现。 1、点灯 类似寄存器操作,点灯就是初始化IO口的时钟,然后设置对应的IO输出模式即可,其他的速度和上下拉也可以一步一步设置 - void LED_Init(void)
- {
- /* Enable the LED Clock */
- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
-
- LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT);
- LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_14, LL_GPIO_MODE_OUTPUT);
- LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT);
-
- }
复制代码闪烁LED 如下:都在使用的内联函数 - void LED_Blinking(void)
- {
- LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_7 | LL_GPIO_PIN_14);
- LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_7 );
-
- }
复制代码
2、串口输出 熟悉了LED初始化之后再来看下串口的,首先是对应的IO口的初始化 - LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOG);
-
- LL_GPIO_SetPinMode(GPIOG, LL_GPIO_PIN_7, LL_GPIO_MODE_ALTERNATE);
- LL_GPIO_SetAFPin_0_7(GPIOG, LL_GPIO_PIN_7, LL_GPIO_AF_8);
- LL_GPIO_SetPinSpeed(GPIOG, LL_GPIO_PIN_7, LL_GPIO_SPEED_FREQ_HIGH);
- LL_GPIO_SetPinOutputType(GPIOG, LL_GPIO_PIN_7, LL_GPIO_OUTPUT_PUSHPULL);
- LL_GPIO_SetPinPull(GPIOG, LL_GPIO_PIN_7, LL_GPIO_PULL_UP);
-
- LL_GPIO_SetPinMode(GPIOG, LL_GPIO_PIN_8, LL_GPIO_MODE_ALTERNATE);
- LL_GPIO_SetAFPin_8_15(GPIOG, LL_GPIO_PIN_8, LL_GPIO_AF_8);
- LL_GPIO_SetPinSpeed(GPIOG, LL_GPIO_PIN_8, LL_GPIO_SPEED_FREQ_HIGH);
- LL_GPIO_SetPinOutputType(GPIOG, LL_GPIO_PIN_8, LL_GPIO_OUTPUT_PUSHPULL);
- LL_GPIO_SetPinPull(GPIOG, LL_GPIO_PIN_8, LL_GPIO_PULL_UP);
复制代码然后是时钟源的初始化 - /* (2) Enable USART peripheral clock and clock source ***********************/
-
- LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
- LL_PWR_EnableVddIO2();
-
- LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1);
-
- /* HSI configuration and activation */
- LL_RCC_HSI_Enable();
- while(LL_RCC_HSI_IsReady() != 1)
- {
- };
-
- /* Set clock source */
- LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_HSI);
复制代码最后就是串口的的参数设置和使能了 - /* TX/RX direction */
- LL_LPUART_SetTransferDirection(LPUART1, LL_LPUART_DIRECTION_TX_RX);
- /* 8 data bit, 1 start bit, 1 stop bit, no parity */
- LL_LPUART_ConfigCharacter(LPUART1, LL_LPUART_DATAWIDTH_8B, LL_LPUART_PARITY_NONE, LL_LPUART_STOPBITS_1);
- /* No Hardware Flow control */
- /* Reset value is LL_USART_HWCONTROL_NONE */
- // LL_USART_SetHWFlowCtrl(LPUART1, LL_USART_HWCONTROL_NONE);
- /* Set Baudrate to 9600 using HSI frequency set to HSI_VALUE */
- LL_LPUART_SetBaudRate(LPUART1, HSI_VALUE,LL_LPUART_PRESCALER_DIV1, 9600);
- /* Enable LPUART1 **********************************************************/
- LL_LPUART_Enable(LPUART1);
复制代码大致的套路和寄存器有点类似,想要使用LL库要对外设的各个初始化流程比较熟悉,没有接触过的可以先参考官方提供的例子。 最后是mian函数 - int main(void)
- {
- SystemClock_Config();
- LPUART1_Init();
- LED_Init();
- while (1)
- {
- LED_Blinking();
- printf("Hi,I am creep!!!\n");
- LL_mDelay(1000);
- }
- }
复制代码下载之后可以看到LED闪烁及串口输出如下:
测试代码: |