嵌入式C 您所在的位置:网站首页 c语言读取键盘按键 嵌入式C

嵌入式C

2024-06-02 16:08| 来源: 网络整理| 查看: 265

独立按键、组合键

图片

介绍该按键驱动的结构、使用方法

由于手上无板子,源码未验证

github.com/L231/ry_key

一些无关紧要的

之前一直很少用到按键,基本识别一下按键是否按压,就没了,所以,就不需要特别设计,能用够用就行。

这不,机会来了,某个项目中,使用一个按键,与机子进行多种互动。自然地,就需要识别单击、双击、长按等。一时间看着代码,难以下手。

构想的形态

初步梳理需求,并预测未来的场景,构建如下形态:

图片

它要识别出独立按键的不同操作,即事件

自动完成按键的状态转移

也需要有组合键功能

必须处理好优先级,不能说三击触发了,双击也能触发

总的要求

对应低性能的MCU,实现有限的按键识别

具备组合键功能

状态与事件处理分离

使用便捷,移植性高

按键结构设计

考虑到按键的管理,使用单向链表串起所有按键:

独立按键链表

组合键链表

尽可能让二者前半部分保持一致,如下:

图片独立按键扫描的数据结构

主要有电平、滤波、连击部分、阈值部分,满足扫描需求:

typedef struct{  uint8_t    level       : 1;    /* 当前电平 */  uint8_t    valid_level : 1;    /* 有效电平,按键激活判断 */  uint8_t    reserve     : 2;    /* 保留 */  uint8_t    click_cnt   : 4;    /* 连击次数 */  uint8_t    filter_cnt;         /* 电平滤波计数 */  uint8_t    filter_limit;       /* 电平滤波阈值 */  uint16_t   tick;               /* 按键扫描计时 */  uint16_t   double_click_limit; /* 双击时间的阈值 */  uint16_t   long_limit;         /* 长按的阈值 */  uint16_t   long_long_limit;    /* 超长按的阈值 */  uint8_t  (*get_level)(void);   /* 电平获取,函数指针 */}key_scan_t; 部分按键初始化配置的API

按键初始化

事件回调函数注册

组合键关联独立按键

/* 注册一个独立按键 */void ry_key_reg(ry_key_t *key,    uint8_t valid_level,        /* 有效电平,按键激活判断 */    uint8_t filter,             /* 电平滤波阈值 */    uint8_t double_click_limit, /* 双击时间的阈值 */    uint8_t long_limit,         /* 长按的阈值 */    uint8_t long_long_limit,    /* 超长按的阈值 */    uint8_t (*get_level)(void))/* 组合键的注册函数 */void ry_key_compound_reg(ry_key_compound_t *key, callback cbk)/* 组合键添加关联的独立按键SN号,以SN大小插入 */void ry_key_compound_insert_key_sn(ry_key_compound_t *key, uint8_t sn)/* 注册按键的回调函数 */#define RY_KEY_CALLBACK_CFG(key, event, cbk)

独立按键的状态机

总的结构:

图片

代码实现:

uint8_t ry_key_state_machine(ry_key_t *key){  __key_level_scan(key);  key->scan.tick++;  switch(key->status)  {  case KEY_IDLE_STATUS :    if(key->scan.level == key->scan.valid_level)    {      __key_event_mark(key, KEY_DOWN_EVENT);      key->scan.tick      = 0;      key->scan.click_cnt = 0;    }    break;  case KEY_DOWN_STATUS :    if(key->scan.level != key->scan.valid_level)    {      __key_event_mark(key, KEY_UP_EVENT);      key->scan.click_cnt++;    }    else if(key->scan.tick > key->scan.long_limit)      __key_event_mark(key, KEY_LONG_PRESS_EVENT);    break;  case KEY_UP_STATUS :    if(key->scan.level == key->scan.valid_level)    {      __key_event_mark(key, KEY_DOWN_STATUS);      key->scan.tick = 0;    }    /* 从按键按下开始计时,若时间超过连击时间阈值,则认为连击结束 */    else if(key->scan.tick > key->scan.double_click_limit)    {      if(1 == key->scan.click_cnt)        __key_event_mark(key, KEY_SINGLE_CLICK_EVENT);      else if(2 == key->scan.click_cnt)        __key_event_mark(key, KEY_DOUBLE_CLICK_EVENT);      else if(3 == key->scan.click_cnt)        __key_event_mark(key, KEY_THREE_CLICK_EVENT);      else key->status = KEY_IDLE_STATUS;    }    break;  case KEY_LONG_PRESS_STATUS :    if(key->scan.tick > key->scan.long_long_limit)      __key_event_mark(key, KEY_LONG_LONG_PRESS_EVENT);    else if(key->scan.level != key->scan.valid_level) key->status = KEY_IDLE_STATUS;    break;  default : key->status = KEY_IDLE_STATUS;    break;  }  return key->event;}

按键扫描的机制

先扫描所有按键,再执行事件处理

某个时刻,只允许一个事件,多了全部无效

图片

应用举例

使用场景

场景说明:

一个电源按键,长按

功能按键,单击

组合键为“电源键” + “功能键”

定义几个按键,并准备电平读取函数:

static ry_key_t __keyPower;              /* 电源按键 */static ry_key_t __keyCtr;                /* 控制按键 */static ry_key_compound_t __compoundKey1; /* 组合键 */ extern uint8_t key_power_get_level(void);extern uint8_t key_ctr_get_level(void);

初始化按键

初始化如下:

void user_key_init(void){  ry_key_reg(&__keyPower, 1, 5, 50, 300, 900, key_power_get_level);  ry_key_reg(&__keyCtr,   1, 5, 50, 300, 900, key_ctr_get_level);  ry_key_compound_reg(&__compoundKey1, compound_key1_callback);  /* 配置事件的回调函数 */  RY_KEY_CALLBACK_CFG(__keyPower, KEY_LONG_PRESS_EVENT, key_power_long_press_callback);  RY_KEY_CALLBACK_CFG(__keyCtr, KEY_SINGLE_CLICK_EVENT, key_ctr_single_click_callback); /* 组合键关联对应的独立按键 */  ry_key_compound_insert_key_sn(&__compoundKey1, __keyPower.sn);  ry_key_compound_insert_key_sn(&__compoundKey1, __keyCtr.sn);}

准备几个回调函数:

void key_power_long_press_callback(ry_key_t *key){  printf("key_power_long_press_callback");}void key_ctr_single_click_callback(ry_key_t *key){  printf("key_ctr_single_click_callback");}void compound_key1_callback(ry_key_t *key){  printf("compound_key1_callback");}

主函数

这样定时扫描按键,就会自动运行我们定义的事件回调函数。

int main(void){  //system_init();  user_key_init();  while(1)  {    /* 配置定时器,定时扫描按键效果更好 */    ry_key_scan();  }}


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有