[STM32F1] STM32F1 LL 库和HAL 库在GPIO 上的区别

[复制链接]
951|13
Jiangxiaopi 发表于 2025-11-9 15:03 | 显示全部楼层 |阅读模式
区别
STM32F1 的LL 库在GPIO_PIN 定义上用了特别的处理,和HAL 库不一样,不能按HAL 库或标准库的惯例处理。LL 的GPIO 头文件中Pin 的定义如下:

/** @defgroup GPIO_LL_EC_PIN PIN
  * @{
  */
#define LL_GPIO_PIN_0                    ((GPIO_BSRR_BS0  << GPIO_PIN_MASK_POS) | 0x00000001U)  /*!< Select pin 0  */
#define LL_GPIO_PIN_1                    ((GPIO_BSRR_BS1  << GPIO_PIN_MASK_POS) | 0x00000002U)  /*!< Select pin 1  */
#define LL_GPIO_PIN_2                    ((GPIO_BSRR_BS2  << GPIO_PIN_MASK_POS) | 0x00000004U)  /*!< Select pin 2  */
#define LL_GPIO_PIN_3                    ((GPIO_BSRR_BS3  << GPIO_PIN_MASK_POS) | 0x00000008U)  /*!< Select pin 3  */
#define LL_GPIO_PIN_4                    ((GPIO_BSRR_BS4  << GPIO_PIN_MASK_POS) | 0x00000010U)  /*!< Select pin 4  */
#define LL_GPIO_PIN_5                    ((GPIO_BSRR_BS5  << GPIO_PIN_MASK_POS) | 0x00000020U)  /*!< Select pin 5  */
#define LL_GPIO_PIN_6                    ((GPIO_BSRR_BS6  << GPIO_PIN_MASK_POS) | 0x00000040U)  /*!< Select pin 6  */
#define LL_GPIO_PIN_7                    ((GPIO_BSRR_BS7  << GPIO_PIN_MASK_POS) | 0x00000080U)  /*!< Select pin 7  */
#define LL_GPIO_PIN_8                    ((GPIO_BSRR_BS8  << GPIO_PIN_MASK_POS) | 0x04000001U)  /*!< Select pin 8  */
// ...


而HAL 库的定义是:

/** @defgroup GPIO_pins_define GPIO pins define
  * @{
  */
#define GPIO_PIN_0                 ((uint16_t)0x0001)  /* Pin 0 selected    */
#define GPIO_PIN_1                 ((uint16_t)0x0002)  /* Pin 1 selected    */
#define GPIO_PIN_2                 ((uint16_t)0x0004)  /* Pin 2 selected    */
#define GPIO_PIN_3                 ((uint16_t)0x0008)  /* Pin 3 selected    */
#define GPIO_PIN_4                 ((uint16_t)0x0010)  /* Pin 4 selected    */
#define GPIO_PIN_5                 ((uint16_t)0x0020)  /* Pin 5 selected    */
#define GPIO_PIN_6                 ((uint16_t)0x0040)  /* Pin 6 selected    */

// ...




HAL 库的Pin 定义就是简单的pin mask,GPIO_PIN_N = 0x1 << N,和GPIO 里ODR,IDR 等寄存器的操作对应,而LL 库把pin mask 左移了八位,然后在低8 位和高8 位附加了其他信息。

所以,首先,HAL 的pin_mask 是uint16_t,而LL 要用uint32_t。如果自己写了函数接收Pin 作为参数,LL 库的类型可能会不匹配。其次,使用LL 库的Pin 定义时,需要一些额外处理,比如LL 里面给引脚设置高电平的函数:

__STATIC_INLINE void LL_GPIO_SetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
  WRITE_REG(GPIOx->BSRR, (PinMask >> GPIO_PIN_MASK_POS) & 0x0000FFFFU);
}


给BSRR 赋值之前,先要把Pin 右移8 位,也就是把pin mask 挪回去,还要把高8 位的东西去掉。对比HAL 库的写引脚电平函数:

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));
  assert_param(IS_GPIO_PIN_ACTION(PinState));

  if (PinState != GPIO_PIN_RESET)
  {
    GPIOx->BSRR = (uint32_t)GPIO_Pin;
  }
  else
  {
    GPIOx->BRR = (uint32_t)GPIO_Pin;
  }
}


直接用Pin 给BSRR 赋值,不需要额外处理。另外,STM32F0 的LL 库也是没有额外处理的:

__STATIC_INLINE void LL_GPIO_SetOutputPin(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
  WRITE_REG(GPIOx->BSRR, PinMask);
}



