久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 11002|回復: 24
打印 上一主題 下一主題
收起左側

一個單片機按鍵消抖程序的分享

  [復制鏈接]
跳轉到指定樓層
樓主
ID:825513 發表于 2021-5-23 19:26 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
一個之前自己寫的按鍵消抖的程序,相信有和我一樣的新手也有遇到過因為按鍵按下時抖動而導致失靈的情況,后來自己在學校里的一位師兄的啟發下弄懂了如何通過軟件方式進行按鍵消抖,現在分享一下源程序。

開發板上的按鍵由于機械觸點的彈性作用,當按下按鍵時機械觸點并不會立刻穩定閉合,松開按鍵時機械觸點也不會立刻穩定斷開,因此在按下或松開按鍵的瞬間(在大約10ms內)電位會有一定的波動(或者叫做抖動);軟件消抖就是當檢測到按鍵狀態發生變化后,先延時大約10ms待電位穩定后,再進行一次檢測,如果仍然保持閉合狀態的電位,則確認為按鍵真正被按下。

按鍵消抖程序如下:
# K5表示按鍵,D8表示LED燈,按下獨立按鍵K5,LEDD8亮;再次按下K5,D8滅。

單片機源程序如下:
#include<reg51.h>

sbit K5=P3^5;
sbit D8=P2^7;

void Delay10ms()                //延時10ms
{
        unsigned char i,j;
        i=20;
        j=113;
        do
        {
                while(--j);
        }
        while(--i);
}

void main()
{
        int Key=0;                //定義一個標志位,變量Key的值置0,表示按鍵處于松開狀態
        while(1)
        {
                if(K5==0&&Key==0)                //判斷按鍵是否被按下
                {
                        Delay10ms();                //延時消抖
                        Key=1;                //把標志位置1,防止程序循環執行
                        if(K5==0)                //再次判斷按鍵狀態,排除干擾
                        {
                                D8=~D8;
                        }
                }
                else if(K5==1)
                {
                        Key=0;                //當按鍵沒有被按下時,重置標志位
                }
        }
}

評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏10 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:47286 發表于 2021-5-24 01:20 來自手機 | 只看該作者
我個人認為一切需要for或 whell這種死延時的防抖都不好 20mS的時間會影響很多事 至少要定時器防抖

評分

參與人數 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

板凳
ID:554500 發表于 2021-5-24 10:20 | 只看該作者
這個代碼還可以,但是感覺效率不高。
一定要想辦法去掉delay函數。小工程代碼看不出效果,工程代碼大了就體現出來了。
#include "reg52.h"

sbit key=P1^2;
//按鍵掃描
void Key_Scan()
{
        static u8 j=0,k=0;
       
        if(key==0)
        {
                if(j==0)
                {
                        if(key==0&&k++>=20)
                        {
                                j=1;
                                //任務代碼       
                        }                       
                }
        }
        else  //按鍵釋放
        {
                j=0;
                k=0;
        }
}

//k的取值和函數調度周期有關系,適當調節,穩定性還是不錯的。
//k取值越大,靈敏度越低,反之越高
回復

使用道具 舉報

地板
ID:301715 發表于 2021-5-24 16:55 | 只看該作者
用定時器計次數啊,Delay10ms();這些如果在程序大點的會耗費很多資源。
回復

使用道具 舉報

5#
ID:475858 發表于 2021-5-24 17:41 | 只看該作者
用定時器計數可以
回復

使用道具 舉報

6#
ID:825513 發表于 2021-5-25 16:33 | 只看該作者
18701931930 發表于 2021-5-24 10:20
這個代碼還可以,但是感覺效率不高。
一定要想辦法去掉delay函數。小工程代碼看不出效果,工程代碼大了就 ...

感謝老哥,已受教
回復

使用道具 舉報

7#
ID:825513 發表于 2021-5-25 16:35 | 只看該作者
lihui2558 發表于 2021-5-24 17:41
用定時器計數可以

我覺得沒必要給按鍵用定時器,在一些稍微大一點的項目中,那樣就太浪費定時器資源了
回復

使用道具 舉報

