標題: 單片機按鍵狀態(tài)檢測程序問題,不能雙擊 [打印本頁]

作者: dcjdcj    時間: 2021-9-23 22:17
標題: 單片機按鍵狀態(tài)檢測程序問題,不能雙擊
/*      雙擊不行,10ms掃描一次,Key_Double_Period=30,不是Key_Double_Period時間的問題
         現(xiàn)在的問題是:第一次按下的時間大于Key_Double_Period,再快速按一下,就可以識別為雙擊
         但是這樣子太別扭了,雙擊應該是再Key_Double_Period時間內(nèi),再次按下,就認為是雙擊
         調(diào)了快一個禮拜,還是同樣的問題

*/
uchar Key_Read(void)
{
        static uchar key_state_buffer=key_state_0;
        static uchar key_timer_cnt=0;
        static uchar key_temp=Key_Null;
        uchar key_return=Key_Null;
        uchar key=Key_Null;
        key=Key_Null|Key_Driver();
        switch(key_state_buffer)
        {
                case key_state_0:
                        if(key!=Key_Null)
                        {
                                key_temp=key;
                                key_timer_cnt=0;
                                key_state_buffer=key_state_1;
                        }
                        else
                        {
                                key_return=key;
                        }
                        break;
                case key_state_1:
                        if(key!=Key_Null)
                        {
                                key_return=key|Key_Double;
                                key_state_buffer=key_state_0;
                        }
                        else if(++key_timer_cnt>=Key_Double_Period)
                        {
                                key_return=key_temp;
                                key_state_buffer=key_state_0;
                                key_timer_cnt=0;
                        }
                        break;
        }
        return key_return;
}


作者: dcjdcj    時間: 2021-9-23 22:21
Key_Null=0x00;Key_Driver()這個函數(shù)返回值是單擊的值和長按得值
作者: bbxyliyang    時間: 2021-9-24 06:48
#include<stc15w202s.h>
#include<stdio.h>
#define key_state_0 0
#define key_state_1 1
#define key_state_2 2
#define key_state_3 3
#define key_no 0
#define key_click 1
#define key_double 2
#define key_long 3
sbit KEY = P3^3;
sbit LED_E1P = P3^1;
sbit LED_G1 = P3^2;
sbit LED2 = P5^5;        //W2OUT
sbit LED3 = P5^4;  
unsigned char flag;
unsigned char cnt = 0;


static unsigned char key_driver(void)
{
        static unsigned char key_state_buffer1 = key_state_0;
        static unsigned char key_timer_cnt1 = 0;
        unsigned char key_return = key_no;
        unsigned char key;
        
        key = KEY;  //read the I/O states
        
        switch(key_state_buffer1)
        {
                case key_state_0:
                        if(key == 0)
                                key_state_buffer1 = key_state_1;
                                //按鍵被按下,狀態(tài)轉換到按鍵消抖和確認狀態(tài)//
                        break;
                case key_state_1:
                        if(key == 0)
                        {
                                key_timer_cnt1 = 0;
                                key_state_buffer1 = key_state_2;
                                //按鍵仍然處于按下狀態(tài)
                                //消抖完成,key_timer開始準備計時
                                //狀態(tài)切換到按下時間計時狀態(tài)
                        }
                        else
                                key_state_buffer1 = key_state_0;
                                //按鍵已經(jīng)抬起,回到按鍵初始狀態(tài)
                        break;  //完成軟件消抖
                case key_state_2:
                        if(key == 1)
                        {
                                key_return = key_click;  //按鍵抬起,產(chǎn)生一次click操作
                                key_state_buffer1 = key_state_0;  //轉換到按鍵初始狀態(tài)
                        }
                        else if(++key_timer_cnt1 >= 100)  //按鍵繼續(xù)按下,計時超過1000ms
                        {
                                key_return = key_long;  //送回長按事件
                                key_state_buffer1 = key_state_3;  //轉換到等待按鍵釋放狀態(tài)
                        }
                        break;
                case key_state_3:  //等待按鍵釋放
                        if(key == 1)  //按鍵釋放
                                key_state_buffer1 = key_state_0;  //切回按鍵初始狀態(tài)
                        break;
        }
        return key_return;
}

