STM32微控制器综合实训4 按键输入

实验4 按键输入
STM32通过按键来控制LED灯的亮灭。

独立按键控制LED灯的亮灭

key.c

key.c文件有两个重要函数:KEY_Init和KEY_Scan

KEY_Init

//按键初始化函数
void KEY_Init(void) //IO初始化
{  	
  GPIO_InitTypeDef GPIO_InitStructure; 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟	
  //KEY0-KEY1  上拉输入	
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6|GPIO_Pin_12;	GPIO_InitStructure.GPIO_Mode =  GPIO_Mode_IPU; //设置成上拉入 	
  GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3	
  //初始化 WK_UP-->GPIOA.0	  下拉输入	
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//PA0设置成输入,默认下拉	  	
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}

1、由于KEY0和KEY1按键按下,变为低电平,所以设置为上拉输入,默认为高电平。

2、由于WK_UP按键按下,变为高电平,所以设置为下拉输入,默认为低电平。

KEY_Scan

//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY3按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY_UP!!
u8 KEY_Scan(u8 mode)
{	 	
  static u8 key_up=1;//按键按松开标志 static再次调用,继承上一次的值	
  if(mode)key_up=1;  //支持连按		  	
  if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))	
  {		
  delay_ms(20);//去抖动 		
  key_up=0;		
  if(KEY0==0)return KEY0_PRES;		
  else if(KEY1==0)return KEY1_PRES;		
  else if(WK_UP==1)return WKUP_PRES;	
  }
  else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1; 	     	
  return 0;// 无按键按下
}

KEY_Scan函数的执行过程;

1、mode=1的时候存在的bug:因为每一次调用KEY_Scan都会返回一个键值,假如一直按着KEY1不松,则每次调用KEY_Scan都会返回一个KEY1_PRES (KEY1_PRES=2),则下面while语句中第4行的if判断一直成立,那么就会一直执行第13行的翻转程序。第13行的程序本想让LED1的状态取反,但是由于执行频率太高,LED1高速地进行取反,以至于人眼看不到翻转,只会看到这个灯变暗。并且松开KEY1的时候,LED1有可能变为0,有可能变为1,即LED灯有可能亮,有可能不亮,存在这样一个bug。

2、mode=0时,key_up作为子程序KEY_Scan的局部静态变量,当没有键按下时,KEY_Scan第13行的if条件不成立,key_up仍然为1,KEY_Scan直接return 0,KEY_Scan结束执行;当有按键按下时,KEY_Scan第13行的if成立,执行if语句(由于程序执行周期远远小于人按键松开时间,所以认为执行1个周期期间key_up一直为1),延迟消抖后,key_up变为0,同时执行嵌套的if语句来再次判断是哪个键被按下,并且return对应的键值。尽管main高速调用KEY_Scan,但由于key_up是static类型的变量,会继承上一次调用的值,也即是说key_up=0,不执行第13行if,KEY_Scan就会return为0。直到按键完全松开,执行KEY_Scan第20行程序,key_up重新为1。做到了按下按键只返回一次对应值,便于main的if条件只执行一次,即LED灯取反一次。

然后进入main函数

main.c

while

while(1)	
  { 		
  key=KEY_Scan(0);	//得到键值	   	
  if(key)		
  {
  switch(key)					   			    			
  {				 				  
   case WKUP_PRES:	     			
     LED0=!LED0;					
     LED1=!LED1;					
     break; 				
   case KEY1_PRES:	//控制LED1翻转	 					    
     LED1=!LED1;					
     break;				
   case KEY0_PRES:	//同时控制LED0,LED1翻转 					
     LED0=!LED0;					
     break;					
   }		
  }
  else delay_ms(10); 	
}

由程序可知,当WKUP键按下时,LED0和LED1的状态都发生改变,KEY1键按下时,LED1的状态发生改变,KEY0键按下时,LED0的状态发生改变。

编译仿真