8#
ID:47286 發表于 2021-5-25 21:33 來自手機 | 只看該作者
NIMITIZ 發表于 2021-5-25 16:35
我覺得沒必要給按鍵用定時器,在一些稍微大一點的項目中,那樣就太浪費定時器資源了

定時器可以用多變量計數 能耽誤什么
回復

使用道具 舉報

9#
ID:384820 發表于 2021-5-25 21:53 | 只看該作者
發一個在用的按鍵消抖程序,供參考
定時器設置每隔20ms掃描一次按鍵

/***** 按鍵掃描 *****/
void keyscan()
{
  if(l_debnce ==0)               //是否需要消抖?
  {              
    P1 |=0x0F;                   //先拉高端口
                if((P1 &0x0F)!=0x0F)         //不需消抖,則判斷有鍵按下否
                {
                        //--- 掃描key1
                        if(key1 ==0)
                        {
                                if(l_keypress ==0)
                                {
                                        l_keypress =1;         //key1鍵按下,則按下標記置1
                                        l_debnce =1;           //消抖標記置1
                                        key_proc(1);           //判為key1短按
                                }
                        }                        
                        //--- 掃描key2
                        if(key2 ==0)
                        {
                                if(l_keypress ==0)
                                {
                                        l_keypress =1;         //key2鍵按下,則按下標記置1
                                        l_debnce =1;           //消抖標記置1
                                        key_proc(2);           //判為key2短按
                                }
                        }
                        //--- 掃描key3
                        if(key3 ==0)
                        {
                                if(l_keypress ==0)
                                {
                                        l_keypress =1;         //key3鍵按下,則按下標記置1
                                        l_debnce =1;           //消抖標記置1
                                        key_proc(3);           //判為key3短按
                                }
                        }
              }
    }
    //--- 無鍵按下,則清除標記
    else  
                        l_keypress =0;
  }
  //--- 需要20ms的消抖
  else  
    l_debnce =0;
}

若加入 n_keypress 計數器,還可以修改為判斷長按短按鍵的情況
有興趣可以私信留言交流
回復

使用道具 舉報

10#
ID:702127 發表于 2021-5-26 14:42 | 只看該作者
直接狀態機吧,定時器消抖,效率高還好用。
回復

使用道具 舉報

11#
ID:548551 發表于 2021-5-26 18:09 | 只看該作者
NIMITIZ 發表于 2021-5-25 16:35
我覺得沒必要給按鍵用定時器,在一些稍微大一點的項目中,那樣就太浪費定時器資源了

什么是浪費資源? 沒有懂。你這個delay 就是在浪費資源。定時器有毒是不是,為什么不用定時器來定時呢。你如果只是亮燈,只是檢測按鍵,適當的延時沒有太大的問題。 但是你是檢測按鍵,又是LED燈,雙是輸出PWM,叒是檢測電池電壓,即ADC檢測,叕是定時自動關機。 你說怎么弄? 用定時器設置一個基準定時,比如5MS,所有的操作都在這5MS的基準之上,比如按鍵檢測,消抖需要40MS,給個變量加8次,一樣的到了40MS, LED燈一樣的效果,實現1S的閃爍,變量到達100的時候打開達到200的時候關閉。一樣的效果,同樣定時自動關機,如果自動定時一分鐘,一個unsigned int 變量 到達12000的時候就表示時間到了需要關機。 這些都是可以同時同步執行的,如果delay你想想,一次delay假如說延時40ms 那到一分鐘的時候不知道延時到幾分鐘去了。所以定時器,不要浪費,它是你提高效率的重要組成部分。
回復

使用道具 舉報

12#
ID:825513 發表于 2021-5-28 14:38 | 只看該作者
順德動力 發表于 2021-5-25 21:53
發一個在用的按鍵消抖程序,供參考
定時器設置每隔20ms掃描一次按鍵

你好,我還不太懂 變量l_debnce 和函數 key_proc() ,可以講解一下嗎,謝謝
回復

使用道具 舉報

13#
ID:47286 發表于 2021-5-28 21:14 | 只看該作者
NIMITIZ 發表于 2021-5-28 14:38
你好,我還不太懂 變量l_debnce 和函數 key_proc() ,可以講解一下嗎,謝謝

