找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2262|回复: 4
收起左侧

非常完备的按键操作系统,单击、双击、N击,长按。移植性良好

  [复制链接]
ID:391730 发表于 2019-12-9 19:15 | 显示全部楼层 |阅读模式
本帖更新内容详见:http://www.51hei.com/bbs/dpj-176186-1.html

#define BaseTime 10        //时间基准10 ms ,如果设计的时基是5ms 则前面的10就用该变成5
#define number_init          0xfffffffd  // 初始化相关的utime(unsigned long)变量(如果是unsigned int 就应该是0xfffd),采用这个值的好处是,即使发生时钟计数器溢出,也不影响其他的程序

typedef unsigned long utime;  //这个类型是为了方便移植专门给时间相关变量使用
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;

enum ButtonModel{noneClick=0,singalClick,doubleClick,repeatClick,longPress}; //doubleClick目前未定义完整动作 pressDownHold,按下保持
enum ButtonStaus{nonePress=1,pressDown,pressUp,pressDownHold};        /


struct button
{
    uchar outPutEn:1; //发送数据使能
    uchar lastButton:1; //按键上次变动后的状态 默认 1
    uchar init_leavel:1; //设置默认按键电平
        uchar longPressFlag:1;            //长按释放标志 默认0,一旦开始长按则置1
        uchar Gpio_level :3;
        uchar applyUseOpenMaxPwm:1;         //在关机状态下,若本按键按下启动了系统,则需要申请全局变量标明,禁止其他按键关闭被本按键打开的系统(自己打开自己负责关闭)

        uchar ticks;            //按键按下次数
    enum ButtonStaus lastButtonStaus;   //按键上个循环的状态
    enum ButtonStaus thisButtonStaus;   //按键本循环的状态
    enum ButtonModel lastButtonModel; //按键上个循环所处的模式
    enum ButtonModel thisButtonModel; //按键本循环应该所处的模式

    uint changeModelTime;  //10ms基准              //长按时间定义
    uint pressLongTime;    //10ms基准              //多击时间定义

    utime lastPressDownMoment;     //上次按键按下所处的时刻
    utime thisPressDownMoment; //本次按键按下所处的时刻
    uint tempTime;           //缓存按键两次按下之间的时长

    utime buttonConfir;     //按键防抖时长


    utime getTimer; //获取时钟精准时刻,用于设定按键扫描周期
    utime acquisitionMoment; //获取时钟精准时刻,用于记录相同按键状态持续时长

    uchar  (*read_gpio)(void); //获取按键状态方法
};
/*================================
outPutEn 是为后面接收按键是单击、多击长按函数准备的参数。比如按键扫描程序10ms运行一次,输出的是单击,如果没有outPutEn这个参数,后面的接收程序会在10ms内一直都接收的是单击指令,这样就会一直执行单击需要进行的操作,本来单击一次档位变化1,结果现在档位变化了n

applyUseOpenMaxPwm 是为多按键且按键操作有优先权做准备,,有优先权的操作不会被其他按键操作打断




===============================*/
struct button button1,button2,button3;

