1、什么是GPIO
GPIO(General Purpose Input/Output通用输入输出 )
通用输入输出 的缩写,是微控制器(MCU)或处理器中用于与外部设备或电路进行交互的 基本硬件接口。
如果某一个引脚支持GPIO功能, 则可以将其配置为输入或者输出.
- 数字输入:接收高电平(1)或低电平(0)信号。
- 典型如按钮输入, 将按钮连接到 GPIO 输入引脚,微控制器可以读取按钮按下或松开的状态。
- 输出模式:输出高电平(1)或低电平(0)信号。
- 典型的LED控制, 将 LED 连接到 GPIO 输出引脚,通过设置引脚的电平来控制 LED 的开关。
2、什么是GPIO组
STM32 的 GPIO 端口通常命名为 GPIOA、GPIOB、GPIOC … 依次类推,每个端口最多包含 16 个引脚,编号从 0 到 15:
- GPIOA: PA0 ~ PA15
- GPIOB: PB0 ~ PB15
- GPIOC: PC0 ~ PC15
- …
并非所有组的所有编号都可用, 需要查看手册

我的来自stm32F103VET6的原理图
3、GPIO的基本结构

4、GPIO位结构

5、GPIO八种工作模式
输入模式
输入浮空(Floating Input)
特点:引脚无内部上拉或下拉电阻,仅作为输入,悬空时状态不确定,容易受到干扰。
适用场景:需要外部电路(如 按键、传感器)来确定电平,或用于复用功能(如 USART RX)。
输入上拉(Input Pull-Up)**
- 特点:内部连接 上拉电阻,默认状态为 高电平(1),外部需要拉低才能改变状态。
- 适用场景:按键检测(常见的 低电平触发按键)。
输入下拉(Input Pull-Down)
- 特点:内部连接 下拉电阻,默认状态为 低电平(0),外部需要拉高才能改变状态。
- 适用场景:按键检测(常见的 高电平触发按键)。
模拟输入(Analog Input)
- 特点:引脚连接到 ADC(模数转换器),无数字输入特性,可接收 模拟信号。
- 适用场景:ADC(模拟传感器、电位器) 输入信号。
输出模式
开漏输出(Open-Drain Output)
特点:只能输出 低电平(0) 或 高阻态(Hi-Z),需要外部上拉电阻才能输出高电平(1)。
适用场景:适用于多个设备共享信号线。
- I2C 通信(总线仲裁机制需要开漏)。
- 驱动大电流外设(如 LED、继电器,上拉到 5V 或更高电压)。
推挽式输出(Push-Pull Output)
特点:可以直接输出 高电平(1)或低电平(0),不需要额外的上拉电阻,驱动能力强。
适用场景:
- 普通数字信号输出(控制 LED、继电器)。
- 高速信号输出(如 SPI、USART TX)。
复用模式
推挽式复用功能(Alternate Function Push-Pull)
- 特点 :引脚用于 外设功能(如 USART、SPI、I2C),输出方式为 推挽模式,能提供稳定的高/低电平。
- 适用场景:USART TX、SPI、PWM 输出。
开漏复用功能(Alternate Function Open-Drain)
- 特点:引脚用于 外设功能,输出方式为 开漏模式,通常需要 外部上拉。
- 适用场景:
- I2C(SDA、SCL) 需要 开漏 以支持多主机通信。
- 某些低功耗通信 场景。
| 模式 | 默认状态 | 是否需要外部电阻 | 主要用途 |
|---|---|---|---|
| 输入浮空 | 悬空 | 可能需要 | 接受外部信号,无内部上拉/下拉 |
| 输入上拉 | 高电平(1) | 无 | 按键检测(低电平触发) |
| 输入下拉 | 低电平(0) | 无 | 按键检测(高电平触发) |
| 模拟输入 | 模拟信号 | 无 | ADC 采样 |
| 开漏输出 | 高阻 | 需要上拉电阻 | I2C 总线、驱动高电压负载 |
| 推挽输出 | 0/1 | 无 | 直接驱动 LED、继电器等 |
| 推挽复用 | 0/1 | 无 | SPI、USART TX、PWM |
| 开漏复用 | 高阻 | 需要上拉电阻 | I2C(SDA/SCL) |
6、GPIO相关寄存器
1. 端口配置低寄存器GPIO[x]_CRL和端口配置高寄存器GPIO[x]_CRH, Config Register High和Config Register Low)