他程序給的不太全 我也看不懂 而且 感覺有點麻煩

大概是它吧按鍵接在P1的0123口上 檢測前先拉高端口 如果按下 那么端口拉不高 然后 應該定義了sbit key1=P1^0;之類的

I_keypress和i_debnce應該是bit 不是char 標記 不是變量 做兩個標記 靠標記變化來判斷和處理 不過沒看見變量累加延時或者計數器累加延時 不知道怎么實現20ms防抖的 也沒有判斷長短按的變量

其實不用先拉高端口吧 每個端口接個10k的弱上拉或者開單片機內部上拉電阻 只要沒按下就一定是高 直接判斷就行了 這樣少一層if嵌套 另外 如果KEY1和KEY2同時按下 怎么防抖 雖然理論上20ms的防抖很短 不會沖突 不過感覺用起來還是不太保險吧 多個單獨按鍵檢測 我是每個按鍵一套標記 獨立的 防止干擾
回復

使用道具 舉報

14#
ID:141497 發表于 2021-5-28 22:16 | 只看該作者
樓上正解,用定時器,效率比較高。
回復

使用道具 舉報

15#
ID:970121 發表于 2021-10-26 17:58 | 只看該作者
dzbj 發表于 2021-5-28 21:14
他程序給的不太全 我也看不懂 而且 感覺有點麻煩

大概是它吧按鍵接在P1的0123口上 檢測前先拉高端口  ...

每個字我都能看懂,也明白意思,但邏輯關系還是搞不定,能出一個示例嗎?
回復

使用道具 舉報

