这个例程来源于杰理的SDK,只做了部分修改,方便移植到各个项目里面,具有短按、短按抬起、长按、长按抬起、连按、多击功能。
#ifndef _KEY_H
#define _KEY_H
#include "sys.h"
#define KEY0 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_3) //KEY0按键PH3
#define KEY1 HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_2) //KEY1按键PH2
#define KEY2 HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) //KEY2按键PC13
#define WK_UP HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) //WKUP按键PA0
#define NO_KEY 0xff //无按键//
/*双击允许间隔时间*/
#define KEY_DOUBLE_CLICK_MAX_CNT 35 //25*10ms//
#define KEY_DOUBLE_CLICK_MIN_CNT 5 //5*10ms//
/*按键门槛值*/
#define KEY_BASE_CNT 4 //消抖时间//
#define KEY_LONG_CNT 50 //长按时间//
#define KEY_HOLD_CNT 50 //连按时间//
#define KEY_SHORT_CNT 7 //短按时间//
/*按键状态*/
#define KEY_SHORT_UP 0x1
#define KEY_LONG 0x2
#define KEY_HOLD 0x3
#define KEY_DOUBLE_CLICK 0x4
#define KEY_THREE_CLICK 0x5
#define KEY_LONG_UP 0X6
#define KEY_SHORT 0X7
extern volatile u8 g_cur_key_num;
extern volatile u8 g_cur_key_status;
void KEY_Init(void);//IO初始化
u8 Get_Key_Value(void);
void Key_Event_Handle(uint8_t key_status, uint8_t key_num);
void Key_Scan(void);
#endif
#include "key.h"
volatile u8 g_cur_key_num;
volatile u8 g_cur_key_status;
//按键初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_13; //PC13
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_2|GPIO_PIN_3; //PH2,3
HAL_GPIO_Init(GPIOH,&GPIO_Initure);
}
u8 Get_Key_Value(void)
{
u8 key_num = NO_KEY;
if (KEY0) {
key_num = 1;
}
else if (KEY1)
{
key_num = 2;
}
else if (KEY2)
{
key_num = 3;
}
else if (WK_UP)
{
key_num = 4;
}
return key_num;
}
void Key_Event_Handle(uint8_t key_status, uint8_t key_num)
{
g_cur_key_status = key_status;
g_cur_key_num = key_num;
}
void Key_Scan(void)
{
uint8_t key_status = 0, cur_key = NO_KEY, back_last_key = 0;
static uint8_t s_filter_value = NO_KEY, s_filter_cnt = 0;
static uint8_t s_last_key = NO_KEY, s_click_delay_cnt = 0, s_click_cnt = 0,s_notify_value = NO_KEY;
static uint16_t s_press_cnt = 0;
//清零按键值及其状态
g_cur_key_status = 0;
g_cur_key_num = NO_KEY;
cur_key = Get_Key_Value(); //获取按键值//
//消抖/
if (cur_key != s_filter_value && s_filter_cnt) { //当前按键与上一次按键不同//
s_filter_cnt = 0;
s_filter_value = cur_key;
return;
}
if (s_filter_cnt < KEY_BASE_CNT) {
s_filter_cnt++;
return;
}
if (cur_key != s_last_key) {
if (cur_key == NO_KEY) { //按键被抬起 s_last_key != NO_KEY//
if (s_press_cnt >= KEY_LONG_CNT) {
key_status = KEY_LONG_UP;
back_last_key = s_last_key;
goto notify;
}
s_click_delay_cnt = 1; //按键等待下次连击开始//
}
else { //按键被按下 s_last_key = NO_KEY//
s_press_cnt = 1;
if (cur_key != s_notify_value) { //当前单击/连击与上次按键值不等//
s_notify_value = cur_key;
s_click_cnt = 1;
}
else {
s_click_cnt++; //连击次数累加
}
}
goto scan_end;
}
else {
if (cur_key == NO_KEY) { //没有按键按下//
if (s_click_cnt > 0) {
if (s_click_delay_cnt >= KEY_DOUBLE_CLICK_MAX_CNT) { //按键抬起,判断单击/连击次数
s_click_delay_cnt = 0;
switch (s_click_cnt) {
case 1: //单击//
key_status = KEY_SHORT_UP;
break;
case 2: //双击//
key_status = KEY_DOUBLE_CLICK;
break;
case 3: //三击//
key_status = KEY_THREE_CLICK;
break;
default:
break;
}
back_last_key = s_notify_value;
goto notify;
} else {
s_click_delay_cnt++;
goto scan_end; //按键抬起延时时间未到,返回
}
} else {
goto scan_end; //没有按键需要处理
}
}
else { //长按,连按//
s_press_cnt++;
if (s_press_cnt == KEY_LONG_CNT) { //长按//
key_status = KEY_LONG;
}
else if (s_press_cnt == (KEY_LONG_CNT+KEY_HOLD_CNT)) { //连按//
key_status = KEY_HOLD;
s_press_cnt = KEY_LONG_CNT;
}
else {
goto scan_end;
}
back_last_key = cur_key;
goto notify;
}
}
notify:
s_notify_value = NO_KEY;
s_click_cnt = 0;
Key_Event_Handle(key_status, back_last_key); //按键值及其状态转换
scan_end:
s_last_key = cur_key;
}
这个例程不仅仅支持普通的IO按键,AD按键等其他类型的也都可以移植,只是获取按键值的函数不同罢了。
|