STM32(7)-- GPIO输出,寄存器版

1. 硬件原理

要点亮D6的LED,需要控制PF6/PF7/PF8输出低电平

2. 软件实现

复制新建的寄存器工程,改名为寄存器点灯

2.1 实现寄存器映射

寄存器就是特殊的内存空间,可以通过指针操作访问寄存器。所以此处我们根据 STM32 的存储映射先定义好各个寄存器的地址,把这些地址定义都统一写在 stm32f4xx.h 文件中

查看参考手册

因为要控制PFx引脚,所以寄存器的地址都是基于GPIOF的外设基地址进行偏移

cpp 复制代码
//GPIOF外设基地址
#define GPIOF_BASE 0X40021400

根据偏移地址,定义出GPIOF各个寄存器的地址

cpp 复制代码
//GPIOF寄存器地址
#define GPIOF_MODER           GPIOF_BASE+0x00
#define GPIOF_OTYPER          GPIOF_BASE+0x04
#define GPIOF_OSPEEDR         GPIOF_BASE+0x08
#define GPIOF_PUPDR           GPIOF_BASE+0x0C
#define GPIOF_IDR             GPIOF_BASE+0x10
#define GPIOF_ODR             GPIOF_BASE+0x14
#define GPIOF_BSRR            GPIOF_BASE+0x18
#define GPIOF_LCKR            GPIOF_BASE+0x1C
#define GPIOF_AFRL            GPIOF_BASE+0x20
#define GPIOF_AFRH            GPIOF_BASE+0x24

因为 STM32 外设很多,为了降低功耗,每个外设都对应着一个时钟,在芯片刚上电的时候这些时
钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。
所有的 GPIO 都挂载到 AHB1 总线上,所以它们的时钟由 AHB1 外设时钟使能寄存器(RCC_AHB1ENR) 来控制
所以还需要定义RCC时钟的寄存器

cpp 复制代码
#define  RCC_BASE         0x40023800
#define  RCC_AHB1ENR      RCC_BASE+0x30

对于编译器来说,以上的宏定义都只是一个常量,以下操作肯定是不对的

RCC_BASE+0x30 |= (1<<6);

我们需要操作的是地址,所以需要对以上的定义进行强制类型转换,寄存器都是32位的,所以类型为 (unsigned int *)(RCC_BASE+0x30 ),告诉编译器这些数是数据地址,要操作此地址下的寄存器,还需要解引用,如下:

* (unsigned int *)(RCC_BASE+0x30 ) |= (1<<6);

那干脆直接寄存器映射的时候,都直接定义成此种形式,如下:

因为"总线基地址 / 外设基地址"在这一层只是"地址常量",

它们还不是"寄存器",所以不需要、也不应该做类型转换

如果在"基地址"阶段就类型转换,会怎样?

❌ 反例(不推荐)

#define GPIOH_BASE ((unsigned int *)0x40021400)

看起来好像也能用,但问题来了:

1️⃣ 失去"地址运算的语义"

GPIOH_BASE + 0x18

现在变成了:

(unsigned int *)0x40021400 + 0x18

⚠️ 指针运算单位变了!

  • 实际偏移 = 0x18 * sizeof(unsigned int)

  • 本来想加 0x18 字节

  • 结果加了 0x60 字节(32 位 MCU)

🔥 直接寄存器地址错位


2️⃣ 强迫所有寄存器类型一致

  • MODER / ODR / IDR 都是 uint32_t

  • 某些外设有 uint16_t / uint8_t 呢?

👉 基地址不该绑死数据类型

2.2 搭建框架

2.3 GPIO配置

2.3.1 GPIO模式

配置PF6为输出模式
在代码中,我们先把 GPIOH MODER 寄存器的 MODER6 对应位清 0,然后再向它赋值"01",从而使 GPIOF6 引脚设置成输出模式。

所以代码如下:

cpp 复制代码
//GPIOF MODER6 清空
GPIOF_MODER &= ~(0x03<<(2*6));
//PF6 输出模式
GPIOF_MODER |= (1<<2*6);

2.3.2 GPIO输出模式

代码如下:

cpp 复制代码
//GPIOF OTYPER6清空
GPIOF_OTYPER &= ~(1<<1*6);
//推挽模式
GPIOF_OTYPER |= (0<<1*6);

2.3.3 GPIO输出速度

cpp 复制代码
//GPIOF_OSPEEDR6清空
GPIOF_OSPEEDR &= ~(0x03<<2*6);
//2MHz
GPIOF_OSPEEDR |= (0<<2*6);

2.3.4 上下拉模式

cpp 复制代码
//清空
GPIOF_PUPDR &= ~(0x03<<2*6);
//上拉
GPIOF_PUPDR |= (1<<2*6);

2.3.5 控制引脚输出电平

cpp 复制代码
//控制引脚输出低电平
GPIOF_BSRR |= (1<<16<<6);
//控制引脚输出高电平
GPIOF_BSRR |= (1<<6);

2.4 开启时钟

cpp 复制代码
//开启GPIOF时钟
RCC_AHB1ENR |= (1<<5);

2.5 下载验证

相关推荐
HUI-47414 分钟前
EG1162|SOP‑16 高压同步整流降压控制器,最高 600V 输入,大电流 5V 输出优选
嵌入式硬件·硬件工程
POMAGTOR19 分钟前
【5G 通讯 POGOPIN 弹簧顶针连接器解决方案】
网络·嵌入式硬件·5g
ZLG_zhiyuan31 分钟前
测试方案 | 从“000”到“111”,高效完成100BASE-TX以太网眼图测试
单片机·嵌入式硬件
恒锐丰科技林技术员31 分钟前
EG1198:120V 宽压内置 MOS 降压 DC‑DC 电源芯片解析
经验分享·嵌入式硬件·硬件工程
数字新视界2 小时前
能耗管理系统的综合方案与优化实战解析
嵌入式硬件·物联网·数据中心·数据中心基础设施管理·dcim管理系统
深圳市恒锐丰科技杨生2 小时前
EG3525S 推挽 / 半桥 PWM 控制器|屹晶 EGmicro
嵌入式硬件·硬件工程
H_oRIZoN_2 小时前
Linux入门DAY39(51 单片机入门笔记|最小系统、位运算、处理器、数码管基础梳理)
单片机·嵌入式硬件
12.=0.2 小时前
【电学基础】
单片机·嵌入式硬件
开发笔记-阿牛2 小时前
嵌入式蓝牙开发:AC2005B 芯片技术解析 —— 省晶振设计、13dBm 射频与 Auracast 广播音响开发指南
人工智能·单片机·音视频
单片机仿真设计2 小时前
【proteus仿真】基于 STM32 单片机电梯运行状态监控设计(仿真图+程序)
stm32·单片机·嵌入式硬件·proteus