unsigned char key_read(void)
{
        static unsigned char key_state_buffer2 = key_state_0;
        static unsigned char key_timer_cnt2 = 0;
        unsigned char key_return = key_no;
        unsigned char key;
        
        key = key_driver();
        
        switch(key_state_buffer2)
        {
                case key_state_0:
                        if(key == key_click)
                        {
                                key_timer_cnt2 = 0;  //第一次單擊,不返回,到下個狀態(tài)判斷是否會出現(xiàn)雙擊
                                key_state_buffer2 = key_state_1;
                        }
                        else
                                key_return = key;  //對于無鍵、長按,返回原事件
                        break;
                case key_state_1:
                        if(key == key_click)  //又一次單擊,時間間隔小于500ms
                        {
                                key_return = key_double;  //返回雙擊事件,回到初始狀態(tài)
                                key_state_buffer2 = key_state_0;
                        }
                        else if(++key_timer_cnt2 >= 50)
                        {   
                        //這里在下一次的按鍵來臨之前,并且時間是小于500ms的時候,就會一直執(zhí)行的是這個key_timer_cnt2++.直到下一次的按鍵到來,再判斷看是雙擊還是單擊。
                                //這里500ms內(nèi)肯定讀到的都是無鍵事件,因為長按大于1000ms
                                //在1s前底層返回的都是無鍵
                                                                        
                                key_return = key_click;  //500ms內(nèi)沒有再次出現(xiàn)單擊事件,返回單擊事件
                                key_state_buffer2 = key_state_0;  //返回初始狀態(tài)
                                       
                        }
                        break;
        }
        
        return key_return;
}

void Timer0Init(void)                //1毫秒@11.0592MHz
{
        AUXR |= 0x80;                //定時器時鐘1T模式
        TMOD &= 0xF0;                //設置定時器模式
        TL0 = 0xCD;                //設置定時初值
        TH0 = 0xD4;                //設置定時初值
        TF0 = 0;                //清除TF0標志
        TR0 = 1;                //定時器0開始計時
        ET0 = 1;

}
void IO_init()
{
   
    P3M0 = 0x01;
    P3M1 = 0x01;
    P5M0 = 0x00;
    P5M1 = 0x00;
   
}


void main(void)
{         
       unsigned char key = 1;
             Timer0Init();
           IO_init();
           LED_E1P = 1;
           LED_G1 = 1;
           LED2 = 1;
           LED3 = 0;
                   EA = 1;
           while(1)
           {
             if(flag)
                 {
                   flag=0;
           key = key_read();
                   switch(key)
                   {
                            case key_click:LED2 = !LED2; break;
                         case key_double:LED2 = !LED2;LED3 = !LED3; break;
                         case key_long: LED2 = 1; LED3 = 1; LED_G1 = 0; LED_E1P = 0; break;
                         default : break;
                   }
                 }
           }
}

