摘要
STM32F103 的 GPIO 所有模式:上拉输入、浮空输入、推挽输出、开漏输出、复用功能、模拟输入,全部由 CRL、CRH 两个端口配置寄存器 控制。绝大多数新手只会调用 GPIO_Init() 库函数,不清楚引脚模式本质就是修改 CRL/CRH 的对应比特位,遇到按键乱跳、串口无波形、I2C 通信失败根本无从排查。本文抛开枯燥手册话术,结合寄存器位结构、手册截图、Keil 内存调试截图,掰开揉碎讲解 CRL、CRH 结构、位定义、组合模式、配置写法、易错点。 关键词:STM32F103,CRL,CRH,GPIO 配置寄存器,CNF,MODE
一、什么是 CRL、CRH 配置寄存器
每组 GPIO(GPIOA/GPIOB/GPIOC...)都拥有两个配置寄存器:
- GPIOx_CRL :Configuration Register Low,端口配置低寄存器 管控当前端口 Pin0 ~ Pin7 共 8 个引脚。
- GPIOx_CRH :Configuration Register High,端口配置高寄存器 管控当前端口 Pin8 ~ Pin15 共 8 个引脚。
二者结构完全一致,只是管控引脚范围不同,均为 32 位寄存器。
📸【截图位置 1:STM 参考手册寄存器映射表】 截取手册中 GPIO 寄存器偏移地址:CRL 偏移
0x00,CRH 偏移0x04。
核心规则:每 4 bit 控制 1 个引脚
不管是 CRL 还是 CRH,规则统一: 4 个二进制位 = 控制 1 个 IO 引脚 32 位寄存器 ÷ 4 位 = 8 组,刚好管控 8 个引脚。
以 CRL 举例分配关系:
表格
| 寄存器比特区间 | 管控引脚 |
|---|---|
| Bit3 ~ Bit0 | Pin0 |
| Bit7 ~ Bit4 | Pin1 |
| Bit11 ~ Bit8 | Pin2 |
| Bit15 ~ Bit12 | Pin3 |
| Bit19 ~ Bit16 | Pin4 |
| Bit23 ~ Bit20 | Pin5 |
| Bit27 ~ Bit24 | Pin6 |
| Bit31 ~ Bit28 | Pin7 |
CRH 结构同理: Bit3~0→Pin8、Bit7~4→Pin9 ... Bit31~28→Pin15。
📸【截图位置 2:手册 CRL 位分配示意图】 截取数据手册里 CRL 寄存器位分段图,可以直观看到 4 位一组对应引脚。
二、4 位内部结构:CNF 1:0 + MODE 1:0
控制单个引脚的 4 位分为两段:
plaintext
高位2位:CNF1 CNF0 → 配置类型
低位2位:MODE1 MODE0 → 模式+输出速度
整体排布:CNF1 CNF0 | MODE1 MODE0
举例:10 00 代表 CNF=10,MODE=00。
2.1 MODE 1:0 低两位:区分输入 / 输出 + 输出速度
MODE 决定这个引脚到底是输入还是输出,如果是输出,还规定最高翻转速度。
表格
| MODE1:0 | 工作类型 | 最大输出速度 | 使用场景 |
|---|---|---|---|
| 00 | 输入模式(上电默认) | 无速度概念 | 按键、外部电平读取、ADC 模拟输入 |
| 01 | 输出模式 | 10 MHz | 普通 LED 指示灯、低速开关输出 |
| 10 | 输出模式 | 2 MHz | 长线输出、低速外设,抗干扰更强 |
| 11 | 输出模式 | 50 MHz | SPI、PWM、串口 TX 高速复用引脚 |
一句话记忆: MODE=00 必然是输入;MODE≠00 就是输出,数值越大速度越快。
2.2 CNF 1:0 高两位:配置引脚电路结构
CNF 的含义取决于当前 MODE 是输入还是输出,两套规则分开理解,不容易混淆。
场景 1:MODE=00(输入模式)
引脚处于输入状态,CNF 用来选择内部上下拉电阻、是否开启施密特触发器。
表格
| CNF1:0 | 输入模式类型 | 内部电路特点 | 常用场景 |
|---|---|---|---|
| 00 | 模拟输入 | 关闭上下拉、关闭施密特 | ADC 电压采集 |
| 01 | 浮空输入(复位默认) | 无上下拉电阻 | 外部自带上拉的传感器 |
| 10 | 上拉输入 | 接通内部 40K 上拉电阻 | 独立按键(按键另一端接地) |
| 11 | 下拉输入 | 接通内部 40K 下拉电阻 | 高电平触发按键检测 |
通俗解释: 按键一端接 PA0、一端接地,如果配置成上拉输入,没有按下时引脚默认高电平;按键按下引脚变低电平,电平稳定不会乱飘。
场景 2:MODE≠00(输出模式)
引脚为输出,CNF 决定是普通推挽、开漏,还是交由外设复用。
表格
| CNF1:0 | 输出模式类型 | 特性 | 适用场景 |
|---|---|---|---|
| 00 | 通用推挽输出 | 既能输出高电平,也能输出低电平,驱动能力强 | LED、继电器、普通 IO 输出 |
| 01 | 通用开漏输出 | 只能主动输出低电平,高电平必须依靠外部上拉电阻 | 多设备线与通信 |
| 10 | 复用推挽输出 | 电平由串口、定时器等外设控制,CPU 写 ODR 无效 | USART 发送、TIM PWM |
| 11 | 复用开漏输出 | 外设控制开漏输出 | I2C SDA/SCL 引脚 |
📸【截图位置 3:官方手册 GPIO 模式组合对照表】 把手册里 MODE+CNF 组合成 8 种 GPIO 模式的表格截图粘贴此处。

