注释 1 MODE 和 CNF 必须组合起来理解:
MODE先决定它是输入还是输出,并在输出时选择速度;CNF再决定输入/输出的具体电气类型。
不能脱离 MODE 单独解释 CNF。
1. 为什么 LED 使用 CNF13=00
板载 LED 的电路是:
text
3.3V → LED → 510Ω → PC13
程序必须主动控制 PC13 输出:
text
低电平 → LED亮
高电平 → LED灭
因此 PC13 必须满足:
- 是普通 GPIO,不是 USART、TIM 等外设控制;
- 能主动输出稳定的高、低电平;
- 不需要多设备共用一根信号线;
- 不需要外部上拉电阻才能输出高电平。
最符合的模式就是:
text
通用推挽输出
CNF13 = 00
"通用"表示引脚由软件通过 ODR/BSRR/BRR 控制;"推挽"表示引脚内部既能主动拉高,也能主动拉低。
2. 为什么 MODE13=10
PC13 是特殊引脚,数据手册规定其作为输出使用时,速度最高为 2 MHz。
STM32F1 的 MODE 编码为:
| MODE | 含义 |
|---|---|
00 |
输入模式 |
01 |
输出,最高10 MHz |
10 |
输出,最高2 MHz |
11 |
输出,最高50 MHz |
所以 PC13 应选择:
text
MODE13 = 10
与 CNF13=00 合起来:
text
CNF13 MODE13
00 10
即:
text
0010₂ = 0x2
PC13 位于 CRH[23:20],所以最终写入:
c
0x2U << 20
也就是:
text
0x00200000
3. CNF 到底控制什么
CNF 是 Configuration,即"配置类型"。
但它的解释取决于 MODE。
3.1 当 MODE=00:输入模式
此时 CNF 的含义是:
| MODE | CNF | 模式 | 典型用途 |
|---|---|---|---|
00 |
00 |
模拟输入 | ADC、电位器、模拟传感器 |
00 |
01 |
浮空输入 | 外部电路已经提供确定电平 |
00 |
10 |
上拉/下拉输入 | 按键、开关、可能悬空的数字信号 |
00 |
11 |
保留 | 不使用 |
模拟输入:MODE=00,CNF=00
text
MODE = 00
CNF = 00
适合:
- ADC 采集;
- 电位器电压;
- 光敏电阻模拟量;
- 热敏电阻模拟量。
模拟输入模式会关闭数字输入部分,减少数字电路对模拟信号的影响和功耗。
例如:
c
/* PA0配置为模拟输入 */
GPIOA->CRL &= ~(0xFU << 0);
清零后就是:
text
CNF0=00
MODE0=00
浮空输入:MODE=00,CNF=01
浮空输入内部既不上拉,也不下拉。
适合外部电路保证信号始终有确定电平的场景,例如:
- 推挽数字芯片的输出;
- 某些串口 RX;
- 外部模块的数字输出;
- 已经有外部上拉/下拉电阻的信号。
如果引脚没有外部驱动,浮空输入可能随机读取到 0 或 1,所以普通按键一般不应直接使用浮空输入。
上拉/下拉输入:MODE=00,CNF=10
适合:
- 按键;
- 开关;
- 开漏输出信号;
- 可能暂时断开的数字输入。
STM32F1 有一个特殊设计:
text
MODE=00,CNF=10
只表示"上拉/下拉输入",具体是上拉还是下拉,由 ODR 对应位决定:
| ODR 对应位 | 模式 |
|---|---|
| 0 | 下拉输入 |
| 1 | 上拉输入 |
例如 PC13 配置为上拉输入:
c
GPIOC->CRH &= ~(0xFU << 20);
GPIOC->CRH |= (0x8U << 20); /* CNF=10,MODE=00 */
GPIOC->ODR |= (1U << 13); /* ODR13=1,选择上拉 */
配置为下拉输入:
c
GPIOC->ODR &= ~(1U << 13); /* ODR13=0,选择下拉 */
这是 STM32F1 很重要的一个特点:
输入上拉/下拉模式下,ODR 不再控制输出电平,而是选择上拉还是下拉。
3.2 当 MODE≠00:输出模式
只要 MODE 是 01、10 或 11,引脚就是输出模式。此时 CNF 的含义变为:
| CNF | 输出模式 | 典型用途 |
|---|---|---|
00 |
通用推挽输出 | LED、蜂鸣器、普通控制信号 |
01 |
通用开漏输出 | 软件 I²C、线与、双向电平转换 |
10 |
复用推挽输出 | USART TX、SPI、PWM、定时器输出 |
11 |
复用开漏输出 | 硬件 I²C 等开漏外设 |
4. 推挽输出是什么
推挽输出内部可以简化成两个开关:
text
3.3V
│
上方晶体管
│
GPIO引脚 ─┤
│
下方晶体管
│
GND
输出高电平时:
text
上方晶体管导通
下方晶体管关闭
GPIO被主动连接到3.3V
输出低电平时:
text
上方晶体管关闭
下方晶体管导通
GPIO被主动连接到GND
特点:
- 可以主动输出高电平;
- 可以主动输出低电平;
- 上升沿和下降沿都比较快;
- 不需要外部上拉;
- 不能与另一个推挽输出直接并联。
典型用途:
- LED;
- 有源蜂鸣器;
- 继电器驱动输入;
- 电机驱动器方向信号;
- 芯片使能信号;
- 普通高低电平控制。
所以板载 LED 使用通用推挽输出最直接:
text
CNF13=00
5. 开漏输出是什么
开漏输出内部只有负责拉低的晶体管:
text
外部上拉电阻
3.3V ── 电阻 ──┬── GPIO信号线
│
下拉晶体管
│
GND
GPIO 写 0:
text
晶体管导通
引脚被主动拉低
GPIO 写 1:
text
晶体管关闭
引脚处于高阻状态
依靠外部上拉电阻变成高电平
因此开漏输出本身只能:
- 主动输出低电平;
- 释放总线;
- 不能主动输出高电平。
5.1 开漏有什么用
多个设备共用一根线
多个开漏设备可以接在一起:
text
只要任意一个设备拉低 → 总线就是低电平
所有设备都释放 → 上拉电阻将总线拉高
这就是"线与"特性,I²C 使用的就是这种方式。
电平转换
例如 MCU 使用 3.3 V,外部总线使用 5 V:
text
GPIO只负责拉低
总线高电平由外部5V上拉产生
但能否这样做还要检查引脚是否 5 V tolerant。
防止输出冲突
两个推挽输出如果一个输出高、另一个输出低,相当于把电源和地短接,可能产生很大电流。
开漏设备之间不会主动"一个拉高、一个拉低",因此更适合共享总线。
5.2 LED 能不能使用开漏输出
理论上可以。
这块板的 LED 已经连接到 3.3 V:
text
3.3V → LED → 电阻 → PC13
如果 PC13 配置成通用开漏:
- 写 0:PC13 拉低,LED 亮;
- 写 1:PC13 高阻,LED 熄灭。
但这里没有共享总线、电平转换等需求,所以使用开漏没有必要。通用推挽输出的状态更确定,也是普通 LED 控制的常规选择。
6. "通用"和"复用"是什么意思
这是 CNF 中另一组关键概念。
通用输出
通用输出由 CPU 软件控制:
c
GPIOC->BSRR = GPIO_BSRR_BS13;
GPIOC->BRR = GPIO_BRR_BR13;
适合:
- 软件控制 LED;
- 软件控制蜂鸣器;
- 普通片选信号;
- 电机方向信号。
对应:
text
CNF=00:通用推挽
CNF=01:通用开漏
复用输出
复用输出不是由普通 GPIO 输出寄存器持续控制,而是把引脚交给某个片上外设。
例如:
text
PA9 交给 USART1_TX
PA5 交给 SPI1_SCK
PA6 交给 TIM3_CH1 输出PWM
PB6 交给 I2C1_SCL
对应:
text
CNF=10:复用推挽
CNF=11:复用开漏
复用推挽什么时候用
典型应用:
- USART TX;
- SPI SCK;
- SPI MOSI;
- 定时器 PWM;
- 定时器输出比较;
- MCO 时钟输出。
例如 PA9 作为 USART1 TX:
text
MODE = 非00
CNF = 10
即复用推挽输出。
此时引脚电平由 USART 外设产生,而不是程序不断调用 GPIO_SetBits()。
复用开漏什么时候用
最典型的是硬件 I²C:
text
SCL:复用开漏输出
SDA:复用开漏输出
对应:
text
MODE = 非00
CNF = 11
因为 I²C 要求多个设备共享信号线,并允许任何设备拉低总线。
7. 一张表记住全部配置
| MODE | CNF | 完整模式 | 常见用途 |
|---|---|---|---|
00 |
00 |
模拟输入 | ADC |
00 |
01 |
浮空输入 | 外部推挽数字信号 |
00 |
10 |
上拉/下拉输入 | 按键、开关 |
00 |
11 |
保留 | 不使用 |
01/10/11 |
00 |
通用推挽输出 | LED、蜂鸣器、控制信号 |
01/10/11 |
01 |
通用开漏输出 | 软件 I²C、共享信号线 |
01/10/11 |
10 |
复用推挽输出 | USART TX、SPI、PWM |
01/10/11 |
11 |
复用开漏输出 | 硬件 I²C |
这里的 01/10/11 分别表示 10 MHz、2 MHz、50 MHz 输出速度,选择时还要看具体引脚的数据手册限制。
8. 以后如何选择 CNF
先问第一个问题:
text
这是输入还是输出?
如果是输入:
text
是模拟电压?
→ 模拟输入
外部电路始终提供确定的高低电平?
→ 浮空输入
信号可能悬空,需要默认电平?
→ 上拉或下拉输入
如果是输出,再问:
text
由CPU软件直接控制,还是交给片上外设?
软件控制:
text
普通独立信号?
→ 通用推挽
需要共享线路、线与或外部上拉?
→ 通用开漏
外设控制:
text
USART、SPI、PWM等普通输出?
→ 复用推挽
I²C等开漏总线?
→ 复用开漏
#mermaid-svg-Q0VL0XgkzSC1ubQR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q0VL0XgkzSC1ubQR .error-icon{fill:#552222;}#mermaid-svg-Q0VL0XgkzSC1ubQR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q0VL0XgkzSC1ubQR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .marker.cross{stroke:#333333;}#mermaid-svg-Q0VL0XgkzSC1ubQR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q0VL0XgkzSC1ubQR p{margin:0;}#mermaid-svg-Q0VL0XgkzSC1ubQR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster-label text{fill:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster-label span{color:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster-label span p{background-color:transparent;}#mermaid-svg-Q0VL0XgkzSC1ubQR .label text,#mermaid-svg-Q0VL0XgkzSC1ubQR span{fill:#333;color:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .node rect,#mermaid-svg-Q0VL0XgkzSC1ubQR .node circle,#mermaid-svg-Q0VL0XgkzSC1ubQR .node ellipse,#mermaid-svg-Q0VL0XgkzSC1ubQR .node polygon,#mermaid-svg-Q0VL0XgkzSC1ubQR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .rough-node .label text,#mermaid-svg-Q0VL0XgkzSC1ubQR .node .label text,#mermaid-svg-Q0VL0XgkzSC1ubQR .image-shape .label,#mermaid-svg-Q0VL0XgkzSC1ubQR .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q0VL0XgkzSC1ubQR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .rough-node .label,#mermaid-svg-Q0VL0XgkzSC1ubQR .node .label,#mermaid-svg-Q0VL0XgkzSC1ubQR .image-shape .label,#mermaid-svg-Q0VL0XgkzSC1ubQR .icon-shape .label{text-align:center;}#mermaid-svg-Q0VL0XgkzSC1ubQR .node.clickable{cursor:pointer;}#mermaid-svg-Q0VL0XgkzSC1ubQR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .arrowheadPath{fill:#333333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q0VL0XgkzSC1ubQR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q0VL0XgkzSC1ubQR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q0VL0XgkzSC1ubQR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster text{fill:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR .cluster span{color:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q0VL0XgkzSC1ubQR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q0VL0XgkzSC1ubQR rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q0VL0XgkzSC1ubQR .icon-shape,#mermaid-svg-Q0VL0XgkzSC1ubQR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q0VL0XgkzSC1ubQR .icon-shape p,#mermaid-svg-Q0VL0XgkzSC1ubQR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q0VL0XgkzSC1ubQR .icon-shape .label rect,#mermaid-svg-Q0VL0XgkzSC1ubQR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q0VL0XgkzSC1ubQR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q0VL0XgkzSC1ubQR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q0VL0XgkzSC1ubQR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输入
模拟电压
外部保证确定电平
需要默认电平
输出
CPU软件
否
是
片上外设
否
是
配置GPIO
输入还是输出?
信号类型
模拟输入 CNF=00
浮空输入 CNF=01
上拉/下拉输入 CNF=10
由谁控制?
是否需要开漏?
通用推挽 CNF=00
通用开漏 CNF=01
外设需要开漏?
复用推挽 CNF=10
复用开漏 CNF=11
这些配置位的原始定义位于《STM32F10xxx参考手册(中文)》第 8.2.1、8.2.2 节,即 GPIOx_CRL 和 GPIOx_CRH 寄存器说明中。:codex-file-citation{path="F:\WorkBuddyOutput\STM32\STM32\参考文档\STM32F10xxx参考手册(中文).pdf" purpose="source"}