• 方案介紹
    • 一 前言
    • 二 思路
    • 資源獲取
    • 三 實現(xiàn)代碼
    • 四 完整代碼
    • 該改進(jìn)版本(1ms太繁瑣了,我改成了25ms檢測一次)
    • ?五、參考
  • 附件下載
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

STM32-實現(xiàn)按鍵的長按與短按檢測(其他單片機可移植)代碼編寫

2024/06/21
6155
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

聯(lián)系方式.docx

共1個文件

一 前言

今天在逛博客的時候,偶然看到了一篇關(guān)于按鍵檢測的文章,興趣使然自己嘗試了一番,寫了一些代碼去驗證自己的思路,通過驗證完美實現(xiàn)了長按和短按檢測,后續(xù)有時間的我也會更新一下連按檢測等

二 思路

首先說一下我使用的思路,第一就是使用我們的單片機定時器去做一個輪詢判斷,首先建立一個1ms反轉(zhuǎn)一次的定時器,定時器的選擇根據(jù)自己手上有的單片機去配置一下就行,我用到是STM32F103C8T6,這款芯片有三個通用定時器,我選擇的是使用TIM4去進(jìn)行一個按鍵的檢測,定時器以每1ms檢測一次按鍵事件的方式,判斷是否按下或長按按鍵,連續(xù)按下定時器會進(jìn)行一個計數(shù),檢測一直按下就將計時器每輪詢一次就加一,從而計算出按鍵按下的時間


資源獲取

歡迎關(guān)注微信公眾號--星之援工作室 發(fā)送關(guān)鍵字(長短按檢測)

三 實現(xiàn)代碼

1.主要代碼

注意:??我在后面沒有放Time定時器的配置函數(shù),但是我之前發(fā)過配置文件,大家可以直接使用

文章連接 :STM32學(xué)習(xí)記錄 -- 通用定時器的配置(TIM2-TIM5)https://blog.csdn.net/herui_2/article/details/126636496

需要注意,如果有同學(xué)使用STM32F103C8T6配置,需要屏蔽TIM5,因為STM32F103C8T6沒有TIM5噢

這段主要檢測按鍵的哪一個被按下了

/*********************************************************************************
 * @Function	:  按鍵處理函數(shù)
 * @Input		:  Gmode:0,不支持連續(xù)按;1,支持連續(xù)按;
 * @Output		:  None
 * @Return		:  0,沒有任何按鍵按下
 *							 1,KEY1按下
 *						   2,KEY2按下
 *							 3,KEY3按下
 * @Others		:  注意此函數(shù)有響應(yīng)優(yōu)先級,KEY0>KEY1>KEY2>KEY3!!
 * @Date			:  2022-07-23
 **********************************************************************************/
u8 KEY_Scan(u8 mode)
{
	static u8 key_up = 1; // 按鍵按松開標(biāo)志
	if (mode)
		key_up = 1; // 支持連按
	if (key_up && (KEY1 == KEY_ON || KEY2 == KEY_ON || KEY3 == KEY_ON || KEY4 == KEY_ON))
	{
		key_up = 0;
		if (KEY1 == KEY_ON)
			return KEY1_PRES;
		else if (KEY2 == KEY_ON)
			return KEY2_PRES;
		else if (KEY3 == KEY_ON)
			return KEY3_PRES;
		else if (KEY4 == KEY_ON)
			return KEY4_PRES;
	}
	else if (KEY1 == KEY_OFF && KEY2 == KEY_OFF && KEY3 == KEY_OFF && KEY4 == KEY_OFF)
		key_up = 1;
	return 0; // 無按鍵按下
}

這段代碼主要是放在定時器里面做一個輪詢判斷,其中key_old,主要是保存上一次按下的按鍵值,方便我們進(jìn)行判斷,這里面的time_4 就是我們的計時器,我們通過判斷time_4的值做一個長按和短按的檢測,Key_Scan_Time是我自己定義的一個短按時長限制,我設(shè)置的是30

