打印
[经验分享]

单片机基础:独立按键,多按键实现单击,双击,长按,多击功能

[复制链接]
225|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Jiangxiaopi|  楼主 | 2025-1-2 09:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
前言
很多人学单片机的时候马马虎虎,许多例程都是知其然不知其所以然。这样其实是害了自己,拿来就用自然很方便,但如果不懂其中的原理,只要功能要求稍微变一点,估计你就蒙圈了。毕竟技巧是基于扎实的基础之上的!

一、明确需求

用户基本操作定义:
    1。短按操作:按键按下,按下时间<1s,属于一次短按操作
    2。长按操作:按键按下,按下时间>1s,属于一次长按操作

在正常0.5s内无按键操作为启始按键扫描条件下,扫描按键将产生以下3种按键事件:
    1。长按事件:任何1次出现的长按操作都属于长按事件
    2。单击事件:1次短按操作后,间隔0.5内没有短按操作
    3。双击事件:2次短按操作间隔时间<0.5s,则2次短按操作为1次双击事件,且2次短按都取消

特别操作情况定义:
    1。短按操作和长按操作间隔<0.5s,以及,长按操作和短按操作间隔<0.5s,均不产生双击事件
    2。连续n次(n为奇数)短按操作,且间隔均<0.5s,产生(n-1)/2次双击事件+1次单击事件
    3。连续n次(n为偶数)短按操作,且间隔均<0.5s,产生n/2次双击事件

对按键操作者的建议:     
    由于按键的多功能性质,建议操作者每次在单击/长按/双击按键事件发生后,隔0.5s后再进行下一次的按键操作。因为在特别操作情况下,程序是保证按定义进行判断和处理的,主要是怕操作者自己记不清楚导致操作失误。

对软件设计者的要求:
    1。应该全面进行分析,给出严格定义和判断条件,如上所示。如果自己都不清楚,你的设计出的系统就不稳定,不可靠。
    2。在1的基础上,编写出符合要求的程序,并进行全面测试。

二,实现过程
1.首先在key.h定义声明结构体,及其所需的函数
typedef struct
{
        uint8_t KeyIO;//按键IO
    uint8_t KeyIOFilterCnt;//滤波次数
        uint8_t KeyPressNum;//按键次数
    uint8_t KeyPressTime;//按键按下时间
        uint8_t KeyPressOverTime;//超时时间
    uint8_t KeyPressFlag;//按键按下标志位
    uint8_t keyEffectNum;//按键次数
    uint8_t KeyLongPressTime;//长按时间
    uint8_t KeyShortPressTime;//短按时间
} KEY_MELEMENT;

//--------------------------flag--------------------------------
#define G_F(VALX,VALY)    ((VALX)&(VALY))        //GET_FLAG
#define C_F(VALX,VALY)    ((VALX)&=(~(VALY)))    //CLEAR_FLAG
#define S_F(VALX,VALY)    ((VALX)|=(VALY))       //SET_FLAG

//---------------------KeyPressFlag-----------------------------
#define   KeyPress       0x01
#define   KeyShotPress   0x02
#define   KeyLongPress   0x04
#define   TwoKeyPress    0x08

//------------------------TwoKeyFlag---------------------------
#define  TwoKeyAllPress      0x01
#define  TwoKeyPressEffect   0x01

//------------------------KEYIO-------------------------
  //根据自己的单片机型号去声明按键IO
#define KEY1IO    ((gpio_porta_read()>>5)&0x01)    //PA5
#define KEY2IO    ((gpio_porta_read()>>6)&0x01)    //PA6
               
//声明函数
void Function_KeyInit(void);//初始化
void Function_OneKeyRead(KEY_MELEMENT * Key);//扫描单个按键
void Function_TwoKeyRead(KEY_MELEMENT *KeyI , KEY_MELEMENT *KeyII);//扫描两个按键
void Function_KeyHandle(void);//按键处理

2.在Key.c中实现函数功能

2.1初始化按键IO
#include "Key.h"

//有多少个按键就声明多少个结构体
KEY_MELEMENT Key1IO;
KEY_MELEMENT Key2IO;

