单片机中关于按键的优化

单片机中关于按键的优化


前言

实习了一个星期,就按键这个细节简单谈谈,好的按键反馈会让操作者心情愉悦,不好的按键体验会让操作者暴跳如雷,恨不得把板子吃了。平常用到的有三种按键方案:中断捕获按键,定时器扫描按键和while死等按键抬起。while死等就不用多说了,按下按键整个系统都停止工作了,体验极其不好,定时器扫描的体验会好很多,体验最好的莫过于中断捕获按键,但不巧的是在一些低端的芯片中,中断是宝贵的资源,功能简单的用中断捕获最好不过了,但是功能稍微复杂一点的中断就会被用于其他更重要的地方了,那么剩下的就只能用定时器扫描了。至于while死等按键抬起方案,在学校学学就可以了。


一、按键的工作原理?

将按键和地(负极)接在一起,单片机通过检测对应的引脚是否为低来判断按键是否按下,如果用while死等方案还要进行20ms的延时消抖,会占用cpu资源。定时器扫描不用消抖,因为定时器没20ms扫描一次按键,这个过程就已经消抖了。这个文章主要介绍我的定时器扫描按键。中断不做讲解。

二、定时器扫描实现过程:

开启一个1ms的定时器中断,定时器中断服务函数中定义一个静态变量Time_Cnt。用来检测进入了多少次中断。当Time_Cnt等于20时,将Key_Scan_Flag标志位置1(注:如果定时器比较空闲或者要处理的操作简单,比如给五六个变量赋值之类的简单操作,这个时候就可以不用Key_Scan_Flag标志位,直接在中断服务函数中进行赋值,本案例就没有用这个标志位,如果定时器要处理很多数据,就可以用这个标志位快进快出)。主循环中一直判断这个标志位,为1后调用Key_Scan函数。Key_Scan函数的功能是将两个全局变量before_state,now_state赋值,如果before_state = 1,now_state = 0,就表示按键1按下的一瞬间,before_state = 0,now_state = 1,就表示按键1弹起的一瞬间,before_state = 0,now_state = 0,就表示按键1持续按下,before_state = 1,now_state = 1,就表示按键1空闲状态。

代码实现:

1、key.c

unsigned char before_state=0,now_state=0;
unsigned char key_keyNumber;

unsigned char Key_GetState()
{
	if(S1==0){ return 1; }
	if(S2==0){ return 2; }
	if(S3==0){ return 3; }
	if(S4==0){ return 4; }
	return 0;
}

void key_loop()
{
	
	S1_flag = 1;
	S2_flag = 1;
	S3_flag = 1;
	S4_flag = 1;
	before_state = now_state;
	now_state = Key_GetState();		//获取按键标志位
	//上一个状态有按键按下,现在状态没有按键按下,说明处于按下后松手阶段	
}

2、Key.h

#ifndef _KEY_H__
#define _KEY_H__

#include <STC8H.H>//单片机包含头文件

#define Key_FREE (before_state == 0 && now_state == 0)     //没有按键按下

#define S1_PRESS (before_state ==1 && now_state == 0)     //S1弹起的一瞬间
#define S1_UPSRRING (before_state == 0 && now_state == 1) //S1按下的一瞬间
#define S1_KEEP (before_state == 1 && now_state == 1)     //S1保持(按下不松开)

#define S2_PRESS (before_state == 2 && now_state == 0)
#define S2_UPSRRING (before_state == 0 && now_state == 2)
#define S2_KEEP (before_state == 2 && now_state == 2)

#define S3_PRESS (before_state == 3 && now_state == 0)
#define S3_UPSRRING (before_state == 0 && now_state == 3)
#define S3_KEEP (before_state == 3 && now_state == 3)

#define S4_PRESS (before_state == 4 && now_state == 0)
#define S4_UPSRRING (before_state == 0 && now_state == 4)
#define S4_KEEP (before_state == 4 && now_state == 4)

/*按键对应的接口*/
sbit S1 = P1^4;
sbit S2 = P1^5;
sbit S3 = P1^6;
sbit S4 = P1^7;

extern bit S1_flag;
extern bit S2_flag;
extern bit S3_flag;
extern bit S4_flag;

void key_loop();
#endif

3、定时器初始化Timer.c

extern void key_loop();

void Timer0Init(void){     	//1毫秒@11.0592MHz
	AUXR |= 0x80;			//定时器时钟1T模式
	TMOD &= 0xF0;			//16位自动重载
	TL0 = 0xCD;				//设置定时初值
	TH0 = 0xD4;				//设置定时初值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	
	ET0=1;
	EA=1; 
}

void TM0_Rountine(void) interrupt 1
{
	static unsigned int key_scan=0;
	
	key_scan++;
	
	if(key_scan==20)//20ms按键检测
	{
		key_scan=0;
		key_loop();
	}
}

4、main函数

int main()
{
	unsigned char keep_temp=0,key_val = 0 ,num = 0;
	while(1)
	{
		if( S2_KEEP && S2_flag)//长按操作:长按每100ms加1,短按的话只要没有超过100ms就只加1,相当于短按一下按键。
		{
			keep_temp++;
			if(keep_temp==5)//长按了100ms
			{
				keep_temp=0;
				if( num  < 255 )//长按每100ms对num进行++操作
				{
					num++;
				}
			}
			S2_flag = 0;
		}
		
		if( S2_PRESS && S2_flag )//S2_flag防止多次触发按键动作
		{	
			keep_temp=0;
			key_val = before_state;//s2按下的一瞬间before_state=2,now_state = 0;
			S2_flag=0;
		}
		if( S3_UPSRRING && S3_flag )//S3抬起的一瞬间
		{
			keep_temp=0;
			key_val = now_state ;//s3抬起的一瞬间before_state=0,now_state = 3;
			S3_flag=0;
		}
		display(1,key_val);//数码管显示函数,1表示第一个数码管,key_val表示要显示的数值
	}
}

4、如果要求s1长按1秒:

main.c中加入:

extern bit Key_LongPress = 0 ;//Timer.c中的全局变量
static bit Key_LongPressFlag = 0 ;

if( S1_KEEP )//如果一直按下,给定时器一个信号,开始计时
{
	Key_LongPressFlag = 1;//告诉定时器开始计时
}
else 
{
	Key_LongPressFlag = 0;//如果中途抬起按键,告诉定时器停止计时并清除状态
}

if( Key_LongPress )//如果定时器发出了已经计时1S的信号。
{
	/*长按1S后的操作*/
	SEG_FlashFLAG=1;//给定时器信号
	Key_LongPress = 0;S1_flag=0;
}

定时器中断函数中加入:

static unsigned int Key_LongPressCnt=0;
if( Key_LongPressFlag )//如果main函数给了定时信号就开始计时
{
	Key_LongPressCnt++;
	if(Key_LongPressCnt > 999)//1s
	{
		Key_LongPress = 1;//告诉main函数,计时到了1秒,可以做出相应的操作
		Key_LongPressCnt=0;
	}
}
else//如果main中断了长按信号,定时器就清除计时并告诉main计时不足1S
{
	Key_LongPressCnt = 0;
	Key_LongPress = 0;	//告诉main计时不足1S
}

总结

以上就是我对定时器扫描按键的优化建议及代码实现,如果有更好的优化方案,欢迎留言。
注:本文章是我根据目前的经验写的,只是一种实现的方案,如果完全Ctrl + c的话,可能达不到预期的要求。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的喷火龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值