// 檢測按鍵是否按下
static u16 time_4
static U8 key_old = 0;
u8 Check_Key_ON_OFF()
{
	u8 key;
	key = KEY_Scan(1);
	// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開始計時
	if (key != 0 && time_4 == 0)
	{
		key_old = key;
		time_4 = 1;
	}
	if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms
	{
		time_4++; // 時間記錄器
	}
	if (key == 0 && time_4 > 0 && time_4 < Key_Scan_Time) // 短按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Shortn");
			break;
		case KEY2_PRES:
			printf("Key2_Shortn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		time_4 = 0;
	}
	else if (key == 0 && time_4 >= Key_Scan_Time) // 長按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Longn");
			break;
		case KEY2_PRES:
			printf("Key2_Longn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		time_4 = 0;
	}

	return 1;
}

最后我只需要在定時器中斷中放入?Check_Key_ON_OFF()函數(shù),即可實現(xiàn)功能

/*********************************************************************************
 * @Function	:  TIMER4定時器中斷服務(wù)
 * @Input		:  None
 * @Output		:	 None
 * @Return		:  None
 * @Others		:	 None
 * @Date			:  2022-08-30
 **********************************************************************************/
#if GENERAL_TIM4
void TIM4_IRQHandler(void) // TIM4中斷
{
	if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) // 檢查TIM3更新中斷發(fā)生與否
	{
		Check_Key_ON_OFF();
	
		TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 清除TIMx更新中斷標(biāo)志
	}
}
#endif

四 完整代碼

Key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"

//  引腳定義
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_Pin_0

#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT GPIOC
#define KEY2_GPIO_PIN GPIO_Pin_13

#define KEY3_GPIO_CLK RCC_APB2Periph_GPIOB
#define KEY3_GPIO_PORT GPIOB
#define KEY3_GPIO_PIN GPIO_Pin_14

#define KEY4_GPIO_CLK RCC_APB2Periph_GPIOB
#define KEY4_GPIO_PORT GPIOB
#define KEY4_GPIO_PIN GPIO_Pin_15

#define KEY1 GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN) // 讀取按鍵0
#define KEY2 GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN) // 讀取按鍵1
#define KEY3 GPIO_ReadInputDataBit(KEY3_GPIO_PORT, KEY3_GPIO_PIN) // 讀取按鍵2
#define KEY4 GPIO_ReadInputDataBit(KEY4_GPIO_PORT, KEY4_GPIO_PIN) // 讀取按鍵2

#define KEY1_PRES 1      // KEY1按下
#define KEY2_PRES 2      // KEY2按下
#define KEY3_PRES 3      // KEY3按下
#define KEY4_PRES 4      // KEY3按下
#define Key_Scan_Time 30 // 短按時長時間判斷

/** 按鍵按下標(biāo)置宏
 *  按鍵按下為高電平,設(shè)置 KEY_ON=1, KEY_OFF=0
 *  若按鍵按下為低電平,把宏設(shè)置成KEY_ON=0 ,KEY_OFF=1 即可
 */
#define KEY_ON 1
#define KEY_OFF 0
/*********************************************************************************
 * @Function	:  初始化控制LED的IO
 * @Input		:  None
 * @Output		:  None
 * @Return		:  None
 * @Others		:  None
 * @Date			:  2022-07-23
 **********************************************************************************/
void Key_GPIO_Config(void);
/*********************************************************************************
 * @Function	:  檢測是否有按鍵按下
 * @Input		:  GPIOx:x 可以是 A,B,C,D或者 E
 *						:  GPIO_Pin:待讀取的端口位
 * @Output		:  None
 * @Return		:  KEY_OFF(沒按下按鍵)、KEY_ON(按下按鍵)
 * @Others		:  None
 * @Date			:  2022-07-23
 **********************************************************************************/
uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
/*********************************************************************************
 * @Function	:  按鍵處理函數(shù)
 * @Input		:  Gmode:0,不支持連續(xù)按;1,支持連續(xù)按;
 * @Output		:  None
 * @Return		:  0,沒有任何按鍵按下
 *							 1,KEY1按下
 *						   2,KEY2按下
 *							 3,KEY3按下
 * @Others		:  注意此函數(shù)有響應(yīng)優(yōu)先級,KEY0>KEY1>KEY2>KEY3!!
 * @Date			:  2022-07-23
 **********************************************************************************/
u8 KEY_Scan(u8 mode);
/*********************************************************************************
 * @Function	:  STM32程序軟件復(fù)位
 * @Input		:  None
 *						:  None
 * @Output		:  None
 * @Return		:  None
 * @Others		:  None
 * @Date			:  2022-08-23
 **********************************************************************************/
void Sys_Restart(void);
#endif /* __KEY_H */

Key.c

#include "key.h"
#include "delay.h"
// 協(xié)議文件
/*********************************************************************************
 * @Function	:  初始化控制LED的IO
 * @Input		:  None
 * @Output		:  None
 * @Return		:  None
 * @Others		:  None
 * @Date			:  2022-07-23
 **********************************************************************************/
void Key_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	/*開啟按鍵端口的時鐘*/
	RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK | KEY2_GPIO_CLK | KEY3_GPIO_CLK, ENABLE);

	// 選擇按鍵的引腳
	GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
	// 設(shè)置按鍵的引腳為浮空輸入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	// 使用結(jié)構(gòu)體初始化按鍵
	GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);

	// 選擇按鍵的引腳
	GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
	// 設(shè)置按鍵的引腳為浮空輸入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	// 使用結(jié)構(gòu)體初始化按鍵
	GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);

	// 選擇按鍵的引腳
	GPIO_InitStructure.GPIO_Pin = KEY3_GPIO_PIN;
	// 設(shè)置按鍵的引腳為浮空輸入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	// 使用結(jié)構(gòu)體初始化按鍵
	GPIO_Init(KEY3_GPIO_PORT, &GPIO_InitStructure);

	// 選擇按鍵的引腳
	GPIO_InitStructure.GPIO_Pin = KEY4_GPIO_PIN;
	// 設(shè)置按鍵的引腳為浮空輸入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	// 使用結(jié)構(gòu)體初始化按鍵
	GPIO_Init(KEY4_GPIO_PORT, &GPIO_InitStructure);

	GPIO_ResetBits(KEY1_GPIO_PORT, KEY1_GPIO_PIN);
	GPIO_ResetBits(KEY2_GPIO_PORT, KEY2_GPIO_PIN);
	GPIO_ResetBits(KEY3_GPIO_PORT, KEY3_GPIO_PIN);
	GPIO_ResetBits(KEY4_GPIO_PORT, KEY4_GPIO_PIN);
}
/*********************************************************************************
 * @Function	:  檢測是否有按鍵按下
 * @Input		:  GPIOx:x 可以是 A,B,C,D或者 E
 *						:  GPIO_Pin:待讀取的端口位
 * @Output		:  None
 * @Return		:  KEY_OFF(沒按下按鍵)、KEY_ON(按下按鍵)
 * @Others		:  None
 * @Date			:  2022-07-23
 **********************************************************************************/
u8 Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	/*檢測是否有按鍵按下 */
	if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON)
	{
		/*等待按鍵釋放 */
		while (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == KEY_ON)
			;
		return KEY_ON;
	}
	else
		return KEY_OFF;
}
/*********************************************************************************
 * @Function	:  按鍵處理函數(shù)
 * @Input		:  Gmode:0,不支持連續(xù)按;1,支持連續(xù)按;
 * @Output		:  None
 * @Return		:  0,沒有任何按鍵按下
 *							 1,KEY1按下
 *						   2,KEY2按下
 *							 3,KEY3按下
 * @Others		:  注意此函數(shù)有響應(yīng)優(yōu)先級,KEY0>KEY1>KEY2>KEY3!!
 * @Date			:  2022-07-23
 **********************************************************************************/
