|
|
@@ -6,7 +6,6 @@ author: "尚硅谷研究院"
|
|
|
date: 2026-07-15
|
|
|
created: 2026-07-15
|
|
|
---
|
|
|
-
|
|
|
# GPIO寄存器详解与三步进化法
|
|
|
|
|
|
> **用生活理解**:GPIO 就像一排开关(每个引脚一个),你可以用程序控制每个开关输出高/低电平(控制 LED),也可以读取每个开关的状态(读取按键)。"三步进化法"就是操作这些开关的方式从"直接掰电线"进化到"用遥控器按按钮"再到"声控"的过程。
|
|
|
@@ -30,13 +29,13 @@ GPIO(General-purpose input/output,通用型输入输出)是 STM32 芯片
|
|
|
|
|
|
### GPIO的主要特点
|
|
|
|
|
|
-| 特性 | 说明 |
|
|
|
-|------|------|
|
|
|
-| 引脚电平 | 0~3.3V(FT 引脚兼容 5V) |
|
|
|
-| 翻转速度 | 最快 50MHz(2 个时钟周期) |
|
|
|
-| 上下拉 | 内置可配置上下拉电阻(约 40KΩ) |
|
|
|
-| 驱动能力 | 最大 ±25mA 拉/灌电流 |
|
|
|
-| 中断能力 | 每个引脚均可作为 EXTI 中断输入 |
|
|
|
+| 特性 | 说明 |
|
|
|
+| -------- | -------------------------------- |
|
|
|
+| 引脚电平 | 0~3.3V(FT 引脚兼容 5V) |
|
|
|
+| 翻转速度 | 最快 50MHz(2 个时钟周期) |
|
|
|
+| 上下拉 | 内置可配置上下拉电阻(约 40KΩ) |
|
|
|
+| 驱动能力 | 最大 ±25mA 拉/灌电流 |
|
|
|
+| 中断能力 | 每个引脚均可作为 EXTI 中断输入 |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -47,6 +46,7 @@ GPIO(General-purpose input/output,通用型输入输出)是 STM32 芯片
|
|
|
### 保护二极管
|
|
|
|
|
|
GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS(0V):
|
|
|
+
|
|
|
- 当输入电压高于 VDD+0.3V 时,上方二极管导通,将电压钳位到 VDD
|
|
|
- 当输入电压低于 VSS-0.3V 时,下方二极管导通,将电压拉高到 VSS
|
|
|
|
|
|
@@ -55,6 +55,7 @@ GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS
|
|
|
### 上下拉电阻
|
|
|
|
|
|
两个开关控制引脚内部的上下拉电阻(典型值 40KΩ):
|
|
|
+
|
|
|
- **上拉**:上开关闭合 → 引脚默认高电平
|
|
|
- **下拉**:下开关闭合 → 引脚默认低电平
|
|
|
- **浮空**:两个开关都断开 → 引脚电平不确定(高阻态)
|
|
|
@@ -63,6 +64,7 @@ GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS
|
|
|
### 施密特触发器(Schmitt Trigger)
|
|
|
|
|
|
施密特触发器是一个带**迟滞特性**的比较器电路:
|
|
|
+
|
|
|
- 输入信号从低到高:阈值约为 1.8V
|
|
|
- 输入信号从高到低:阈值约为 1.2V
|
|
|
- 这种**滞回特性**可以滤除信号上的噪声抖动,实现波形整形
|
|
|
@@ -82,35 +84,36 @@ GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS
|
|
|
|
|
|
## GPIO的8种工作模式
|
|
|
|
|
|
-| 模式 | 类型 | 用途 | P-MOS | N-MOS | 施密特触发器 | 上下拉 |
|
|
|
-|------|------|------|-------|-------|------------|-------|
|
|
|
-| 输入浮空 | 输入 | 按键、通信引脚 | 关闭 | 关闭 | 开 | 禁止 |
|
|
|
-| 输入上拉 | 输入 | 按键(默认高电平) | 关闭 | 关闭 | 开 | 上拉 |
|
|
|
-| 输入下拉 | 输入 | 按键(默认低电平) | 关闭 | 关闭 | 开 | 下拉 |
|
|
|
-| 模拟输入 | 输入 | ADC 采集 | 关闭 | 关闭 | **关闭** | 禁止 |
|
|
|
-| 通用推挽输出 | 输出 | LED、普通信号 | 交替 | 交替 | 开 | 禁止 |
|
|
|
-| 通用开漏输出 | 输出 | I2C 总线、电平转换 | 关闭 | 交替 | 开 | 禁止 |
|
|
|
-| 复用推挽输出 | 输出 | USART_TX、SPI_SCK | 交替 | 交替 | 开 | 禁止 |
|
|
|
-| 复用开漏输出 | 输出 | I2C_SCL/SDA | 关闭 | 交替 | 开 | 禁止 |
|
|
|
+| 模式 | 类型 | 用途 | P-MOS | N-MOS | 施密特触发器 | 上下拉 |
|
|
|
+| ------------ | ---- | ------------------ | ----- | ----- | -------------- | ------ |
|
|
|
+| 输入浮空 | 输入 | 按键、通信引脚 | 关闭 | 关闭 | 开 | 禁止 |
|
|
|
+| 输入上拉 | 输入 | 按键(默认高电平) | 关闭 | 关闭 | 开 | 上拉 |
|
|
|
+| 输入下拉 | 输入 | 按键(默认低电平) | 关闭 | 关闭 | 开 | 下拉 |
|
|
|
+| 模拟输入 | 输入 | ADC 采集 | 关闭 | 关闭 | **关闭** | 禁止 |
|
|
|
+| 通用推挽输出 | 输出 | LED、普通信号 | 交替 | 交替 | 开 | 禁止 |
|
|
|
+| 通用开漏输出 | 输出 | I2C 总线、电平转换 | 关闭 | 交替 | 开 | 禁止 |
|
|
|
+| 复用推挽输出 | 输出 | USART_TX、SPI_SCK | 交替 | 交替 | 开 | 禁止 |
|
|
|
+| 复用开漏输出 | 输出 | I2C_SCL/SDA | 关闭 | 交替 | 开 | 禁止 |
|
|
|
|
|
|
### 常见模式选择指导
|
|
|
|
|
|
-| 应用场景 | 推荐模式 |
|
|
|
-|----------|---------|
|
|
|
-| 驱动 LED | 推挽输出 |
|
|
|
-| 读取按键 | 上拉输入 或 下拉输入 |
|
|
|
+| 应用场景 | 推荐模式 |
|
|
|
+| -------- | ---------------------------- |
|
|
|
+| 驱动 LED | 推挽输出 |
|
|
|
+| 读取按键 | 上拉输入 或 下拉输入 |
|
|
|
| I2C 通信 | 复用开漏输出(需要外部上拉) |
|
|
|
-| 串口 TX | 复用推挽输出 |
|
|
|
-| 串口 RX | 浮空输入 或 上拉输入 |
|
|
|
-| ADC 采样 | 模拟输入 |
|
|
|
-| SPI 通信 | 复用推挽输出 |
|
|
|
-| PWM 输出 | 复用推挽输出 |
|
|
|
+| 串口 TX | 复用推挽输出 |
|
|
|
+| 串口 RX | 浮空输入 或 上拉输入 |
|
|
|
+| ADC 采样 | 模拟输入 |
|
|
|
+| SPI 通信 | 复用推挽输出 |
|
|
|
+| PWM 输出 | 复用推挽输出 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## GPIO的7个关键寄存器
|
|
|
|
|
|
每个 GPIO 端口有 7 个 32 位寄存器,基地址如下:
|
|
|
+
|
|
|
- GPIOA: `0x4001 0800`
|
|
|
- GPIOB: `0x4001 0C00`
|
|
|
- GPIOC: `0x4001 1000`
|
|
|
@@ -125,37 +128,40 @@ GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS
|
|
|
|
|
|
控制 **Pin0~Pin7** 的配置模式。
|
|
|
|
|
|
-| 位域 | 名称 | 说明 |
|
|
|
-|------|------|------|
|
|
|
-| 31:30 | CNF7[1:0] | Pin7 配置位 |
|
|
|
+| 位域 | 名称 | 说明 |
|
|
|
+| ----- | ---------- | ----------- |
|
|
|
+| 31:30 | CNF7[1:0] | Pin7 配置位 |
|
|
|
| 29:28 | MODE7[1:0] | Pin7 模式位 |
|
|
|
-| ... | ... | ... |
|
|
|
-| 3:2 | CNF0[1:0] | Pin0 配置位 |
|
|
|
-| 1:0 | MODE0[1:0] | Pin0 模式位 |
|
|
|
+| ... | ... | ... |
|
|
|
+| 3:2 | CNF0[1:0] | Pin0 配置位 |
|
|
|
+| 1:0 | MODE0[1:0] | Pin0 模式位 |
|
|
|
|
|
|
**MODE 位含义**:
|
|
|
-| MODE[1:0] | 模式 |
|
|
|
-|-----------|------|
|
|
|
-| 00 | 输入模式(复位状态) |
|
|
|
-| 01 | 输出,最大速度 10MHz |
|
|
|
-| 10 | 输出,最大速度 2MHz |
|
|
|
-| 11 | 输出,最大速度 50MHz |
|
|
|
+
|
|
|
+| MODE[1:0] | 模式 |
|
|
|
+| --------- | -------------------- |
|
|
|
+| 00 | 输入模式(复位状态) |
|
|
|
+| 01 | 输出,最大速度 10MHz |
|
|
|
+| 10 | 输出,最大速度 2MHz |
|
|
|
+| 11 | 输出,最大速度 50MHz |
|
|
|
|
|
|
**CNF 位含义(输入模式下,即 MODE=00)**:
|
|
|
-| CNF[1:0] | 模式 |
|
|
|
-|----------|------|
|
|
|
-| 00 | 模拟输入 |
|
|
|
-| 01 | 浮空输入(复位状态) |
|
|
|
-| 10 | 上拉/下拉输入 |
|
|
|
-| 11 | 保留 |
|
|
|
+
|
|
|
+| CNF[1:0] | 模式 |
|
|
|
+| -------- | -------------------- |
|
|
|
+| 00 | 模拟输入 |
|
|
|
+| 01 | 浮空输入(复位状态) |
|
|
|
+| 10 | 上拉/下拉输入 |
|
|
|
+| 11 | 保留 |
|
|
|
|
|
|
**CNF 位含义(输出模式下,即 MODE>00)**:
|
|
|
-| CNF[1:0] | 模式 |
|
|
|
-|----------|------|
|
|
|
-| 00 | 通用推挽输出 |
|
|
|
-| 01 | 通用开漏输出 |
|
|
|
-| 10 | 复用功能推挽输出 |
|
|
|
-| 11 | 复用功能开漏输出 |
|
|
|
+
|
|
|
+| CNF[1:0] | 模式 |
|
|
|
+| -------- | ---------------- |
|
|
|
+| 00 | 通用推挽输出 |
|
|
|
+| 01 | 通用开漏输出 |
|
|
|
+| 10 | 复用功能推挽输出 |
|
|
|
+| 11 | 复用功能开漏输出 |
|
|
|
|
|
|
> **配置示例**:PA0 推挽输出 50MHz → MODE0[1:0]=11, CNF0[1:0]=00 → CRL 低 4 位 = 0011 = 0x3
|
|
|
|
|
|
@@ -165,34 +171,34 @@ GPIO 引脚内部有两个保护二极管,分别连接到 VDD(3.3V)和 VSS
|
|
|
|
|
|
### 3. GPIOx_IDR — 端口输入数据寄存器(Address: 0x08)
|
|
|
|
|
|
-| 位 | 说明 |
|
|
|
-|----|------|
|
|
|
-| 31:16 | 保留 |
|
|
|
-| 15:0 | IDRy:读取 Pin y 的输入电平(0=低,1=高) |
|
|
|
+| 位 | 说明 |
|
|
|
+| ----- | ----------------------------------------- |
|
|
|
+| 31:16 | 保留 |
|
|
|
+| 15:0 | IDRy:读取 Pin y 的输入电平(0=低,1=高) |
|
|
|
|
|
|
### 4. GPIOx_ODR — 端口输出数据寄存器(Address: 0x0C)
|
|
|
|
|
|
-| 位 | 说明 |
|
|
|
-|----|------|
|
|
|
-| 31:16 | 保留 |
|
|
|
-| 15:0 | ODRy:写入 Pin y 的输出电平(0=低,1=高) |
|
|
|
+| 位 | 说明 |
|
|
|
+| ----- | ----------------------------------------- |
|
|
|
+| 31:16 | 保留 |
|
|
|
+| 15:0 | ODRy:写入 Pin y 的输出电平(0=低,1=高) |
|
|
|
|
|
|
### 5. GPIOx_BSRR — 端口位设置/清除寄存器(Address: 0x10)
|
|
|
|
|
|
-| 位 | 名称 | 功能 |
|
|
|
-|----|------|------|
|
|
|
-| 31:16 | BRy | 清除 ODR 的对应位(写 1 清 0) |
|
|
|
-| 15:0 | BSy | 设置 ODR 的对应位(写 1 置 1) |
|
|
|
+| 位 | 名称 | 功能 |
|
|
|
+| ----- | ---- | ------------------------------ |
|
|
|
+| 31:16 | BRy | 清除 ODR 的对应位(写 1 清 0) |
|
|
|
+| 15:0 | BSy | 设置 ODR 的对应位(写 1 置 1) |
|
|
|
|
|
|
> **特点**:BSRR 是**原子操作**,同时写低 16 位和高 16 位不会冲突。
|
|
|
> **对比**:`GPIOA->BSRR = (1<<0)` 相当于 `GPIOA->ODR |= (1<<0)`,但 BSRR 不需要"读-改-写"三步,不会被打断。
|
|
|
|
|
|
### 6. GPIOx_BRR — 端口位清除寄存器(Address: 0x14)
|
|
|
|
|
|
-| 位 | 说明 |
|
|
|
-|----|------|
|
|
|
-| 31:16 | 保留 |
|
|
|
-| 15:0 | BRy:清除 ODR 的对应位(写 1 清 0) |
|
|
|
+| 位 | 说明 |
|
|
|
+| ----- | ----------------------------------- |
|
|
|
+| 31:16 | 保留 |
|
|
|
+| 15:0 | BRy:清除 ODR 的对应位(写 1 清 0) |
|
|
|
|
|
|
相当于 BSRR 高 16 位的简化版。
|
|
|
|
|
|
@@ -215,6 +221,7 @@ LED1 连接 PA0,低电平点亮。
|
|
|