三、8 种完整 GPIO 模式对照表(CRL/CRH 配置真值表)
把 CNF+MODE 组合,就能得到 STM3 全部 8 种 GPIO 工作模式,也是标准库枚举的本质来源:
表格
| GPIO 模式 | CNF | MODE | 4 位组合 |
|---|---|---|---|
| 模拟输入 | 00 | 00 | 0000 |
| 浮空输入 | 01 | 00 | 0100 |
| 上拉输入 | 10 | 00 | 1000 |
| 下拉输入 | 11 | 00 | 1100 |
| 通用推挽输出 50M | 00 | 11 | 0011 |
| 通用开漏输出 50M | 01 | 11 | 0111 |
| 复用推挽输出 50M | 10 | 11 | 1011 |
| 复用开漏输出 50M | 11 | 11 | 1111 |
举例:上拉输入对应的 4 位二进制是 1000,十六进制就是 0x8。


四、实操:直接操作寄存器改写 CRL(不用标准库)
需求:配置 PA0 为上拉输入,PA1 为 50MHz 推挽输出。
步骤 1:开启 GPIOA 时钟
时钟不开启,CRL 寄存器无法写入,所有配置无效。
c
运行
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
步骤 2:配置 PA0(Pin0,占用 CRL Bit3~Bit0)
目标模式:上拉输入 = 1000(二进制)= 0x8 标准写法:先清空对应 4 位,再赋值,避免覆盖其他引脚配置
c
运行
GPIOA->CRL &= ~(0x0F << 0); // 把Pin0对应的4位全部清零
GPIOA->CRL |= (0x08 << 0); // 写入上拉配置 1000
步骤 3:配置 PA1(Pin1,占用 CRL Bit7~Bit4)
目标:推挽输出 50MHz → CNF=00 MODE=11 → 二进制 0011 = 0x03
c
运行
GPIOA->CRL &= ~(0x0F << 4);
GPIOA->CRL |= (0x03 << 4);
为什么一定要先 & 清零?
假设原先 PA1 是浮空输入(0100),直接|=0x03会变成 0100 | 0011 = 0111,模式错乱。 先使用掩码 0x0F 把目标 4 位清零,再赋值,是寄存器操作通用规范。
五、CRH 使用示例(配置 Pin8~Pin15)
配置 PA8 为复用推挽输出(串口 TX 引脚,50MHz) 复用推挽输出:CNF=10,MODE=11 → 4 位数值 1011 = 0xB PA8 对应 CRH 的最低 4 位(Bit3~Bit0):
c
运行
GPIOA->CRH &= ~(0x0F << 0);
GPIOA->CRH |= (0x0B << 0);
六、通俗区分容易混淆的模式(靠 CRL 配置分辨)
-
普通推挽输出 vs 复用推挽输出
- 普通推挽:CNF=00,电平由
ODR/BSRR控制,手动控制高低电平点灯; - 复用推挽:CNF=10,CPU 无法通过 ODR 控制引脚,电平交给串口、定时器等外设接管。 很多人串口发送不出波形,就是把 TX 引脚配置成普通推挽而非复用推挽。
- 普通推挽:CNF=00,电平由
-
上拉输入 vs 浮空输入
- 浮空输入(CNF=01)没有上下拉电阻,引脚悬空时电平随机高低,按键极易抖动;
- 上拉输入(CNF=10)内置上拉电阻,没按下按键时固定高电平,按键电平稳定。
-
模拟输入 CNF=00、MODE=00,关闭数字上下拉与施密特触发器,防止数字电路干扰模拟电压,专门给 ADC 采集使用。
七、CRL/CRH 开发高频错误总结
- 记开启 APB2 端口时钟 CRL、CRH 写入任何数值都不会生效,LED 不亮、按键无反应,排查优先查时钟寄存器。
- 不清位直接 |= 赋值 多个引脚配置相互干扰,引脚模式莫名其妙错乱,是新手最高发错误。
- 复用引脚配置成普通输出 USART、I2C、TIM 复用引脚必须配置复用模式,普通推挽输出外设无法接管引脚,外设失效。
- ADC 引脚配置成上拉输入 开启上拉电阻会分流模拟电压,ADC 采集数值不准,ADC 引脚必须配置模拟输入。
- PC13/14/15 配置高速输出 备份域引脚驱动能力极弱,即便 CRL 配置 50MHz 推挽,也无法正常驱动 LED。
八、标准库 GPIO_Init 本质就是改写 CRL/CRH
标准库封装之后隐藏了寄存器操作,拆开底层源码可以看到: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU 等价于给 CRL 对应引脚赋值 CNF=10 MODE=00。 库只是帮我们封装了「清位 + 赋值」的过程,底层依旧操作 CRL 与 CRH 两个寄存器。
九、总结
- GPIOx_CRL 管控 Pin0~7,GPIOx_CRH 管控 Pin8~15,结构完全一致;
- 单个引脚占用寄存器 4bit:
CNF[1:0] + MODE[1:0],组合出全部 8 种 GPIO 工作模式; - MODE 区分输入 / 输出与速度,CNF 决定内部电路结构(上下拉、推挽、复用等);
- 寄存器标准配置格式:清零 4 位掩码
&= ~(0x0F<<n)→ 写入配置|= val<<n; - 所有 GPIO 异常问题,绝大多数根源都是 CRL/CRH 模式配置错误。
掌握 CRL、CRH 端口配置寄存器,才算真正看懂 GPIO 底层,不再只会复制粘贴库函数代码。后续可以结合 IDR、BSRR 寄存器实现完整按键点灯工程。