第九章 输入设备—按键检测

目录

9.1 实验要求

9.2 硬件设计

9.2.1 按键消抖

9.3 软件设计

9.3.1 按键引脚宏定义

9.3.2 按键GPIO初始化函数

9.3.3 检测按键的状态

9.3.4 主函数

9.4 下载验证

9.5 拓展实验(按键控制LED灯的功能)

9.5.1 任务描述

9.5.2 代码实现


9.1 实验要求

        使用按键控制LED灯亮不同的颜色。

9.2 硬件设计

        从按键的原理图可知,这些按键在没有被按下的时候, GPIO 引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时, GPIO 引脚的输入状态为高电平 (按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。 

9.2.1 按键消抖

        按键消抖是指在使用机械式按键时,由于按下或松开按键时会产生瞬间的接触反弹现象,导致电路系统可能会误判按键的状态。为了解决这个问题,需要对按键进行消抖处理。具体来说,按键消抖可以通过以下几种方式实现:

        硬件滤波:在电路中加入电容、电阻等元器件,利用它们的特性对按键信号进行过滤和处理,从而消除抖动。

        软件延时:在程序中添加一定的延时,在检测到按键变化后等待一段时间,再进行状态检测,从而避免误判。

        状态机检测:利用状态机的思想,记录按键的前一状态和当前状态,并根据状态转换的规则进行判断,从而消除按键抖动。

        总之,按键消抖是在电子设备中常见的技术应用,能够提高设备的可靠性和稳定性。

9.3 软件设计

        同项目一的工程,为了使工程更加有条理,我们把按键相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_button.c”及“bsp_button.h”文件,这些文件也可根据您的喜好命名,这些文件不属于 STM32 标准库的内容,是由我们自己根据应用需要编写的。

9.3.1 按键引脚宏定义

#ifndef _BSP_BUTTON_H
#define _BSP_BUTTON_H

#include "stm32f10x.h"
//按键状态标志位
#define BUTTON_ON					1
#define BUTTON_OFF					0

//按键1
#define BUTTON1_GPIO_PORT			GPIOA
#define BUTTON1_GPIO_CLK			RCC_APB2Periph_GPIOA
#define BUTTON1_GPIO_PIN			GPIO_Pin_0

//按键2
#define BUTTON2_GPIO_PORT			GPIOC
#define BUTTON2_GPIO_CLK			RCC_APB2Periph_GPIOC
#define BUTTON2_GPIO_PIN			GPIO_Pin_13

void BUTTON_GPIO_Config(void);
uint8_t BUTTON_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);

#endif /*_BSP_BUTTON_H*/

        在这段程序中对按键所用到的GPIO,对应的PIN脚,以及时钟进行了宏定义,便于代码的移植,除了这些内容代码中还定义了按键检测的状态标志位,bsp_button.c文件中编写的功能函数的声明。

9.3.2 按键GPIO初始化函数

void BUTTON_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(BUTTON1_GPIO_CLK|
						   BUTTON2_GPIO_CLK,ENABLE);
	
	//模式设置位浮空输入
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	
	//按键1的初始化
	GPIO_InitStruct.GPIO_Pin = BUTTON1_GPIO_PIN;
	GPIO_Init(BUTTON1_GPIO_PORT,&GPIO_InitStruct);
	
	GPIO_InitStruct.GPIO_Pin = BUTTON2_GPIO_PIN;
	GPIO_Init(BUTTON2_GPIO_PORT,&GPIO_InitStruct);
}

        同为 GPIO 的初始化函数,初始化的流程与“LED GPIO 初始化函数”章节中的类似,主要区别是引脚的模式。函数执行流程如下:

        (1)使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。

        (2)调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用“|”操作同时配置两个按键的时钟。

        (3)向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏“KEYx_GPIO_PIN”来赋值,使函数的实现方便移植。由于引脚的默认电平受按键电路影响,所以设置成浮空输入。

        (4)使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用“KEYx_GPIO_PORT”宏来赋值,也是为了程序移植方便。

        (5)使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的 GPIO引脚。

9.3.3 检测按键的状态

uint8_t BUTTON_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
	if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == BUTTON_ON)
	{
		//实现松手检测
		while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == BUTTON_ON);
		return BUTTON_ON;
	}
	else return BUTTON_OFF; 
}

        在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。 GPIO 引脚的输入电平可通过读取 IDR寄存器对应的数据位来感知,而 STM32 标准库提供了库函数 GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回0。 Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。若按键的硬件没有做消抖处理,需要在这个 Key_Scan 函数中做软件滤波,防止波纹抖动引起误触发。

9.3.4 主函数

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_button.h"