u8 KEY_Scan(u8 mode)
{
	static u8 key_up = 1; // 按鍵按松開標(biāo)志
	if (mode)
		key_up = 1; // 支持連按
	if (key_up && (KEY1 == KEY_ON || KEY2 == KEY_ON || KEY3 == KEY_ON || KEY4 == KEY_ON))
	{
		delay_ms(10); // 去抖動
		key_up = 0;
		if (KEY1 == KEY_ON)
			return KEY1_PRES;
		else if (KEY2 == KEY_ON)
			return KEY2_PRES;
		else if (KEY3 == KEY_ON)
			return KEY3_PRES;
		else if (KEY4 == KEY_ON)
			return KEY4_PRES;
	}
	else if (KEY1 == KEY_OFF && KEY2 == KEY_OFF && KEY3 == KEY_OFF && KEY4 == KEY_OFF)
		key_up = 1;
	return 0; // 無按鍵按下
}

/*********************************************************************************
 * @Function	:  STM32程序軟件復(fù)位
 * @Input		:  None
 *						:  None
 * @Output		:  None
 * @Return		:  None
 * @Others		:  None
 * @Date			:  2022-08-23
 **********************************************************************************/
void Sys_Restart(void)
{
	__set_FAULTMASK(1);
	NVIC_SystemReset();
}
// 檢測按鍵是否按下
static u16 time_4
static u8 key_old = 0;
u8 Check_Key_ON_OFF()
{
	u8 key;
	key = KEY_Scan(1);
	// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開始計時
	if (key != 0 && time_4 == 0)
	{
		key_old = key;
		time_4 = 1;
	}
	if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms
	{
		time_4++; // 時間記錄器
	}
	if (key == 0 && time_4 > 0 && time_4 < Key_Scan_Time) // 短按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Shortn");
			break;
		case KEY2_PRES:
			printf("Key2_Shortn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		time_4 = 0;
	}
	else if (key == 0 && time_4 >= Key_Scan_Time) // 長按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Longn");
			break;
		case KEY2_PRES:
			printf("Key2_Longn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		time_4 = 0;
	}

	return 1;
}
/*********************************************END OF FILE**********************/

串口效果如下

該改進(jìn)版本(1ms太繁瑣了,我改成了25ms檢測一次)

主要代碼如下

?1.定時器部分

#if GENERAL_TIM4
void TIM4_IRQHandler(void) // TIM4中斷
{
	if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) // 檢查TIM3更新中斷發(fā)生與否
	{
		time_4++;
		if (time_4 % 25 == 0)
		{
			Check_Key_ON_OFF();
			time_4 = 0;
		}

		TIM_ClearITPendingBit(TIM4, TIM_IT_Update); // 清除TIMx更新中斷標(biāo)志
	}
}
#endif

2.按鍵檢測部分

Key_Scan_Time 檢測次數(shù)我設(shè)置的為 10

// 檢測按鍵是否按下
static U8 num_on = 0;
static U8 key_old = 0;
int Check_Key_ON_OFF()
{
	U8 key;
	key = KEY_Scan(1);
	// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開始計時
	if (key != 0 && num_on == 0)
	{
		key_old = key;
		num_on = 1;
	}
	if (key != 0 && num_on >= 1 && num_on <= Key_Scan_Time) // 25*10ms
	{
		num_on++; // 時間記錄器
	}
	if (key == 0 && num_on > 0 && num_on < Key_Scan_Time) // 短按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Shortn");
			break;
		case KEY2_PRES:
			printf("Key2_Shortn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		num_on = 0;
	}
	else if (key == 0 && num_on >= Key_Scan_Time) // 長按
	{
		switch (key_old)
		{
		case KEY1_PRES:
			printf("Key1_Longn");
			break;
		case KEY2_PRES:
			printf("Key2_Longn");
			break;
		case KEY3_PRES:
			break;
		case KEY4_PRES:
			break;
		default:
			break;
		}
		num_on = 0;
	}

	return 1;
}

