当前位置: 首页 > news >正文

单片机按键处理模块

一 介绍

1.key_board用于单片机中的小巧多功能按键支持,软件采用了分层的思想,并且做到了与平台无关,用户只需要提供按键的基本信息和读写io电平的函数即可,非常方便移植,同时支持多个矩阵键盘及多个单io控制键盘。

2. 目前已实现按下触发、弹起触发、长按自动触发、长按弹起触发、多击触发、连续触发等功能,并且能够随意组合(支持状态的同一时间轴和非同一时间轴),后续还会添加更多的功能。

二 使用说明

1.初始化相关硬件资源
2.提供一个1ms的定时器,用于周期性的调用‘key_check’函数
3.提供按键的描述及读写IO的函数
4.将键盘注册到系统
5.具体的操作参考提供的STM32例程
6.因为程序默认使用了堆内存,当发现程序运行结果不正常时,尝试增大你的程序堆空间,或者注册调试接口查看原因

三 已支持的键盘

矩阵键盘
单IO按键
将key_board.c,key_board.h,key_board_config.h放进key_board文件夹中并包含进你的工程,添加头文件路径。

四 基础功能移植(以stm32矩阵键盘为例)

首先需要一个可使用的定时器(如果不想使用定时器也可直接放到主循环中,但不推荐,会导致时基不准确),固定为1ms触发一次;
准备待检测的按键的基本信息,可参考key_board_sample.c文件中的struct key_pin_t结构体,如:

struct key_pin_t {GPIO_TypeDef *port;     //按键端口号uint16_t pin;           //按键的引脚号GPIO_PinState valid;    //按键的有效电平(即按键按下时的电平)GPIO_PinState invalid;  //按键的无效电平(即按键空闲时的电平)/*可添加你的其它参数*/
};

定义待检测的按键信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_sig[]结构体数组,对应头文件为key_board_sample.h,如:

//全局变量
const struct key_pin_t key_pin_sig[] = {{.port = KEY_PORT_J12,.pin = KEY_PIN_J12,.valid = KEY_PRESS_LEVEL_J12,.invalid = KEY_RELEASE_LEVEL_J12},{.port = KEY_PORT_J34,.pin = KEY_PIN_J34,.valid = KEY_PRESS_LEVEL_J34,.invalid = KEY_RELEASE_LEVEL_J34},{.port = KEY_PORT_J56,.pin = KEY_PIN_J56,.valid = KEY_PRESS_LEVEL_J56,.invalid = KEY_RELEASE_LEVEL_J56},
};

如果为矩阵键盘还需要定义控制io的相关信息,可参考key_board_sample.c文件中的const struct key_pin_t key_pin_ctrl[]结构体数组,对应头文件为key_board_sample.h,如:

const struct key_pin_t key_pin_ctrl[] = {{.port = KEY_PORT_J135,.pin = KEY_PIN_J135,.valid = KEY_CTL_LINE_ENABLE,.invalid = KEY_CTL_LINE_DISABLE},{.port = KEY_PORT_J246,.pin = KEY_PIN_J246,.valid = KEY_CTL_LINE_ENABLE,.invalid = KEY_CTL_LINE_DISABLE},
};

实现按键io的电平读取函数,可参考key_board_sample.c文件中的pin_level_get函数,如:

static inline bool pin_level_get(const void *desc)
{struct key_pin_t *pdesc;pdesc = (struct key_pin_t *)desc;return HAL_GPIO_ReadPin(pdesc->port, pdesc->pin) == pdesc->valid;
}

如果为矩阵键盘还需要实现按键io的电平写入函数,可参考key_board_sample.c文件中的pin_level_set函数,如:

static inline void pin_level_set(const void *desc, bool flag)
{struct key_pin_t *pdesc;pdesc = (struct key_pin_t *)desc;HAL_GPIO_WritePin(pdesc->port, pdesc->pin, flag ? pdesc->valid : pdesc->invalid);
}

定义按键的id及功能结构体struct key_public_sig_t,可参考key_board_sample.c文件中的const struct key_public_sig_t key_public_sig[]结构体数组,对应头文件key_board.h,如:

const struct key_public_sig_t key_public_sig[] = {KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),KEY_PUBLIC_SIG_DEF(KEY_LEFT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),KEY_PUBLIC_SIG_DEF(KEY_DOWN, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),//下面的是因为使用的矩阵键盘而扩展出来的三个按键KEY_PUBLIC_SIG_DEF(KEY_ENTER, &key_pin_sig[0], pin_level_get, KEY_FLAG_NONE),KEY_PUBLIC_SIG_DEF(KEY_RIGHT, &key_pin_sig[1], pin_level_get, KEY_FLAG_NONE),KEY_PUBLIC_SIG_DEF(KEY_EXIT, &key_pin_sig[2], pin_level_get, KEY_FLAG_NONE),
};

如果为矩阵键盘还需要定义控制io的id及功能结构体struct key_public_ctrl_t,可参考key_board_sample.c文件中的const struct key_public_ctrl_t key_public_ctrl[]结构体数组,对应头文件key_board.h,如:

const struct key_public_ctrl_t key_public_ctrl[] = {KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[0], pin_level_set),KEY_PUBLIC_CTRL_DEF(&key_pin_ctrl[1], pin_level_set),
};

初始化键盘,可参考key_board_sample.c文件中的GPIO_Key_Board_Init函数,如:

void GPIO_Key_Board_Init(void)
{//硬件io的初始化GPIO_InitTypeDef GPIO_InitStruct;unsigned int i;RCC_KEY_BOARD_CLK_ENABLE();GPIO_InitStruct.Pull  = GPIO_PULLUP;GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;for(i = 0;i < ARRAY_SIZE(key_pin_sig);i++){GPIO_InitStruct.Pin   = key_pin_sig[i].pin;HAL_GPIO_Init(key_pin_sig[i].port, &GPIO_InitStruct);}GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Pull  = GPIO_NOPULL;GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;for(i = 0;i < ARRAY_SIZE(key_pin_ctrl);i++){GPIO_InitStruct.Pin   = key_pin_ctrl[i].pin;HAL_GPIO_Init(key_pin_ctrl[i].port, &GPIO_InitStruct);}//初始化键盘key_board_init();//注册键盘到系统中(矩阵键盘)key_board_register(KEY_BOARD_MATRIX, key_public_sig, ARRAY_SIZE(key_public_sig), key_public_ctrl, ARRAY_SIZE(key_public_ctrl));
}

主流程伪代码框架,更多例子参考main_test.c文件:

int main(void)
{//初始化硬件io,并注册键盘GPIO_Key_Board_Init();//初始化定时器,用于按键扫描(1ms)init_tmr();for(;;){if(key_check_state(KEY_UP, KEY_RELEASE)){PRINTF("KEY_UP KEY_RELEASE\r\n");}if(key_check_state(KEY_UP, KEY_PRESS)){PRINTF("KEY_UP KEY_PRESS\r\n");}}
}//定时器到期回调处理函数
void tmr_irq_callback(void)
{//调用按键扫描核心函数key_check();
}

五 按键长按功能的使用

首先确保key_board_config.h文件中宏KEY_LONG_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_LONG_TRRIGER_TIME的值;
设置按键功能需要对长按进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_LONG | KEY_FLAG_RELEASE_LONG)

使用例程:

if(key_check_state(KEY_UP, KEY_PRESS_LONG))
{PRINTF("KEY_UP KEY_PRESS_LONG\r\n");
}
if(key_check_state(KEY_UP, KEY_RELEASE_LONG))
{PRINTF("KEY_UP KEY_RELEASE_LONG\r\n");
}

六 按键连按的使用

首先确保key_board_config.h文件中宏KEY_CONTINUOUS_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_CONTINUOUS_INIT_TRRIGER_TIME和KEY_DEFAULT_CONTINUOUS_PERIOD_TRRIGER_TIME的值;

设置按键功能需要对连按进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_CONTINUOUS)

使用例程:

if(key_check_state(KEY_UP, KEY_PRESS_CONTINUOUS))
{PRINTF("KEY_UP KEY_PRESS_CONTINUOUS\r\n");
}

七 按键多击的使用

首先确保key_board_config.h文件中宏KEY_MULTI_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_MULTI_INTERVAL_TIME的值;

设置按键功能需要多击进行检测,如:

KEY_PUBLIC_SIG_DEF(KEY_UP, &key_pin_sig[0], pin_level_get, KEY_FLAG_PRESS_MULTI | KEY_FLAG_RELEASE_MULTI)

使用例程:

unsigned int res;
res = key_check_state(KEY_UP, KEY_PRESS_MULTI);
if(res)
{PRINTF("KEY_UP KEY_PRESS_MULTI:%d\r\n", res);
}
res = key_check_state(KEY_UP, KEY_RELEASE_MULTI);
if(res)
{PRINTF("KEY_UP KEY_RELEASE_MULTI:%d\r\n", res);
}