/*******************************************************
*
* 按键端口初始化
*
*******************************************************/
void Function_KeyInit(void)
{
        //====================按键引脚初始化==================
     //根据自己的单片机型号初始化按键IO
        system_set_port_mux(GPIO_PORT_A, GPIO_BIT_5, PORTA5_FUNC_A5);
        gpio_set_dir(GPIO_PORT_A, (GPIO_BIT_5), GPIO_DIR_IN);
        system_set_port_pull(GPIO_PA5, false);

        system_set_port_mux(GPIO_PORT_A, GPIO_BIT_6, PORTA6_FUNC_A6);
        gpio_set_dir(GPIO_PORT_A, (GPIO_BIT_6), GPIO_DIR_IN);
        system_set_port_pull(GPIO_PA6, false);

        //====================按键结构体初始化==================
        Key1IO.KeyIOFilterCnt = 0;
        Key1IO.KeyPressNum = 0;
        Key1IO.KeyPressTime = 0;
        Key1IO.KeyPressOverTime = 0;
        Key1IO.KeyPressFlag = 0;
        Key1IO.keyEffectNum = 0;
     //长按,短按时间可根据自己的需求进行调节
        Key1IO.KeyLongPressTime = 60; //时间MS = KeyLongPressTime * 扫描函数周期
        Key1IO.KeyShortPressTime = 20;//时间MS = KeyShortPressTime * 扫描函数周期

        Key2IO.KeyIOFilterCnt = 0;
        Key2IO.KeyPressNum = 0;
        Key2IO.KeyPressTime = 0;
        Key2IO.KeyPressOverTime = 0;
        Key2IO.KeyPressFlag = 0;
        Key2IO.keyEffectNum = 0;
        Key2IO.KeyLongPressTime = 50;
        Key2IO.KeyShortPressTime = 20;
}



2.2按键扫描函数
2.2.1单按键扫描函数
/*******************************************************
KeyIO IO口寄存器
KeyPressNum   为按键次数。   KeyPressNum = 0xFF为长按生效
KeyPressFlag  bit0=1按键按下  bit1=0按键松开
  *******************************************************/
void Function_OneKeyRead(KEY_MELEMENT * Key)//扫描单个按键
{
    Key->keyEffectNum = 0;

      if(Key->KeyIO==0)//低电平生效  //if(Key->KeyIO==1)//高电平生效
                {
                        if(Key->KeyIOFilterCnt<2)
                        {
               Key->KeyIOFilterCnt++;
                        }
                        else
                        {
                                 Key->KeyPressOverTime = 0;
                                 S_F(Key->KeyPressFlag,KeyPress);

                if(G_F(Key->KeyPressFlag,TwoKeyPress)==0)
                                 {
                                                if(G_F(Key->KeyPressFlag,KeyLongPress)==0)
                                                {
                                                                Key->KeyPressTime++;
                                                                if(Key->KeyPressTime > Key->KeyLongPressTime)
                                                                {
                                                                                Key->KeyPressTime = 0;
                                                                                S_F(Key->KeyPressFlag,KeyLongPress);
                                                                                C_F(Key->KeyPressFlag,KeyShotPress);
                                                                                Key->KeyPressNum = 0;
                                                                                Key->keyEffectNum = 0xff;
                                                                }
                                                                else
                                                                {
                                                                                S_F(Key->KeyPressFlag,KeyShotPress);
                                                                }
                                                }
                                                else
                                                {
                                                                Key->KeyPressTime = 0;
                                                                C_F(Key->KeyPressFlag,KeyShotPress);
                                                }
                                 }
                                 else
                                 {
                                             Key->KeyPressTime = 0;
                                             C_F(Key->KeyPressFlag,KeyShotPress);
                                                 C_F(Key->KeyPressFlag,KeyLongPress);
                                                 Key->keyEffectNum = 0;
                                                 Key->KeyPressNum = 0;
                                 }
                        }
                }
                else
                {
                        if(Key->KeyIOFilterCnt>0)
                        {
               Key->KeyIOFilterCnt--;
                        }
                        else
                        {
                                 Key->KeyPressTime = 0;
                                 C_F(Key->KeyPressFlag,KeyLongPress);
                                 C_F(Key->KeyPressFlag,KeyPress);

                                 if(G_F(Key->KeyPressFlag,KeyShotPress)!=0)
                                 {
                                          C_F(Key->KeyPressFlag,KeyShotPress);

                                          Key->KeyPressNum++;
                                          Key->KeyPressOverTime = 0;
                                 }
                                 else
                                 {
                                           if(Key->KeyPressNum!=0)
                                                 {
                                                          if(++Key->KeyPressOverTime > Key->KeyShortPressTime)
                                                                {
                                                                         Key->KeyPressOverTime = 0;
                                                                         Key->keyEffectNum = Key->KeyPressNum;
                                                                         Key->KeyPressNum = 0;
                                                                }
                                                 }
                                 }
                        }
                }
}