void Timr0_ISR() interrupt 1
{
   cnt++;
   if(cnt>=10)
   {
       cnt = 0;
              flag = 1;
   }
}
作者: yzwzfyz    時間: 2021-9-24 08:52
1、按鍵有彈動,如果沒有防彈動過慮則,則無法界定按了幾下(按一下,彈N次)。
2、防彈動的過慮時間,既要能慮除彈動時間,又不能錯將雙擊過慮掉。這就要求過慮時間設置適當。
建議過慮時間設置為32ms。
3、為了能識別按了幾次,就需要有一個計數(shù)器JN。
4、JN何時起計,何時結束,也是有講究的。JN計數(shù)完成之前,你不要做界定,因為還不確定是連續(xù)按了幾次。
建議的做法是:雙擊的間隔時間定義為:0.08-0.6秒之間。
界定開始時間(JN計數(shù)結束時間):這個0.6秒就是JN界定的時間,它的意思是:當按鍵彈起后0.6秒后,開始界定JN。這時JN=幾,就是連續(xù)擊鍵幾次。
如此:無論連續(xù)擊幾次都能識別。
短按:JN=1
雙擊:JN=2。
N擊:JN=N。
JN計時的開始時間(復0):JN被識別后,就清0,準備下次使用。當然,初始化也清0。
由于涉及多種計時,所以,建議做個定時中斷,如4ms中斷一次,中斷服務程序中做以下事件:
1、掃鍵:
彈動過慮計時器JT:
鍵按下時:+1,本次鍵值THIS_KEY與上次鍵值LAST_KEY不同時清0(不同,就是彈動),相同時+1。>8時(彈動過慮時間)不再增加。
彈起時界定:JT>=8時(32ms),鍵值有效(可以做個標記:KEY_P=1),即認為按了一次。
2、擊鍵次數(shù)界定計時器JC:
鍵按下時:+1,>150(0.6秒)時不再增加。
彈起時界定:JC>=150時(600ms),去界定JN。隨即將JC清0。
3、擊鍵次數(shù)JN:
鍵值被認定有效(KEY_P=1)時+1,>多少時不再增加,自己去定。將KEY_P清0。






作者: xianfajushi    時間: 2021-9-24 14:36
現(xiàn)在認為不用雙擊,用組合鍵更簡單靈活,程序設計也相對容易實現(xiàn).
作者: dcjdcj    時間: 2021-9-24 14:51
bbxyliyang 發(fā)表于 2021-9-24 06:48
#include
#include
#define key_state_0 0

感謝回復,這個程序是觸摸按鍵的,機械按鍵是可以正常識別雙擊事件的,觸摸按鍵的庫是別提供的,只有一個接口函數(shù)
作者: dcjdcj    時間: 2021-9-24 14:52
xianfajushi 發(fā)表于 2021-9-24 14:36
現(xiàn)在認為不用雙擊,用組合鍵更簡單靈活,程序設計也相對容易實現(xiàn).

觸摸按鍵
作者: dcjdcj    時間: 2021-9-24 14:52
yzwzfyz 發(fā)表于 2021-9-24 08:52
1、按鍵有彈動,如果沒有防彈動過慮則,則無法界定按了幾下(按一下,彈N次)。
2、防彈動的過慮時間,既 ...

感謝回復,我試一下
作者: xianfajushi    時間: 2021-9-24 15:36
dcjdcj 發(fā)表于 2021-9-24 14:52
觸摸按鍵

只要是按鍵,不論是何種,都是可以實現(xiàn)組合鍵,有什么不能實現(xiàn)的理由?
作者: xianfajushi    時間: 2021-9-24 15:44
dcjdcj 發(fā)表于 2021-9-24 14:52
觸摸按鍵

設定一個比較時間,可以實現(xiàn)組合,當然,雙擊也是.
作者: 188610329    時間: 2021-9-24 15:47
一般情況下, 長按短按, 單擊雙擊。
這兩種區(qū)分按鍵的模式,在沒有必要的前提下,不同時使用。
因為,會給你的按鍵判斷復雜程度提高一個級別,如果你采取了 單擊雙擊,還嫌不夠可以增加三擊四擊,沒必要再去搞長按。一個鍵也就算了,鍵多了,你的按鍵判斷邏輯會變得及其混亂,因為一旦牽涉到長按和雙擊,那么你必須考慮,第二擊你達到了長按標準的話,到底算,長按,還是雙擊。諸如此類各種麻煩。
作者: dcjdcj    時間: 2021-9-24 19:16
xianfajushi 發(fā)表于 2021-9-24 15:44
設定一個比較時間,可以實現(xiàn)組合,當然,雙擊也是.