void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime)
{
         enOutTime/=BaseTime;
     noiseProofTime/=BaseTime;
     if(number_init==Key->getTimer) //如果是第一次运行,则更新时间
     {
         Key->getTimer=timer;
     }

     else
     {
         if(timer-Key->getTimer>=enOutTime) //如果时间足够“定义的循环时间” ,则更新时间并允许运行
         {
            Key->getTimer=timer;
            Key->Gpio_level=Key->read_gpio();
            if(Key->Gpio_level>1);
                        else
            {
                         if(Key->lastButton^Key->Gpio_level)//按键有电平变化模块处理开始----------------------------------------------------------------------------//
             {
                                 if(number_init==Key->buttonConfir)
                                        Key->buttonConfir=timer;

                 if(timer-Key->buttonConfir>=noiseProofTime)//按键防抖,必须再确认状态
                 {
                     Key->lastButton=Key->Gpio_level;
                                         Key->acquisitionMoment=number_init;
                     Key->buttonConfir=number_init; //二次确认标志重置



                     if(Key->init_leavel^Key->Gpio_level)//本次按键状态改变后与定义的电平不一致模块处理开始
                     {
                         switch(Key->lastButtonStaus)
                         {
                         case nonePress: //上个循环是定义的初始电平
                         case pressUp:
                            {
                                if(number_init==Key->lastPressDownMoment)//是第一次记录按键按下时刻
                                {
                                    Key->thisPressDownMoment=Key->lastPressDownMoment=timer;
                                }
                                else //不第一次记录
                                {
                                    Key->thisPressDownMoment=timer;
                                    Key->tempTime+=Key->thisPressDownMoment-Key->lastPressDownMoment; //获取两次按键按下之间的时间间隔
                                    Key->lastPressDownMoment=Key->thisPressDownMoment;  //更新
                                }


                                Key->ticks++;


//不在此处增加pressLongTime判断的原因是,buftime0记录的是两次按键按下之间的时长,在两次按下之间必有弹起,一旦弹起,pressLongTime 就置零

                                switch(Key->ticks)
                                {
                                case 1:
                                    {
                                        if(Key->tempTime>=Key->changeModelTime)//ticks未初始化。
                                        {
                                            Key->ticks=0;
                                            Key->tempTime=0;

                                        }
                                                                                Key->thisButtonModel=singalClick;
                                        break;
                                    }
                                case 2:
                                    {
                                        if(Key->tempTime<Key->changeModelTime)//ticks未初始化
                                        Key->thisButtonModel=doubleClick;
                                        else
                                        {
                                            Key->thisButtonModel=singalClick;
                                            Key->ticks=0;
                                            Key->tempTime=0;
                                        }
                                        break;
                                    }
                                case 3:  //三击,想要增加N击自己在后面添加 CASE 4,CASE 5,CASE N
                                    {
                                        if(Key->tempTime<Key->changeModelTime)//第三击的时间也小于定义的改变模式时间
                                        {
                                            Key->thisButtonModel=repeatClick;
                                        }
                                        else                //第三击的时间大于定义的改变模式时间
                                        {
                                            Key->thisButtonModel=singalClick;
                                        }


                                        Key->ticks=0;
                                        Key->tempTime=0;
                                                                                Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
                                        break;
                                    }
                                default :
                                    {
                                       Key->ticks=0;
                                       Key->thisButtonModel=noneClick;
                                    }

                                }

                                Key->thisButtonStaus=pressDown; //按键按下

                                break;
                            }

                         }

                     } //按键状态改变后是与定义的电平模块不同处理结束//
                         ///////////////////////////////////////////////////////////////////////////
                     else //按键状态改变与定义的电平模块相同开始处理//
                     {
                          Key->longPressFlag=0;
                                                  switch(Key->lastButtonStaus)
                         {
                         case pressDown://上个循环是低电平,机器人手速
                         case pressDownHold:
                            {

                                if(Key->tempTime>=Key->changeModelTime)
                                {
                                    Key->tempTime=0;
                                    Key->ticks=0;

                                }

                                Key->thisButtonStaus=pressUp; //按键弹起
                                Key->thisButtonModel=noneClick;

                                break;
                            }

                         }

                     }//本次按键状态改变后是定义的电平处理结束//
                 }

             }//按键有电平变化模块处理结束-------------------------------------------------------------------------------------------------------//
             else if(Key->init_leavel==Key->Gpio_level)//按键无改变且是初始电平处理开始//
             {
                if(number_init==Key->acquisitionMoment)
                 {
                     Key->acquisitionMoment=timer;
                 }
                if(timer-Key->acquisitionMoment>Key->pressLongTime||timer-Key->acquisitionMoment>Key->changeModelTime||Key->tempTime>=Key->pressLongTime||Key->tempTime>=Key->changeModelTime)
                 {
                     Key->ticks=0;
                     Key->tempTime=0;  //tempTime 一旦置零,记录按键按下时刻的值都应当重置
                                         Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
                 }
                                 if(timer-Key->acquisitionMoment>259200000)//一个月都没有使用了,所以清零
                                 {
                                        Open_timer0;                  //重置timer0 防止一年后溢出
                                 }

                 switch(Key->lastButtonStaus)
                 {
                 case nonePress:
                 case pressUp:
                    {
                        Key->thisButtonStaus=nonePress;
                        Key->thisButtonModel=noneClick;
                        break;
                    }
/*
                 case pressDown:
                 case pressDownHold:          //容错处理,理论上若上个循环与本循环电平不一致,应该在电平有变化模块处理不会在本模块
                    {

                        Key->thisButtonStaus=pressUp;
                        if(Key->tempTime+1>=Key->pressLongTime)
                        {
                           Key->thisButtonModel=longPress;
                        }
                        else
                        {
                          Key->thisButtonModel=noneClick;
                        }

                        Key->tempTime=0;
                                                Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
                        break;
                    }
*/
                 }

             }//按键状态无改变且是初始电平处理结束//
             else//按键状态无改变且是非初始电平处理开始//
             {
                 if(number_init==Key->acquisitionMoment)
                 {
                     Key->acquisitionMoment=timer;
                 }
                 switch(Key->lastButtonStaus)
                 {
/*
                 case nonePress:          //容错处理,理论上若上个循环与本循环电平不一致,应该在电平有变化模块处理不会在本模块
                 case pressUp:
                    {
                        if(longPress==Key->lastButtonModel)
                        {
                            Key->thisButtonStaus=nonePress;
                            Key->thisButtonModel=noneClick;
                        }
                        else
                        {
                            if(timer-Key->acquisitionMoment>Key->pressLongTime||Key->tempTime>=Key->pressLongTime)
                            {
                                Key->thisButtonModel=longPress;
                                                                Key->lastPressDownMoment=Key->thisPressDownMoment=number_init; //一但超时,所谓的记录按键按下时间间隔就无意义,必须重置
                                                                Key->tempTime=0;  //按键键缓冲时间不清零,对操作体验影响很不好
                                                                Key->ticks=0;
                            }
                            else
                            {
                                Key->thisButtonModel=singalClick;
                            }
                            Key->thisButtonStaus=pressDown;
                        }



                    }
*/
                 case pressDown:
                 case pressDownHold:
                    {
                       if(longPress==Key->lastButtonModel||Key->longPressFlag)
                       {
                           Key->thisButtonModel=noneClick;        // 改变了模式,下个循环程序进入其它分支,所以必须在上面加入模式判断
                           Key->thisButtonStaus=pressDownHold;
                           Key->acquisitionMoment=timer;
                                                   Key->lastPressDownMoment=Key->thisPressDownMoment=timer; //PressDownMoment必须在按键按下保持状态下更新时间,否则tempTime会计算总的按键时长,以至于无法进入repeat模式

                       }
                       else
                       {
                           if(timer-Key->acquisitionMoment>Key->pressLongTime)
                           {

                                                           Key->longPressFlag=1;
                                                           Key->thisButtonModel=longPress;
                               Key->tempTime=0;
                               Key->ticks=0;
                               Key->thisPressDownMoment=Key->lastPressDownMoment=Key->acquisitionMoment=number_init;
                           }
                           else
                           {
                               Key->thisButtonModel=noneClick;
                           }
                           Key->thisButtonStaus=pressDownHold;
                       }
                        break;
                    }

             }




         }
                 Key->lastButtonStaus=Key->thisButtonStaus; //最后更新
         Key->lastButtonModel=Key->thisButtonModel;


                 Key->outPutEn=1;

        }
     }


  }
}

