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

embedded/2024/11/13 2:19:47/

区别

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 maskGPIO_PIN_N = 0x1 << N,和GPIO 里ODR,IDR 等寄存器的操作对应,而LL 库把pin mask 左移了八位,然后在低8 位和高8 位附加了其他信息。

所以,首先,HAL 的pin_maskuint16_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 操作很简单,就自己写点寄存器代码喽。


http://www.ppmy.cn/embedded/136141.html

相关文章

Rust重写万物之——从头开始编写浏览器引擎

一款用 Rust 编写的全新“轮子”最近备受关注—— 因不满大公司垄断,Gosub 项目团队用 Rust 从头开始编写了一个新的浏览器引擎,目前 star 数已超过 3k。 Gosub 项目的诞生是因为不少用户对当前的 Web 浏览器现状感到不满。 尽管市面上有许多浏览器可供选择,但其中大多数…

简单介绍一下mvvm mvc mvp以及区别、历史

MVC&#xff08;Model - View - Controller&#xff09; 因MVC架构的灵活性&#xff0c;架构图形式很多&#xff0c;仅供参考 历史&#xff1a; MVC 是最早出现的软件架构模式之一&#xff0c;其历史可以追溯到 20 世纪 70 年代&#xff0c;最初被用于 Smalltalk - 80 环境。…

使用Python和Vosk库实现语音识别

使用Python和Vosk库实现语音识别 在人工智能和机器学习领域&#xff0c;语音识别技术正变得越来越重要。Python作为一种强大的编程语言&#xff0c;拥有丰富的库和框架&#xff0c;可以方便地实现语音识别功能。今天&#xff0c;我们将介绍如何使用Python中的SpeechRecognitio…

RabbitMQ 存储机制

一、消息存储机制 不管是持久化的消息还是非持久化的消息都可以被写入到磁盘。持久化的消息在到达队列时就被写入到磁盘&#xff0c;非持久化的消息一般只保存在内存中&#xff0c;在内存吃紧的时候会被换入到磁盘中&#xff0c;以节省内存空间。这两种类型的消息的落盘处理都…

批量归一化 BN(Batch Normalization) (减少重复学习 - 加速损失收敛) + 代码实现 ——笔记2.15《动手学深度学习》

目录 0. 前言 1. 训练深层网络 1.1 批量归一化的表达式 1.2 训练模式&预测模式 2. 批量规范化层 2.1 全连接层 2.2 卷积层 2.2.1 预测模式中的BN 3. 从零实现 (pytorch) 3.1 使用BN层的 LeNet 4. 简明实现 (调包) 5. 争议&#xff08;可解释性问题&#xff09;…

【笔记】前后端互通中前端登录无响应

后来的前情提要 &#xff1a; 后端的ip地址在本地测试阶段应该设置为localhost 前端中写cors的配置 后端也要写cors的配置 且两者的url都要为localhost 前端写的baseUrl是指定对应的后端的ip地址以及端口号 很重要 在本地时后端的IP的地址也必须为本地的 F12的网页报错是&a…

HTB:GreenHorn[WriteUP]

目录 连接至HTB服务器并启动靶机 使用nmap对靶机TCP端口进行开放扫描 再次使用nmap对这三个端口进行脚本、服务扫描 尝试先通过curl访问靶机80端口 将靶机IP与该域名写入hosts使DNS本地解析 使用浏览器访问greenhorn.htb 使用Wappalyzer插件查看该页面技术栈 尝试在sea…

【数据结构】快慢指针探秘:理解链表与数组中的环结构

在处理链表或数组时&#xff0c;我们经常需要在一次遍历中找到特定的位置或检测某种模式。这时&#xff0c;快慢指针技术就能成为强大的工具&#xff0c;尤其在链表面试题中。本文将详细介绍什么是快慢指针、它们的工作原理&#xff0c;并通过一些实际应用帮助你理解这种技巧。…