【嵌入式外设精学】Day01|GPIO 输入:上拉、下拉、浮空怎么选
今天解决:按键和空闲线到底该上拉还是下拉?为什么悬空脚会“乱跳”?
目录
1. 今天解决什么问题
- 按键有时灵、有时不灵
- 没接东西的引脚读数乱跳
- 不知道“上拉/下拉/浮空”在电路上到底意味着什么
一句话:输入脚在空闲时必须有确定电平(高或低),不能靠运气。
2. 原理
2.1 三种输入方式
| 上拉 | 被电阻拉到高 | 键按下接地时读到 0 |
| 下拉 | 被电阻拉到低 | 键按下接电源时读到 1 |
| 浮空 | 没人定电平 | 仅当外部一直有器件驱动 |
浮空 = 高阻抗,像一根天线,噪声会当信号,中断可能误触发。
2.2 和按键怎么配对
硬件上按键把脚短接到哪一端,输入就配相反的默认电平:
| 键一端接地 | 上拉输入 | 读 0 | 读 1 |
| 键一端接 3.3V | 下拉输入 | 读 1 | 读 0 |
配反了:怎么按读数都不变。
2.3 内部上拉 vs 外部上拉
| 优点 | 省元件,软件开一下就行 | 更“有力”,抗干扰好 |
| 缺点 | 电阻偏大,力偏弱 | 要焊电阻 |
| 建议 | 板载短按键 | 长线、噪声大、I2C 等 |
I2C 这类开漏总线,一般必须外部上拉,不能只靠内部。
2.4 为什么有时“阈值边上”会抖
脚电压刚好落在芯片认 0 / 认 1 的交界区时,轻微噪声就会来回判。 所以:电压要尽量靠“确定的高”或“确定的低”,并留出噪声余量。
3. 代码示例
#include "stm32f4xx.h"
#include <stdbool.h>
void key_gpio_init(void)
{
GPIO_InitTypeDef g;
GPIO_StructInit(&g);
g.GPIO_Pin = GPIO_Pin_0;
g.GPIO_Mode = GPIO_Mode_IN;
g.GPIO_PuPd = GPIO_PuPd_UP; /* 键接地 → 上拉 */
GPIO_Init(GPIOA, &g);
}
/* 读原始电平:1=高,0=低 */
bool key_raw_is_low(void)
{
return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET;
}
/* 极性统一:1 = 按下 */
bool key_is_pressed(void)
{
return key_raw_is_low(); /* 上拉+接地,按下为低 */
}
消抖(在定时器里周期调用,不要在死循环里 delay):
#include <stdbool.h>
#include <stdint.h>
typedef struct {
bool raw_last;
bool stable;
uint8_t cnt;
} key_filter_t;
/* 1ms 调一次;true 表示稳定按下 */
bool key_filter_update(key_filter_t *f, bool pressed_now)
{
if (pressed_now == f->raw_last) {
if (f->cnt < 15u) {
f->cnt++;
}
} else {
f->raw_last = pressed_now;
f->cnt = 0u;
}
if (f->cnt >= 10u) {
f->stable = f->raw_last;
}
return f->stable;
}
4. 常见误区
| 空脚不用配,放着就行 | 要上下拉或设为输出确定电平 |
| 内部上拉一定能替代 I2C 上拉 | 不行 |
| 读键直接 if(pin) 就完事 | 要消抖,并统一“按下=1” |
| 5V 器件随便接 3.3V 脚 | 可能损坏,先看是否 5V-tolerant |
5. 自测
6. 答案
作者:shuifanyu 标签:GPIO 上拉下拉 按键 嵌入式