|
|
|
|
|
|
**原理**:
|
|
|
+
|
|
|
```
|
|
|
PA0=1(3.3V) → LED 灭(两端电压差接近 0)
|
|
|
PA0=0(0V) → LED 亮(电流经 VCC→限流电阻→LED→PA0→GND)
|
|
|
@@ -398,27 +405,27 @@ int main(void)
|
|
|
|
|
|
### 三步进化法总结
|
|
|
|
|
|
-| 阶段 | 写法 | 优点 | 缺点 |
|
|
|
-|------|------|------|------|
|
|
|
-| 进化0 | 裸地址 | 理解硬件本质 | 烦琐、不安全 |
|
|
|
-| 进化1 | 结构体宏 | 可读性好 | 仍可能覆盖其他位 |
|
|
|
-| 进化2 | 位运算 | 只改需要的位 | 需查手册知移位次数 |
|
|
|
-| **进化3** | **位宏** | **最安全、最可读** | 需要了解宏名含义 |
|
|
|
+| 阶段 | 写法 | 优点 | 缺点 |
|
|
|
+| --------------- | -------------- | ------------------------ | ------------------ |
|
|
|
+| 进化0 | 裸地址 | 理解硬件本质 | 烦琐、不安全 |
|
|
|
+| 进化1 | 结构体宏 | 可读性好 | 仍可能覆盖其他位 |
|
|
|
+| 进化2 | 位运算 | 只改需要的位 | 需查手册知移位次数 |
|
|
|
+| **进化3** | **位宏** | **最安全、最可读** | 需要了解宏名含义 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 核心位操作速查表
|
|
|
|
|
|
-| 操作 | 语法 | 解释 |
|
|
|
-|------|------|------|
|
|
|
-| 位置1 | `REG |= BITMASK;` | 按位或:其他位不变,指定位置1 |
|
|
|
-| 位清0 | `REG &= ~BITMASK;` | 按位与+取反:其他位不变,指定位清0 |
|
|
|
-| 位翻转 | `REG ^= BITMASK;` | 按位异或:指定位置1变0,0变1 |
|
|
|
-| 位移位 | `x << N` | 左移N位(相当于乘 2^N) |
|
|
|
-| 位移位 | `x >> N` | 右移N位(相当于除 2^N) |
|
|
|
-| 测试位 | `if (REG & BITMASK)` | 按位与:测试指定位是否为1 |
|
|
|
-| 连续位置1 | `REG |= (MASK << N)` | 将 N 位开始的 MASK 位写入1 |
|
|
|
-| 连续位清0 | `REG &= ~(MASK << N)` | 将 N 位开始的 MASK 位清0 |
|
|
|
+| 操作 | 语法 | 解释 |
|
|
|
+| --------- | ----------------------- | ---------------------------------- |
|
|
|
+| 位置1 | `REG \|= BITMASK;` | 按位或:指定位设为1,其他位不变 |
|
|
|
+| 位清0 | `REG &= ~BITMASK;` | 按位与+取反:其他位不变,指定位清0 |
|
|
|
+| 位翻转 | `REG ^= BITMASK;` | 按位异或:指定位置1变0,0变1 |
|
|
|
+| 位移位 | `x << N` | 左移N位(相当于乘 2^N) |
|
|
|
+| 位移位 | `x >> N` | 右移N位(相当于除 2^N) |
|
|
|
+| 测试位 | `if (REG & BITMASK)` | 按位与:测试指定位是否为1 |
|
|
|
+| 连续位置1 | `REG \|= (MASK << N)` | 将 N 位开始的 MASK 位置1 |
|
|
|
+| 连续位清0 | `REG &= ~(MASK << N)` | 将 N 位开始的 MASK 位清0 |
|
|
|
|
|
|
> **典型技巧**:`先清除后设置` 是嵌入式位操作的黄金法则。对连续位域(如 MODE[1:0])总是先 `&= ~MASK` 清空,再 `|= VALUE` 设置。
|
|
|
|
|
|
@@ -428,12 +435,12 @@ int main(void)
|
|
|
|
|
|
(本笔记以寄存器操作为主,无标准库/HAL 库函数)
|
|
|
|
|
|
-| 操作 | 寄存器方式 | 说明 |
|
|
|
-|------|----------|------|
|
|
|
-| 设置输出高 | `GPIOx->BSRR = (1<<n)` | 原子操作,推荐 |
|
|
|
-| 设置输出低 | `GPIOx->BRR = (1<<n)` | 原子操作,推荐 |
|
|
|
-| 读取输入 | `uint8_t val = (GPIOx->IDR >> n) & 1` | 提取第n位值 |
|
|
|
-| 翻转输出 | `GPIOx->ODR ^= (1<<n)` | 异或翻转 |
|
|
|
+| 操作 | 寄存器方式 | 说明 |
|
|
|
+| ---------- | --------------------------------------- | -------------- |
|
|
|
+| 设置输出高 | `GPIOx->BSRR = (1<<n)` | 原子操作,推荐 |
|
|
|
+| 设置输出低 | `GPIOx->BRR = (1<<n)` | 原子操作,推荐 |
|
|
|
+| 读取输入 | `uint8_t val = (GPIOx->IDR >> n) & 1` | 提取第n位值 |
|
|
|
+| 翻转输出 | `GPIOx->ODR ^= (1<<n)` | 异或翻转 |
|
|
|
|
|
|
## 常见问题与避坑
|
|
|
|