?五、參考

【stm32單片機基礎(chǔ)】按鍵狀態(tài)機實現(xiàn)長按和短按icon-default.png?t=N7T8https://blog.csdn.net/qq_34142812/article/details/119721386?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%9E%E7%8E%B0%E9%95%BF%E7%9F%AD%E6%8C%89&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-119721386.nonecase&spm=1018.2226.3001.4450


完整代碼請關(guān)注衛(wèi)星公眾號進(jìn)行獲取和咨詢


聯(lián)系方式 微信號:13648103287

  • 聯(lián)系方式.docx
    下載

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
TLP291(GR-TP,SE 1 Toshiba America Electronic Components OPTOISOLATOR 3.75KV TRANS 4-SO

ECAD模型

下載ECAD模型
$0.49 查看
KSZ8567STXI 1 Microchip Technology Inc IC ETHERNET SWITCH 7PORT 128TQFP

ECAD模型

下載ECAD模型
$12.44 查看
TLP185(V4GHTL,SE(T 1 Toshiba America Electronic Components Transistor Output Optocoupler, 1-Element, 3750V Isolation
暫無數(shù)據(jù) 查看
意法半導(dǎo)體

意法半導(dǎo)體

意法半導(dǎo)體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導(dǎo)體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導(dǎo)體有限公司。意法半導(dǎo)體是世界最大的半導(dǎo)體公司之一,公司銷售收入在半導(dǎo)體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據(jù)最新的工業(yè)統(tǒng)計數(shù)據(jù),意法半導(dǎo)體是全球第五大半導(dǎo)體廠商,在很多市場居世界領(lǐng)先水平。例如,意法半導(dǎo)體是世界第一大專用模擬芯片和電源轉(zhuǎn)換芯片制造商,世界第一大工業(yè)半導(dǎo)體和機頂盒芯片供應(yīng)商,而且在分立器件、手機相機模塊和車用集成電路領(lǐng)域居世界前列.

意法半導(dǎo)體(ST)集團于1987年6月成立,是由意大利的SGS微電子公司和法國Thomson半導(dǎo)體公司合并而成。1998年5月,SGS-THOMSON Microelectronics將公司名稱改為意法半導(dǎo)體有限公司。意法半導(dǎo)體是世界最大的半導(dǎo)體公司之一,公司銷售收入在半導(dǎo)體工業(yè)五大高速增長市場之間分布均衡(五大市場占2007年銷售收入的百分比):通信(35%),消費(17%),計算機(16%),汽車(16%),工業(yè)(16%)。 據(jù)最新的工業(yè)統(tǒng)計數(shù)據(jù),意法半導(dǎo)體是全球第五大半導(dǎo)體廠商,在很多市場居世界領(lǐng)先水平。例如,意法半導(dǎo)體是世界第一大專用模擬芯片和電源轉(zhuǎn)換芯片制造商,世界第一大工業(yè)半導(dǎo)體和機頂盒芯片供應(yīng)商,而且在分立器件、手機相機模塊和車用集成電路領(lǐng)域居世界前列.收起

查看更多

相關(guān)推薦

方案定制

去合作
方案開發(fā)定制化,2000+方案商即時響應(yīng)!

方案定制,程序設(shè)計方案、單片機程序設(shè)計與講解、APP定制開發(fā)。本公眾號致力于向讀者傳遞關(guān)于程序設(shè)計和開發(fā)的相關(guān)知識,并分享一些關(guān)于軟件開發(fā)的最佳實踐。如果您有什么問題或建議,請隨時聯(lián)系我們。我們將竭誠為您服務(wù)