除此之外, 这篇博客: 控制LED灯和蜂鸣器的按键实验中的 不支持连续按 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
当有键按下并且没有松开,只作一次键输入,不当作连续输入。
led.h中的代码
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
#endif
led.c中的代码
#include "stm32f10x.h"//顶级头文件引用
#include "led.h"
void LED_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE);//PB组和PE组IO口时钟使能
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_5;//引脚5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出最大速度是50MHZ
GPIO_Init(GPIOB,&GPIO_InitStruct);//PB5初始化
GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB5初始化为高电平,LED0灭
GPIO_Init(GPIOE,&GPIO_InitStruct);//PE5初始化
GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE5初始化为高电平,LED1灭
}
beep.h中的代码
#ifndef __BEEP_H
#define __BEEP_H
void BEEP_Init(void);
#endif
beep.c中的代码
#include "stm32f10x.h"//顶级头文件引用
#include "beep.h"
void BEEP_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//在LED初始化函数中已经将PB组IO口使能过了,这里可以不使能
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;//引脚5
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;//输出最大速度是50MHZ
GPIO_Init(GPIOB,&GPIO_InitStruct);//PB8初始化
GPIO_ResetBits(GPIOB,GPIO_Pin_8);//PB5初始化为低电平,蜂鸣器不响
}
key.h中的代码
#ifndef __KEY_H
#define __KEY_H
#include "stm32f10x.h"//引用顶级头文件
#include "sys.h"//使用位带操作的头文件
#define KEY0 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)
#define KEY1 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)
#define KEY2 GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//宏定义按键名就是IO的值
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define WK_UP_PRES 4//定义键按下后返回的值
#define LED0 PBout(5)
#define LED1 PEout(5)
#define beep PBout(8)//使用位带操作加宏定义,直接对硬件名字进行操作
void KEY_Init(void);
uint8_t KEY_Scan(void);//键盘扫描函数
#endif
这里充分使用了宏定义,将每个硬件的名字都赋予了意义,在主程序中直接对硬件名字操作以及使用就可以。
为了方便改变LED灯以及蜂鸣器的状态,我们使用位带操作的方法,这样就可以直接对LED灯以及蜂鸣器的状态进行取反。
其中unit8_t类型的变量是STM32固件库中定义的变量类型。
typedef unsigned char uint8_t;
可以看到,它的本质是一个无符号的char类型,大小只有一个字节。因为STM32的空间比较小,尽量使用占内存较小的变量。
key.c中的代码
#include "stm32f10x.h"
#include "delay.h"
#include "key.h"
void KEY_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;//创建GPIO结构体变量
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_GPIOA,ENABLE);//使能PE和PA组IO口
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//设置成上拉输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_2;//PE4,PE3,PE2引脚,也就是KEY0,KEY1,KEY2按键
GPIO_Init(GPIOE,&GPIO_InitStruct);//按键初始化为上拉输入
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;//设置为下拉输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;//PA引脚,也就是WK_UP按键
GPIO_Init(GPIOA,&GPIO_InitStruct);//按键初始化为下拉输入
}
uint8_t KEY_Scan()
{
static uint8_t key = 1;//储存键的状态,没有键按下时是1
if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否有键按下
{
delay_ms(10);//延时消抖
if(key&&(KEY0==0|KEY1==0|KEY2==0|WK_UP==1))//是否真的有键按下
{
key=0;//有键按下,储存为0
if(KEY0==0)
return KEY0_PRES;//KEY0按下
else if(KEY1==0)
return KEY1_PRES;//KEY1按下
else if(KEY2==0)
return KEY2_PRES;//KEY2按下
else if(WK_UP==1)//最好加上if,如果只有else的话可除了前面三个键的情况还可能有其他情况干扰WK_UP键的状态
return WK_UP_PRES;//WK_UP按下
}
}
else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)//为了会更加灵敏
key=1;//没有键按下,储存1
return 0;//没有键按下,返回0
}
键盘扫描函数的功能就是扫描每个与硬件相连按键的IO口的状况,然后通过这个状况来判断是否有键按下,不同的键按下对应着不同的处理。
注意:
判断按键是否按下时,需要进行消抖处理,通常消抖都是通过延时消抖,还可以采用消抖电路来消抖。
消抖的原因:
由于外界干扰,以及按键上的弹簧产生的机械作用,按键上的电平会在没有人按下的时候发生微小的变化,如果不作消抖处理,CPU就会将这些由其他因素引起的电平变化当作是键输入来处理,这样就产生了BUG,所以我们要进行消抖。
当检测到按键上有电平变化时,进行延时处理,一般10ms就够。如果是其他因素引起的电平变化,10ms之内就电平就会恢复,如果是用户按键导致的电平变化,那么在这10ms内就不会恢复到原来的电平状态,就可以认为是有键输入,并且进行相应的处理。
是不是有个疑问?
如果我在10ms内按下键并松开,是不是CPU就不会当作键输入处理?
是这样的,只要你手速够快,就可以骗过CPU,但是我们平常按键时,按一下并松开的时长肯定会超过10ms的。
让按键更加灵敏
else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)
这是判断没有键输入的语句,这种情况是与有键输入的情况对立的。如果写成只有一个else就笼统的概括没有键输入的情况,这样就会导致按键不灵敏,有时候需要按好几次才会出现对应的反应。
main.c中的代码
#include "led.h"
#include "beep.h"
int main()
{
delay_init();//延时初始化
LED_Init();//led初始化
BEEP_Init();//蜂鸣器初始化
KEY_Init();//按键初始化
while(1)
{
uint8_t ret=KEY_Scan();
if(ret)
{
switch(ret)
{
case KEY0_PRES:
LED0=!LED0;
LED1=!LED1;//同时反转俩个LED灯
break;
case KEY1_PRES:
LED0=!LED0;//反转LED0
break;
case KEY2_PRES:
LED1=!LED1;//反转LED1
break;
case WK_UP_PRES:
beep=!beep;//蜂鸣器状态反转
break;
}
}
else
delay_ms(10);//每隔10ms扫描一次键盘
}
}
当按下右键时,同时控制俩个LED灯的状态发生反转,当按下下键和左键则分别只控制一个LED灯的状态发生反转,当按下上键时,蜂鸣器的状态发生反转。