STM32-蓝桥杯嵌入式之三行按键检测(按键的长、短,单击、双击)

STM32-蓝桥杯嵌入式之三行按键检测(按键的长、短,单击、双击)


首先先说说异或,异或即相同为0不同为1,可以通过使用异或来判断某一状态发生改变,思路:如果当前状态和上一状态一样,即没有发生改变,那么当前状态和上一状态相异或得到的结果就是0,否则得到的结果不是0,所以通过异或可以用于检测按键的状态发生改变。

后面附上了按键检测程序的核心代码,现在来过一遍:

key_Value |= GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) | (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) << 1) | \
					(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) << 2) | (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2) << 3);
key_Value = key_Value ^ 0xff;		// key1按下,key_state = 0x01,如果没有按键按下,则key_Value = 0

上面这一段程序的作用是检测当前按键的状态,首先key_Value这个变量将按键的状态读取出来,
假设key1按键按下,最后转换得到的key_Value = 0x01,
假设key2按键按下,最后转换得到的key_Value = 0x02,
假设key3按键按下,最后转换得到的key_Value = 0x04,
假设key4按键按下,最后转换得到的key_Value = 0x08,

//Key_Trg = (!key_Value) * (key_Value ^ key_State); // 检测按键的下降沿
	
	Key_Trg = key_Value & (key_Value ^ key_State); // 检测按键的上降沿
	// 情况1:key1按键第一次按下,此时key_State,Key_Trg = 0x01
	// 情况2:key1按键持续按下,第二次扫描的时候,此时key_State = 0x01,Key_Trg = 0
	// 情况3:如果没有按键按下,则key_Value = 0,Key_Trg = 0
	// 由以上的分析可以看出,Key_Trg只有在按键的下降沿那一时刻有值。

上面这一段代码就是检测按键的上升沿和下降沿,这里就用到了我们之前说到的异或,程序中(key_Value ^ key_State)这一段代码通过异或得到了哪一个按键的状态发生了变化

一、检测按键下降沿分析

Key_Trg = key_Value & (key_Value ^ key_State);通过当前按键的状态key_Value去和 (key_Value ^ key_State)相与,

  1. 此时我们假设key1当前时刻被按下,那么key_Value 此时就应该等于0x01,显然此时的按键状态发生了变化,由之前的松开变成了按下状态,那么(key_Value ^ key_State)的值就等于0x01,所以最终结果是0x01,表示当前时刻按键被按下。
  2. 如果按键持续被按下是个什么情况呢,按键被持续按下,那么按键的状态就没有发生变化,由我们上面的分析可知,(key_Value ^ key_State)就为0,那么 key_Value & (key_Value ^ key_State)的值就应该为0啦

二、检测按键上升沿分析

Key_Trg = (!key_Value) * (key_Value ^ key_State);

这一段程序可以用来检测按键的上升沿,为什么要把key_Value取反,然后乘上状态的改变呢?下面来分析分析。
我们知道一个按键的上升沿,就是按键由按下变成松开,首先按键的状态肯定改变了对吧,同样可以用 (key_Value ^ key_State)来记录改变状态的按键,我们要的是按键被释放的状态发生改变的按键,被释放的时候key_Value 应该是为0的,如果被按下key_Value 是不为0的,所以我们要的是key_Value = 0时刻发生变化的按键,所以我们通过将key_Value 取反,如果按键松开,取反后就是1,1乘以发生变化的按键得到的就是变化的按键啦,到此我们的问题就解决了。但是我在写程序的后,最开始写的(!key_Value) &(key_Value ^ key_State),为什么这样子不行呢?经过一阵的深思熟虑过后终于找到根源所在,如果(!key_Value)的值只能是0或者1,变化成二进制不就是0b00000001或者0b00000000了吗?然后与上(key_Value ^ key_State),那么我们就最多只能取出最低位,而将其他的位全部清零了,所以如果这样写的话,最多就只能检测出key1,所以是行不通滴…

三、按键检测程序的核心代码

void Key_Read(void)
{
    
    
	uint8_t key_Value = 0xF0;
	key_Value |= GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) | (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) << 1) | \
					(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) << 2) | (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2) << 3);
	key_Value = key_Value ^ 0xff;		// key1按下,key_state = 0x01,如果没有按键按下,则key_Value = 0
	
	//Key_Trg = (!key_Value) * (key_Value ^ key_State); // 检测按键的下降沿
	
	Key_Trg = key_Value & (key_Value ^ key_State); // 检测按键的上降沿
	// 情况1:key1按键第一次按下,此时key_State,Key_Trg = 0x01
	// 情况2:key1按键持续按下,第二次扫描的时候,此时key_State = 0x01,Key_Trg = 0
	// 情况3:如果没有按键按下,则key_Value = 0,Key_Trg = 0
	// 由以上的分析可以看出,Key_Trg只有在按键的下降沿那一时刻有值。
	key_State = key_Value;
	// key_State即保存的是扫描过后的按键的值
}

按键扫描程序的代码