16#
ID:384475 發表于 2021-10-26 21:13 | 只看該作者
下面這個是金沙灘宋老師的教程,很好:
那么消抖操作所需要的延時該怎么處理呢?其實除了這種簡單的延時,我們還有更優異
的方法來處理按鍵抖動問題。舉個例子:我們啟用一個定時中斷,每 2ms 進一次中斷,掃描
一次按鍵狀態并且存儲起來,連續掃描 8 次后,看看這連續 8 次的按鍵狀態是否是一致的。
8 次按鍵的時間大概是 16ms,這 16ms 內如果按鍵狀態一直保持一致,那就可以確定現在按
鍵處于穩定的階段,而非處于抖動的階段,如圖 8-12。 圖 8-12 按鍵連續掃描判斷
假如左邊時間是起始 0 時刻,每經過 2ms 左移一次,每移動一次,判斷當前連續的 8 次
按鍵狀態是不是全 1 或者全 0,如果是全 1 則判定為彈起,如果是全 0 則判定為按下,如果
0 和 1 交錯,就認為是抖動,不做任何判定。想一下,這樣是不是比簡單的延時更加可靠?
利用這種方法,就可以避免通過延時消抖占用單片機執行時間,而是轉化成了一種按鍵
狀態判定而非按鍵過程判定,我們只對當前按鍵的連續 16ms 的 8 次狀態進行判斷,而不再
關心它在這 16ms 內都做了什么事情,那么下面就按照這種思路用程序實現出來,同樣只以
K4 為例。
#include <reg52.h>
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit KEY1 = P2^4;
sbit KEY2 = P2^5;
sbit KEY3 = P2^6;
sbit KEY4 = P2^7;
unsigned char code LedChar[] = { //數碼管顯示字符轉換表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
bit KeySta = 1; //當前按鍵狀態
void main()
{
bit backup = 1; //按鍵值備份,保存前一次的掃描值
unsigned char cnt = 0; //按鍵計數,記錄按鍵按下的次數

EA = 1; //使能總中斷
ENLED = 0; //選擇數碼管 DS1 進行顯示
ADDR3 = 1;
ADDR2 = 0;
ADDR1 = 0;
ADDR0 = 0;
TMOD = 0x01; //設置 T0 為模式 1
TH0 = 0xF8; //為 T0 賦初值 0xF8CD,定時 2ms
TL0 = 0xCD;
ET0 = 1; //使能 T0 中斷
TR0 = 1; //啟動 T0
P2 = 0xF7; //P2.3 置 0,即 KeyOut1 輸出低電平
P0 = LedChar[cnt]; //顯示按鍵次數初值

while (1)
{
if (KeySta != backup) //當前值與前次值不相等說明此時按鍵有動作
{
if (backup == 0) //如果前次值為 0,則說明當前是彈起動作
{
cnt++; //按鍵次數+1
if (cnt >= 10)
{ //只用 1 個數碼管顯示,所以加到 10 就清零重新開始
cnt = 0;
}
P0 = LedChar[cnt]; //計數值顯示到數碼管上
}
backup = KeySta; //更新備份為當前值,以備進行下次比較
}
} }
/* T0 中斷服務函數,用于按鍵狀態的掃描并消抖 */
void InterruptTimer0() interrupt 1
{
static unsigned char keybuf = 0xFF; //掃描緩沖區,保存一段時間內的掃描值

TH0 = 0xF8; //重新加載初值
TL0 = 0xCD;
keybuf = (keybuf<<1) | KEY4; //緩沖區左移一位,并將當前掃描值移入最低位
if (keybuf == 0x00)
{ //連續 8 次掃描值都為 0,即 16ms 內都只檢測到按下狀態時,可認為按鍵已按下
KeySta = 0;
}
else if (keybuf == 0xFF)
{ //連續 8 次掃描值都為 1,即 16ms 內都只檢測到彈起狀態時,可認為按鍵已彈起
KeySta = 1;
}
else
{} //其它情況則說明按鍵狀態尚未穩定,則不對 KeySta 變量值進行更新
}
這個算法是我們在實際工程中經常使用按鍵所總結的一個比較好的方法,介紹給大家
回復

使用道具 舉報

17#
ID:298123 發表于 2021-10-27 10:13 | 只看該作者
jjkk11 發表于 2021-10-26 21:13
下面這個是金沙灘宋老師的教程,很好:
那么消抖操作所需要的延時該怎么處理呢?其實除了這種簡單的延時,我 ...

教程只是用來學習的,沒有別的方法來教你,只能用這種方式。實際項目絕對不是你認知里這樣方式來實現的,等你向上跳過這個階段,你就明白我說的意思了。千萬不要用的自己的認知來誤導別人。
回復

使用道具 舉報

18#
ID:954686 發表于 2021-10-27 16:29 | 只看該作者
我常用的,可作為參考。
uchar KeyGetKeyCode(void)
{
    if(IDR_ADJ== 0)
    {
        return  KEY_ADJ;
    }
    if(IDR_SET== 0)
    {
        return  KEY_SET;
    }
    return  KEY_NONE;

void KeyScan(void)
{
    uchar CurrentkeyVal;
    static uint Cnt;
    if(PLScanStatus == NotAllowScan)return; //5ms 掃描一次,時間不到則返回
    PLScanStatus = NotAllowScan;
    switch(KeyScanState)
    {
        case KEY_STATE_Idle: //在空閑狀態下
            CurrentkeyVal = KeyGetKeyCode(); //讀取當前的按鍵值,
            if(CurrentkeyVal != KEYCODE_NOPRESS) //如果不等于KEYCODE_NOPRESS,說明此時有按鍵按下
            {
                KeyScanState = KEY_STATE_FirstPress; //把掃描狀態置為第一次按下狀態
                KeyVal = CurrentkeyVal;  //并且記錄當前的按鍵值
            }
            else
                KeyVal = KEYCODE_NOPRESS;
            break;
        case KEY_STATE_FirstPress://第一次按下狀態
            if(KeyVal == KeyGetKeyCode()) //如果KeyGetKeyCode讀出的值和KeyVal一致的話(即和空閑狀態下的記錄的按鍵值一致的話)
            {
                KeyScanState = KEY_STATE_RepeatPress; //把按鍵狀態置為 重復按下狀態,
                //  dPuts("\r\n key repeat");

                Cnt = 0;
            }
            else  //不一致的話(和上次讀的按鍵不一致),
            {
                KeyScanState = KEY_STATE_Idle; //把按鍵狀態置為空閑狀態,要重新再讀
                KeyVal = KEYCODE_NOPRESS;

            }
            break;
        case KEY_STATE_RepeatPress://在重復按下狀態下,
            if(KeyVal == KeyGetKeyCode())
            {
                if(++Cnt == 60)
                {
                    Cnt = 0;
                    KeyScanState = KEY_STATE_LongPress;
                    // dPuts("\r\n key long");
                }
            }
            else
            {
                KeyScanState = KEY_STATE_Idle;
                GetKey = KeyVal;                              //得到鍵值
                //   dPuts("\r\nget key");
                //  if(GetKey == KEY_MENU)  //
                //     GetKey = KEY_NONE;
            }
            break;
        case KEY_STATE_LongPress://長按狀態
            if(KeyVal == KeyGetKeyCode())
            {
                if(++Cnt == 15)
                {
                    Cnt = 0;
                    GetKey = KeyVal + 0x10;
                    //   dPuts("\r\nget long key");
                    //  if(GetKey == KEY_SET_LONG)
                    KeyScanState = KEY_STATE_NoneStatus;
                }
            }
            else
            {
                Cnt = 0;
                KeyScanState = KEY_STATE_Idle;
                KeyVal = KEYCODE_NOPRESS;
            }
            break;

        case KEY_STATE_NoneStatus:
            if( KeyGetKeyCode() == KEY_NONE)
            {
                KeyScanState = KEY_STATE_Idle; //把按鍵狀態置為空閑狀態,要重新再讀
                KeyVal = KEYCODE_NOPRESS;
            }
            break;

    }
}


回復

使用道具 舉報

19#
ID:977804 發表于 2022-2-13 17:41 | 只看該作者
18701931930 發表于 2021-5-24 10:20
這個代碼還可以,但是感覺效率不高。
一定要想辦法去掉delay函數。小工程代碼看不出效果,工程代碼大了就 ...

親測好用。
回復

使用道具 舉報

20#
ID:1006076 發表于 2022-3-20 18:24 | 只看該作者
試了一下,很好用
回復

使用道具 舉報

21#
ID:1006181 發表于 2022-3-22 16:45 來自手機 | 只看該作者
挺好,很多可以學習參考的
回復

使用道具 舉報

22#
ID:148126 發表于 2022-3-23 16:43 | 只看該作者
我認為你這個做不到消抖的目的,要不你認真看一下程序
回復

使用道具 舉報

23#
ID:1011952 發表于 2022-3-23 16:52 | 只看該作者
為什么不做硬消斗?有什么原因嗎??
回復

使用道具 舉報

24#
ID:415064 發表于 2022-3-23 16:56 | 只看該作者
乙豬 發表于 2022-3-23 16:52
為什么不做硬消斗?有什么原因嗎??

機械輕觸按鍵,硬消抖,錢多?
回復

使用道具 舉報

25#
ID:499136 發表于 2022-3-23 17:39 | 只看該作者
任何形式的while或for循環的延時在實際項目中都是不被允許的,一個延時會錯過好多事情的。
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 久久久久久九九九九九九 | 波多野结衣电影一区 | 亚洲成人一二区 | 女女百合av大片一区二区三区九县 | 日韩一区二区三区四区五区 | h在线免费观看 | av黄色在线 | 夜夜操天天操 | 超碰一区二区 | 婷婷狠狠 | 欧美中文字幕在线观看 | 国产日韩欧美一区 | 91精品国产一区二区三区蜜臀 | 日韩精品在线观看视频 | 国产亚洲一区二区三区 | 99在线资源| 99re视频精品 | 精品久久久久久久人人人人传媒 | 精品一级 | 久久伊| 日日综合 | 久久久久国产一区二区三区 | 草草精品 | 久久精品99国产精品日本 | 免费视频一区二区 | 中文字幕国产视频 | 国产最新精品视频 | 人人干在线视频 | 中文字幕 国产精品 | 色婷婷影院 | 99热播精品| www中文字幕 | 久草影视在线 | 在线小视频 | 黄色三级毛片 | www.色53色.com | 国产精品一区二 | 在线观看特色大片免费网站 | 成人av在线大片 | 久久久www成人免费无遮挡大片 | 欧美aⅴ |