【单片机笔记】单个按键实现单击、双击、长按的实现

本文介绍了一种51单片机上的按键处理方法,实现了单击、双击及长按三种操作,并通过定时器区分不同的按键行为。该方法采用两层状态机进行按键事件的识别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接贴上源码和测试例程,附带的都有中文注释不多做解释。

底层驱动:

#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3

#define key_no 		0
#define key_click	1
#define key_double	2
#define key_long	3

#define key_input	P30
/***************************************************************************
程序功能:一个按键的单击、双击、长按。三种按键方式,然后做不同的处理。   
***************************************************************************/

static unsigned char key_driver(void)
{
	static unsigned char key_state_buffer1 = key_state_0;
	static unsigned char key_timer_cnt1 = 0;
	unsigned char key_return = key_no;
	unsigned char key;
	
	key = key_input;  //read the I/O states
	
	switch(key_state_buffer1)
	{
		case key_state_0:
			if(key == 0)
				key_state_buffer1 = key_state_1; 
				//按键被按下,状态转换到按键消抖和确认状态//
			break;
			
		case key_state_1:
			if(key == 0)
			{
				key_timer_cnt1 = 0;
				key_state_buffer1 = key_state_2;
				//按键仍然处于按下状态
				//消抖完成,key_timer开始准备计时
				//状态切换到按下时间计时状态
			}
			else
				key_state_buffer1 = key_state_0;
				//按键已经抬起,回到按键初始状态
			break;  //完成软件消抖
			
		case key_state_2:
			if(key == 1) 
			{
				key_return = key_click;  //按键抬起,产生一次click操作
				key_state_buffer1 = key_state_0;  //转换到按键初始状态
			}
			else if(++key_timer_cnt1 >= 100)  //按键继续按下,计时超过1000ms
			{
				key_return = key_long;  //送回长按事件
				key_state_buffer1 = key_state_3;  //转换到等待按键释放状态
			}
			break;
			
		case key_state_3:  //等待按键释放
			if(key == 1)  //按键释放
				key_state_buffer1 = key_state_0;  //切回按键初始状态
			break;
	}
	return key_return;
}

/***************************************************************************
函数功能:中层按键处理函数,调用底层函数一次,处理双击事件的判断,
                                        返回上层正确的无键、单击、双击、长按四种状态
本函数由上层循环调用,间隔10ms
***************************************************************************/
unsigned char key_read(void)
{
	static unsigned char key_state_buffer2 = key_state_0;
	static unsigned char key_timer_cnt2 = 0;
	unsigned char key_return = key_no;
	unsigned char key;
	
	key = key_driver();
	
	switch(key_state_buffer2)
	{
		case key_state_0:
			if(key == key_click)
			{
				key_timer_cnt2 = 0;  //第一次单击,不返回,到下个状态判断是否会出现双击
				key_state_buffer2 = key_state_1;
			}
			else 
				key_return = key;  //对于无键、长按,返回原事件
			break;
			
		case key_state_1:
			if(key == key_click)  //又一次单击,时间间隔小于500ms
			{
				key_return = key_double;  //返回双击事件,回到初始状态
				key_state_buffer2 = key_state_0;
			}
			else if(++key_timer_cnt2 >= 50)
			{
				//这里500ms内肯定读到的都是无键事件,因为长按大于1000ms
				//在1s前底层返回的都是无键
														
				key_return = key_click;  //500ms内没有再次出现单击事件,返回单击事件
				key_state_buffer2 = key_state_0;  //返回初始状态
							
			}
			break;
	}
	
	return key_return;
}

测试例程(基于51内核)

void main(void)
{
	u8 cnt_1ms=0,cnt_100ms=0;
	u8 key_value = 0;
	u8 led_flash=0;
	GPIO_Configuration();
	P3M1 &= ~(1<<0);P3M2 &= ~(1<<0); //P30准双向
	P1M1 &= ~(1<<2);P1M2 |=  (1<<2); //P12推挽
	
	P12=0;Delay_ms(500);P12=1;Delay_ms(500);IWDG_Feed();
	P12=0;Delay_ms(500);P12=1;Delay_ms(500);IWDG_Feed();
	P12=0;Delay_ms(500);P12=1;Delay_ms(500);IWDG_Feed();
	P12=0;Delay_ms(500);P12=1;Delay_ms(500);IWDG_Feed();
	
	enableInterrupts();
	IWDG_Configuration();

	while(1)
	{
		if(++cnt_1ms>=10)
		{
			cnt_1ms=0;
			key_value = key_read();
			if(key_value != key_no) led_flash = key_value*2;
		}
		if(++cnt_100ms>=100)
		{
			cnt_100ms=0;
			if(led_flash)
			{
				led_flash--;
				P12 = ~P12;
			}
		}
	}
}

By Urien 2018年6月7日 11:29:47


评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值