2.2.2双按键长按扫描函数


void Function_TwoKeyRead(KEY_MELEMENT *KeyI , KEY_MELEMENT *KeyII)
{
          static uint8_t TwoKeyPressCnt=0;

          C_F(TwoKeyFlag,TwoKeyPressEffect);

          if((G_F(KeyI->KeyPressFlag,KeyPress)!=0)&&(G_F(KeyII->KeyPressFlag,KeyPress)!=0))//按下
                {
                   S_F(KeyI->KeyPressFlag,TwoKeyPress);
                         S_F(KeyII->KeyPressFlag,TwoKeyPress);

                         if(G_F(TwoKeyFlag,TwoKeyAllPress)==0)
                         {
                                  if(TwoKeyPressCnt<187)//长按的时间
                                        {
                                                        TwoKeyPressCnt++;
                                        }
                                        else
                                        {
                                                        TwoKeyPressCnt = 0;
                                                        S_F(TwoKeyFlag,TwoKeyAllPress);
                                                        S_F(TwoKeyFlag,TwoKeyPressEffect);
                                        }
                         }
                }
                else
                {
                          TwoKeyPressCnt = 0;

                                if((G_F(KeyI->KeyPressFlag,KeyPress)==0)&&(G_F(KeyII->KeyPressFlag,KeyPress)==0))
                                {
                                          C_F(TwoKeyFlag,TwoKeyAllPress);
                                          C_F(KeyI->KeyPressFlag,TwoKeyPress);
                              C_F(KeyII->KeyPressFlag,TwoKeyPress);
                                }
                }
}

3.按键处理
/********************************************
*
*按键处理
*
*********************************************/
void Function_KeyHandle(void)
{   
        //key1键处理
                        if(Key1IO.keyEffectNum==Long_Key)
                        {

                        }
                        else if(Key1IO.keyEffectNum==Once_Key)
                        {
       
                        }
                        else if(Key1IO.keyEffectNum==Double_Key)
                        {
                               
                        }//需要实现多击功能,自行拓展

   //key2键处理
                        if(Key2IO.keyEffectNum==Long_Key)
                        {

                        }
                        else if(Key2IO.keyEffectNum==Once_Key)
                        {
       
                        }
                        else if(Key2IO.keyEffectNum==Double_Key)
                        {
                               
                        }//需要实现多击功能,自行拓展

   //双按键长按处理   
                if(G_F(TwoKeyFlag,TwoKeyAllPress)!=0)
                {
                    
             
            }
}

4.实际运用
void Main(void)
{
        if(TimeData.InterruptFlag_1ms != 0)
        {
                TimeData.InterruptFlag_1ms = 0;//1MS中断标志位

                TimeData.InterruptCnt_1ms++;//时间轮询

                switch(TimeData.InterruptCnt_1ms & 0x07)8MS
                {
                        case 0x00:
                                break;
                       
                        case 0x02:
                                break;
                       
                        case 0x04:
                                break;
                       
                        case 0x06:
                                break;
                       
                        default:
                                break;
                }
               
                switch(TimeData.InterruptCnt_1ms & 0x0f)//16MS
                {
                        case 0x01:
                            Key1IO.KeyIO = KEY1IO;
                                Key2IO.KeyIO = KEY2IO;

                                Function_OneKeyRead(&Key1IO);                               
                                Function_OneKeyRead(&Key2IO);

                                Function_TwoKeyRead(&Key1IO,&Key2IO);
                               
                                break;
                       
                        case 0x05:
                             Function_KeyHandle();
                                break;
                       
                        case 0x09:
                                break;
                       
                        case 0x0d:
                                break;
                       
                        default:
                                break;
                }
               
                switch(TimeData.InterruptCnt_1ms & 0x1f)//32ms
                {
                        case 0x03:
                                break;
                       
                        case 0x07:
                                break;
                       
                        case 0x0b:

                                break;
                       
                        case 0x0f:
                                break;
                       
                        case 0x13:
                                break;
                       
                        case 0x17:
                                break;
                       
                        case 0x1b:
                                break;
                       
                        case 0x1f:
                                break;
                        default:
                                break;
                }
        }       
}

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_52596850/article/details/144758072

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

16

帖子

0

粉丝