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

功能概述

本按键方案硬件部分由两个独立按键组成, 在移植后能够适配市面上绝大部分单片机. 独立按键分为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); // SW1
  GPIOPadConfigSet(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); // SW2
  GPIOPadConfigSet(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);
	}	
}
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值