单片机基于独立按键的屏幕翻页/功能选择方案(支持长按,短按,双击操作)

本文主要是介绍单片机基于独立按键的屏幕翻页/功能选择方案(支持长按,短按,双击操作),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

功能概述

本按键方案硬件部分由两个独立按键组成, 在移植后能够适配市面上绝大部分单片机. 独立按键分为A, B两键, 轻击A键代表上一页, 轻击B键代表下一页, 同时开发者可自由定义双击, 长按操作的功能.

本文给出了两个使用案例, 分别是51单片机上的简单移植和德州仪器Tiva系列TM4C123GXL评估板的OLED翻页及功能选择的实现.

方案中按键共有如下9种状态

编号程序中代号状态名称
0NO_PRESS状态清空/无按键状态
1SHORT_PRESS_BOTH双键短按
2LONG_PRESS_BOTH双键长按
3SHORT_PRESS_AA键短按
4LONG_PRESS_AA键长按
5SHORT_PRESS_BB键短按
6LONG_PRESS_BB键长按
7DOUBLE_PRESS_A双击A键
8DOUBLE_PRESS_B双击B键

在Cortex M4内核的Tiva TM4C系列单片机上的实例

注意事项

1.KeyScan()函数在主函数中扫描
2.OLED_Hybernate()休眠函数在定时器中断函数中运行
3.Menu_Display()函数在主函数中运行, 不能在定时器中断函数运行
4.该工程实例可以在以下链接中找到, 方便大家参考, 本文仅放置部分代码片段
Gitee/李思睿/基于Tiva单片机的分布式温度控制节能系统

一.按键初始化函数

/***********************************************************
@函数名:Key_Init
@入口参数:无
@出口参数:无
功能描述:按键初始化
@作者:skylisan
@日期:2021年01月26日
*************************************************************/
void Key_Init(void)
{SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);//  Unlock PF0 so we can change it to a GPIO input//  Once we have enabled (unlocked) the commit register then re-lock it//  to prevent further changes.  PF0 is muxed with NMI thus a special case.HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= 0x01;HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0;GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_0, GPIO_DIR_MODE_IN); // SW1GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_0, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_DIR_MODE_IN); // SW2GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
}

二. 按键扫描及OLED显示函数