/*============================
void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime) 中,timer 是授时系统,比如每10ms加一位,enOutTime 是循环检测时间,比如允许每50ms建成运行一次,noiseProofTime是按键防抖动时间,程序运行后检测按键的thisButtonModel状态就能知道是单击双击多击还是长按。目前在函数中定义了单击到三击,三击以上可自己添加
================================*/

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:391730 发表于 2019-12-9 19:19 | 显示全部楼层
void Button_init(struct button *Key,uchar(*get_leavel)(),bit init_button_leavel,uint LongPressTimes,uint changeModelTime)
{

        Key->read_gpio=get_leavel;
        Key->outPutEn=0;
    Key->lastButton=init_button_leavel;             //系统默认高电平
        Key->Gpio_level=0;
    Key->init_leavel=init_button_leavel;
        if(0==Key->init_leavel)
        {
    Key->lastButtonStaus=pressDownHold;         //系统默认电平
    Key->thisButtonStaus=pressDownHold;         //系统默认电平
        }
        else
        {
    Key->lastButtonStaus=Key->init_leavel;         //系统默认电平
    Key->thisButtonStaus=Key->init_leavel;         //系统默认电平
        }
    Key->lastButtonModel=noneClick;
    Key->thisButtonModel=noneClick;
    Key->changeModelTime=changeModelTime;//10ms基准
    Key->pressLongTime=LongPressTimes;    //10ms基准
    Key->lastPressDownMoment=number_init;
    Key->thisPressDownMoment=number_init;
    Key->tempTime=0;
    Key->ticks=0;
    Key->buttonConfir=number_init;
        Key->longPressFlag=0;
        Key->applyUseOpenMaxPwm=0;
    Key->getTimer=number_init;
    Key->acquisitionMoment=number_init;


}
回复

使用道具 举报

ID:98992 发表于 2019-12-10 19:16 | 显示全部楼层
看的头大 暂时还是入门级别
回复

使用道具 举报

ID:71535 发表于 2019-12-10 19:20 | 显示全部楼层
制作单击、双击、N击,长按。移植性良好,厉害了,收藏了
回复

使用道具 举报

ID:391730 发表于 2019-12-17 15:17 | 显示全部楼层
waerdeng 发表于 2019-12-10 19:20
制作单击、双击、N击,长按。移植性良好,厉害了,收藏了

目前程序使用在公司的产品上在,所以暂时没法发一个完整的DEMO,等过段时间改一个DEMO在讨论下。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表