一、ESP32
之后的项目要用到ESP32,对按键驱动进行移植
二、实现
1.源文件
支持短按、长按和多个按键。
我在这里设置了五个按键,如果还有需要可以继续添加,在头文件这里进行修改。
#include "user_key.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
KeyEvent_CallBack_t KeyScanCBS;
static void hal_keyConfig(void);
static unsigned char hal_getKey1Sta(void);
static unsigned char hal_getKey2Sta(void);
static unsigned char hal_getKey3Sta(void);
static unsigned char hal_getKey4Sta(void);
static unsigned char hal_getKey5Sta(void);
static unsigned char hal_getKey6Sta(void);
unsigned char (*getKeysState[KEYNUM])() = { hal_getKey1Sta,hal_getKey2Sta,hal_getKey3Sta,hal_getKey4Sta,hal_getKey5Sta,hal_getKey6Sta
};
unsigned char KeyStep[KEYNUM]; //按键检测流程
unsigned short KeyScanTime[KEYNUM]; //去抖延时
unsigned short KeyPressLongTimer[KEYNUM]; //长按延时
unsigned short KeyContPressTimer[KEYNUM]; //连续长按延时
void hal_KeyInit(void)
{
unsigned char i;
KeyScanCBS = 0;
hal_keyConfig();
for(i=0; i<KEYNUM; i++)
{
KeyStep[i] = KEY_STEP_WAIT;
KeyScanTime[i] = KEY_SCANTIME;
KeyPressLongTimer[i] = KEY_PRESS_LONG_TIME;
KeyContPressTimer[i] = KEY_PRESS_CONTINUE_TIME;
}
}
void hal_KeyScanCBSRegister(KeyEvent_CallBack_t pCBS)
{
if(KeyScanCBS == 0)
{
KeyScanCBS = pCBS;
}
}
void hal_KeyProc(void)
{
unsigned char i,KeyState[KEYNUM];
unsigned char keys;
for(i=0; i<KEYNUM; i++)
{
keys = 0;
KeyState[i] = getKeysState[i]();
switch(KeyStep[i])
{
case KEY_STEP_WAIT: //等待按键
if(KeyState[i])
{
KeyStep[i] = KEY_STEP_CLICK;
}
break;
case KEY_STEP_CLICK: //按键单击按下
if(KeyState[i])
{
if(!(--KeyScanTime[i]))
{
KeyScanTime[i] = KEY_SCANTIME;
KeyStep[i] = KEY_STEP_LONG_PRESS;
//keys = i+1; //记录按键ID号
//state = KEY_CLICK; //按键单击按下
keys = (i*5)+1;
}
}else
{
KeyScanTime[i] = KEY_SCANTIME;
KeyStep[i] = KEY_STEP_WAIT;
}
break;
case KEY_STEP_LONG_PRESS: //按键长按
if(KeyState[i])
{
if(!(--KeyPressLongTimer[i]))
{
KeyPressLongTimer[i] = KEY_PRESS_LONG_TIME;
KeyStep[i] = KEY_STEP_CONTINUOUS_PRESS;
//keys = i+1; //记录按键ID号
//state = KEY_LONG_PRESS;
keys = (i*5)+3; //长按确认
}
}else
{
KeyPressLongTimer[i] = KEY_PRESS_LONG_TIME;
KeyStep[i] = KEY_STEP_WAIT;
//keys = i+1; //记录按键ID号
//state = KEY_CLICK_RELEASE; //单击释放
keys = (i*5)+2; //单击释放
}
break;
case KEY_STEP_CONTINUOUS_PRESS:
if(KeyState[i])
{
if(!(--KeyContPressTimer[i]))
{
KeyContPressTimer[i] = KEY_PRESS_CONTINUE_TIME;
//keys = i+1; //持续长按
//state = KEY_LONG_PRESS_CONTINUOUS;
keys = (i*5)+4; //持续长按
}
}else
{
KeyStep[i] = KEY_STEP_WAIT;
KeyContPressTimer[i] = KEY_PRESS_CONTINUE_TIME;
keys = i+1; //记录按键ID号
keys = (i*5)+5; //长按释放
}
break;
}
if(keys)
{
if(KeyScanCBS)
{
KeyScanCBS((KEY_VALUE_TYPEDEF)keys);
}
}
}
}
static void hal_keyConfig(void)
{
gpio_pullup_en(K1_PIN);
gpio_reset_pin(K1_PIN);
gpio_set_direction(K1_PIN,GPIO_MODE_INPUT);
// gpio_reset_pin(K2_PIN);
// gpio_set_direction(K2_PIN,GPIO_MODE_OUTPUT);
// gpio_reset_pin(K3_PIN);
// gpio_set_direction(K3_PIN,GPIO_MODE_OUTPUT);
// gpio_reset_pin(K4_PIN);
// gpio_set_direction(K4_PIN,GPIO_MODE_OUTPUT);
// gpio_reset_pin(K5_PIN);
// gpio_set_direction(K5_PIN,GPIO_MODE_OUTPUT);
// gpio_reset_pin(K6_PIN);
// gpio_set_direction(K6_PIN,GPIO_MODE_OUTPUT);
}
static unsigned char hal_getKey1Sta(void)
{
return (!gpio_get_level(K1_PIN));
}
static unsigned char hal_getKey2Sta(void)
{
return (!gpio_get_level(K2_PIN));
}
static unsigned char hal_getKey3Sta(void)
{
return (!gpio_get_level(K3_PIN));
}
static unsigned char hal_getKey4Sta(void)
{
return (!gpio_get_level(K4_PIN));
}
static unsigned char hal_getKey5Sta(void)
{
return (!gpio_get_level(K5_PIN));
}
static unsigned char hal_getKey6Sta(void)
{
return (!gpio_get_level(K6_PIN));
}
2.头文件
#ifndef _HAL_KEY_H
#define _HAL_KEY_H
//上键
// #define K1_PORT GPIOB
// #define K1_PIN GPIO_Pin_3
#define K1_PIN GPIO_NUM_2
#define K2_PIN 0
#define K3_PIN 0
#define K4_PIN 0
#define K5_PIN 0
#define K6_PIN 0
// //下键
// #define K2_PORT GPIOB
// #define K2_PIN GPIO_Pin_5
// //左键
// #define K3_PORT GPIOB
// #define K3_PIN GPIO_Pin_6
// //右键
// #define K4_PORT GPIOB
// #define K4_PIN GPIO_Pin_7
// //取消/返回
// #define K5_PORT GPIOB
// #define K5_PIN GPIO_Pin_10
//确定/设置
// #define K6_PORT GPIOB
// #define K6_PIN GPIO_Pin_11
typedef enum
{
KEY_S1, //上键
KEY_S2, //下键
KEY_S3,
KEY_S4,
KEY_S5,
KEY_S6,
KEYNUM
}KEY_TYPEDEF; //按键定义
// 按键检测过程
typedef enum
{
KEY_STEP_WAIT, //等待按键
KEY_STEP_CLICK, //按键按下
KEY_STEP_LONG_PRESS, //长按
KEY_STEP_CONTINUOUS_PRESS, //持续按下
}KEY_STEP_TYPEDEF;
typedef enum
{
KEY_IDLE, //按键空闲
KEY_CLICK, //单击确认
KEY_CLICK_RELEASE, //单击释放
KEY_LONG_PRESS, //长按确认
KEY_LONG_PRESS_CONTINUOUS, //长按持续
KEY_LONG_PRESS_RELEASE //长按释放
}KEY_EVENT_TYPEDEF;
typedef enum
{
KEY_IDLE_VAL,
KEY1_CLICK,
KEY1_CLICK_RELEASE,
KEY1_LONG_PRESS,
KEY1_LONG_PRESS_CONTINUOUS,
KEY1_LONG_PRESS_RELEASE, //5
KEY2_CLICK, //6
KEY2_CLICK_RELEASE,
KEY2_LONG_PRESS,
KEY2_LONG_PRESS_CONTINUOUS,
KEY2_LONG_PRESS_RELEASE,
KEY3_CLICK, //11
KEY3_CLICK_RELEASE,
KEY3_LONG_PRESS,
KEY3_LONG_PRESS_CONTINUOUS,
KEY3_LONG_PRESS_RELEASE,
KEY4_CLICK, //16
KEY4_CLICK_RELEASE,
KEY4_LONG_PRESS,
KEY4_LONG_PRESS_CONTINUOUS,
KEY4_LONG_PRESS_RELEASE,
KEY5_CLICK, //21
KEY5_CLICK_RELEASE,
KEY5_LONG_PRESS,
KEY5_LONG_PRESS_CONTINUOUS,
KEY5_LONG_PRESS_RELEASE,
KEY6_CLICK, //26
KEY6_CLICK_RELEASE,
KEY6_LONG_PRESS,
KEY6_LONG_PRESS_CONTINUOUS,
KEY6_LONG_PRESS_RELEASE,
}KEY_VALUE_TYPEDEF;
typedef void (*KeyEvent_CallBack_t)(KEY_VALUE_TYPEDEF keys);
//扫描按键的定时器Tick,以系统Tick(1ms)为单位,10=10ms
#define KEY_SCANT_TICK 10 //10ms
//按键消抖时间,以10ms为Tick,2=20ms
#define KEY_SCANTIME 2 //20ms
//连续长按时间
#define KEY_PRESS_LONG_TIME 200 //2s
//持续长按间隔时间
#define KEY_PRESS_CONTINUE_TIME 15 //150ms
void hal_KeyInit(void);
void hal_KeyProc(void);
void hal_KeyScanCBSRegister(KeyEvent_CallBack_t pCBS);
void user_key_task(void *pr);
#endif
三、调用
首先,调用void hal_KeyInit(void)函数,然后需要在应用层实现按键的回调函数static void KeyEventHandle(KEY_VALUE_TYPEDEF keys),当按键被按下之后,将会调用该回调函数。一般可以把蜂鸣器响或者LED闪烁放在这里。
void hal_KeyProc(void)函数需要创建一个定时器放在其中进行执行,推荐使用ESP32的软件定时器。freertos的软件定时器,最小定时的时间要参考systick,只能到ms级别。而ESP32的软件定时器能道us级别。
当然,具体定时器的时间,要根据实际情况设置,如果是对按键反应时间要求比较高的场合,比如说秒表,计时器,要显示精准,那就可以将时间设置短一些。
如果对按键反应时间要求不高,就可以设置长一些,少消耗一些cpu资源。
//按键回调函数
static void KeyEventHandle(KEY_VALUE_TYPEDEF keys)
{
if((keys==KEY1_CLICK)
|| (keys==KEY2_CLICK)
|| (keys==KEY3_CLICK)
|| (keys==KEY4_CLICK)
|| (keys==KEY5_CLICK)
|| (keys==KEY6_CLICK))
{
printf("key1::key_CLICK%d",keys);
//LedMsgInput(LED1,LED_LIGHT_100MS,1);
printf("beep is sound\n");
//hal_Oled_ClearArea(16,20,128,30);
//OLED_ShowString(16,20,(uint8_t*)"key_CLICK",16,1);
//OLED_Refresh();
//LedMsgInput(LED1,LED_LIGHT,1);
}else if((keys==KEY1_CLICK_RELEASE)
|| (keys==KEY2_CLICK_RELEASE)
|| (keys==KEY3_CLICK_RELEASE)
|| (keys==KEY4_CLICK_RELEASE)
|| (keys==KEY5_CLICK_RELEASE)
|| (keys==KEY6_CLICK_RELEASE))
{
printf("key1::CLICK_RELEASE%d",keys);
//LedMsgInput(LED1,LED_BLINK4,1);
}else if((keys==KEY1_LONG_PRESS)
|| (keys==KEY2_LONG_PRESS)
|| (keys==KEY3_LONG_PRESS)
|| (keys==KEY4_LONG_PRESS)
|| (keys==KEY5_LONG_PRESS)
|| (keys==KEY6_LONG_PRESS))
{
printf("key1::LONG_PRESS%d",keys);
//LedMsgInput(LED1,LED_DARK,1);
}
}
//软件定时器回调函数
static void periodic_timer_callback(void* arg)
{
//int64_t time_since_boot = esp_timer_get_time();
hal_KeyProc();
//ESP_LOGI("example", "Periodic timer called, time since boot: %lld us", time_since_boot);
}
总结
ESP-IDF example中也提供了相应的例程,相对更加完善