int main(void)
{
	LED_GPIO_Config();
	BUTTON_GPIO_Config();
	
	while(1)
	{
		if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
		{
			LED1_Toggle;
		}
		if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
		{
			LED2_Toggle;
		} 
	}
}

        代码中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则反转 LED 灯的状态。

9.4 下载验证

        当按下KEY1时,红灯亮,再次按下,红灯灭。

        当按下KEY2时,绿灯亮,再次按下,绿灯灭。

9.5 拓展实验(按键控制LED灯的功能)

9.5.1 任务描述

        当按下KEY1时,实现流水灯操作(红、绿、蓝三种颜色循环点亮),再次按下KEY1时灯灭。

        当按下KEY2时,实现一定节奏(红、绿、绿、红),再次按下KEY2时灯灭。

9.5.2 代码实现

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_button.h"

#define SOFT_DELAY		delay(0xFFFFF)
void delay(uint32_t count);

int main(void)
{
	LED_GPIO_Config();
	BUTTON_GPIO_Config();
	
	while(1)
	{
		if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
		{
			while(1)
			{
				//红灯
				LED_RED;
				SOFT_DELAY;
		
				//绿灯
				LED_GREEN;
				SOFT_DELAY;
		
				//蓝灯
				LED_BLUE;
				SOFT_DELAY;
				
				if(BUTTON_Scan(BUTTON1_GPIO_PORT,BUTTON1_GPIO_PIN) == BUTTON_ON)
				{
					LED_RGBOFF;
					break;
				}	
			}
		}
		if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
		{
			while(1)
			{
				//红灯
				LED1_ON;
				SOFT_DELAY;SOFT_DELAY;
				LED1_OFF;
				SOFT_DELAY;SOFT_DELAY;
				
				//绿灯
				LED2_ON;
				SOFT_DELAY;SOFT_DELAY;
				LED2_OFF;
				SOFT_DELAY;SOFT_DELAY;
				
				//绿灯
				LED2_ON;
				SOFT_DELAY;SOFT_DELAY;
				LED2_OFF;
				SOFT_DELAY;SOFT_DELAY;
				
				//红灯
				LED1_ON;
				SOFT_DELAY;SOFT_DELAY;
				LED1_OFF;
				SOFT_DELAY;SOFT_DELAY;
				
				if(BUTTON_Scan(BUTTON2_GPIO_PORT,BUTTON2_GPIO_PIN) == BUTTON_ON)
				{
					LED_RGBOFF;
					break;
				}	
			}
		}
	}
}

void delay(uint32_t count)
{
	for(;count != 0;count--);
}

谢谢阅读!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是详细的步骤: 1. 打开 STM32CubeMX,选择 STM32F030K6 型号,创建一个新的工程。 2. 在 Pinout & Configuration 选项卡中,选择一个GPIO引脚作为按键输入引脚,例如 PA0。将它设置为输入模式,上拉或下拉。 3. 在 Configuration 选项卡中,打开 NVIC 配置,启用 EXTI0_IRQn 中断。 4. 在 Code Generator 选项卡中,选择生成代码的语言和工具链。 5. 在 STM32CubeMX 中生成代码,并在 IDE 中打开。 6. 在 main.c 文件中,定义两个全局变量: ``` uint32_t buttonState = 0; uint32_t buttonTimer = 0; ``` buttonState 用于保存按键状态,buttonTimer 用于保存按键按下的时间。 7. 在 main 函数中调用 HAL_GPIO_EXTI_Callback 函数,处理按键中断: ``` void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { uint32_t now = HAL_GetTick(); if (buttonState == 0) { // 按键第一次按下 buttonState = 1; buttonTimer = now; } else if (buttonState == 1) { // 按键第二次按下 if (now - buttonTimer < 500) { // 双击 } buttonState = 2; } } } ``` 如果 buttonState 等于 0,说明是按键第一次按下,将 buttonState 设为 1,并记录按键按下的时间。如果 buttonState 等于 1,说明是按键第二次按下,如果按键按下的时间小于 500 毫秒,说明是双击,否则是长按。将 buttonState 设为 2。 8. 在 main 函数中添加一个死循环,在循环中检测按键状态: ``` while (1) { uint32_t now = HAL_GetTick(); if (buttonState == 1 && now - buttonTimer > 1000) { // 长按 buttonState = 3; } if (buttonState == 2 || buttonState == 3) { // 双击或长按 buttonState = 0; } } ``` 如果 buttonState 等于 1,说明是按键第一次按下,如果按键按下的时间超过 1 秒,说明是长按,将 buttonState 设为 3。如果 buttonState 等于 2 或 3,说明是双击或长按,将 buttonState 设为 0。 9. 至此,我们已经实现了基于中断的按键输入实现单击、双击和长按功能。你可以根据需要,在双击和长按的代码块中添加相应的操作代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KermanXin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值