void Key_Scan(void)	// 在主程序中10ms调用一次
{
    
    
	Key_Read();
	if(Key_Trg == 0x01)	// key1下降沿
	{
    
    
		LED_Ctrl(0x10,ENABLE);
	}
	else if(Key_Trg == 0x02)	// key2下降沿
	{
    
    
		LED_Ctrl(0x20,ENABLE);
	}
	else if(Key_Trg == 0x04)	// key3下降沿
	{
    
    
		LED_Ctrl(0x40,ENABLE);
	}
	else if(Key_Trg == 0x08)	// key4下降沿
	{
    
    
		LED_Ctrl(0x80,ENABLE);
	}
	
	if(key_State == 0x01)	// key1长按检测
	{
    
    
		key_pressed_time+=10;
		if(key_time != 0xffff && ++key_time >= 50)	// 由于是10ms调用一次按键检测,所以计数到50就是500ms,按键按下达到500ms就认为是长按键
		{
    
    
			key_time = 0xffff;	// 防止重复进入次程序,在松开按键的时候,将key_time置零			
		}
	}
	
	if(key_State == 0 && Key_Trg == 0)
	{
    
    
		key_pressed_time = 0;
		key_time = 0;
		LED_Ctrl(0xF0,DISABLE);
	}
}

三、按键检测单击与双击的核心代码

在这里插入图片描述
如上图,编号①表示第一次按键按下,编号②表示第二次按键按下。我们如果要检测按键的双击与单击,我们肯定就要知道两次按键之间的间隔时间,也就是t,表示第一次按键的上升沿(即松开的时刻)与第二次按键的下降沿(即按键按下的时刻)之间的时间,如果时间在指定的范围内,则判断为双击,否则超时判断为单击

// 按键状态读取函数
void Key_Read(void)
{
    
    
	uint8_t key_Value = 0xF0;
	key_Value |= GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) | (GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8) << 1) | \
					(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) << 2) | (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2) << 3);
	key_Value = key_Value ^ 0xff;		// key1按下,key_state = 0x01,如果没有按键按下,则key_Value = 0
	
	Key_Trg_Rising = (!key_Value) * (key_Value ^ key_State); // 检测按键的下降沿
	
	Key_Trg_Falling = key_Value & (key_Value ^ key_State); // 检测按键的上降沿
	// 情况1:key1按键第一次按下,此时key_State,Key_Trg = 0x01
	// 情况2:key1按键持续按下,第二次扫描的时候,此时key_State = 0x01,Key_Trg = 0
	// 情况3:如果没有按键按下,则key_Value = 0,Key_Trg = 0
	// 由以上的分析可以看出,Key_Trg只有在按键的下降沿那一时刻有值。
	key_State = key_Value;
	// key_State即保存的是扫描过后的按键的值
}
// 按键扫描函数
void Key_Scan(void)
{
    
    
	Key_Read();	// 读一次按键的状态
	
	if(key1PressNumber == 1)	// 检测到一次按键的上升沿,开始计时
	{
    
    
		key1Time += 10;			// 由于按键程序是10ms扫描一次,所以这里加10ms
		if(key1Time >= 150)	// 如果超过1500ms,表示按键是单击,初始化双击检测
		{
    
    
			// begin
			// 在这里添加按键单击的代码
			LCD_DisplayStringLine(Line8,(unsigned char *)"SINGLE              ");
			// end
			key1Time = 0;
			key1PressNumber = 0;
		}
	}
	
	if(Key_Trg_Rising == 0x01)	// 按键的上升沿,即按键松开
	{
    
    
		if(key1PressNumber == 0)	// 表示按键是第一次按键的上升沿
		{
    
    
			key1PressNumber = 1;		// 做个标记
		}
	}
	
	if(Key_Trg_Falling == 0x01)	// 检测到按键的下降沿
	{
    
    
		LED_Ctrl(0x10,ENABLE);		
		if(key1PressNumber == 1)	// 如果已经捕获到了一次按键的上升沿,并且现在还没有超时,那么这就是一个双击的动作
		{
    
    
			// begin
			// (在这里添加双击需要处理的程序)
			// end
			LCD_DisplayStringLine(Line8,(unsigned char *)"DOUBLE              ");	
			key1Time = 0;
			key1PressNumber = 0;		// 初始化按键双击相关的变量
		}		
	}
	else if(Key_Trg_Falling == 0x02)
	{
    
    
		LED_Ctrl(0x20,ENABLE);
	}
	else if(Key_Trg_Falling == 0x04)
	{
    
    
		LED_Ctrl(0x40,ENABLE);
	}
	else if(Key_Trg_Falling == 0x08)
	{
    
    
		LED_Ctrl(0x80,ENABLE);
	}
	
	if(key_State == 0x01)
	{
    
    
		key_pressed_time+=10;
		if(key_time != 0xffff && ++key_time >= 50)
		{
    
    
			key_time = 0xffff;	// 防止重复进入次程序,在松开按键的时候,将key_time置零			
		}
	}
	
	if(key_State == 0 && Key_Trg_Falling == 0)	// 按键松开
	{
    
    
		key_pressed_time = 0;
		key_time = 0;
		LED_Ctrl(0xF0,DISABLE);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/113004685