完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
明确按键的使用环境和终极目标 使用环境按键的程序信号、逻辑状态、物理状态、开启计数、关闭计数、多击标志、多击计数 根据我们的环境和目标,天神总结出来我们的按键需要有1+4个信息来记录按键的状态,也就是标题中的程序信号、逻辑状态、物理状态、开启计数、关闭计数。其中程序信号写成信号是因为这些信号不需要存储,通过逻辑状态、物理状态的关系直接返回。对此天神画了一个图进行分析: 此外还有两个状态所对应几种实际情况,按照图中从左到右(红灰绿蓝灰那里)顺序总结一下分别为 具体代码(在stm32上实现) 根据以上分析,天神得出结论,对于每一个按键都需要存储它的逻辑状态、物理状态、开启计数、关闭计数,最后反馈给程序的信号是由这些状态计算而来。注意是每一个按键,也就是说如果你有10个按键,就需要存10组。为此定义一个结构体: //按键状态结构体,存储四个变量 typedef struct { uint8_t KeyLogic; uint8_t KeyPhysic; uint8_t KeyONCounts; uint8_t KeyOFFCounts; }KEY_TypeDef; 一些宏定义,如果你的开关时按下低电平,松开高电平就把KEY_OFF,KEY_ON对调一下就ok了。 //宏定义 #define KEY_OFF 0 #define KEY_ON 1 #define KEY_HOLD 2 #define KEY_IDLE 3 #define KEY_ERROR 10 #define HOLD_COUNTS 50 #define SHAKES_COUNTS 5 创建一个结构体数组,用来对应每一个实际按键,我这里有两个。 //按键结构体数组,初始状态都是关闭 static KEY_TypeDef Key[2] = {{KEY_OFF, KEY_OFF, 0, 0}, {KEY_OFF, KEY_OFF, 0, 0}}; 接下里是关键的key_scan()函数,这个函数要在操作系统的任务中循环执行,因此其中不能有阻塞延时。 /* * 函数名:Key_Scan * 描述 :检测是否有按键按下 * 输入 :GPIOx:gpio的port * GPIO_Pin:gpio的pin * 输出 :KEY_OFF、KEY_ON、KEY_HOLD、KEY_IDLE、KEY_ERROR */ uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { KEY_TypeDef *KeyTemp; //检查按下的是哪一个按钮 switch ((uint32_t)GPIOx) { case ((uint32_t)KEY1_GPIO_PORT): switch (GPIO_Pin) { case KEY1_GPIO_PIN: KeyTemp = &Key[0]; break; //port和pin不匹配 default: printf("error: GPIO port pin not matchrn"); return KEY_IDLE; } break; case ((uint32_t)KEY2_GPIO_PORT): switch (GPIO_Pin) { case KEY2_GPIO_PIN: KeyTemp = &Key[1]; break; //port和pin不匹配 default: printf("error: GPIO port pin not matchrn"); return KEY_IDLE; } break; default: printf("error: key do not existrn"); return KEY_IDLE; } /* 检测按下、松开、长按 */ KeyTemp->KeyPhysic = GPIO_ReadInputDataBit(GPIOx, GPIO_Pin); switch (KeyTemp->KeyLogic) { case KEY_ON: switch (KeyTemp->KeyPhysic) { //(1,1)中将关闭计数清零,并对开启计数累加直到切换至逻辑长按状态 case KEY_ON: KeyTemp->KeyOFFCounts = 0; KeyTemp->KeyONCounts++; if (KeyTemp->KeyONCounts >= HOLD_COUNTS) { KeyTemp->KeyONCounts = 0; KeyTemp->KeyLogic = KEY_HOLD; return KEY_HOLD; } return KEY_IDLE; //(1,0)中对关闭计数累加直到切换至逻辑关闭状态 case KEY_OFF: KeyTemp->KeyOFFCounts++; if (KeyTemp->KeyOFFCounts >= SHAKES_COUNTS) { KeyTemp->KeyLogic = KEY_OFF; KeyTemp->KeyOFFCounts = 0; return KEY_OFF; } return KEY_IDLE; default: break; } case KEY_OFF: switch (KeyTemp->KeyPhysic) { //(0,1)中对开启计数累加直到切换至逻辑开启状态 case KEY_ON: (KeyTemp->KeyONCounts)++; if (KeyTemp->KeyONCounts >= SHAKES_COUNTS) { KeyTemp->KeyLogic = KEY_ON; KeyTemp->KeyONCounts = 0; return KEY_ON; } return KEY_IDLE; //(0,0)中将开启计数清零 case KEY_OFF: (KeyTemp->KeyONCounts) = 0; return KEY_IDLE; default: break; } case KEY_HOLD: switch (KeyTemp->KeyPhysic) { //(2,1)对关闭计数清零 case KEY_ON: KeyTemp->KeyOFFCounts = 0; return KEY_HOLD; //(2,0)对关闭计数累加直到切换至逻辑关闭状态 case KEY_OFF: (KeyTemp->KeyOFFCounts)++; if (KeyTemp->KeyOFFCounts >= SHAKES_COUNTS) { KeyTemp->KeyLogic = KEY_OFF; KeyTemp->KeyOFFCounts = 0; return KEY_OFF; } return KEY_IDLE; default: break; } default: break; } //一般不会到这里 return KEY_ERROR; } 最后在主程序中对按键进行循环检测,天神使用的是FREERTOS操作系统。 static void DataProcess_Task(void *parameter) { while (1) { switch (Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN)) { case KEY_ON: printf("Key1ONn"); break; case KEY_HOLD: printf("Key1HOLDn"); break; case KEY_OFF: printf("Key1OFFn"); break; case KEY_ERROR: printf("errorn"); break; default: break; } switch (Key_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN)) { case KEY_ON: printf("Key2ONn"); break; case KEY_HOLD: printf("Key2HOLDn"); break; case KEY_OFF: printf("Key2OFFn"); break; case KEY_ERROR: printf("errorn"); break; default: break; } vTaskDelay(20); } } 效果 按下按键1两秒钟后松开多击功能:双击、三击、四击、N击均可 为了实现多击功能,我们要在上文的基础上添加内容。首先添加两个存储的信息,分别是多击标志和多击计数,以下进行解释:在上文基础上实现多击功能 //按键状态结构体,存储四个变量 typedef struct { uint8_t KeyLogic; uint8_t KeyPhysic; uint8_t KeyONCounts; uint8_t KeyOFFCounts; //增加两个信息 uint8_t MulClickCounts; uint8_t MulClickFlag; }KEY_TypeDef; 增加一些宏定义,注意让第2击到第N击的标号是连续的,这样我们就可以方便进行递推了。代码写完后,如果要添加更高次数的多击功能,只需要添加宏定义,不需要修改代码,是不是很棒? 天神这里设置的最多5击,当然理论上不论多少次都可以的。 #define KEY_OFF 0 #define KEY_ON 1 #define KEY_HOLD 7 #define KEY_1ClICK KEY_ON #define KEY_2ClICK 2 #define KEY_3ClICK 3 #define KEY_4ClICK 4 #define KEY_5ClICK 5 #define KEY_MAX_MULCLICK KEY_5ClICK #define KEY_IDLE 8 #define KEY_ERROR 10 #define HOLD_COUNTS 100 #define SHAKES_COUNTS 8 #define MULTIPLE_CLICK_COUNTS 20 按键扫描函数,用于在定时器或者操作系统中进行10ms定时的循环检测,逻辑KEY_ON和KEY_HOLD的情况相比上面的代码没有变化,主要变化在KEY_OFF /* * 函数名:Key_Scan * 描述 :检测是否有按键按下 * 输入 :GPIOx:x 可以是 A,B,C,D或者 E * GPIO_Pin:待读取的端口位 * 输出 :KEY_OFF(没按下按键)、KEY_ON(按下按键)、KEY_HOLD(长按)、KEY_2ClICK、KEY_NCLICK(双击一直到设计的多击)、KEY_IDLE(等待状态) */ static KEY_TypeDef Key[KEY_NUMS] = {{KEY_OFF, KEY_OFF, 0, 0, 0, KEY_1ClICK}, {KEY_OFF, KEY_OFF, 0, 0, 0, KEY_1ClICK}}; uint8_t Key_Scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) { KEY_TypeDef *KeyTemp; //检查按下的是哪一个按钮 switch ((uint32_t)GPIOx) { case ((uint32_t)KEY1_GPIO_PORT): switch (GPIO_Pin) { case KEY1_GPIO_PIN: KeyTemp = &Key[0]; break; //port和pin不匹配 default: printf("error: GPIO port pin not matchrn"); return KEY_IDLE; } break; case ((uint32_t)KEY2_GPIO_PORT): switch (GPIO_Pin) { case KEY2_GPIO_PIN: KeyTemp = &Key[1]; break; //port和pin不匹配 default: printf("error: GPIO port pin not matchrn"); return KEY_IDLE; } break; default: printf("error: key do not existrn"); return KEY_IDLE; } /* 检测按下、松开、长按、多击 */ KeyTemp->KeyPhysic = GPIO_ReadInputDataBit(GPIOx, GPIO_Pin); switch (KeyTemp->KeyLogic) { case KEY_ON: //KEY_ON情况相比上面代码没有变化 switch (KeyTemp->KeyPhysic) { //(1,1)中将关闭计数清零,并对开启计数累加直到切换至逻辑长按状态 case KEY_ON: KeyTemp->KeyOFFCounts = 0; KeyTemp->KeyONCounts++; if (KeyTemp->KeyONCounts >= HOLD_COUNTS) { KeyTemp->KeyONCounts = 0; KeyTemp->KeyLogic = KEY_HOLD; return KEY_HOLD; } return KEY_IDLE; //(1,0)中对关闭计数累加直到切换至逻辑关闭状态 case KEY_OFF: KeyTemp->KeyOFFCounts++; if (KeyTemp->KeyOFFCounts >= SHAKES_COUNTS) { KeyTemp->KeyLogic = KEY_OFF; KeyTemp->KeyOFFCounts = 0; return KEY_OFF; } return KEY_IDLE; default: break; } case KEY_OFF: switch (KeyTemp->KeyPhysic) { //(0,1)中对开启计数累加直到切换至逻辑开启状态 case KEY_ON: (KeyTemp->KeyONCounts)++; if (KeyTemp->KeyONCounts >= SHAKES_COUNTS) { //KeyTemp->KeyLogic = KEY_ON; KeyTemp->KeyLogic = KEY_ON; KeyTemp->KeyONCounts = 0; if(KeyTemp->MulClickFlag == KEY_1ClICK) //单击情况单独列出来 { KeyTemp->MulClickFlag = KEY_2ClICK; //预备双击状态 return KEY_ON; } else { if(KeyTemp->MulClickFlag != KEY_MAX_MULCLICK) { /*这句可以看到如果我们的多击标志设置为连续的话 进行递推就会非常方便*/ KeyTemp->MulClickFlag++; KeyTemp->MulClickCounts = 0; /* 因为前面++了所以返回值要减1 */ return (KeyTemp->MulClickFlag - 1); } else { /* 回到单击情况 */ KeyTemp->MulClickFlag = KEY_1ClICK; return KEY_MAX_MULCLICK; } } } return KEY_IDLE; //(0,0)中将开启计数清零,并且对多击计数 case KEY_OFF: (KeyTemp->KeyONCounts) = 0; if(KeyTemp->MulClickFlag != KEY_1ClICK) { if(KeyTemp->MulClickCounts++ > MULTIPLE_CLICK_COUNTS) //超过多击最大间隔时间,关闭多击状态 { KeyTemp->MulClickCounts = 0; KeyTemp->MulClickFlag = KEY_1ClICK; } } return KEY_IDLE; default: break; } case KEY_HOLD: switch (KeyTemp->KeyPhysic) { //(2,1)对关闭计数清零 case KEY_ON: KeyTemp->KeyOFFCounts = 0; return KEY_HOLD; //(2,0)对关闭计数累加直到切换至逻辑关闭状态 case KEY_OFF: (KeyTemp->KeyOFFCounts)++; if (KeyTemp->KeyOFFCounts >= SHAKES_COUNTS) { KeyTemp->KeyLogic = KEY_OFF; KeyTemp->KeyOFFCounts = 0; return KEY_OFF; } return KEY_IDLE; default: break; } default: break; } return KEY_ERROR; } 多击效果 单独测试按键1:天神刚写完就发现问题了,按键哪有五击的时候也把2,3,4击也返回来的233。不过也很好改,有空回来就改。还可以继续在基础上实现组合按键,有空就添加。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1848 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1647 浏览 1 评论
1120 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
745 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1704 浏览 2 评论
1959浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
766浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
596浏览 3评论
618浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
578浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-4 15:20 , Processed in 0.655517 second(s), Total 45, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号