一.最终实现的现象
本篇文章最终的现象就是通过按键的按下,实现LED的亮灭,比如当我第一次按下,LED亮,第二次按下,LED灭。
二.按键简介
1.按键是什么?
按键是一种常见的输入设备,按下导通,松手断开。
2.按键抖动是什么?
由于案件内部使用的是机械式的弹簧片来进行通断的,所以在按下和松手的瞬间都会伴有一连串的抖动。
当我们人体按下按键时是感受不到的,但是对于单片机来说,这已经无法忽视了,所以当我们要使用按键时,需要进行按键的消抖。也就是延迟一小段时间,等待弹簧片一连串的抖动消失之后,再确定按键的状态。
具体延迟多长的时间呢?可以看看下面的图片,大概延时5至10ms即可,而且在按下和松开时都需要消抖。
3.按键原理图
![](https://img-blog.csdnimg.cn/direct/ef0a69872cb94412bd53e520393b336d.png)
![](https://img-blog.csdnimg.cn/direct/a4e3e9d736c04fe8926dc2100accde8e.png)
由图一可知,按键K1一端接GND,一端接PA0口。
当按键被按下时,PA0被直接下拉到GND,此时读取PA0口的电压就是低电平。
当按键未被按下时,PA0被悬空,引脚电压不确定,这是我们应该避免的。所以在这种情况下,必须要求PA0是上拉输入(此时,引脚再悬空,就是高电平)的模式,否则就会出现引脚电压不确定的错误现象。
所以有了下接图二,我们可以看到图二中外部接了一个上拉电阻。此时,当按键未被按下,引脚由于上拉作用,自然保持为高电平。按键按下,引脚为低电平。
这种情况下,引脚不会出现悬空状态,所以此时PA0口可以被配置为浮空输入或者上拉输入。如果是上拉输入,那就是内外两个上拉电阻共同作用了;这时高电平就会更强一些,对应高电平就会更加稳定。当然,当按键按下,引脚被拉到低时,损耗也就更大一些。
![](https://img-blog.csdnimg.cn/direct/f49a0abf962e4a228c4ffdb0c8274d9d.png)
![](https://img-blog.csdnimg.cn/direct/bf5ab21388254964a2bc463f1b447790.png)
由图一可知,按键K1一端接3.3V电源,一端接PA0口。
当按键被按下时,PA0被直接上拉到3.3V,此时读取PA0口的电压就是高电平。
当按键未被按下时,PA0被悬空,引脚电压不确定,这是我们应该避免的。所以在这种情况下,必须要求PA0是下拉输入(此时,引脚再悬空,就是低电平)(不过一般单片机可能并没有下拉输入的模式,所以最好用下接按键的方式)的模式,否则就会出现引脚电压不确定的错误现象。
所以有了下接图二,我们可以看到图二中外部接了一个下拉电阻。此时,当按键未被按下,引脚由于下拉作用,自然保持为低电平。按键按下,引脚为高电平。
这种情况下,引脚不会出现悬空状态,所以此时PA0口可以被配置为浮空输入或者下拉输入。具体分析和下接按键方式差不多,此处不再细讲。
那么总结一下,对于下接按键方式,也就是第一二张原理图,按键按下时引脚为低电平,松手时是高电平。
对于上接按键方式,也就是第三四张原理图,按键按下时引脚为高电平,松手时是低电平。
三.代码部分
1.按键代码部分
#include "stm32f10x.h" // Device header
#include "Delay.h"
/**
* 函 数:按键初始化
* 参 数:无
* 返 回 值:无
*/
void Key_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
//这里我们选择开启GPIOB上的1口和11口,这里两个接口的作用就如同前面所讲的PA0口的作用一样
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//将PB1和PB11引脚初始化为上拉输入
//我们使用下接模式,所以配置为上拉输入
}
/**
* 函 数:按键获取键码
* 参 数:无
* 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下
* 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0; //定义变量,默认键码值为0
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
//读PB1输入寄存器(PB1口)的状态,如果为0,则代表按键1按下
{
Delay_ms(20); //延时消抖
//消抖的重要性!在写代码时,一定不要忘了消抖!!
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手
//按键按下时为低电平,也就是0,若一直按着不松手,则程序会在while循环中不断循环,直到松手变为高电平,才会退出循环
//注意,此处的while为循环语句,最后要记得加个分号
Delay_ms(20); //延时消抖
KeyNum = 1; //置键码为1
}
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
//读PB11输入寄存器(PB11口)的状态,如果为0,则代表按键2按下
{
Delay_ms(20); //延时消抖
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手
Delay_ms(20); //延时消抖
KeyNum = 2; //置键码为2
}
return KeyNum;
//返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0
}
2.主函数部分
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum; //定义用于接收按键键码的变量
int main(void)
{
/*模块初始化*/
LED_Init(); //LED初始化
Key_Init(); //按键初始化
while (1)
{
KeyNum = Key_GetNum(); //获取按键键码
if (KeyNum == 1) //按键1按下
{
LED1_Turn(); //LED1翻转,也就是从亮(灭)变为灭(亮)
//LED代码在前面已发布的文章中也讲过,不太了解的可以去看看,希望有帮助
}
if (KeyNum == 2) //按键2按下
{
LED2_Turn(); //LED2翻转
}
}
}