3、GPIO基础与按键输入:学习GPIO模式配置,用按键控制LED开关,理解上拉/下拉电阻
好,咱们进入第三节课。这节课我打算聊聊GPIO,也就是通用输入输出口。这东西是单片机跟外界打交道最基础的方式,说白了就是芯片的「手脚」。
你想想看,单片机要控制一个LED亮灭,或者检测一个按键有没有被按下,靠的就是GPIO。这节课我们就用按键控制LED开关这个经典实验,把GPIO的模式配置、上拉下拉电阻这些概念一次性讲透。
3.1 GPIO到底是什么?
GPIO的全称是General Purpose Input Output,通用输入输出。每个GPIO引脚都可以独立配置成输入模式或者输出模式。
- 输出模式:单片机主动往外送高电平(3.3V或5V)或低电平(0V),用来驱动LED、蜂鸣器这类外设。
- 输入模式:单片机读取外部信号是高还是低,比如检测按键是否按下。
嗯,这里要注意一点:同一个引脚,同一时间只能干一件事。要么输入,要么输出,不能又当爹又当妈。我见过不少新手把引脚同时配成输入和输出,结果逻辑混乱,查了半天才发现问题。
3.2 GPIO的几种工作模式
现在的单片机(比如STM32)GPIO模式远不止输入输出这么简单。我列个表,你一看就明白:
| 模式 | 说明 | 典型用途 |
|---|---|---|
| 推挽输出 | 可以输出高电平和低电平,驱动能力强 | 驱动LED、继电器 |
| 开漏输出 | 只能输出低电平,高电平需要外部上拉 | I2C总线、多路信号线与 |
| 浮空输入 | 引脚电平不确定,容易受干扰 | 基本不用,除非你知道在干什么 |
| 上拉输入 | 内部接一个电阻到VCC,默认高电平 | 按键检测(按下为低) |
| 下拉输入 | 内部接一个电阻到GND,默认低电平 | 按键检测(按下为高) |
| 模拟输入 | 不读数字电平,直接读电压值 | ADC采集 |
我个人习惯,做按键输入时首选上拉输入模式。为什么?因为大多数按键电路都是按下接地,上拉输入正好匹配,而且省掉一个外部电阻。
3.3 上拉电阻与下拉电阻——别让引脚「飘着」
这里有个关键概念:上拉电阻和下拉电阻。说白了,就是给引脚一个「默认状态」。
你想想看,如果引脚什么都不接,它处于什么电平?答案是——不确定。可能受周围电磁干扰乱跳,读出来的值一会儿0一会儿1。这就是所谓的「浮空」状态。
上拉电阻:在引脚和VCC之间接一个电阻(通常4.7kΩ~10kΩ)。这样引脚默认就是高电平。当按键按下接地时,引脚被拉低,变成低电平。
下拉电阻:在引脚和GND之间接一个电阻。默认低电平,按键按下接VCC时变成高电平。
3.4 实战:用按键控制LED开关
好,理论讲完了,咱们直接上代码。这个实验的目标是:按下按键,LED亮;松开按键,LED灭。
硬件连接很简单:
- LED正极接GPIOA Pin0(推挽输出)
- LED负极串联一个220Ω电阻到GND
- 按键一端接GPIOA Pin1(上拉输入)
- 按键另一端接GND
下面是基于STM32的HAL库代码:
// 初始化GPIO
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置PA0为推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL; // 输出不需要上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速即可
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置PA1为上拉输入
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 主循环
int main(void)
{
HAL_Init();
GPIO_Init();
while (1)
{
// 读取按键状态
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
{
// 按键按下(低电平),点亮LED
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
}
else
{
// 按键松开(高电平),熄灭LED
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
}
}
}
3.5 按键消抖——别忽略这个细节
你实际测试时会发现一个问题:有时候按一下按键,LED会闪好几下。这不是代码写错了,而是按键的机械抖动。
按键内部是金属弹片,按下时不会瞬间稳定接触,会在几毫秒内反复弹跳。单片机速度很快,可能在这几毫秒内读到了多次电平变化。
解决办法很简单:延时消抖。检测到按键按下后,等10~20ms再读一次,确认确实是按下状态。
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
{
// 延时10ms消抖
HAL_Delay(10);
// 再次确认
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
{
// 这才是真正的按键按下
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 翻转LED状态
}
}
3.6 总结一下这节课的核心
- GPIO有输入和输出两种基本方向,同一时间只能选一种。
- 上拉/下拉电阻给引脚一个确定的默认电平,避免浮空。
- 按键输入建议用内部上拉,省外部元件。
- 机械按键必须做消抖处理,10~20ms延时足够。
下节课咱们会用到定时器,让LED实现呼吸灯效果。到时候你会发现,GPIO配合定时器,能玩出很多花样。今天就到这儿,动手试试吧!