八 功能组合状态(同一时间轴)

使用例程:

unsigned int key_down_release_long, key_up_release_long;
key_down_release_long = key_check_state(KEY_DOWN, KEY_RELEASE_LONG);
key_up_release_long = key_check_state(KEY_UP, KEY_RELEASE_LONG);
if(key_down_release_long && key_up_release_long)
{PRINTF("KEY_DOWN KEY_RELEASE_LONG && KEY_UP KEY_RELEASE_LONG\n");
}

九 功能组合状态(非同一时间轴)

首先确保key_board_config.h文件中宏KEY_COMBINE_SUPPORT已处于使能状态,并且正确设置了宏KEY_DEFAULT_COMBINE_INTERVAL_TIME的值;
使用例程:

//用于保存注册后的组合状态id
static unsigned int test_id1, test_id2;//定义要检测的状态
const struct key_combine_t test_combine1[] = {{ .id = KEY_UP,     .state = KEY_PRESS },{ .id = KEY_DOWN,   .state = KEY_PRESS_LONG },{ .id = KEY_UP,     .state = KEY_PRESS },
};
//注册组合状态
test_id1 = key_combine_register(test_combine1, ARRAY_SIZE(test_combine1));const struct key_combine_t test_combine2[] = {{ .id = KEY_UP,     .state = KEY_PRESS },{ .id = KEY_DOWN,   .state = KEY_PRESS },{ .id = KEY_UP,     .state = KEY_PRESS },{ .id = KEY_DOWN,   .state = KEY_PRESS },
};
test_id2 = key_combine_register(test_combine2, ARRAY_SIZE(test_combine2));if(key_check_combine_state(test_id1))
{PRINTF("combine test_id1\r\n");
}if(key_check_combine_state(test_id2))
{PRINTF("combine test_id2\r\n");
}

相关文章:

  • PostgreSQL的学习心得和知识总结(一百四十四)|深入理解PostgreSQL数据库之sendTuples的实现原理及功能修改
  • JZ2440笔记:rtc驱动
  • 修改wsl2默认配置使宿主机不能使用localhost或127.0.0.1访问WSL
  • 32. 【Java教程】集合
  • rtl8723DU移植 android4.4 4418
  • Go基础编程 - 03 - init函数、main函数、_(下划线)
  • 练习题-17
  • Spring IoC 的实现机制
  • Linux用户权限管理小实验002
  • C++牛客周赛43题目分享(3)小红平分糖果,小红的完全平方数,小苯的字符串变化,小红的子数组排列判断
  • 【云原生】kubernetes中的认证、权限设置--RBAC授权原理分析与应用实战
  • 虚拟机Ubuntu 22.04上搭建GitLab操作步骤
  • 黑客团伙利用Python、Golang和Rust恶意软件袭击印国防部门;OpenAI揭秘,AI模型如何被用于全球虚假信息传播? | 安全周报0531
  • 《异常检测——从经典算法到深度学习》29 EasyTSAD: 用于时间序列异常检测模型的工业级基准
  • 深入分析 Android Activity (二)
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • HTML-表单
  • httpie使用详解
  • iOS编译提示和导航提示
  • Javascript编码规范
  • JavaScript的使用你知道几种?(上)
  • JavaScript服务器推送技术之 WebSocket
  • PaddlePaddle-GitHub的正确打开姿势
  • Python 基础起步 (十) 什么叫函数?
  • python_bomb----数据类型总结
  • SSH 免密登录
  • XML已死 ?
  • 番外篇1:在Windows环境下安装JDK
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • ionic入门之数据绑定显示-1
  • 从如何停掉 Promise 链说起
  • 翻译 | The Principles of OOD 面向对象设计原则
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #每天一道面试题# 什么是MySQL的回表查询
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (Git) gitignore基础使用
  • (k8s中)docker netty OOM问题记录
  • (二)springcloud实战之config配置中心
  • (六)什么是Vite——热更新时vite、webpack做了什么
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (顺序)容器的好伴侣 --- 容器适配器
  • (轉)JSON.stringify 语法实例讲解
  • .htaccess 强制https 单独排除某个目录
  • .NET Framework 3.5中序列化成JSON数据及JSON数据的反序列化,以及jQuery的调用JSON
  • .Net 路由处理厉害了
  • .Net多线程总结
  • .net反编译的九款神器
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @AutoConfigurationPackage的使用