目录
学习目标:
1、了解按键输入的原理。
2、了解 STM32 的输入模式。
2.1 按键输入的原理图
通过第一章的学习,我们学会了如何使用 GPIO 输出模式,接下来,我们来
学习怎么使用GPIO的输入模式。首先我们来看在我们开发板上面按键的原理图。
通过上面的原理图,我可能看到四个按键分别接到单片机的 PA0、PE2、PE3、
PE4。
2.2 按键消抖
通常的按键所用开关为机械弹性开关,当机械触点断开 、闭合时,电压信号
如下图:
由于机械点的弹性作用,一个按键开关在闭合时不会马上稳定的接通,在断
开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。抖动
时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms。按键稳定闭合时间
的长短则由操作人员的按键动作决定的,一般为零点几秒至数秒。按键抖动会引
起按键被误读多次。为了确保 CPU 对键的一次闭合仅作一次处理,必须去除按
键抖动。一般来说一个简单的按键消抖就是先读取一次按键的状态,如果得到按
键按下之后,延时 10ms,再次读取一次按键的状态,如果按键还是按下状态,
那么说明按键已经按下。
2.3 V3.5 库函数输入函数介绍
在第一章 LED 灯中我们已经介绍了,STM32 的 IO 口配置方式了,那么这里
我们就不在作介绍了,我们直接介绍库函数中的输入读取函数。
输入读取函数有多个函数,详细大家可以查看《STM32 固件库使用手册(中
文 翻 译 版 ).pdf 》, 在 这 里 我 们 简 单 介 绍 一 个 要 用 到 的 输 入 函 数
GPIO_ReadInputDataBit()函数:
这个函数是读取一个设置为输出模式时,一个 IO 口的状态值。当读取的 IO
口为 1 的时候,输出 1,当 IO 口输出为 0 的时候,输出 0。
2.4 按键输入程序
1) 主函数:
int main(void)
{
uint8_t ledState = 0, keyValue = 0;
LED_Config();
KEY_Config();
while(1)
{
/* 设置 LED 灯的状态 */
LED_SetState(ledState);
/* 扫描按键 */
keyValue = KEY_Scan();
/* 根据按键值,设置不一样的 LED 状态 */
switch(keyValue)
{
case(KEY_UP): //上按键按下
if((ledState == 0xFF) | (ledState == 0)) //若灯全亮或全灭
{
ledState = 0xFE; //点亮一盏灯
}
else //否则移动
{
ledState <<= 1;
ledState += 1;
}
if(ledState == 0xFF)
{
ledState = 0xFE;
}
break;
case(KEY_DOWN): //下按键按下
if((ledState == 0xFF) | (ledState == 0)) //若灯全亮或全灭
{
ledState = 0x7F; //点亮一盏灯
}
else //否则移动
{
ledState >>= 1;
ledState |= 0x80;
}
if(ledState == 0xFF)
{
ledState = 0x7F;
}
break;
case(KEY_LEFT): //左按键按下
ledState = 0; //点亮全部灯
break;
case(KEY_RIGHT): //右按键按下
ledState = 0xFF; //熄灭全部灯
break;
default:
break;
} // end of switch(keyValue)
}
}
这个主函数程序下载到板子上面得到的效果是:按 KEY_UP 键,LED 灯右
移;按 KEY_DOWN 键,LED 灯左移;按 KEY_LEFT 键,LED 灯全亮;按
KEY_RIGHT 键,LED 灯全灭。
2) 宏定义输入:
/* 按键读取 */
#define KEY0 GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)
#define KEY1 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_2)
#define KEY2 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)
#define KEY3 GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)
这 样 子 我 们 就 可 以 很 简 单 使 用 KEY0 在 程 序 中 代 替
GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)了。
3) 按键 IO 口初始化函数(主要配置输入模式):
void KEY_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 开启 GPIO 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
/* 配置 GPIO 的模式和 IO 口 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_KEY; //选择你要设置的 IO 口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置传输速率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置输入上拉模式
/* 初始化 GPIO */
GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 GPIO_LED
/* PA0 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //选择你要设置的 IO 口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置输入上拉模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
4) 按键扫描函数
uint8_t KEY_Scan(void)
{
uint8_t keyValue = 0, timeCount = 0;
if((KEY0 == 1) || (KEY1 == 0) || (KEY2 == 0) || (KEY3 == 0)) //检测是否
有按键按下
{
KEY_Delay10ms(); //延时消抖
/* 检测是哪个按键按下 */
if(KEY0 == 1)
{
keyValue = KEY_UP;
}
else if(KEY1 == 0)
{
keyValue = KEY_LEFT;
}
else if(KEY2 == 0)
{
keyValue = KEY_DOWN;
}
else if(KEY3 == 0)
{
keyValue = KEY_RIGHT;
}
else
{
keyValue = 0;
}
/* 有按键按下时,做松手检测 */
if(keyValue != 0)
{
while(((KEY0 == 1) || (KEY1 == 0) || (KEY2 == 0) || (KEY3 ==
0)) && (timeCount < 150))
{
KEY_Delay10ms();
timeCount++;
}
KEY_Delay10ms();//由于主函数中程序较少,连续扫描的速度
太快,加一个松手消抖减少误读
}
}
return keyValue;
}
这个按键扫描函数只是一个简单的函数,在一个比较简单的程序里面才比较
使用,它使用起来有很明显的缺陷,比如不支持多按键输入,而且由于使用的是
延时消抖,在延时的时候,CPU 是空跳的,所以不能充分利用 CPU 资源。大家
如果对按键比较感兴趣的话,或者在比较复杂的程序使用按键扫描的话,大家可
以去看一下使用状态机方式的按键扫描,或者零时消抖的按键扫描方式。