找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 2980|回復(fù): 4
打印 上一主題 下一主題
收起左側(cè)

非常完備的按鍵操作系統(tǒng),單擊、雙擊、N擊,長(zhǎng)按。移植性良好

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:391730 發(fā)表于 2019-12-9 19:15 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
本帖更新內(nèi)容詳見(jiàn):http://www.torrancerestoration.com/bbs/dpj-176186-1.html

#define BaseTime 10        //時(shí)間基準(zhǔn)10 ms ,如果設(shè)計(jì)的時(shí)基是5ms 則前面的10就用該變成5
#define number_init          0xfffffffd  // 初始化相關(guān)的utime(unsigned long)變量(如果是unsigned int 就應(yīng)該是0xfffd),采用這個(gè)值的好處是,即使發(fā)生時(shí)鐘計(jì)數(shù)器溢出,也不影響其他的程序

typedef unsigned long utime;  //這個(gè)類型是為了方便移植專門給時(shí)間相關(guān)變量使用
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;

enum ButtonModel{noneClick=0,singalClick,doubleClick,repeatClick,longPress}; //doubleClick目前未定義完整動(dòng)作 pressDownHold,按下保持
enum ButtonStaus{nonePress=1,pressDown,pressUp,pressDownHold};        /


struct button
{
    uchar outPutEn:1; //發(fā)送數(shù)據(jù)使能
    uchar lastButton:1; //按鍵上次變動(dòng)后的狀態(tài) 默認(rèn) 1
    uchar init_leavel:1; //設(shè)置默認(rèn)按鍵電平
        uchar longPressFlag:1;            //長(zhǎng)按釋放標(biāo)志 默認(rèn)0,一旦開(kāi)始長(zhǎng)按則置1
        uchar Gpio_level :3;
        uchar applyUseOpenMaxPwm:1;         //在關(guān)機(jī)狀態(tài)下,若本按鍵按下啟動(dòng)了系統(tǒng),則需要申請(qǐng)全局變量標(biāo)明,禁止其他按鍵關(guān)閉被本按鍵打開(kāi)的系統(tǒng)(自己打開(kāi)自己負(fù)責(zé)關(guān)閉)

        uchar ticks;            //按鍵按下次數(shù)
    enum ButtonStaus lastButtonStaus;   //按鍵上個(gè)循環(huán)的狀態(tài)
    enum ButtonStaus thisButtonStaus;   //按鍵本循環(huán)的狀態(tài)
    enum ButtonModel lastButtonModel; //按鍵上個(gè)循環(huán)所處的模式
    enum ButtonModel thisButtonModel; //按鍵本循環(huán)應(yīng)該所處的模式

    uint changeModelTime;  //10ms基準(zhǔn)              //長(zhǎng)按時(shí)間定義
    uint pressLongTime;    //10ms基準(zhǔn)              //多擊時(shí)間定義

    utime lastPressDownMoment;     //上次按鍵按下所處的時(shí)刻
    utime thisPressDownMoment; //本次按鍵按下所處的時(shí)刻
    uint tempTime;           //緩存按鍵兩次按下之間的時(shí)長(zhǎng)

    utime buttonConfir;     //按鍵防抖時(shí)長(zhǎng)


    utime getTimer; //獲取時(shí)鐘精準(zhǔn)時(shí)刻,用于設(shè)定按鍵掃描周期
    utime acquisitionMoment; //獲取時(shí)鐘精準(zhǔn)時(shí)刻,用于記錄相同按鍵狀態(tài)持續(xù)時(shí)長(zhǎng)