没去细究pin_mask 里附加的那些东西有什么用,估计是和F1 系列特殊的GPIO 模式寄存器有关。F0 和F4 系列GPIO 寄存器把输出模式、速度、上下拉之类的配置都用单独的寄存器实现,而F1 系列是全部塞进CRH 和CRL 寄存器里,各个模式之间相互耦合,某一个寄存器位的含义取决于其他寄存器位的值;这就给驱动代码编写造成了麻烦,比如,没办法临时将输出切换成输入,而不修改原来配置的输出速度。

影响
如果实际代码中,调用写GPIO 引脚函数时用的参数都是常量,比如:

#define LED_PIN    LL_GPIO_PIN_0
#define LED_PORT   GPIOB

// ...

// 拉高LED 引脚
LL_GPIO_SetOutputPin(LED_PORT, LED_PIN);



这种情况编译器有可能把右移和位运算都优化省略掉,变成常量操作。但是如果引脚参数不是常量,而是用变量传入的,比如结构体:

typedef struct {
        GPIO_TypeDef *port;
        uint32_t pin_mask;
} PinType;

// 多个引脚放进一个数组
const PinType pins[] = {{GPIOB, LL_GPIO_PIN_0}, {GPIOB, LL_GPIO_PIN_1}, {GPIOB, LL_GPIO_PIN_2}};

void 拉高所有引脚() {
        for(int i = 0; i< 3; ++i) {
                LL_GPIO_SetOutputPin(pins[i].port, pins[i].pin_mask);
        }
}



此时,对LL 函数的调用难以被优化,引脚操作的效率有所降低。当然效率低一点还是其次,重点是我的代码不兼容STM32F1 的LL 库这种引脚定义ヽ(≧□≦)ノ。除了STM32F1,其他单片机的库代码基本都是HAL 库那样定义的,所以我就默认pin mask 可以用(0x1 << N) 这种方式生成,然而拿这种pin mask 去调用LL 库的函数是没效果的。总之,建议别用STM32F1 的LL 库,实在用不了HAL,GPIO 操作很简单,就自己写点寄存器代码喽。
————————————————
版权声明:本文为CSDN博主「刻BITTER」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Etberzin/article/details/143611283

51xlf 发表于 2025-11-14 20:07 | 显示全部楼层
HAL 库追求 “跨型号兼容性、易用性、封装完整性”,LL 库追求 “轻量、高效、底层直接控制”。
everyrobin 发表于 2025-11-14 21:32 | 显示全部楼层
STM32CubeMX 支持 HAL
jimmhu 发表于 2025-11-14 21:49 | 显示全部楼层
HAL库
执行效率较低由于抽象层级高,函数调用开销较大,可能影响实时性。
适合原型开发快速验证功能,后续可优化为LL库或直接操作寄存器。
LL库
执行效率高接近寄存器操作,适合对性能要求严格的场景。
代码体积更小减少不必要的函数调用和条件编译,适合资源受限的STM32F1。
wengh2016 发表于 2025-11-15 16:14 | 显示全部楼层
均支持 STM32F1 GPIO 的所有核心功能
mickit 发表于 2025-11-15 16:36 | 显示全部楼层
LL 库        执行效率高,无冗余延迟
minzisc 发表于 2025-11-17 14:03 | 显示全部楼层
推荐使用HAL库以利用STM32CubeMX等工具提升开发效率
geraldbetty 发表于 2025-11-17 15:25 | 显示全部楼层
HAL 库通过 GPIO_InitTypeDef 结构体封装所有配置参数,调用 HAL_GPIO_Init() 一次性完成配置,无需关注寄存器细节。
sheflynn 发表于 2025-11-17 16:45 | 显示全部楼层
实现方式不同              
sesefadou 发表于 2025-11-17 17:49 | 显示全部楼层
两者的 API 设计风格、抽象程度、易用性、代码风格​ 存在显著差异。
beacherblack 发表于 2025-11-17 20:29 | 显示全部楼层

STM32F0的LL库:无额外处理,引脚定义和操作逻辑与HAL库类似
STM32F1的LL库:因GPIO寄存器模式耦合,需附加信息区分配置,导致操作复杂化
phoenixwhite 发表于 2025-11-17 21:41 | 显示全部楼层
从HAL库入手,快速掌握STM32开发流程。
iyoum 发表于 2025-11-18 22:27 | 显示全部楼层
LL库设计 接近HAL库              
ingramward 发表于 2025-11-19 16:29 | 显示全部楼层
优先选择HAL库              
您需要登录后才可以回帖 登录 | 注册

本版积分规则

71

主题

300

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部