51蓝桥按键处理

一.基础 》1(独立按键检测 2(矩阵按键检测 3(定时器扫描矩阵按键 4(按键综合 

二.进阶 》1(状态机按键综合 2(矩阵按键底层 3(独立按键底层 4(独立按键使用实例 5(矩阵按键使用实例

·进阶部分用到的算法参考新型的按键扫描程序,仅三行程序 (amobbs.com 阿莫电子技术论坛),结尾附有原理图,所有程序均在开发板成功测试,不建议花过多的时间在基础部分在使用矩阵按键的时候,一定要将P34公共端跳线帽拔掉,否则第四列按键会收到干扰!


1)立按附键的检测

===========================================================================================
#include <STC15F2K60S2.H>
unsigned char key_Init()
{
	unsigned char keynum = 0;
	if(P30==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 1;}
	if(P31==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 2;}
	if(P32==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 3;}
	if(P33==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 4;}
	return keynum;
}
//简单的按键消抖:当按键按下的一瞬间延时20ms,按键不松开则陷入while循环,松开的一瞬间延时20ms
===========================================================================================


2)矩阵按键4*4普通扫描检测

=========================================================================================
#include <STC15F2K60S2.H>
#include <ALL.H>
unsigned char matrix_key()  //逐列逐行扫描,先扫描列在扫描行
{
	unsigned char keynum = 0;
	P44 = 0;P42 = 1;P35 = 1;P34 = 1;
	if(P30==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 1;}
	if(P31==0){Delay1ms(20);while(P31==0);Delay1ms(20);keynum = 5;}
	if(P32==0){Delay1ms(20);while(P32==0);Delay1ms(20);keynum = 9;}
	if(P33==0){Delay1ms(20);while(P33==0);Delay1ms(20);keynum = 13;}
	
	P44 = 1;P42 = 0;P35 = 1;P34 = 1;
	if(P30==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 2;}
	if(P31==0){Delay1ms(20);while(P31==0);Delay1ms(20);keynum = 6;}
	if(P32==0){Delay1ms(20);while(P32==0);Delay1ms(20);keynum = 10;}
	if(P33==0){Delay1ms(20);while(P33==0);Delay1ms(20);keynum = 14;}
	
	P44 = 1;P42 = 1;P35 = 0;P34 = 1;
	if(P30==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 3;}
	if(P31==0){Delay1ms(20);while(P31==0);Delay1ms(20);keynum = 7;}
	if(P32==0){Delay1ms(20);while(P32==0);Delay1ms(20);keynum = 11;}
	if(P33==0){Delay1ms(20);while(P33==0);Delay1ms(20);keynum = 15;}
	
	P44 = 1;P42 = 1;P35 = 1;P34 = 0;
	if(P30==0){Delay1ms(20);while(P30==0);Delay1ms(20);keynum = 4;}
	if(P31==0){Delay1ms(20);while(P31==0);Delay1ms(20);keynum = 8;}
	if(P32==0){Delay1ms(20);while(P32==0);Delay1ms(20);keynum = 12;}
	if(P33==0){Delay1ms(20);while(P33==0);Delay1ms(20);keynum = 16;}
	return keynum;
}
=========================================================================================

3)定时器扫描独立按键(跳线帽选择独立按键)

中断每20ms调用一次扫描函数key_loop();相当于实现了消抖;按键按下长按无反应,松开的时刻才会有;

============================================main.c=======================================
//定时器扫描按键与数码管
#include <STC15F2K60S2.H>
#include "ALL.H"
unsigned char keys;
void main()
{
	Timer0_Init();
	switch_138(5);P0 &= 0XAF;
	while(1)
	{
		keys = key_set();
		if(keys){nixie_set(1,keys);}
	}
}

void Timer0_Rutine() interrupt 1
{
	static unsigned char c1=0,c2=0;
	c1++;c2++;
	if(c1==2){nixie_loop();c1=0;}
	if(c2==20){key_loop();c2=0; }
	
}
===============================================key.c=====================================
#include <STC15F2K60S2.H>
sbit K1 = P3^0; sbit K2 = P3^1; sbit K3 = P3^2; sbit K4 = P3^3;
unsigned char keynum;
unsigned char key_set()
{
	unsigned char cash=0;//中转
	cash = keynum;
	keynum = 0;
	return cash;
}
unsigned char key()//在中断中每隔20ms调用一次,相当于进行了消抖
{
	unsigned char num=0;
	if(K1==0){num = 1;} if(K2==0){num = 2;} if(K3==0){num = 3;} if(K4==0){num = 4;}
	return num;
}
void key_loop()//松手检测,放中断中,if上一刻按键按下和现在按键没有按下,返回对应值
{
	static unsigned char lastdata,nowdata;
	lastdata = nowdata;nowdata = key();
	if(lastdata==1&&nowdata==0){keynum = 1;}
	if(lastdata==2&&nowdata==0){keynum = 2;}
	if(lastdata==3&&nowdata==0){keynum = 3;}
	if(lastdata==4&&nowdata==0){keynum = 4;}
}
=========================================================================================

4)按键综合 按键的长按、短按、双击检测

若要调整单击双击或长按灵敏度,调整COUNT2、COUNT3的数值即可。实现功能:单击NUM+1,双击归中NUM=50,长按两秒及以上NUM清零

//==================================================主函数==============================
#include <STC15F2K60S2.H>
#include "MAIN.H"
unsigned char COUNT1;
unsigned int COUNT2;
extern char NUM,KEY_FLAG,STATE;
extern int COUNT3;
void main()
{
  TIMER1_INIT();
	while(1)
	{
		NIXIE_SET(1,NUM/10);NIXIE_SET(2,NUM%10);
		SCAN_KEY();
	}
}

void Timer1() interrupt 3
{
	COUNT1++;if(COUNT1==2){COUNT1=0;NIXIE_LOOP();}
	if(KEY_FLAG){COUNT2++;}
	if(STATE){COUNT3++;}
}

//========================================================key.c=========================

#include <STC15F2K60S2.H>
#include "MAIN.H"
sbit S1=P3^0;
unsigned char KEY_FLAG,NUM,STATE=1;
unsigned int COUNT3;
extern int COUNT2;

void DELAY1MS(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms)
	{
		i = 12;j = 169;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}
void SCAN_KEY()
{
	if(S1==0)
	{
		DELAY1MS(10);
		if(S1==0){COUNT2=0;KEY_FLAG=1;}
		while(S1==0){NIXIE_SET(1,NUM/10);NIXIE_SET(2,NUM%10);}KEY_FLAG=0;
		if(COUNT2<200)
		{
			STATE++;
			if(COUNT2<200&&COUNT3>250){NUM++;NUM%=100;COUNT3=0;STATE=1;}//单击
			if(STATE==2&&COUNT2<250){NUM=50;STATE=1;COUNT3=0;}//双击
		}
		if(COUNT2>2000){NUM=0;}//长按
	} 
}
//=======================================================================================








1) 状态机——按键综合(单双击,长按)

@annotation :适用于单个按键检测,本程序实现单双击及长按检测的功能,其中单双击标志建立后只执行一次,长按标志建立后若仍在按下状态则持续返回长按标志。这里把持续返回长按标志单独拿出来建立了一个状态,方便大家理解。以下程序中出现到的其它模块可以参考我的其它文章。

79a28121a837484f8177215955fa2261.png

/****************************************************************************************
* @data :2024/2/15/23:50
* @pattern :IO
* @IRC: 12MHZ
* @MCU :STC15F2K60S2
******************************************************************************************/
#include "main.h"
sbit K1 = P3^0;
//============================================================
//@brief	:state_0(按键按下检测), state_1(确认按键按下并完成消抖),state_2(长按逻辑检测),state_3(长按释放检测),state_4(单双击检测),state_5(单双击检测);
typedef enum 
{state_0 = 0,state_1,state_2,state_3,state_4,state_5} states;

//@brief	:N_KEY(返回无标志),S_KEY(单击标志),D_KEY(双击标志),L_KEY(长按标志)
typedef enum
{N_KEY = 5,S_KEY,L_KEY,D_KEY} flags;

//@brief	:key_error_time(消抖时间设定3*10 = 30ms),key_long_time(长按时间设定),key_double_time(双击间隔设定)
typedef enum
{key_error_time = 2,	key_long_time = 100,  key_double_time = 20} times;
//============================================================

u8 key_drive(void)
{
	 static u8 key_time = 0,key_return = 0,state = 0;     //keytime(计时变量) ,key_return(返回间接变量), state(状态变量)
     key_return = N_KEY;                                                //初始化key_return;
     switch(state)                                                              //选择状态,每调用一次Switch,以下的case中只会执行一个
	 {
		 case 0:                                                                   //仅检测是否按下
		 {
		      if(!K1){key_time = 0;state = state_1;}             //如果按下,清零计时变量
			  else {state = state_0;key_return = N_KEY;}   //没有按下,返回无标志
		 }break;
		 
		 case 1:                                                                    //状态1,用于完成消抖和确认按键按下
		 {
		      if(!K1){key_time++; if(key_time>=key_error_time){key_time=0; state = state_2;}} //计时变量值大于设定抖动值,确认按键按下
			  else{key_time=0; state = state_0;}																				 //抖动
		 }break;
		 
		 case 2:                                                                //状态2,用于长按的判断,先不返回长按标志                    
		 {			   
			  if(!K1){key_time++; if(key_time>=key_long_time){key_time=0; state = state_3;}}  //如果计时变量值大于设定长按值,判定长按,先不返回长按标志,进入长按释放检测逻辑
				else {state = state_4;}                                                                                       //else ,进入状态4判断单双击
		 }break;
		 
		 case 3:                                                            //状态3,长按释放逻辑检测,把这个状态独立出来的目的主要是为了长按状态下持续触发,松手停止触发,若只想触发一次,直接在上个状态返回长按标志即可
		 {
			 if(!K1)
			 {
				key_return =  L_KEY;
			 }
			 else {key_time=0;state = state_0;}          //长按释放,回到状态0,等待新的响应
		 }break;		 
			 
		 case 4:                 																								                  //单双击检测,能进入到状态4就说明了前面已经出发了一次单击
		 {	  key_time++;
			 if(key_time<=key_double_time) {if(!K1){key_return=D_KEY; key_time = 0; state = state_5;}}    // 在设定的双击间隔时间内若按键再次被触发,被判定为双击 ,进入按键5等待按键释放
			 else{key_return = S_KEY; key_time=0; state = state_5;}                                                                 //超过这个时间,被判定为单击,进入状态5等待按键释放
		 }break;
		 
		 case 5:                                                                               									                   //按键释放检测
		 {
			   key_time++;
			   if(key_time>=key_error_time)        //释放消抖
			   {
				   if(K1){state = state_0;} 
			   }		  
		 }break;
		 
		 default:          //预料之外的情况,直接重启程序
		 {
		     state = state_0;
		 }break;
		
	 }
	 return key_return;
}



//按键综合示例代码
#include "MAIN.H"
u8 Key_flag, KeyValue, NUM;
void main()
{
	Delay1ms(100);     //开机延时,使电路达到稳定状态
    SYS_Init();
	Nixie_Cartoon();  //开机动画
	while(1)
	{
		if(Key_flag)
		{   Key_flag = 0;            
			KeyValue = key_drive();
			switch(KeyValue)
			{
				case 5:{;}break;  //按键空闲状态
				case 6:{NUM++;}break;    //按键单击状态
				case 7:{NUM++;NUM%=100;Display_Set(1,NUM/100,0); Display_Set(2,NUM%100/10,0); Display_Set(3,NUM%10,0);}break; //按键长按状态
				case 8:{NUM = 0;}break;    //按键双击状态
			}
		}
		Display_Set(1,NUM/100,0); Display_Set(2,NUM%100/10,0); Display_Set(3,NUM%10,0);		
	}
}
void T0() interrupt 1
{
    static u8 Tube_Count;
	static u8 Key_Count;
	Tube_Count++; if(Tube_Count==2){Tube_Count = 0;Display_Loop();}
	Key_Count++;if(Key_Count==10){Key_Count=0;Key_flag=1;}
}

2)算法——矩阵按键底层驱动

#include "main.h"
typedef enum{S7_S19=0X01,S6_S18=0X02,S5_S17=0X04,S4_S16=0X08}keys;
u8 data rd[12] = 0;
void KEY_SCAN()                                       
	{  /*采用定列扫行,为避免冲突,公共端跳线帽必须拔掉。20ms调用一次实现消抖*/ 
	 static u8 i = 0;                   
	 P4 = P3 = 0XFF; P3 =~ (0X80 >> i); P44 = P37; P42 = P36; 
     rd[0+3*i] = P3 ^ 0XFF;                      
	 rd[1+3*i] = rd[0+3*i] & (rd[0+3*i] ^ rd[2+3*i]); 
	 rd[2+3*i] = rd[0+3*i];
     i++; i%=4;  
    }

3)算法——独立按键底层驱动

#include "main.h"
/******************************************************************************************/
typedef enum{S4 = 0X08,S5 = 0X04,S6 =0X02,S7 = 0X01}keys;
u8 data rd[3]=0; 
/***************************************************************************************/
void KEY_SCAN()
{	P3 &= 0XEF;
	rd[0] = P3 ^ 0XFF;
	rd[1] = rd[0] & (rd[0] ^ rd[2]);
	rd[2] = rd[0];
}
/***************************************************************************************/
	

4)算法——独立按键使用实例

#include "main.h"
/******************************************************************************************/
typedef enum{S4 = 0X08,S5 = 0X04,S6 =0X02,S7 = 0X01}keys;
u8 data rd[3]=0; 
u8 data press_cnt = 0; //按下次数
bit data s_flag = 0,d_flag = 0,l_flag = 0,w_flag = 0;/*单、双、长、误标志位*/
u8 data NUM = 0;
/******************************************************************************************/
void KEY_SCAN()
{	P3 &= 0XEF;
	rd[0] = P3 ^ 0XFF;
	rd[1] = rd[0] & (rd[0] ^ rd[2]);
	rd[2] = rd[0];
}
/******************************************************************************************/
/*基于释放检测对单个按键附加单击,双击,长按三种功能*/
void KEY_CONFIG()
{  static u8 s_cnt = 0, l_cnt  = 0;/*计时变量*/                
	 if(rd[1]&S4){press_cnt ++;} 
	 if(rd[2] & S4) l_cnt ++;		
	 if(!(rd[2] & S4))/*完全释放*/                   
	 {	if(press_cnt != 0)
			{	s_cnt ++; 							
/*双击*/if(s_cnt <= 30 && press_cnt == 2){ press_cnt = 0; s_cnt = 0; d_flag = 1; } 
/*单击*/else if(s_cnt >= 15 && press_cnt != 2){ press_cnt = 0; s_cnt = 0; if(w_flag){s_flag =0;w_flag = 0;}else s_flag = 1;}//w_flag是告诉释放检测,我正在执行长按	
/*长按*/if(l_cnt >= 75){l_cnt = 0; l_flag = 1; w_flag = 1; }
						else l_cnt = 0;
			}
	 }
}		

5)算法,矩阵按键使用实例同独立按键使用方法


462116a9b89c43c8bc37f54f9e8c254f.png

2facdf4f900848b99c6ace29964e8588.png

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值