#define LONG_PRESS_MAX 5000           //设定长按阈值(尽量设定其在一秒左右, 由于程序问题计算值并非实际操作值)
int16_t Page_Number = 0;
uint8_t Key_Right_Release = 0;
uint8_t Oled_Show_Enable = 1;
uint16_t OLED_Hybernate_Counter=0;/***********************************************************
@函数名:OLED_hybernate
@入口参数:无
@出口参数:无
功能描述:一分钟的OLED屏无操作自动休眠时间
@作者:skylisan
@日期:2021年12月31日
*************************************************************/
void OLED_Hybernate(void)
{if(OLED_Hybernate_Counter!=12000) OLED_Hybernate_Counter++;
}bool IR_Temp_Debugger=0;
bool IR_Temp_Setter=0;
bool Power_Switcher_Ctl=0;
bool Current_Outside_Temp_Switch=0;
bool Temp_Humi_Display_Switch=0;
press_state KeyStat;
/***********************************************************
@函数名:Key_Scan
@入口参数:release
@出口参数:bool
功能描述:按键扫描,入口参数release决定是否开放按键扫描权限
正常扫描返回TRUE,按键按下时为低电平,释放后IO配置的是上拉
输入模式,悬空为高电平
@作者:skylisan
@日期:2021年01月04日
*************************************************************/
bool Key_Scan(uint8_t release)
{uint16_t long_press_cnt = 0, double_press_cnt = 100;if (release == 1)return false;if (QuadKey1 == 0 && QuadKey2 == 0)                       //两个按键同时按键{delay_ms(10);if (QuadKey1 == 0 && QuadKey2 == 0){while (QuadKey1 == 0 && QuadKey2 == 0)                //长按判断{long_press_cnt++;delay_us(110);}delay_ms(1);                                          //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_BOTH;                            //两键长按//printf("LongPressBoth\n");while (QuadKey1 == 0 || QuadKey2 == 0);             //等待按键松开方便执行下一步}else{KeyStat=SHORT_PRESS_BOTH;                           //两键短按//printf("ShortPressBoth\n");while (QuadKey1 == 0 || QuadKey2 == 0);             //等待按键松开方便执行下一步}long_press_cnt = 0;OLED_Hybernate_Counter = 0;return true;}}if (QuadKey1 == 0 && QuadKey2 != 0){delay_ms(10);                                            //延时消抖if (QuadKey1 == 0 && QuadKey2 != 0){while (QuadKey1 == 0 && QuadKey2 != 0){long_press_cnt++;delay_us(110);}delay_ms(1);                            //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_A;                 //A键长按//printf("LongPressA\n");}else{while(QuadKey1!=0&&double_press_cnt>0){double_press_cnt--;delay_ms(1);}if(QuadKey1==0){delay_ms(10);if(QuadKey1==0&&QuadKey2!=0) {KeyStat=DOUBLE_PRESS_A;         //A键双击while(QuadKey1==0);//printf("DoublePressA\n");}}else{KeyStat=SHORT_PRESS_A;            //A键短按//printf("ShortPressA\n");}}long_press_cnt = 0;OLED_Hybernate_Counter = 0;return true;}}else if (QuadKey1 != 0 && QuadKey2 == 0){delay_ms(10);if (QuadKey1 != 0 && QuadKey2 == 0){while (QuadKey1 != 0 && QuadKey2 == 0){long_press_cnt++;delay_us(110);}delay_ms(1);                          //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_B;               //B键长按//printf("LongPressB\n");}else{while(QuadKey2!=0&&double_press_cnt>0){double_press_cnt--;delay_ms(1);  }if(QuadKey2==0){delay_ms(10);if(QuadKey2==0&&QuadKey1!=0) {KeyStat=DOUBLE_PRESS_B;         //B键双击while(QuadKey2==0);//printf("DoublePressB\n");}}else{KeyStat=SHORT_PRESS_B;            //B键短按//printf("ShortPressB\n");}}}long_press_cnt = 0;                 OLED_Hybernate_Counter = 0;             //若触发按键操作, 自动将休眠计数器清零return true;}return false;
}int16_t AC_Temp_Setup=26;
bool Opr_Sgn=false;
/***********************************************************
@函数名:Menu_Display
@入口参数:无
@出口参数:无
功能描述:主选单的作用, 在主循环中运行, 进行参数的显示与调整
@作者:skylisan
@日期:2021年12月29日
*************************************************************/
void Menu_Display()
{if(OLED_Hybernate_Counter==12000) LCD_CLS();if(OLED_Hybernate_Counter!=12000){if(KeyStat==SHORT_PRESS_A) { Page_Number--; LCD_CLS(); }if(KeyStat==SHORT_PRESS_B) { Page_Number++; LCD_CLS(); }if(Page_Number==-1) Page_Number=5;                                  //本实例中, 统共有5页if(Page_Number==6)  Page_Number=0;if(Page_Number==0){LCD_clear_L(90, 0);display_6_8_string(0, 0, "PAGE 0");   }if(Page_Number==1){display_6_8_string(0, 0, "PAGE 1");display_6_8_string(0, 1, "AirCond Remote Ctl");}if(Page_Number==2){LCD_clear_L(90, 0);    display_6_8_string(0, 0, "PAGE 2");LCD_clear_L(0, 2);     if(KeyStat==LONG_PRESS_BOTH) IR_Temp_Setter=!IR_Temp_Setter;if(IR_Temp_Setter==0)  display_6_8_string(0, 2, "Disabled");else                   display_6_8_string(0, 2, "Enabled");      //功能选择display_6_8_string(0, 6, "Long Hold L&R Button");display_6_8_string(0, 7, "to Adjust");}if(Page_Number==3){LCD_clear_L(90, 0);   display_6_8_string(0, 0, "PAGE 3");display_6_8_string(0, 1, "Switch");if(KeyStat==DOUBLE_PRESS_A)  Temp_Humi_Display_Switch=!Temp_Humi_Display_Switch;if(Temp_Humi_Display_Switch==0){LCD_clear_L(90, 6);   display_6_8_string(0, 6, "Double Click to AFunc:");}else{LCD_clear_L(90, 6);   display_6_8_string(0, 6, "Double Click to BFunc:");}}if(Page_Number==4){LCD_clear_L(90, 0);   display_6_8_string(0, 0, "PAGE 4");if(KeyStat==LONG_PRESS_BOTH){/*DO SOMETHING*/}}if(Page_Number==5){display_6_8_string(0, 0, "PAGE 5");if(KeyStat==LONG_PRESS_BOTH){/*DO SOMETHING*/} if(KeyStat==SHORT_PRESS_BOTH){/*DO SOMETHING*/}if(KeyStat==LONG_PRESS_B){/*DO SOMETHING*/}if(KeyStat==LONG_PRESS_A){/*DO SOMETHING*/}}}KeyStat=NO_PRESS;                 //清空状态
}
  1. 头文件(Key.h)
#ifndef __KEY_H__
#define __KEY_H__#define QuadKey1 GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_4)
#define QuadKey2 GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0)void Key_Init(void);
bool Key_Scan(uint8_t release);
void Menu_Display(void);
void OLED_Hybernate(void);extern uint8_t Key_Right_Release, Oled_Show_Enable;
extern int16_t Page_Number;
extern int16_t AC_Temp_Setup;typedef enum
{NO_PRESS = 0,SHORT_PRESS_BOTH,LONG_PRESS_BOTH,SHORT_PRESS_A,LONG_PRESS_A,SHORT_PRESS_B,LONG_PRESS_B,DOUBLE_PRESS_A,DOUBLE_PRESS_B,
} press_state;#endif
  1. 主函数
#include "Headfile.h"uint8_t tempL,humiL;
int main(void)
{HardWave_Init(); //芯片资源、外设初始化while (1) //主循环{Key_Scan(Key_Right_Release);Menu_Display();}
}

在51单片机中的简单移植

单片机平台: 国信长天CT107D
注:
11.0592Mhz, 一个机器周期约等于1us.
采用8bitLED灯亮灭表示按键触发状态

#include "reg52.h"
#include <intrins.h>					   #define u8 unsigned char
#define u16	unsigned int
#define u32 unsigned long
#define LONG_PRESS_MAX 5000           //设定长按阈值(尽量设定其在一秒左右, 由于程序问题计算值并非实际操作值)sbit Chip_138_C=P2^5;
sbit Chip_138_B=P2^6;
sbit Chip_138_A=P2^7;
sbit KEY1=P3^0;
sbit QuadKey1=P3^1;
sbit QuadKey2=P3^2;
sbit KEY4=P3^3;
sbit BUZZ=P0^6;
sbit LED=P0^0;typedef enum
{NO_PRESS = 0,SHORT_PRESS_BOTH,LONG_PRESS_BOTH,SHORT_PRESS_A,LONG_PRESS_A,SHORT_PRESS_B,LONG_PRESS_B,DOUBLE_PRESS_A,DOUBLE_PRESS_B,
} press_state;void delay(u16 t)
{while(t--){_nop_();}
}void Chip_138_Sel(u8 sel_num)
{switch(sel_num){case 0:Chip_138_A=0;Chip_138_B=0;Chip_138_C=0;break;case 1:Chip_138_A=0;Chip_138_B=0;Chip_138_C=1;break;case 2:Chip_138_A=0;Chip_138_B=1;Chip_138_C=0;break;case 3:Chip_138_A=0;Chip_138_B=1;Chip_138_C=1;break;case 4:Chip_138_A=1;Chip_138_B=0;Chip_138_C=0;break;case 5:Chip_138_A=1;Chip_138_B=0;Chip_138_C=1;break;case 6:Chip_138_A=1;Chip_138_B=1;Chip_138_C=0;break;case 7:Chip_138_A=1;Chip_138_B=1;Chip_138_C=1;break;}
}
u8 Page_Number = 0;
u8 Key_Right_Release = 0;press_state KeyStat;
/***********************************************************
@函数名:Key_Scan
@入口参数:release
@出口参数:bool
功能描述:按键扫描,入口参数release决定是否开放按键扫描权限
正常扫描返回TRUE,按键按下时为低电平,释放后IO配置的是上拉
输入模式,悬空为高电平<51版>
@作者:skylisan
@日期:2021年02月20日
*************************************************************/
u8 Key_Scan(u8 release)
{u16 long_press_cnt = 0, double_press_cnt = 100;if (release == 0)return 0;if (QuadKey1 == 0 && QuadKey2 == 0)                       //两个按键同时按键{delay(10000);if (QuadKey1 == 0 && QuadKey2 == 0){while (QuadKey1 == 0 && QuadKey2 == 0)                //长按判断{long_press_cnt++;delay(110);}delay(1000);                                          //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_BOTH;                            //两键长按//printf("LongPressBoth\n");Chip_138_Sel(4);P0=0x01;while (QuadKey1 == 0 || QuadKey2 == 0);             //等待按键松开方便执行下一步}else{KeyStat=SHORT_PRESS_BOTH;                           //两键短按//printf("ShortPressBoth\n");Chip_138_Sel(4);P0=0x02;while (QuadKey1 == 0 || QuadKey2 == 0);             //等待按键松开方便执行下一步}long_press_cnt = 0;return 1;}}if (QuadKey1 == 0 && QuadKey2 != 0){delay(10000);                                            //延时消抖if (QuadKey1 == 0 && QuadKey2 != 0){while (QuadKey1 == 0 && QuadKey2 != 0){long_press_cnt++;delay(110);}delay(1000);                            //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_A;                 //A键长按Chip_138_Sel(4);P0=0x03;//printf("LongPressA\n");}else{while(QuadKey1!=0&&double_press_cnt>0){double_press_cnt--;delay(1000);}if(QuadKey1==0){delay(10000);if(QuadKey1==0&&QuadKey2!=0) {KeyStat=DOUBLE_PRESS_A;         //A键双击while(QuadKey1==0);Chip_138_Sel(4);P0=0x04;//printf("DoublePressA\n");}}else{KeyStat=SHORT_PRESS_A;            //A键短按Chip_138_Sel(4);P0=0x05;//printf("ShortPressA\n");}}long_press_cnt = 0;return 1;}}else if (QuadKey1 != 0 && QuadKey2 == 0){delay(10000);if (QuadKey1 != 0 && QuadKey2 == 0){while (QuadKey1 != 0 && QuadKey2 == 0){long_press_cnt++;delay(110);}delay(1000);                          //延时去抖if (long_press_cnt >= LONG_PRESS_MAX){KeyStat=LONG_PRESS_B;               //B键长按Chip_138_Sel(4);P0=0x06;//printf("LongPressB\n");}else{while(QuadKey2!=0&&double_press_cnt>0){double_press_cnt--;delay(1000);  }if(QuadKey2==0){delay(10000);if(QuadKey2==0&&QuadKey1!=0) {KeyStat=DOUBLE_PRESS_B;         //B键双击while(QuadKey2==0);Chip_138_Sel(4);P0=0x07;//printf("DoublePressB\n");}}else{KeyStat=SHORT_PRESS_B;            //B键短按Chip_138_Sel(4);P0=0x08;//printf("ShortPressB\n");}}}long_press_cnt = 0;                 return 1;}return 0;
}void main(void)
{bit flag=0;Chip_138_Sel(5);BUZZ=0;while(1){Key_Scan(1);}	
}

这篇关于单片机基于独立按键的屏幕翻页/功能选择方案(支持长按,短按,双击操作)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/578955

相关文章

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

mysql表操作与查询功能详解

《mysql表操作与查询功能详解》本文系统讲解MySQL表操作与查询,涵盖创建、修改、复制表语法,基本查询结构及WHERE、GROUPBY等子句,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随... 目录01.表的操作1.1表操作概览1.2创建表1.3修改表1.4复制表02.基本查询操作2.1 SE

MySQL之InnoDB存储页的独立表空间解读

《MySQL之InnoDB存储页的独立表空间解读》:本文主要介绍MySQL之InnoDB存储页的独立表空间,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、独立表空间【1】表空间大小【2】区【3】组【4】段【5】区的类型【6】XDES Entry区结构【

SQLite3 在嵌入式C环境中存储音频/视频文件的最优方案

《SQLite3在嵌入式C环境中存储音频/视频文件的最优方案》本文探讨了SQLite3在嵌入式C环境中存储音视频文件的优化方案,推荐采用文件路径存储结合元数据管理,兼顾效率与资源限制,小文件可使用B... 目录SQLite3 在嵌入式C环境中存储音频/视频文件的专业方案一、存储策略选择1. 直接存储 vs

Golang如何用gorm实现分页的功能

《Golang如何用gorm实现分页的功能》:本文主要介绍Golang如何用gorm实现分页的功能方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景go库下载初始化数据【1】建表【2】插入数据【3】查看数据4、代码示例【1】gorm结构体定义【2】分页结构体

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

springboot如何通过http动态操作xxl-job任务

《springboot如何通过http动态操作xxl-job任务》:本文主要介绍springboot如何通过http动态操作xxl-job任务的问题,具有很好的参考价值,希望对大家有所帮助,如有错... 目录springboot通过http动态操作xxl-job任务一、maven依赖二、配置文件三、xxl-

Java Web实现类似Excel表格锁定功能实战教程

《JavaWeb实现类似Excel表格锁定功能实战教程》本文将详细介绍通过创建特定div元素并利用CSS布局和JavaScript事件监听来实现类似Excel的锁定行和列效果的方法,感兴趣的朋友跟随... 目录1. 模拟Excel表格锁定功能2. 创建3个div元素实现表格锁定2.1 div元素布局设计2.

Oracle 数据库数据操作如何精通 INSERT, UPDATE, DELETE

《Oracle数据库数据操作如何精通INSERT,UPDATE,DELETE》在Oracle数据库中,对表内数据进行增加、修改和删除操作是通过数据操作语言来完成的,下面给大家介绍Oracle数... 目录思维导图一、插入数据 (INSERT)1.1 插入单行数据,指定所有列的值语法:1.2 插入单行数据,指