| 
 
        对于官方的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闪烁及串口输出如下: 
 测试代码: |