大航母网站建设,用wordpress建立学校网站,手机网站要域名吗,WordPress命令执行漏洞平台无关的嵌入式通用按键管理器
本文代码仓库地址#xff1a;https://gitee.com/holymiao/Platform-independent-Embedded-Universal-Key-Manager.git 本文是在《通用的按键代码#xff08;上#xff09;》和《通用按键代码#xff08;下#xff09;》两篇文章的基础上添…平台无关的嵌入式通用按键管理器本文代码仓库地址https://gitee.com/holymiao/Platform-independent-Embedded-Universal-Key-Manager.git本文是在《通用的按键代码上》和《通用按键代码下》两篇文章的基础上添加组合按键重新整理架构后编写而来中间用到了双向链表库使用方法可以参考《手搓算法(3) 双向链表 支持头尾遍历、排序、增删、正负数索引、查找》一文;程序架构用到了面向对象的C语言设计方法牵扯结构体的继承、多态、封装、内部函数等功能技术点可以参考《面向对象的C语言编程》由于本代码跟上一篇程序代码的结构变化较大这是本项目经理考虑不周导致这也就是为啥让程序员添加一个小功能会得罪对方的原因但也不是推翻重做所以代码不讲也不合适从头到尾再讲一遍也不合适。所以这里只编写使用方法具体实现方法大家可以根据上一篇文章的基础再根据代码的注释自行分析。1.按键库的功能和特点功能CPU、架构无关的按键驱动只需提供基本接口即可畅游自带独立按键、矩阵按键、AD模拟按键功能的识别需要用户提供基本的IO读写或AD值获取函数就能应用留出接口方便用户扩展其他类型的按键支持获取按键的按下、弹开、双击、长按的事件获取支持组合键支持中断模式获取按键事件也支持轮询模式查看按键值支持添加按键字符对应表方便按键和字符进行对应可随时注册更改和卸载支持按键分组可以把不同功能或者不同驱动方式的按键分组处理让一个项目中支持多种按键项目且可以随时注册和卸载按键组。2.跟上一篇使用方法的区别总览大家可以根据下边的条目对比《通用的按键代码上》和《通用按键代码下》分析代码的异同。区别添加组合按键导致程序结构大改把之前的按键组的事件回调删除添加按键事件总驱动统一处理单个按键和组合按键的问题按键值支持组合按键故按键值从keyValue_t类型替换成keyValueArr_t类型可以保存多个按键值相关的按键事件标志从keyValue_t类型挪到keyValueArr_t类型中按键组获取按键值也可能是多个故基类的getKeyValue的返回值改成void并在基类中添加组合按键值combinKeyValuesgetKeyValue()函数获取的值放到combinKeyValues中处理对应的派生类相关的函数接口都要跟着变化按键管理器的初始化函数keyManagerInit添加组合按键识别时间参数保证用户在按下组合按键稳定之后再识别组合事件添加组合按键值的添加删除函数供程序或者继承类中相关程序调用因为添加组合按键故独立按键必须知道当前组有多少个按键所以必须重新设计独立按键类添加按键个数count根据索引获取IO电平的函数指针getKey两个类成员。相应的独立按键组分配函数也要做相关修改按键事件分析函数keyManagerScankeyEvent在原程序的基础上添加了组合按键的识别代码。3.独立按键的使用方法这里使用stm32HAL库为例进行讲解主要是我不想自己编写硬件初始化代码。后边其他例子也做相关处理不再赘述。3.1 外设设置准备cubemx软件生成调试串口并编写printf支持的程序这里不再讲解;生成20ms定时中断当然也可以使用我之前编写的定时管理器详情看这篇文章《通用的定时事件管理器》.在定时中断里边调用keyManagerScankeyEvent()函数用户驱动按键的状态识别。void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { keyManagerScankeyEvent(); }把库中的“KeyManager.c”、“KeyManager.h”、“LinkList.c”、“LinkList.h”添加并导入到工程中导入方法这里不再详解根据自己的开发板硬件电路添加几个按键这里以四个按键为例名字分别改为“key1”、“key2”、“key3”、“key4”。注意我这个板子key1~key3接低电平故IO设置为输入上拉而key4接高电平故IO口需要设置成输入下拉看代码时务必注意3.2 主程序中调用流程咱们先讲主程序如何配置然后再说需要哪些回调函数。调用流程如下初始化串口、GPIO、定时器等相关的硬件MX_GPIO_Init(); MX_TIM6_Init(); MX_USART1_UART_Init(); HAL_TIM_Base_Start_IT(htim6);初始化按键管理器注册超时时间及事件回调函数keyEvent和应用参数这里“事件回调函数keyEvent”需要我们手动去编写//长按识别时间1s双击识别时间100ms组合按键识别事件100ms //事件回调函数keyEvent为NULL表示没有回调函数 //回调参数设置为NULL表示没有也可以设置成其他数据的指针 keyManagerInit(1000, 100, 100, keyEvent, NULL);分配独立按键的设备空间这里为了分组 (炫技)把四个按键分成两组key1、key2一组key3和key4一组所以设备空间分成两个每组俩个按键这里出现两个获取IO电平的回调函数getkeyGroup1、getkeyGroup2需要我们手动去实现。//每组两个按键并注册回调函数 key_base_t *keyGroup1 creatIndepKey(2, getkeyGroup1); key_base_t *keyGroup2 creatIndepKey(2, getkeyGroup2);注册两个按键组根据顺序分配的ID分别是1号和二号可以通过注册函数的返回值来确认这里第一组按键值从1开始第二组按键值从10开始uint8_t id1 keyManagerAddGroup(keyGroup1, 1);//按键值从1开始 uint8_t id2 keyManagerAddGroup(keyGroup2, 10);//按键值从10开始注册按键字符对应表让按键对应字符某些场合下适用。对应表分为全局对应表GlobalKeyCharTab和局部对应表KeyCharTab可以共用其中局部表优先级最高。另外程序运行时调用下边的函数可以随时更新按键值实现不同场合不同按键功能的作用。出现的GlobalKeyCharTab、KeyCharTab两个数组需要我们手动去实现。RegistIdKeyCharTab(GlobalKeyCharTab, 2); RegistKeyCharTab(keyGroup2, KeyCharTab, 2);至此初始化全部完成期间需要我们实现一个事件函数keyEvent、两个回调函数keyGroup1、keyGroup2两个对应表GlobalKeyCharTab、KeyCharTab。3.3 编写按键事件回调函数因为回调函数的原型如下/// brief 按键对应的事件函数指针类型 /// param value 按键值内含事件类型、组别、按键值 /// param data 用户传入数据的地址需要用户自行管理指针类型 typedef void (*fun_keyEvent_t)(keyValueArr_t* value, void *data);故需要在用户程序中按照这个规则编写回调函数。在此函数中分析是否是组合键分析按键的组别ID和键值并显示此按键对应的字符。实际应用的时候不要把按下事件当做按键的处理事件因为信息太杂了void keyEvent(keyValueArr_t* values, void *data) { if(values-state KEY_EVENT_COMBIN) { //识别是组合键 printf(组合按键分别是); for(int i 0; i values-count; i) { //逐次显示组合键信息 printf(id:%d,value:%d;,values-valueArr[i].id,values-valueArr[i].keyValue); } } else { //不是组合键则只需处理第一个数据即可 //分析按键的事件类型 switch(values-state) { case KEY_EVENT_DOWN:printf(按下事件);break; case KEY_EVENT_UP:printf(弹开事件);break; case KEY_EVENT_LONG:printf(长按事件);break; case KEY_EVENT_DOUBLE:printf(双击事件);break; } //显示组别ID和键值 printf(id:%d,value:%d;,values-valueArr[0].id,values-valueArr[0].keyValue); //显示按键对应的字符 printf(按键字符是%c, GetKeyChar(values-valueArr[0])); } printf(\r\n); }3.3 编写IO读取函数独立按键在分配空间时需要用到如下回调原型/// brief 独立按键获取IO状态的函数 /// param index IO索引从0开始计数 /// return 返回IO按下状态IO_pressed_state表示按下IO_unpressed_state表示未按下 typedef IO_state_t (*fun_getIO_t)(uint16_t index);按照这个规则编写独立按键IO读取函数。这里把四个按键分成两组key1、key2一组key3和key4一组再次强调key4另一端连接高电平其状态跟其他按键正好相反所以需要两个IO读取函数。//第一组识别key1和key2 IO_state_t getkeyGroup1(uint16_t index) { GPIO_PinState retValue GPIO_PIN_SET; switch(index) { case 0: retValue HAL_GPIO_ReadPin(key1_GPIO_Port, key1_Pin); break; case 1: retValue HAL_GPIO_ReadPin(key2_GPIO_Port, key2_Pin); break; } //hal库的GPIO_PinState和按键库的IO_state_t类型是兼容的故可以强制转换下同 return (IO_state_t)retValue; } //第一组识别key3和key4 IO_state_t getkeyGroup2(uint16_t index) { GPIO_PinState retValue GPIO_PIN_SET; switch(index) { case 0: retValue HAL_GPIO_ReadPin(key3_GPIO_Port, key3_Pin); break; //注意 //key4按键按下时为高电平未按时为低电平跟其他按键状态相反故这里需要取反 case 1: retValue (GPIO_PinState)(!HAL_GPIO_ReadPin(key4_GPIO_Port, key4_Pin)); break; } return (IO_state_t)retValue; }3.4 编写按键字符对应表按键字符对应表有两种可以同时使用多个按键可以对应一个字符某些按键也可以不设置字符。带ID的全局按键字符对应表所有按键组都可以用一张表来对应属于低优先级对应表//全局按键字符对应表 const IdKeyChar_t GlobalKeyCharTab[] { {{1, 1}, a}, {{1, 2}, b}, {{2, 10}, c}, //第二组按键值从10开始 {{2, 11}, d}, };不带ID的按键组内部字符对应表只适用于某一组别内部按键字符识别优先级高const KeyChar_t KeyCharTab[] { {10, A}, //第二组按键值从10开始 {11, B} };3.5 执行结果编译下载后按下按键串口打印信息如下大家对着事件函数keyEvent()自行分析4. 矩阵按键的声明方法矩阵键盘的声明方法跟独立按键方法大同小异。在独立按键的代码基础上直接添加cubemx中添加四个行IO名字分别是row0~row3设置成输出模式添加四个列IO名字分别是col0~col3设置成输入上拉模式在主程序中为矩阵按键分配设备空间这里出现两个回调函数setRow, getCol//行列都是4个IO注册设置行IO函数setRow、读取列IO函数getCol key_base_t *keyGroup3 creatMatrixKey(4, 4, setRow, getCol);把矩阵键盘组注册到按键管理器中按键值从1开始。//矩阵键盘按键值从1开始这就跟上边第一组独立按键键值重叠 //不用担心两个组的ID不一样 uint8_t id3 keyManagerAddGroup(keyGroup3, 1);编写字符对应表可以修改全局表GlobalKeyCharTab也可以重新注册矩阵表这里选择后者//4*4计算器小键盘字符对应表 const KeyChar_t MatrixKeyCharTab[] { {1, 7}, {2, 8}, {3, 9}, {4, }, {5, 4}, {6, 5}, {7, 6}, {8, -}, {9, 1}, {10, 2}, {11, 3}, {12, *}, {13, C}, {14, 0}, {15, }, {16, /}, }; RegistKeyCharTab(keyGroup3, MatrixKeyCharTab, 16);至此初始化工作已完成需要实现两个回调函数setRow, getCol4.1 设置行IO高低电平的函数setRow函数原型如下:/// brief 设置行IO电平状态根据上下拉不一样高低电平也不一样一般来说用户将“列”设置上拉则行设置成IO_pressed_state表示为低电平 /// param index 行索引从0开始 /// param state 行IO状态IO_pressed_state表示设置为识别电平若“列”IO为上拉则此处为低电平IO_unpressed_state表示设置为未识别电平若“列”IO为上拉则此处为高电平 typedef void (*fun_setRow_t)(uint16_t index, IO_state_t state);由此编写的函数如下void setRow(uint16_t index, IO_state_t state) { switch(index) { case 0: HAL_GPIO_WritePin(row0_GPIO_Port, row0_Pin, (GPIO_PinState)state); break; case 1: HAL_GPIO_WritePin(row1_GPIO_Port, row1_Pin, (GPIO_PinState)state); break; case 2: HAL_GPIO_WritePin(row2_GPIO_Port, row2_Pin, (GPIO_PinState)state); break; case 3: HAL_GPIO_WritePin(row3_GPIO_Port, row3_Pin, (GPIO_PinState)state); break; } }4.2 获取列IO电平函数getCol函数原型如下/// brief 获取列IO高低电平的状态 /// param index 列IO索引从0开始 /// return IO状态IO_pressed_state表示此列被识别按下若列IO为上拉则此处低电平为识别状态IO_unpressed_state表示此列识别未按下若列IO为上拉则此处高电平为未识别状态 typedef IO_state_t (*fun_getCol_t)(uint16_t index);由此编写的函数如下IO_state_t getCol(uint16_t index) { GPIO_PinState retValue GPIO_PIN_SET; switch(index) { case 0: retValue HAL_GPIO_ReadPin(col0_GPIO_Port, col0_Pin); break; case 1: retValue HAL_GPIO_ReadPin(col1_GPIO_Port, col1_Pin); break; case 2: retValue HAL_GPIO_ReadPin(col2_GPIO_Port, col2_Pin); break; case 4: retValue HAL_GPIO_ReadPin(col3_GPIO_Port, col3_Pin); break; } return (IO_state_t)retValue; }4.3 总结编译运行下载其实验现象跟独立按键一模一样这里不再赘述。5. AD模拟按键的声明方法AD模拟按键的声明方法跟上边的代码类似在矩阵代码的基础上添加如下内容cubemx分配一个AD引脚进型适当的配置软件触发或定时触发都没问题这里以软件触发为例。在主程序上初始化AD模块按键值给AD模块分配内存注册AD值获取函数getAD、电压识别范围表ADtab//这里分配8个按键 key_base_t *keyGroup4 creatADKey(getAD, ADtab, 8);把AD按键注册到按键管理器中//按顺序来说id为4按键值从1开始 uint8_t id4 keyManagerAddGroup(keyGroup4, 1);给模拟按键添加字符识别数组这里方法跟上边一样不再赘述5.1 获取AD的回调函数函数原型//获取AD值的回调函数 typedef uint16_t (*fun_GetAD_t)(void);由此编写代码uint16_t getAD(void) { HAL_ADC_Start(hadc1); //软件触发ADC HAL_ADC_PollForConversion(hadc1,1);//查询函数 return HAL_ADC_GetValue(hadc1); //得到ADC的 }5.2 模拟按键的电压范围识别表先分析下边的图假如配置的是12位AD则最大值是4096当s1按下时ADC_KEY口电压为0VAD值是0S2按下时分压150/(1501000)对应的AD值是4096*150/(1501000)534S3按下时分压(150240)/(1502401000)对应的AD值是4096*(150240)/(1502401000)1390S4按下时对应的AD值是4096*(150240360)/(1502403601000)1755S5按下时对应的* AD值是4096*(150240360620)/(1502403606201000)2367S6按下时对应的AD值是4096*(1502403606201000)/(15024036062010001000)2880S7按下时对应的AD值是4096*(15024036062010003600)/(150240360620100036001000)3508S8按下时对应的AD值是4096*(1502403606201000360030000)/(15024036062010003600300001000)3985由此我们得到每个按键的AD值识别范围const ADKeyTab_t ADTab[] { {0, 200}, {300, 700}, {1000, 1550}, {1600, 2000}, {2100, 2500}, {2600, 3000}, {3300, 3700}, {3800, 4000}};5.3 总结模拟按键的初始化工作已经全部完成实验结果跟上边一致。注意AD按键内部不支持组合键因为两个按键按下后输出电压值只有一个但AD按键可以跟其他组按键进行组合识别。6. 自定义按键的添加方法独立按键、矩阵键盘、AD模拟按键是目前最常用的三种按键可满足九成以上的嵌入式需求。但架不住还有一些特殊按键比如电磁炉的触摸按键等该如何自定义添加呢思路如下想办法实现按键基类中的getKeyValue函数比如独立按键的readIndepKey()、矩阵键盘的scanMatrixKey()、AD键盘的scanADKey()为了实现上边的函数必须注册相关用户处理函数回调还要注册按键个数比如独立按键的getKey()、矩阵键盘的getCol()和setRow()、AD按键的getAD()且按键个数和用户回调都要放到继承类里边继承类中添加必要的其他参数例如AD键盘的类的ADTab识别数组大家可以根据KeyManager.c中独立按键、矩阵按键、AD按键的代码体会其中的方法编写自己的按键程序比如触摸按键识别程序。