一 前言
今天在逛博客的時(shí)候,偶然看到了一篇關(guān)于按鍵檢測(cè)的文章,興趣使然自己嘗試了一番,寫了一些代碼去驗(yàn)證自己的思路,通過(guò)驗(yàn)證完美實(shí)現(xiàn)了長(zhǎng)按和短按檢測(cè),后續(xù)有時(shí)間的我也會(huì)更新一下連按檢測(cè)等
二 思路
首先說(shuō)一下我使用的思路,第一就是使用我們的單片機(jī)的定時(shí)器去做一個(gè)輪詢判斷,首先建立一個(gè)1ms反轉(zhuǎn)一次的定時(shí)器,定時(shí)器的選擇根據(jù)自己手上有的單片機(jī)去配置一下就行,我用到是STM32F103C8T6,這款芯片有三個(gè)通用定時(shí)器,我選擇的是使用TIM4去進(jìn)行一個(gè)按鍵的檢測(cè),定時(shí)器以每1ms檢測(cè)一次按鍵事件的方式,判斷是否按下或長(zhǎng)按按鍵,連續(xù)按下定時(shí)器會(huì)進(jìn)行一個(gè)計(jì)數(shù),檢測(cè)一直按下就將計(jì)時(shí)器每輪詢一次就加一,從而計(jì)算出按鍵按下的時(shí)間
資源獲取
歡迎關(guān)注微信公眾號(hào)--星之援工作室 發(fā)送關(guān)鍵字(長(zhǎng)短按檢測(cè))
三 實(shí)現(xiàn)代碼
1.主要代碼
注意:??我在后面沒(méi)有放Time定時(shí)器的配置函數(shù),但是我之前發(fā)過(guò)配置文件,大家可以直接使用
文章連接 :STM32學(xué)習(xí)記錄 -- 通用定時(shí)器的配置(TIM2-TIM5)https://blog.csdn.net/herui_2/article/details/126636496
需要注意,如果有同學(xué)使用STM32F103C8T6配置,需要屏蔽TIM5,因?yàn)镾TM32F103C8T6沒(méi)有TIM5噢
這段主要檢測(cè)按鍵的哪一個(gè)被按下了
/*********************************************************************************
* @Function : 按鍵處理函數(shù)
* @Input : Gmode:0,不支持連續(xù)按;1,支持連續(xù)按;
* @Output : None
* @Return : 0,沒(méi)有任何按鍵按下
* 1,KEY1按下
* 2,KEY2按下
* 3,KEY3按下
* @Others : 注意此函數(shù)有響應(yīng)優(yōu)先級(jí),KEY0>KEY1>KEY2>KEY3!!
* @Date : 2022-07-23
**********************************************************************************/
u8 KEY_Scan(u8 mode)
{
static u8 key_up = 1; // 按鍵按松開(kāi)標(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; // 無(wú)按鍵按下
}
這段代碼主要是放在定時(shí)器里面做一個(gè)輪詢判斷,其中key_old,主要是保存上一次按下的按鍵值,方便我們進(jìn)行判斷,這里面的time_4 就是我們的計(jì)時(shí)器,我們通過(guò)判斷time_4的值做一個(gè)長(zhǎng)按和短按的檢測(cè),Key_Scan_Time是我自己定義的一個(gè)短按時(shí)長(zhǎng)限制,我設(shè)置的是30
// 檢測(cè)按鍵是否按下
static u16 time_4
static U8 key_old = 0;
u8 Check_Key_ON_OFF()
{
u8 key;
key = KEY_Scan(1);
// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開(kāi)始計(jì)時(shí)
if (key != 0 && time_4 == 0)
{
key_old = key;
time_4 = 1;
}
if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms
{
time_4++; // 時(shí)間記錄器
}
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) // 長(zhǎng)按
{
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;
}
最后我只需要在定時(shí)器中斷中放入?Check_Key_ON_OFF()函數(shù),即可實(shí)現(xiàn)功能
/*********************************************************************************
* @Function : TIMER4定時(shí)器中斷服務(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 // 短按時(shí)長(zhǎng)時(shí)間判斷
/** 按鍵按下標(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 : 檢測(cè)是否有按鍵按下
* @Input : GPIOx:x 可以是 A,B,C,D或者 E
* : GPIO_Pin:待讀取的端口位
* @Output : None
* @Return : KEY_OFF(沒(méi)按下按鍵)、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,沒(méi)有任何按鍵按下
* 1,KEY1按下
* 2,KEY2按下
* 3,KEY3按下
* @Others : 注意此函數(shù)有響應(yīng)優(yōu)先級(jí),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;
/*開(kāi)啟按鍵端口的時(shí)鐘*/
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 : 檢測(cè)是否有按鍵按下
* @Input : GPIOx:x 可以是 A,B,C,D或者 E
* : GPIO_Pin:待讀取的端口位
* @Output : None
* @Return : KEY_OFF(沒(méi)按下按鍵)、KEY_ON(按下按鍵)
* @Others : None
* @Date : 2022-07-23
**********************************************************************************/
u8 Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
/*檢測(cè)是否有按鍵按下 */
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,沒(méi)有任何按鍵按下
* 1,KEY1按下
* 2,KEY2按下
* 3,KEY3按下
* @Others : 注意此函數(shù)有響應(yīng)優(yōu)先級(jí),KEY0>KEY1>KEY2>KEY3!!
* @Date : 2022-07-23
**********************************************************************************/
u8 KEY_Scan(u8 mode)
{
static u8 key_up = 1; // 按鍵按松開(kāi)標(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); // 去抖動(dòng)
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; // 無(wú)按鍵按下
}
/*********************************************************************************
* @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();
}
// 檢測(cè)按鍵是否按下
static u16 time_4
static u8 key_old = 0;
u8 Check_Key_ON_OFF()
{
u8 key;
key = KEY_Scan(1);
// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開(kāi)始計(jì)時(shí)
if (key != 0 && time_4 == 0)
{
key_old = key;
time_4 = 1;
}
if (key != 0 && time_4 >= 1 && time_4 <= 100) // 100ms
{
time_4++; // 時(shí)間記錄器
}
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) // 長(zhǎng)按
{
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檢測(cè)一次)
主要代碼如下
?1.定時(shí)器部分
#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.按鍵檢測(cè)部分
Key_Scan_Time 檢測(cè)次數(shù)我設(shè)置的為 10
// 檢測(cè)按鍵是否按下
static U8 num_on = 0;
static U8 key_old = 0;
int Check_Key_ON_OFF()
{
U8 key;
key = KEY_Scan(1);
// 與上一次的鍵值比較 如果不相等,表明有鍵值的變化,開(kāi)始計(jì)時(shí)
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++; // 時(shí)間記錄器
}
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) // 長(zhǎng)按
{
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單片機(jī)基礎(chǔ)】按鍵狀態(tài)機(jī)實(shí)現(xiàn)長(zhǎng)按和短按https://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
完整代碼請(qǐng)關(guān)注衛(wèi)星公眾號(hào)進(jìn)行獲取和咨詢
聯(lián)系方式 微信號(hào):13648103287