目标:读取按键和传感器模块电平,实现按键控制 LED、光敏传感器控制蜂鸣器
GPIO 输入路径
外部信号进入 MCU 须经过以下各级:
外部引脚 → 保护二极管 → 上拉/下拉电阻 → 施密特触发器 → 输入数据寄存器 (IDR)
输入模式对比
| 模式 | 上拉/下拉 | 施密特触发器 | 典型用途 |
|---|---|---|---|
| 浮空输入 | 关闭 | 使能 | 引脚外部已接上拉/下拉电阻(如按键接外部上拉) |
| 上拉输入 | 上拉 ~40kΩ 至 VDD | 使能 | 按键接 GND(松开默认高电平) |
| 下拉输入 | 下拉 ~40kΩ 至 GND | 使能 | 按键接 VDD(松开默认低电平) |
浮空输入在引脚悬空时电平不确定(由环境耦合决定),实际使用中务必确保引脚有确定的电平来源。
施密特触发器滞回特性
施密特触发器通过正反馈实现两个不同的阈值电压:
滞回特性确保输入信号在阈值附近噪声不会导致输出在高低之间频繁震荡。没有施密特触发的普通 CMOS 输入在缓变信号(如 RC 充放电波形)上会产生多次触发脉冲。
按键消抖原理
机械按键依靠金属弹片接触导通,按下和松手瞬间弹片会发生多次弹跳,持续时间 5~20ms:
开关接触时弹跳曲线:‾‾‾\_‾\_‾\_‾\_‾\_\_\_\_\_\_‾\_‾\_‾‾‾‾‾
弹跳源自机械阻尼振荡:弹片接触时的动能转换为弹性势能,反复弹跳直到能量被摩擦消耗殆尽。消抖的实质是低通滤波:
τ = R × C。以 10kΩ 上拉 + 0.1μF 为例,τ = 10k × 0.1μ = 1ms,约 5τ = 5ms 后电平稳定,可滤除大多数弹跳。按键是最简单的输入设备,按下导通,松手断开。由于内部采用机械弹簧片,按下和松手瞬间会产生抖动(5-10ms 的不稳定电平):
理想波形: 高‾‾‾‾‾‾‾‾‾‾‾\_\_\_\_\_‾‾‾‾‾‾‾‾‾‾‾
实际波形: 高‾‾‾‾\_‾\_‾\_‾\_\_\_\_\_\_‾\_‾\_‾‾‾‾‾
↑按下 ↑稳定 ↑松手
抖动区 低电平 抖动区
软件消抖:检测到电平变化后,延时 10ms 再次读取确认。
传感器模块内部结构:
传感器(光敏/热敏/红外) ──┬── 定值电阻 ─── VCC
│
└── 分压输出(模拟) ── 电压比较器(LM393) ── DO(数字输出)
↑
参考电压(电位器分压)
| 函数 | 说明 |
|---|---|
HAL_GPIO_ReadPin(GPIOx, GPIO_PIN_x) |
读取引脚电平,返回 GPIO_PIN_SET(1) 或 GPIO_PIN_RESET(0) |
/* main.c */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init(); // PB0_Input + PC13_Output
uint8_t key_flag = 0; // 防止一次按下触发多次
while (1)
{
/* 检测按键按下(低电平有效) */
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET)
{
HAL_Delay(10); // 消抖
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET)
{
if (key_flag == 0)
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
key_flag = 1; // 防止重复触发
}
}
}
else
{
key_flag = 0; // 松手后清零
}
}
}
松手沿检测(另一种更可靠的写法):
uint8_t pre_key = 1, cur_key = 1;
while (1)
{
pre_key = cur_key;
cur_key = (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) ? 0 : 1;
if (pre_key != cur_key) // 电平变化
{
HAL_Delay(10); // 消抖
if (cur_key == 1) // 松手沿:上一次按下,这一次松开
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
}
}
/* PA0(DO) 读取传感器输出 */
while (1)
{
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
{
// 光线暗 → DO 输出低电平 → 蜂鸣器响
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET); // 低电平驱动
}
else
{
// 光线亮 → DO 输出高电平 → 蜂鸣器关
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_SET);
}
}