STM32——HAL库中寄存器地址名称映射分析

本文详细解释了HAL库如何通过结构体组织单片机寄存器,以STM32F1xx.h为例,介绍GPIOA基地址的计算过程,以及如何通过偏移获取寄存器地址。

前言

本篇文章是为了明白HAL 库中那些结构体是怎么与寄存器地址对应起来的。部分知识参考正点原子资料。

一、HAL库中寄存器地址名称映射分析

最根本的单片机开发就是直接操作寄存器的值,给这些位赋值,但是32单片机的寄存器太多,所以MDK就使用结构体将这些寄存器组织在一起。那么只要我们修改结构体的变量就可以修改对应寄存器的值。这些结构体的关联组织是在stm32f1xx.h 的头文件中完成的。
每个寄存器的地址都是偏移过来的,以GPIO为例,它的基地址是由 APB2 总线的基地址+GPIOA 在 APB2 总线上的偏移地址决定的。同理依次类推,我们便可以算出 GPIOA 基地址。
GIPO的结构体是根据GPIO的地址来定义的,GPIO的地址是从GPIO基地址偏移过来的,GPIO的基地址是从时钟线的基地址偏移过来的,时钟线的地址是由PERIPH基地址偏移的,如下所示:

typedef struct 
{ 
__IO uint32_t CRL; 
__IO uint32_t CRH; 
__IO uint32_t IDR; 
__IO uint32_t ODR; 
__IO uint32_t BSRR; 
__IO uint32_t BRR; 
__IO uint32_t LCKR; 
} GPIO_TypeDef;

我们跳转GPIOA的定义

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

这个宏是将GPIOA的基地址强转为结构体指针,我们继续跳转GPIOA_BASE

#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) 

这个宏是将APB2时钟线的地址偏移0x0800得到的GPIOA基地址,我们以此类推依次跳转

#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) 
#define PERIPH_BASE ((uint32_t)0x40000000)

最后得到始终总线的基地址
其他外设也是一样。

二、计算寄存器地址

计算寄存器的地址:(以GPIOA为例)
GPIOA 的寄存器的地址=GPIOA 基地址+寄存器相对 GPIOA 基地址的偏移值
这个偏移值在上面的寄存器地址映像表中可以查到。

我们对应好第一个地址,结构体的地址是连续的,而GPIOA的类型是结构体指针,每个成员变量偏移0x04,寄存器地址映像顺序与结构体中的IO口的成员变量的顺序是一样的,加上偏移,那么结构体中的成员变量的地址都一一对应。

后面关于MDK如何组织代码我们就不总结了,在这里简单说一下,对于变量、函数等我们选中右键GO TO DEFINED就可以跳转到定义处和引用处,然后根据定义范围来赋值或使用,其他的软件小妙招之后可以简单总结如debug、configuration等应用和配置。

<think>嗯,用户想了解如何使用STM32F103的HAL进行寄存器位操作。首先,我需要回忆一下HAL的基本结构和相关函数。HAL提供了硬件抽象层,但有时候直接操作寄存器可以更高效。不过,用户可能需要具体的示例,比如设置GPIO或者定时器的某个位。 首先,我得确认HAL中是否有直接操作寄存器的函数或者宏。记得HAL通常不推荐直接访问寄存器,但有时候通过结构体指针可以做到,比如GPIOA->BSRR这样的方式。需要提醒用户注意HAL版本和兼容性问题。 然后,用户可能需要配置GPIO的模式,比如输入输出,或者设置定时器的某个位。例如,配置GPIO的CRL或CRH寄存器来设置引脚模式,或者使用BSRR寄存器快速设置引脚电平。需要给出具体的代码示例,比如如何将PA5设置为推挽输出,并拉高或拉低电平。 还要考虑不同的外设,比如定时器,如何操作定时器的寄存器。例如,设置TIM2的ARR值或者使能更新中断。这时候需要用到__HAL_TIM_SET_AUTORELOAD这样的宏,或者直接访问TIM2->ARR。 另外,需要提到使用HAL时的注意事项,比如在修改寄存器前是否需要先初始化外设,如何确保操作不会与其他HAL函数冲突。可能还需要引用提供的参考文献,比如引用[2]中关于GPIO寄存器的结构体定义,或者引用[3]中关于定时器的信息。 最后,生成相关问题时,要考虑用户可能进一步想了解的内容,比如直接操作寄存器与使用HAL函数的优缺点,或者在不同场景下的最佳实践。确保问题覆盖常见的使用场景和潜在的问题。</think>### 使用STM32F103 HAL进行寄存器位操作的方法 #### 1. 寄存器位操作基本原理 在STM32 HAL开发中,直接操作寄存器位常用于需要精确控制硬件或提高执行效率的场景。HAL通过结构体映射寄存器地址,例如GPIO寄存器可通过$GPIOA->CRL$访问[^2]。操作关键步骤: - 确定目标寄存器的物理地址映射 - 使用位运算(与、或、移位)操作特定bit位 - 注意外设时钟使能状态 #### 2. GPIO寄存器位操作示例 ```c // 设置PA5为推挽输出模式(参考CRL寄存器配置[^2]) GPIOA->CRL &= ~(0x0F << (5*4)); // 清除原有配置 GPIOA->CRL |= (0x03 << (5*4)); // 输出模式,最大速度50MHz // 使用BSRR寄存器快速置位/复位(原子操作) GPIOA->BSRR = (1<<5); // 置位PA5 GPIOA->BSRR = (1<<(16+5));// 复位PA5 ``` #### 3. 定时器寄存器操作示例 以TIM2为例,直接修改自动重装载值: ```c // 设置TIM2自动重载值(32寄存器[^3]) TIM2->ARR = 9999; // 直接写入寄存器 // 使用HAL提供的宏更安全 __HAL_TIM_SET_AUTORELOAD(&htim2, 9999); // 使能更新中断 TIM2->DIER |= TIM_DIER_UIE; ``` #### 4. 寄存器操作注意事项 1. **时钟使能**:操作寄存器前必须确保外设时钟已使能 ```c __HAL_RCC_GPIOA_CLK_ENABLE(); ``` 2. **临界保护**:关键操作建议关闭中断 ```c HAL_NVIC_DisableIRQ(TIM2_IRQn); // 寄存器操作 HAL_NVIC_EnableIRQ(TIM2_IRQn); ``` 3. **位带操作**:可使用STM32位带特性实现原子操作 ```c #define GPIOA_ODR_5 (*((volatile uint32_t *)(0x42000000 + (0x4001080C-0x40000000)*32 + 5*4))) GPIOA_ODR_5 = 1; // 直接操作PA5输出 ``` #### 5. HAL寄存器操作最佳实践 - 优先使用HAL提供的宏定义(如`__HAL_TIM_ENABLE`) - 修改寄存器时保持`HAL_StatusTypeDef`返回值检查 - 混合使用HAL函数和寄存器操作时注意时序要求
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值