狀態(tài)機的邏輯可以實現(xiàn)嗎?我同時檢測5顆按鍵
作者: dcjdcj    時間: 2021-9-24 19:19
188610329 發(fā)表于 2021-9-24 15:47
一般情況下, 長按短按, 單擊雙擊。
這兩種區(qū)分按鍵的模式,在沒有必要的前提下,不同時使用。
因為, ...

邏輯不會很亂,因為一次只能檢測一個按鍵
作者: dcjdcj    時間: 2021-9-24 19:24
yzwzfyz 發(fā)表于 2021-9-24 08:52
1、按鍵有彈動,如果沒有防彈動過慮則,則無法界定按了幾下(按一下,彈N次)。
2、防彈動的過慮時間,既 ...

每次單擊都可以檢測到,就是雙擊不行
作者: xianfajushi    時間: 2021-9-24 19:25
dcjdcj 發(fā)表于 2021-9-24 19:16
狀態(tài)機的邏輯可以實現(xiàn)嗎?我同時檢測5顆按鍵

可以,在一定時間范圍內(nèi)對按鍵進行計算得值即可實現(xiàn)組合,計數(shù)可實現(xiàn)雙擊,也可實現(xiàn)按次數(shù),這樣寫起來很方便。
作者: dcjdcj    時間: 2021-9-24 20:14
xianfajushi 發(fā)表于 2021-9-24 19:25
可以,在一定時間范圍內(nèi)對按鍵進行計算得值即可實現(xiàn)組合,計數(shù)可實現(xiàn)雙擊,也可實現(xiàn)按次數(shù),這樣寫起來很 ...

比如,在一段時間內(nèi),第一次按下的鍵值是0x80,第二次按下的鍵值是0x01,然后將這兩個值相或,得到0x81,用0x81區(qū)判斷是不是組合鍵嗎?
作者: xianfajushi    時間: 2021-9-24 20:45
dcjdcj 發(fā)表于 2021-9-24 20:14
比如,在一段時間內(nèi),第一次按下的鍵值是0x80,第二次按下的鍵值是0x01,然后將這兩個值相或,得到0x81, ...


作者: dcjdcj    時間: 2021-9-24 21:13
xianfajushi 發(fā)表于 2021-9-24 20:45

還有一個問題,一個無源蜂鳴器,比如4k頻率,定時器中斷125us,要怎么驅(qū)動,可否來個例程,謝謝了
作者: 188610329    時間: 2021-9-24 21:28
dcjdcj 發(fā)表于 2021-9-24 21:13
還有一個問題,一個無源蜂鳴器,比如4k頻率,定時器中斷125us,要怎么驅(qū)動,可否來個例程,謝謝了

中斷里面一句話

Beep_IO = !Beep_IO;
作者: dcjdcj    時間: 2021-9-24 21:52
188610329 發(fā)表于 2021-9-24 21:28
中斷里面一句話

Beep_IO = !Beep_IO;

Beep_IO = ~Beep_IO;不是這個嗎?那么響一聲要怎么寫,要多久才算一聲
作者: 188610329    時間: 2021-9-24 22:12
dcjdcj 發(fā)表于 2021-9-24 21:52
Beep_IO = ~Beep_IO;不是這個嗎?那么響一聲要怎么寫,要多久才算一聲

一般,習慣上,  字節(jié) 用  ~ 取反, 位 用 !取反。 至少 別人看到我寫的那一行一眼就能知道 Beep_IO 是個位地址。

最后,響一聲,看你打算 長響還是短響, 長響一般 1.5 秒, 短響一般 0.3 秒,(習慣上)。

作者: dcjdcj    時間: 2021-9-24 23:16
188610329 發(fā)表于 2021-9-24 22:12
一般,習慣上,  字節(jié) 用  ~ 取反, 位 用 !取反。 至少 別人看到我寫的那一行一眼就能知道 Beep_IO 是 ...

好的,謝謝指教,我試一下
作者: dcjdcj    時間: 2021-9-25 19:50
bbxyliyang 發(fā)表于 2021-9-24 06:48
#include
#include
#define key_state_0 0

觸摸按鍵的,不行,波動按鍵就可以




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1