蓝桥杯嵌入式学习日记(三)——按键的长按、短按与双击(三行按键法)【STM32】【HAL库】

一、查阅相关资料

  想要进行一块板子的开发,需要先查阅资料了解器件连接。
在这里插入图片描述
  从CT117E-M4产品手册中不难发现,按键分别有PB0、PB1、PB2、PA0分别对应B1、B2、B3、B4四个按键。

按键抖动
在这里插入图片描述
在这里插入图片描述
  我们所使用这类按键时,当按键按下,内部的机械触点因为弹性,并不会立马导通,同样松开时也不是立马分离,而是会产生一连串的抖动,这段时间大概在5~10ms,为了防止紊乱我们要进行消抖。这里我选择利用uwTick进行软件延时,实际情况中也可以使用定时器,但不建议使用HAL_Delay函数进行延时。

	if( uwTick - KeyTick < 10) return ;//建议至少大于等于10ms,但不要过大,例如超过100ms
	KeyTick = uwTick ;

了解了这些,我们可以进行STM32cubemx工程创建。依旧是上一篇的工程进行延展。

二、程序的编写

1、创建工程

在这里插入图片描述
如图设置完以后便可以生成工程进行编写,不会利用STM32cubemx生成工程的可以参考上一篇利用STM32cubemx创建工程并以STM32G4点灯【HAL库】
依旧是照例创建key.c和key.h方便管理。

2、三行按键法

#define B1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) 
#define B2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) 
#define B3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) 
#define B4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) 
#define Key_Flag  B1 | (B2<<1) | (B3<<2) | (B4<<3) | 0xf0      //初始端口状态

u8 Trg ;//Triger
u8 Cont ;//Continue

void Key_Scan()
{
	u8 ReadDate = Key_Flag ^ 0xff ;
	Trg = ReadDate & (ReadDate ^ Cont) ;
	Cont = ReadDate ;
}

想要理解这三行代码需要了解下最基本的与或知识,对于学过数电的同学可能已经掌握,可以直接跳过。

1为真值
0 为假值

与(and)(&)
逻辑表达式:F=AB
运算规则为:全1为1 ,有0为0 。真值表为

操作数1操作数2结果值
111
100
010
000

或(or)(|)
逻辑表达式:F=A+B
运算规则为:全0为0,有1为1 。真值表为

操作数1操作数2结果值
111
101
011
000

异或(XOR)(^)
运算规则为:相异为1,相同为0 。真值表为

操作数1操作数2结果值
110
101
011
000

了解了基础知识再来进行推理就很简单了。

1、当没有按键按下的初始状态

Key_Flag = 0xff ;
ReadDate = 0xff^0xff = 0000 0000 ;
Trg = 0000 0000 ;
Cont = 0000 0000 ;

2、当B1被按下

Key_Flag = 0xfe ;
ReadDate = 0xfe ^ 0xff = 0x01 ; 
Trg = 0x01 & (0x01 ^ 0x00) = 0x01 ;
Cont = 0x01 ;

3、当B1被长按

Key_Flag = 0xfe ;
ReadDate = 0xfe ^ 0xff = 0x01 ;
Trg = 0x01 & (0x01 ^ 0x01) = 0x00 ;
Cont = 0x01 ;
一步步来理解就是当初始状态没有按键被按下时,Key_Flag 为0xff

ReadDate = Key_Flag ^ 0xff = 0xff ^ 0xff // 对于0xff取反则为0000 0000 ;
Cont默认为0x00 
Trg = ReadDate & (ReadDate ^ Cont) = 0x00 & ( 0x00 ^ 0x00) = 0x00


-------------------------------------------------------------------
当有按键被按下例如B1,则有 Key_Flag 为 0xfe

ReadDate = Key_Flag ^ 0xff = 0xfe ^ 0xff = 0x01 // 0000 0001
Trg = ReadDate & (ReadDate ^ Cont) = 0x01 & (0x01 ^ 0x00) = 0x01 & 0x01 = 0x01 
Cont = 0x01 


-------------------------------------------------------------------
当有按键被持续按下例如B1,则有 Key_Flag 为 0xfe

此事件发生前Cont = 0x01

ReadDate = Key_Flag ^ 0xff = 0xfe ^ 0xff = 0x01 // 0000 0001
Trg = ReadDate & (ReadDate ^ Cont) = 0x01 & (0x01 ^ 0x01) =  0x01 & 0x00 = 0x00 
Cont = 0x01 

在结束按按键之后即所有回复0x00的状态。
如有不懂的同学,可以自己动手推到一两次即可理解。

3、短按与长按

  在了解上述内容后,后续只需要进行逻辑判断即可。
  
短按
  判断Trg的值即可进行按键的判断

按键KB1KB2KB3KB4
Trg0x010x020x040x08
void KEY_Judge()
{
	if( uwTick - KeyTick < 10) return ;
	KeyTick = uwTick ;
	
	Key_Scan() ;
//短按
	if(Trg & 0x01)  LED_Control(0x01) ; //B1
	if(Trg & 0x02)  LED_Control(0x02) ; //B2
	if(Trg & 0x04)  LED_Control(0x04) ; //B3
	if(Trg & 0x08)  LED_Control(0x08) ; //B4
}

通过上篇LED进行按键反馈。B1 ~ B4对于LED1 ~ LED4。

