昨天 有说到 LED 的配置 ,关于UI 的三大组件 :LED 、按键 、提示音。今天就说下按键部分.
BES 平台里面 包含了 大部分按键代码 : 按键l类型分power 、普通GPIO按键 ADC 按键
按键事件有单击 双击 长按 长长按 重复等。 先分析下 默认按键的实现逻辑。
一、普通按键实现逻辑分析:
1.先看下普通按键:
从上结构体 和按键注册表 可以得知 按键FN1对应的GPIO为 PIO_02,并且拉低(按下)有效,默认是高电平。
按键实现原理 :
按键的按下和弹起会产生相应中断 通知到 按键处理函数(hal_gpiokey_irqhandler / hal_key.c)
所有的按键抖动 都会通过硬件定时器 计时 消抖触发并在hal_gpiokey_irqhandler 函数里面处理。处理后的按键事件通过 函数
static int send_key_event(enum HAL_KEY_CODE_T code, enum HAL_KEY_EVENT_T event) 广播到应用层。
下面的 宏定义 表示 按键时间 ,用户可以根据自定义修改按键事件长
#define CFG_SW_KEY_LLPRESS_THRESH_MS 4000
#define CFG_SW_KEY_LPRESS_THRESH_MS 1500
#define CFG_SW_KEY_REPEAT_THRESH_MS 500
#define CFG_SW_KEY_DBLCLICK_THRESH_MS 500
#define CFG_SW_KEY_INIT_DOWN_THRESH_MS 200
#define CFG_SW_KEY_INIT_LPRESS_THRESH_MS 6500
#define CFG_SW_KEY_INIT_LLPRESS_THRESH_MS 10000
#define CFG_SW_KEY_CHECK_INTERVAL_MS 25
//common key define // hardware ticks conversion to ms.
#define KEY_LONGLONGPRESS_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_LLPRESS_THRESH_MS)
#define KEY_LONGPRESS_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_LPRESS_THRESH_MS)
#define KEY_DOUBLECLICK_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_DBLCLICK_THRESH_MS)
#define KEY_LONGPRESS_REPEAT_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_REPEAT_THRESH_MS)
#define KEY_INIT_DOWN_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_INIT_DOWN_THRESH_MS)
#define KEY_INIT_LONGPRESS_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_INIT_LPRESS_THRESH_MS)
#define KEY_INIT_LONGLONGPRESS_THRESHOLD MS_TO_TICKS(CFG_SW_KEY_INIT_LLPRESS_THRESH_MS)
#define KEY_CHECKER_INTERVAL MS_TO_TICKS(CFG_SW_KEY_CHECK_INTERVAL_MS)
#define KEY_DEBOUNCE_INTERVAL (KEY_CHECKER_INTERVAL * 2)
2. 增加特殊按键事件 比如:单击/双击/三击/四击 后长按事件 :
a. 在结构体中 中增加 两个数据表示当前按下次数:
struct HAL_KEY_STATUS_T {
enum HAL_KEY_CODE_T code_down;
enum HAL_KEY_CODE_T code_ready;
enum HAL_KEY_CODE_T code_click;
enum HAL_KEY_EVENT_T event;
uint32_t time_updown;
uint32_t time_click;
uint8_t cnt_repeat;
uint8_t cnt_click;
uint8_t click_flag; // Record the current press times and count the subsequent long press events.
uint8_t cnt_click_long_repeat;//Record the current press times and count the subsequent repeat events.
};
b. 修改按键弹起事件时候清除的上次按键事件类型:
if (up_new) {
if (key_status.event == HAL_KEY_EVENT_LONGPRESS || key_status.event == HAL_KEY_EVENT_LONGLONGPRESS) {
// LongPress is finished when all of the LongPress keys are released
if ((code_down & key_status.code_ready) == 0) {
key_status.event = HAL_KEY_EVENT_NONE;
}
}
else if (key_status.event == HAL_KEY_EVENT_DOWN) {
// Enter click handling if not in LongPress
key_status.event = HAL_KEY_EVENT_UP;
}
else if(key_status.event >= HAL_KEY_EVENT_LONGPRESS&&key_status.event <= HAL_KEY_EVENT_LONGLONGPRESS_AFTER_RCLICK) {
key_status.cnt_repeat = 0;
key_status.cnt_click = 0;
key_status.click_flag = 0;
key_status.code_click = HAL_KEY_CODE_NONE;
key_status.event = HAL_KEY_EVENT_NONE;
}
}
c. 在按键弹起时候,记录按键次数,为后续长按事件做准备:
if (key_status.event == HAL_KEY_EVENT_UP) {
if (key_status.code_click == HAL_KEY_CODE_NONE || key_status.code_click != key_status.code_ready) {
if (key_status.code_click != HAL_KEY_CODE_NONE) {
send_key_event(key_status.code_click, HAL_KEY_EVENT_CLICK + key_status.cnt_click);
}
key_status.code_click = key_status.code_ready;
key_status.cnt_click = 0;
key_status.click_flag = 1;
key_status.time_click = time;
} else if (up_new && (up_new | key_status.code_down) == key_status.code_click) {
key_status.cnt_click++;
key_status.click_flag++;
key_status.time_click = time;
}
if (time - key_status.time_click >= KEY_DOUBLECLICK_THRESHOLD || key_status.cnt_click >= MAX_KEY_CLICK_COUNT) {
if(key_status.cnt_click >= MAX_KEY_CLICK_COUNT)
send_key_event(key_status.code_click, HAL_KEY_EVENT_RAMPAGECLICK);
else
send_key_event(key_status.code_click, HAL_KEY_EVENT_CLICK + key_status.cnt_click);
key_status.code_click = HAL_KEY_CODE_NONE;
key_status.cnt_click = 0;
key_status.click_flag = 0;
key_status.event = HAL_KEY_EVENT_NONE;
}
}
d. 在按键按下去的时候 修改如下代码:
if (key_status.event == HAL_KEY_EVENT_DOWN){
if ((time - key_status.time_updown) >= MS_TO_TICKS(2000)&&(time - key_status.time_updown) <=MS_TO_TICKS(3500)){
if (key_status.click_flag){
key_status.cnt_click_long_repeat = 0;
key_status.event = (HAL_KEY_EVENT_LONGPRESS_AFTER_CLICK+key_status.click_flag-1);
send_key_event(key_status.code_ready, key_status.event);
TRACE("press_time over 2s,KEY_CODE=%d\n",key_status.event);
}
}
}
if(key_status.event>HAL_KEY_EVENT_LONGPRESS&&key_status.event<HAL_KEY_EVENT_LONGLONGPRESS){
if (time - key_status.time_updown >= KEY_LONGPRESS_THRESHOLD){
key_status.event =key_status.event+(HAL_KEY_EVENT_LONGLONGPRESS-HAL_KEY_EVENT_LONGPRESS);
send_key_event(key_status.code_ready, key_status.event);
TRACE("press_time over 4s,KEY_CODE=%d\n",key_status.event);
}
key_status.cnt_repeat = 0;
key_status.cnt_click = 0;
key_status.click_flag = 0;
key_status.code_click = HAL_KEY_CODE_NONE;
key_status.event = HAL_KEY_EVENT_NONE;
}
按键底层修改整体代码 如下 链接 :
https://share.weiyun.com/mwR5yWUO
二 、按键应用层的实现原理、对接、修改
1. BES 按键的应用层衔接 介绍:
类似于线程处理,考虑到按键的并发处理,所以所有的按键事件放在一个list 链表里面 存储,处理完就 删除当前节点。
hal_key_open(checkPwrKey, key_event_process);
这个函数开启了 所有底层按键驱动 ,并且注册回调函数 (key_detected_callback 即上面send_key_event)。即 后续的按键事件都会通过 主线程下的app_key_handle_process 这个任务处理
2.单边(头戴式 挂脖)按键配置表:
有多个按键可能会遇到组合按键,其实组合按键也可以说时现成的 ,我们看下 按键的定义:
直接修改案件注册表就好了
3.TWS模式 按键执行注册表:
值得说下的是 ,TWS耳机的按键 很多时候时候需要由从耳转发到主耳:
三、外设触摸IC Senor检测按键
很多时候 耳机不仅仅用自带的GPIO按键 ,还需要外设辅助按键
有了上面的分析 ,其实外设按键操作就比较简单了 ,在 原有的按键体系中 添加按键注册 ,执行 销毁的相关操作就好了 。
比如: 触摸按键的 初始化:
然后我们看下触摸按键的 处理:
利用现有的案件处理 如果触摸按键那边有动作 ,就会触发GPIO 中断 然后通过I2C 读取按键状态值,利用回调函数 广播按键事件。
#include "cmsis_os.h"
#include "hal_timer.h"
#include "app_key.h"
#include "apps.h"
#include "hal_trace.h"
//#include "btapp.h"
#include "string.h"
#include "app_thread.h"
#include <hal_i2c.h>
extern "C"{
#include "SX921x.h"
}
struct info{
uint8_t keycode;
uint8_t keyevent;
};
struct info keyinfo[2];
//common key define
#define TOUCHKEY_SINGLEPRESS_THRESHOLD MS_TO_TICKS(1500) //1500mS
#define TOUCHKEY_DEBOUNCE_INTERVAL MS_TO_TICKS(40) //40mS,中断延迟约50mS
#define TOUCHKEY_LONGPRESS_THRESHOLD 3000 ///3S
#define TOUCHKEY_CLICK_INTERVAL 400 ///400mS
#define TOUCHKEY_STEADY_THRESHOLD 500 ///500mS
///=============mack:add for touchkey=======================
void app_touchkey_handler(void const *param);
void app_longpress_handler(void const *param);
void app_inear_handler(void const *param);
osTimerId click_interval_timer = NULL;
osTimerDef (CLICKINTV, app_touchkey_handler);
osTimerId longpress_timer = NULL;
osTimerDef (LPRESS,app_longpress_handler);
osTimerId steady_timer = NULL;
osTimerDef (STEADY, app_inear_handler);
///=============end=========================================
uint16_t keycode[2]; //閿€煎垵濮嬪寲 keyinfo[0].keycode = HAL_KEY_CODE_FN3; keyinfo[1].keycode = HAL_KEY_CODE_FN4;
uint8_t keyevent; //閿€煎垵濮嬪寲涓篐AL_KEY_EVENT_ULTRACLICK // APP灞傛墠鏈夐敭鍊奸€夐」:鍗曞嚮锛屽弻鍑伙紝涓夊嚮锛岄暱鎸夛紝鍗曞嚮+闀挎寜;閿€?閫夐」:鍏ヨ€筹紝鍑鸿€?
bool keystatus[2]; //1涓烘寜涓?鑰冲唴)锛?涓烘澗寮€(鑰冲)
//extern int key_event_process(uint32_t key_code, uint8_t key_event);
extern int (*key_detected_callback)(uint32_t, uint8_t);
static void SX921x_processing(uint8_t data)
{
if(data==0x05){ ///1:in_ear
if(!keystatus[1]){ //鑻ヤ箣鍓嶇姸鎬佹槸out_ear,鍒欐湰娆℃寜涓嬫湁鏁堬紱鍚﹀垯蹇界暐
keystatus[1] =true;
key_detected_callback(keycode[1],keyevent);
TRACE(0,"The proximity sensor detects in-ear movement!\n");
}
}
else{ //0:out_ear
if(keystatus[1]){ //鑻ヤ箣鍓嶇姸鎬佹槸in_ear,鍒欐湰娆℃寜涓嬫湁鏁堬紱鍚﹀垯蹇界暐
keystatus[1] =false;
key_detected_callback(keycode[1],keyevent);
TRACE(0,"The proximity sensor detects out-ear movement!\n");
}
}
}
static void message_send_handle(void){
APP_MESSAGE_BLOCK msg;
msg.mod_id = APP_MODUAL_SX9210;
msg.msg_body.message_id = 0x00;
msg.msg_body.message_ptr = (u32)NULL;
msg.msg_body.message_Param0 = 0x00;
msg.msg_body.message_Param1 = 0x01;
app_mailbox_put(&msg);
}
static int sx921x_irq_readreg(APP_MESSAGE_BODY *msg_body){
u8 temp_data=0;
if(msg_body->message_id!=0x00) return -1;
ReadButtons((u8)(msg_body->message_Param0)); //clear NIRQ to Hight
temp_data=ReadButtons((u8)(msg_body->message_Param1));
TRACE(1,"%s STAT0_REG=0x%x", __func__,temp_data);
SX921x_processing(temp_data);
//SX921x_Calibration();
return 0;
}
static void app_SX921x_irqhandler(enum HAL_GPIO_PIN_T pin){
(void)pin;
//ReadButtons(SX921x_IRQSTAT_REG); //clear NIRQ to Hight
//temp_data=ReadButtons(SX921x_STAT0_REG);
//TRACE("%s STAT0_REG=0x%x", __func__,temp_data);
//SX921x_processing(temp_data);
message_send_handle();
}
static void touch_proximity_sensor_irq_enable(void){
struct HAL_GPIO_IRQ_CFG_T gpiocfg;
gpiocfg.irq_enable = true;
gpiocfg.irq_debounce = true;
gpiocfg.irq_type = HAL_GPIO_IRQ_TYPE_EDGE_SENSITIVE;
gpiocfg.irq_polarity =HAL_GPIO_IRQ_POLARITY_LOW_FALLING;
gpiocfg.irq_handler = app_SX921x_irqhandler;
hal_gpio_setup_irq((enum HAL_GPIO_PIN_T)TOUTH_IRQ_PIN, &gpiocfg);
}
static void touchkey_init(void){
keycode[0]=HAL_KEY_CODE_FN3; //HAL_KEY_CODE_FN3; //artificial defined to app layer recognation
keycode[1]=HAL_KEY_CODE_FN4;
keyevent=HAL_KEY_EVENT_ULTRACLICK;
app_set_threadhandle(APP_MODUAL_SX9210, sx921x_irq_readreg);//open message box
}
void hal_touchkey_open(void){
static struct HAL_I2C_CONFIG_T sx9210_i2c_cfg;
struct HAL_IOMUX_PIN_FUNCTION_MAP SX921x_cfg_gpio={TOUTH_IRQ_PIN, HAL_IOMUX_FUNC_AS_GPIO,\
HAL_IOMUX_PIN_VOLTAGE_VIO, HAL_IOMUX_PIN_NOPULL};
hal_iomux_set_i2c1();
sx9210_i2c_cfg.mode = HAL_I2C_API_MODE_TASK;
sx9210_i2c_cfg.use_dma = 0;
sx9210_i2c_cfg.use_sync = 1;
sx9210_i2c_cfg.speed = 20000;
sx9210_i2c_cfg.as_master = 1;
hal_i2c_open(HAL_I2C_ID_1, &sx9210_i2c_cfg);
hal_iomux_init(&SX921x_cfg_gpio, 1);
hal_gpio_pin_set_dir((enum HAL_GPIO_PIN_T) SX921x_cfg_gpio.pin, HAL_GPIO_DIR_IN, 1);
touch_proximity_sensor_irq_enable();
InitSX921x();
osDelay(10);
ReadButtons(SX921x_IRQSTAT_REG); //clear NIRQ to Hight
touchkey_init();
REL_TRACE(1,"hal_touchkey_open successful!");
}
void hal_touchkey_close(void){
struct HAL_GPIO_IRQ_CFG_T gpiocfg = {false,false,HAL_GPIO_IRQ_TYPE_EDGE_SENSITIVE,\
HAL_GPIO_IRQ_POLARITY_HIGH_RISING,NULL};
SX921x_suspend(); //disable SX9210
app_set_threadhandle(APP_MODUAL_SX9210, NULL);//close message box
hal_gpio_setup_irq((enum HAL_GPIO_PIN_T)TOUTH_IRQ_PIN, &gpiocfg);
hal_i2c_close(HAL_I2C_ID_1); //close I2C
REL_TRACE(0,"hal_touchkey_close !");
}
void app_touch_key(APP_KEY_STATUS *status, void *param)
{
static uint32_t period[3]; //用于按键计时,period[0]按下时间;period[1]松开后间隔时间;
uint32_t time;
switch(status->code){
case HAL_KEY_CODE_FN3:
if(keystatus[0] ==true){///1:pressed
osTimerStop(click_interval_timer);//400mS double click interval timer;
osTimerStart(longpress_timer,TOUCHKEY_LONGPRESS_THRESHOLD); //3S longpresstimer;
time = hal_sys_timer_get();
period[0]=time; //record pressed time stamp;
period[2] =time-period[1]; //record key intv period;
period[1] =0; //clear released time stamp;
}
else{ ///0:released
osTimerStop(longpress_timer); //清1.5S长按timer;
time = hal_sys_timer_get();
period[1] =time; //record released time stamp;
TRACE(1,"time-period[0]=%d ms\n",TICKS_TO_MS(time-period[0]));
if((time-period[0]) >TOUCHKEY_DEBOUNCE_INTERVAL && (time-period[0]) <TOUCHKEY_SINGLEPRESS_THRESHOLD) //80mS~1.5S
{
if(keyinfo[0].keyevent<3){ ///3 鍑讳簨浠?
keyinfo[0].keyevent++;
}
if(keyinfo[0].keyevent>2){
if((period[2]*13>(time-period[1])*10)&&(period[2]*7<(time-period[1])*10)){ //+-30%余度
osTimerStart(click_interval_timer,40); //40mS后即上传键值,不必再等按键间隔;
period[2]=0; //clear intv period;
}
}
period[0] =0; //clear pressed time stamp;
osTimerStart(click_interval_timer,TOUCHKEY_CLICK_INTERVAL); //启动400mS muticlicktimer;
TRACE(1,"click_interval_timer start\n");
}
}
break;
///==========================================================
case HAL_KEY_CODE_FN4:
// app_voice_report(APP_STATUS_INDICATION_GSOUND_MIC_OPEN,0);
if(keystatus[1] ==true){///1:pressed///1:in_ear
keyinfo[1].keyevent =true;
osTimerStop(steady_timer);
osTimerStart(steady_timer, TOUCHKEY_STEADY_THRESHOLD); //启动500mS steady_timer;
}
else{ ///0:out_ear
keyinfo[1].keyevent =false;
osTimerStop(steady_timer);
osTimerStart(steady_timer, TOUCHKEY_STEADY_THRESHOLD); //启动500mS steady_timer;
}
break;
}
}
void app_longpress_handler(void const *param)
{
///send key_code key_event to app layer(by key_event_process())
if(keyinfo[0].keyevent ==1){ //单击1次+长按
//TRACE(2,"keycode=0x%X keyevent =0x%X ========click+longpress========\n",keyinfo[0].keycode,keyinfo[0].keyevent+16);
key_detected_callback(HAL_KEY_CODE_FN3,HAL_KEY_EVENT_UP_AFTER_LONGPRESS);
}
else{
//TRACE(2,"keycode=0x%X keyevent =0x%X =============longpress===========\n",keyinfo[0].keycode,16); //仅长按
key_detected_callback(HAL_KEY_CODE_FN3,HAL_KEY_EVENT_LONGPRESS);
}
keyinfo[0].keyevent=0;
keystatus[0]=0;
}
void app_touchkey_handler(void const *param)
{
if(keyinfo[0].keyevent){
if(keyinfo[0].keyevent==1){
//TRACE(2,"keycode=0x%X keyevent =0x%X =========single click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_CLICK);
}
else if(keyinfo[0].keyevent==2){
//TRACE(2,"keycode=0x%X keyevent =0x%X =========double click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_DOUBLECLICK);
}
else{
//TRACE(2,"keycode=0x%X keyevent =0x%X ========== triple click==========\n ",keyinfo[0].keycode,keyinfo[0].keyevent);
key_detected_callback(keyinfo[0].keycode,HAL_KEY_EVENT_TRIPLECLICK);
}
}
keyinfo[0].keyevent=0;
keystatus[0]=0;
}
void app_inear_handler(void const *param)
{
if(keyinfo[1].keyevent){
//TRACE(2,"keycode=0x%X keystatus =0x%X =========== in ear===========\n",keyinfo[1].keycode,keyinfo[1].keyevent);
key_detected_callback(keyinfo[1].keycode,HAL_KEY_EVENT_DOWN);
}
else{
//TRACE(2,"keycode=0x%X keystatus =0x%X ==========out ear==========\n",keyinfo[1].keycode,keyinfo[1].keyevent);
key_detected_callback(keyinfo[1].keycode,HAL_KEY_EVENT_UP);
}
}
void app_touchkey_open(void)
{
keyinfo[0].keycode = HAL_KEY_CODE_FN3;
keyinfo[1].keycode = HAL_KEY_CODE_FN4;
click_interval_timer =osTimerCreate(osTimer(CLICKINTV),osTimerOnce,NULL);
steady_timer =osTimerCreate(osTimer(STEADY),osTimerOnce,NULL);
longpress_timer =osTimerCreate(osTimer(LPRESS),osTimerOnce,NULL);
hal_touchkey_open();
// app_set_threadhandle(APP_MODUAL_KEY, app_key_handle_process);
}
void app_touchkey_close(void){
hal_touchkey_close();
if (click_interval_timer) {
osTimerStop(click_interval_timer);
osTimerDelete(click_interval_timer);
click_interval_timer = NULL;
}
if (longpress_timer) {
osTimerStop(longpress_timer);
osTimerDelete(longpress_timer);
longpress_timer = NULL;
}
if (steady_timer) {
osTimerStop(steady_timer);
osTimerDelete(steady_timer);
steady_timer = NULL;
}
}
触摸按键的销毁:
按键的大致类型介绍和讲解就到这里了。欢迎大家继续关注 ,下一个章节介绍 提示音相关问题和解决办法。
QQ:1902026113