打开仿真
在这里插入图片描述
设置断点
在这里插入图片描述
定义按键初始状态:
因为KEY0和KEY1默认为高电平,所以PE.6和PE.12初始值为1
在这里插入图片描述

因为WK_UP默认为低电平,PA.0初始值为0
在这里插入图片描述

先点击运行
在这里插入图片描述

1、按下PE12
在这里插入图片描述

2、松开PE12
在这里插入图片描述

程序停在了LED1=!LED1,说明case KEY1_PRES执行,独立按键程序成功。
在这里插入图片描述

组合键按键控制LED灯的亮灭

修改程序

key.c

主要是修改KEY_Scan

u8 KEY_Scan(u8 mode)
{	 	
  u8 key=0;	
  static u8 tem;//按键按松开标志 static再次调用,继承上一次的值	  	
  if((KEY0==0||KEY1==0||WK_UP==1))//任何一个按下	
  {		
    delay_ms(20);//去抖动 	
    	
    if(KEY0==0&&tem<4)//不是由组合按键松开而导致的KEY0=0			
      tem=1;		
    if(KEY1==0&&tem<4)//不是由组合按键松开而导致的KEY1=0			
      tem=2;		
    if(WK_UP==1)			
      tem=3;		
    if(KEY0==0&&KEY1==0)//组合键按下			
      tem=4;
  }	
  if(KEY0==1&&KEY1==1&&WK_UP==0)	
  {			
    key=tem;		
    tem=0;		
    return key;	
  }  	
  return 0;// 无按键按下或者按键没有被全部释放 
}

组合键按下过程:
1、KEY0按下,还没松开,此时第9行的if判断成立,tem=1,但由于KEY0按下还没松开,导致KEY0一直等于0,第18行的if判断不成立,所以返回0。

2、此时KEY1接着按下,且KEY0和KEY1都还没有松开,此时第15行的if判断成立,tem=4。

3、其中一个松开,因为tem=4,导致第9行和第11行的if判断不成立,所以tem维持4。并且由于只有一个按键松开,还有一个按键没有松开,因此第18行if判断不成立,所以返回0。

4、之后另一个按键也随之松开,第18的行if判断最终成立,key=tem=4,返回4,tem清0。

key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"
#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_6)//读取按键0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_12)//读取按键1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) #define KEY0_PRES 	1	//KEY0按下
#define KEY1_PRES	  2	//KEY1按下
#define WKUP_PRES   3	//KEY_UP按下(即WK_UP/KEY_UP)
#define k0k1_PRES 	4	//K0 K1按下
void KEY_Init(void);//IO初始化u8 KEY_Scan(u8);  	//按键扫描函数					    #endif

宏定义组合键k0k1_PRES,表示KEY0和KEY1都被按下。

编译仿真

打开仿真
在这里插入图片描述
设置断点
在这里插入图片描述
定义按键初始状态:
对于GPIOE引脚:PE.6和PE.12初始值为0
在这里插入图片描述

组合键运行过程:
1、按下KEY1不松
在这里插入图片描述

2、接着按下KEY0不松
在这里插入图片描述

3、KEY0松开
在这里插入图片描述
4、KEY1松开
在这里插入图片描述
程序断在了LED2=!LED2;说明case k0k1_PRES执行,组合键程序成功。
在这里插入图片描述

总结

1、初始化按键GPIO的时候需要注意按键的硬件连接方式,如果默认高电平,就选择输入上拉GPIO_Mode_IPU,如果默认为低电平,就选择输入下拉GPIO_Mode_IPD。

2、 static修饰的变量只执行一次,一开始运行就开辟了内存,内存放在全局。当再次调用,会继承上一次的值。

3、 不断扫描按键对应引脚的电平状态,如果电平有变化,消抖处理来消除抖动的影响后,再次判断是哪个引脚的电平发生了变化,返回相应的键值。电平没有变化就返回0。

按键扫描流程在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值