bit位说明:
- 每四个bit位决定一个端口的状态
- 每个寄存器共32bit, 可以决定8个端口的状态
- 每个端口组需要两个寄存器来配置, 也就是每个端口组都有一个
CRL和CRH寄存器 - 寄存器为
RW权限
低两位**
MODE[y](mode=模式)**决定输入和输出状态, 以及输出速率(速率越高, 功耗越高, 波形越不平滑)00: 输入01: 输出10MHz10: 输出2MHz11: 输出50MHz
高两位**
CNF[y](CNF=config配置)**决定具体模式- 输入模式
00: 模拟输入01: 输入浮空10: 上拉下拉11: 保留
- 输出模式
00: 通用推挽01: 通用开漏10: 复用推挽11: 复用开漏
- 输入模式
2. 端口输入数据寄存器(GPIO[x]_IDR)

- 说明:
- 每一个寄存器bit记录了一个端口的输入状态
- 每个端口组需要最多16个bit, 因此只需要
IDR的低16bit就可以了 - 寄存器为
R权限
bit值为1说明输入为高bit值为0说明输入为低
3. 端口输出数据寄存器GPIO[x]_ODR, Output Data Register

- 同输入类似,
RW权限
4. 端口位设置/清除寄存器GPIO[x]_BSRR Bit Set Reset Register

- 同样可以实现拉高拉低输出的效果, 但是权限是
W - 低16bit写入
1, 会拉高某个引脚 - 高16bit写入
1, 会拉低某个引脚 - 写入
0, 不会影响其他引脚
5. 端口位清除寄存器GPIO[x]_BRR, Bit Reset Regsiter

- 同
BSRR, 权限同样是W, 但是只能拉低 - 低16bit写入
1, 会拉低某个引脚 - 低16bit写入
0, 不起作用 - 高16bit不起作用
相关术语:
Set= 将输出设置为高电平 = 拉高 = 置位 = 置为1 = 输出1 = 使能Reset= 将输出设置为低电平 = 拉低 = 复位 = 置为0 = 输出0 = 禁用输出相关的寄存器逻辑:
正常来说,
ODR可以直接控制某个引脚高低// 正确的操作方式, 注意操作符是 |= 和 &= GPIOA->ODR |= (1 << 5); // 拉高 PA5 GPIOA->ODR &= ~(1 << 5); // 拉低 PA5但是对
ODR直接操作, 可能有意无意修改到其他引脚的状态GPIOA->ODR = (1 << 5);// 此时PA5被置为1, 但是PA的其他端口被置为0STM32于是设置了一个
BSRR寄存器, 操作BSRR可以间接控制端口GPIOA->BSRR = (1 << 5);// 此时PA5被置为1, 其他端口不受影响 GPIOA->BSRR = (1 << (5 + 16)); // // 此时PA5被置为0, 其他端口不受影响
BSRR对于拉高的操作比较方便 ,某组BSRR = (1 << 引脚编号);即可, 但是拉低操作稍显复杂, 需要使用某组BSRR = (1 << (引脚编号 + 16));来操作, 于是又设置了一个BRR, 专门用来简化拉低操作GPIOA->BSRR = (1 << 5); // 用BSRR来拉高引脚 GPIOA->BRR = (1 << 5); // 用BRR拉低引脚
6、影子寄存器
BSRR和BRR实际都是一种叫做影子寄存器Shadow Register的寄存器类型影子寄存器在硬件上实际没有真正对应的寄存器, 硬件会自动将对影子寄存器的操作转换为对其他寄存器的操作, 所以大多数影子寄存器写
1和0都不用考虑之后其状态如何, 是否需要复位和置位等, 对影子寄存器的每次操作都是独立的, 不受前后代码影响影子寄存器通常只能写