长按
  长按相较于短按只需要加入时间来进行判断Cont即可。

//长按
	if(Trg & 0x01)  ContTick = uwTick ; //B1
	if(Trg & 0x02)  ContTick = uwTick ; //B2
	if(Trg & 0x04)  ContTick = uwTick ; //B3
	if(Trg & 0x08)  ContTick = uwTick ; //B4
///B1
	if( Cont & 0x01 ) 
	{
		if(uwTick - ContTick < 1000) return ;
		{
			LED_Control (0x00) ;
		    ContTick = uwTick ;
		}
	}
//省略

这里设置短按B1则LED1亮起,长按一秒关闭。

4、双击

双击

  双击只需要加入标志位来判断,并且人手设置双击时间小于200ms即可(自适应)。这里需要填入一位变量来记录按键输入值。

#define Key_Temp  ((B1 | (B2<<1) | (B3<<2) | (B4<<3) ) ^ 0x0f)    //端口状态

经过上面学习应该对于Key_Temp的值有所了解,可以试着自己算一算。

	Key_Down = Key_Temp & (Key_Old ^ Key_Temp) ;   //按下为按键值
	Key_Up  = ~Key_Temp & (Key_Old ^ Key_Temp) ;  //松手为抬起前的按键值
	Key_Old = Key_Temp ;

这里跟上面三行按键类似不再进行分析,如果有不会的可以在评论区询问。

	if(Key_Up)	DoubleTick = uwTick;		//判断按键上升沿弹起并记录当前时间 

	if(uwTick - DoubleTick >=200 ) //200ms以内执行以下操作
	{
		switch(Key_Down) 
		{
			case 1 : LED_Control(0x01) ; break ;
			case 2 : break ;
			case 3 : break ;
			case 4 : break ;
		}
		if(Cont & 0x01)
		{
			if(uwTick - ContTick < 1000) return ;
			{
				LED_Control (0x00) ;
				ContTick = uwTick ;
		    }
		}
	}
	else //反之
	{
			switch(Key_Down) 
			{
				case 1 : LED_Control(0xff) ; break ;
				case 2 : break ;
				case 3 : break ;
				case 4 : break ;
			}
	}
}

这里只放出了B1的短按、长按和双击,对应的现象分别是,LED1亮起、LED1一秒熄灭和LED1~8全部亮起。如有不懂,可以在评论区留言。这里放上完整工程,如有需要自行下载。

链接:https://pan.baidu.com/s/1KmYiYB84tBnmpjXxt94sFg?pwd=1111
提取码:1111

  • 45
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
Arduino和STM32都是常用的单片机开发板,用于各种电子项目和嵌入式系统的开发。下面我将用300字介绍如何使用这两种开发板编写按键按和短按的程序。 对于Arduino开发板,可以使用内置的库函数来实现按键按和短按的功能。首先,需要通过数字IO口连接一个机械按键并设置为输入模式。然后,在程序中设置一个计时器,用来检测按键的按下时间。通过判断按键按下的持续时间来确定是按还是短按。 以下是Arduino的示例代码: ```Arduino const int buttonPin = 2; // 连接按键的数字IO口 int buttonState = 0; // 按键状态 unsigned long buttonTimer = 0; // 按键计时器 bool isLongPress = false; // 是否按 void setup() { pinMode(buttonPin, INPUT); Serial.begin(9600); } void loop() { buttonState = digitalRead(buttonPin); if (buttonState == HIGH) { if (buttonTimer == 0) { buttonTimer = millis(); // 记录按下的起始时间 } } else { if (buttonTimer != 0) { if (millis() - buttonTimer > 1000) { isLongPress = true; } else { isLongPress = false; } buttonTimer = 0; // 清空计时器 } } if (isLongPress) { Serial.println("Button long press"); } else { Serial.println("Button short press"); } delay(100); // 延时100毫秒 } ``` 对于STM32开发板,可以使用CubeMX和HAL库来实现按键按和短按的功能。首先,在CubeMX中配置按键对应的GPIO口,并设置为输入模式。然后,在程序中使用HAL库提供的函数来检测按键的状态和按下时间。通过判断按键按下的持续时间来确定是按还是短按。 以下是STM32的示例代码: ```C #include "main.h" #include "stdio.h" #include "stm32f1xx_hal.h" GPIO_TypeDef* BUTTON_GPIO_Port = GPIOA; uint16_t BUTTON_Pin = GPIO_PIN_0; uint32_t buttonTimer = 0; _Bool isLongPress = 0; void SystemClock_Config(void); static void MX_GPIO_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); while (1) { if (HAL_GPIO_ReadPin(BUTTON_GPIO_Port, BUTTON_Pin) == GPIO_PIN_SET) { if (buttonTimer == 0) { buttonTimer = HAL_GetTick(); } } else { if (buttonTimer != 0) { if (HAL_GetTick() - buttonTimer > 1000) { isLongPress = 1; } else { isLongPress = 0; } buttonTimer = 0; } } if (isLongPress) { printf("Button long press\n"); } else { printf("Button short press\n"); } HAL_Delay(100); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /**Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /**Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_AFIO_CLK_ENABLE(); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` 以上就是使用Arduino和STM32开发板编写按键按和短按程序的方。你可以根据自己的需求进行修改和扩展。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lumine.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值