    uchar  (*read_gpio)(void); //獲取按鍵狀態(tài)方法
};
/*================================
outPutEn 是為后面接收按鍵是單擊、多擊長(zhǎng)按函數(shù)準(zhǔn)備的參數(shù)。比如按鍵掃描程序10ms運(yùn)行一次,輸出的是單擊,如果沒(méi)有outPutEn這個(gè)參數(shù),后面的接收程序會(huì)在10ms內(nèi)一直都接收的是單擊指令,這樣就會(huì)一直執(zhí)行單擊需要進(jìn)行的操作,本來(lái)單擊一次檔位變化1,結(jié)果現(xiàn)在檔位變化了n

applyUseOpenMaxPwm 是為多按鍵且按鍵操作有優(yōu)先權(quán)做準(zhǔn)備,,有優(yōu)先權(quán)的操作不會(huì)被其他按鍵操作打斷




===============================*/
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) //如果是第一次運(yùn)行,則更新時(shí)間
     {
         Key->getTimer=timer;
     }

     else
     {
         if(timer-Key->getTimer>=enOutTime) //如果時(shí)間足夠“定義的循環(huán)時(shí)間” ,則更新時(shí)間并允許運(yùn)行
         {
            Key->getTimer=timer;
            Key->Gpio_level=Key->read_gpio();
            if(Key->Gpio_level>1);
                        else
            {
                         if(Key->lastButton^Key->Gpio_level)//按鍵有電平變化模塊處理開(kāi)始----------------------------------------------------------------------------//
             {
                                 if(number_init==Key->buttonConfir)
                                        Key->buttonConfir=timer;

                 if(timer-Key->buttonConfir>=noiseProofTime)//按鍵防抖,必須再確認(rèn)狀態(tài)
                 {
                     Key->lastButton=Key->Gpio_level;
                                         Key->acquisitionMoment=number_init;
                     Key->buttonConfir=number_init; //二次確認(rèn)標(biāo)志重置



                     if(Key->init_leavel^Key->Gpio_level)//本次按鍵狀態(tài)改變后與定義的電平不一致模塊處理開(kāi)始
                     {
                         switch(Key->lastButtonStaus)
                         {
                         case nonePress: //上個(gè)循環(huán)是定義的初始電平
                         case pressUp:
                            {
                                if(number_init==Key->lastPressDownMoment)//是第一次記錄按鍵按下時(shí)刻
                                {
                                    Key->thisPressDownMoment=Key->lastPressDownMoment=timer;
                                }
                                else //不第一次記錄
                                {
                                    Key->thisPressDownMoment=timer;
                                    Key->tempTime+=Key->thisPressDownMoment-Key->lastPressDownMoment; //獲取兩次按鍵按下之間的時(shí)間間隔
                                    Key->lastPressDownMoment=Key->thisPressDownMoment;  //更新
                                }


                                Key->ticks++;


//不在此處增加pressLongTime判斷的原因是,buftime0記錄的是兩次按鍵按下之間的時(shí)長(zhǎng),在兩次按下之間必有彈起,一旦彈起,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)//第三擊的時(shí)間也小于定義的改變模式時(shí)間
                                        {
                                            Key->thisButtonModel=repeatClick;
                                        }
                                        else                //第三擊的時(shí)間大于定義的改變模式時(shí)間
                                        {
                                            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;
                            }

                         }

                     } //按鍵狀態(tài)改變后是與定義的電平模塊不同處理結(jié)束//
                         ///////////////////////////////////////////////////////////////////////////
                     else //按鍵狀態(tài)改變與定義的電平模塊相同開(kāi)始處理//
                     {
                          Key->longPressFlag=0;
                                                  switch(Key->lastButtonStaus)
                         {
                         case pressDown://上個(gè)循環(huán)是低電平,機(jī)器人手速
                         case pressDownHold:
                            {

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

                                }

                                Key->thisButtonStaus=pressUp; //按鍵彈起
                                Key->thisButtonModel=noneClick;

                                break;
                            }

                         }

                     }//本次按鍵狀態(tài)改變后是定義的電平處理結(jié)束//
                 }

             }//按鍵有電平變化模塊處理結(jié)束-------------------------------------------------------------------------------------------------------//
             else if(Key->init_leavel==Key->Gpio_level)//按鍵無(wú)改變且是初始電平處理開(kāi)始//
             {
                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 一旦置零,記錄按鍵按下時(shí)刻的值都應(yīng)當(dāng)重置
                                         Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
                 }
                                 if(timer-Key->acquisitionMoment>259200000)//一個(gè)月都沒(méi)有使用了,所以清零
                                 {
                                        Open_timer0;                  //重置timer0 防止一年后溢出
                                 }

                 switch(Key->lastButtonStaus)
                 {
                 case nonePress:
                 case pressUp:
                    {
                        Key->thisButtonStaus=nonePress;
                        Key->thisButtonModel=noneClick;
                        break;
                    }
/*
                 case pressDown:
                 case pressDownHold:          //容錯(cuò)處理,理論上若上個(gè)循環(huán)與本循環(huán)電平不一致,應(yīng)該在電平有變化模塊處理不會(huì)在本模塊
                    {

                        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;
                    }
*/
                 }

             }//按鍵狀態(tài)無(wú)改變且是初始電平處理結(jié)束//
             else//按鍵狀態(tài)無(wú)改變且是非初始電平處理開(kāi)始//
             {
                 if(number_init==Key->acquisitionMoment)
                 {
                     Key->acquisitionMoment=timer;
                 }
                 switch(Key->lastButtonStaus)
                 {
/*
                 case nonePress:          //容錯(cuò)處理,理論上若上個(gè)循環(huán)與本循環(huán)電平不一致,應(yīng)該在電平有變化模塊處理不會(huì)在本模塊
                 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; //一但超時(shí),所謂的記錄按鍵按下時(shí)間間隔就無(wú)意義,必須重置
                                                                Key->tempTime=0;  //按鍵鍵緩沖時(shí)間不清零,對(duì)操作體驗(yàn)影響很不好
                                                                Key->ticks=0;
                            }
                            else
                            {
                                Key->thisButtonModel=singalClick;
                            }
                            Key->thisButtonStaus=pressDown;
                        }



                    }
*/
                 case pressDown:
                 case pressDownHold:
                    {
                       if(longPress==Key->lastButtonModel||Key->longPressFlag)
                       {
                           Key->thisButtonModel=noneClick;        // 改變了模式,下個(gè)循環(huán)程序進(jìn)入其它分支,所以必須在上面加入模式判斷
                           Key->thisButtonStaus=pressDownHold;
                           Key->acquisitionMoment=timer;
                                                   Key->lastPressDownMoment=Key->thisPressDownMoment=timer; //PressDownMoment必須在按鍵按下保持狀態(tài)下更新時(shí)間,否則tempTime會(huì)計(jì)算總的按鍵時(shí)長(zhǎng),以至于無(wú)法進(jìn)入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 是授時(shí)系統(tǒng),比如每10ms加一位,enOutTime 是循環(huán)檢測(cè)時(shí)間,比如允許每50ms建成運(yùn)行一次,noiseProofTime是按鍵防抖動(dòng)時(shí)間,程序運(yùn)行后檢測(cè)按鍵的thisButtonModel狀態(tài)就能知道是單擊雙擊多擊還是長(zhǎng)按。目前在函數(shù)中定義了單擊到三擊,三擊以上可自己添加
================================*/

評(píng)分

參與人數(shù) 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎(jiǎng)勵(lì)!

查看全部評(píng)分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏7 分享淘帖 頂1 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:391730 發(fā)表于 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;             //系統(tǒng)默認(rèn)高電平
        Key->Gpio_level=0;
    Key->init_leavel=init_button_leavel;
        if(0==Key->init_leavel)
        {
    Key->lastButtonStaus=pressDownHold;         //系統(tǒng)默認(rèn)電平
    Key->thisButtonStaus=pressDownHold;         //系統(tǒng)默認(rèn)電平
        }
        else
        {
    Key->lastButtonStaus=Key->init_leavel;         //系統(tǒng)默認(rèn)電平
    Key->thisButtonStaus=Key->init_leavel;         //系統(tǒng)默認(rèn)電平
        }
    Key->lastButtonModel=noneClick;
    Key->thisButtonModel=noneClick;
    Key->changeModelTime=changeModelTime;//10ms基準(zhǔn)
    Key->pressLongTime=LongPressTimes;    //10ms基準(zhǔn)
    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;


}
回復(fù)

使用道具 舉報(bào)

板凳
ID:98992 發(fā)表于 2019-12-10 19:16 | 只看該作者
看的頭大 暫時(shí)還是入門級(jí)別
回復(fù)

使用道具 舉報(bào)

地板
ID:71535 發(fā)表于 2019-12-10 19:20 | 只看該作者
制作單擊、雙擊、N擊,長(zhǎng)按。移植性良好,厲害了,收藏了
回復(fù)

使用道具 舉報(bào)

5#
ID:391730 發(fā)表于 2019-12-17 15:17 | 只看該作者
waerdeng 發(fā)表于 2019-12-10 19:20
制作單擊、雙擊、N擊,長(zhǎng)按。移植性良好,厲害了,收藏了

目前程序使用在公司的產(chǎn)品上在,所以暫時(shí)沒(méi)法發(fā)一個(gè)完整的DEMO,等過(guò)段時(shí)間改一個(gè)DEMO在討論下。
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表