一種可靠的按鍵消抖方法


方法介紹

按一定的間隔采樣,連續多次都處於按下狀態才判定為按下狀態,可以有效的減少誤操作。具體實現如下:

按鍵頭文件

#ifndef	__KEY_H__
#define __KEY_H__	

#include <stdint.h>

typedef struct Key_t {
	uint8_t inited : 1;     //引腳是否已經初始化
	uint8_t press : 1;		//按下表示,1表示發生了按下事件,等待用戶處理
	uint8_t release : 1;	//松開標志,1表示發送了松開事件,等待用戶處理
	uint8_t state : 1;		//按鍵的穩定狀態
	uint8_t states : 4;		//最近的4次采樣
	uint8_t pin;			//按鍵的編號或者所在引腳
} Key_t;

#ifdef __cplusplus
extern "C" {
#endif

//初始化一個按鍵
void Key_Init(Key_t* key, uint8_t pin);

//掃描一下按鍵,一般每隔5個毫秒掃描一次即可
//連續4次掃描就需要20個毫秒了,如果4次檢測都是低電平才判斷為低電平。
//由於一般情況下都是只處理按下事件,有按下事件返回1
uint8_t Key_Scan(Key_t* key);

//獲取按鍵所在引腳的狀態,高電平返回1,低電平返回0
uint8_t Key_GetPinState(Key_t* key);

//檢查按鍵是否存在按下事件
static inline uint8_t Key_HasPressEvent(Key_t* key)
{
	return key->press;
}

//清除按鍵的按下事件標志
static inline void Key_ClearPressEvent(Key_t* key)
{
	key->press = 0;
}

//檢查按鍵是否存在松開事件
static inline uint8_t Key_HasReleaseEvent(Key_t* key)
{
	return key->release;
}

//清除按鍵松開事件標志
static inline void Key_ClearReleaseEvent(Key_t* key)
{
	key->release = 0;
}

#ifdef __cplusplus
}
#endif

#endif //__KEY_H__

按鍵檢測具體實現

#include "key.h"
#include <ioCC2530.h> 

uint8_t Key_GetPinState(Key_t* key)
{
    if (1 == key->pin) {
        return P0_1;
    }
    else {
        return 0;
    }
}

//計算一個數字里面為1的位的個數
static inline uint8_t CountOne(uint8_t num)
{
    uint8_t high = (num & 0xf0) >> 4;
    uint8_t low = num & 0x0f;
    const uint8_t table[] = {
        0, 1, 1, 2,	//0000 0001 0010 0011
        1, 2, 2, 3,	//0100 0101 0110 0111
        1, 2, 2, 3, //1000 1001 1010 1011
        2, 3, 3, 4,	//1100 1101 1110 1111
    };

    return table[high] + table[low];
}

void Key_Init(Key_t* key, uint8_t pin)
{
    key->pin = pin;
    key->press = 0;
    key->release = 0;
    key->state = Key_GetPinState(key);
    key->states = key->state ? 0x0f : 0x00;
    key->inited = 1;
}

uint8_t Key_Scan(Key_t* key)
{
    uint8_t press = 0;
    uint8_t state = 0;
    uint8_t states = 0;

    if (key && key->inited) {
        //移出上一次的掃描狀態並存入本次的掃描狀態
        states = key->states;
        states <<= 1;
        states |= Key_GetPinState(key);
        key->states = states;

        state = CountOne(states) ? 1 : 0;	//4次采樣全部為0才判定為低電平
        if (state != key->state) {          //引腳狀態發生變化
            key->state = state;
            if (state) {
                key->release = 1;
            }
            else {
                press = 1;
                key->press = 1;
            }
        }
    }

    return press;
}

測試代碼

#include <ioCC2530.h> 
#include "key.h"

#define LED1   P1_0     //定義LED1所在引腳
#define KEY1   P0_1     //定義BTN1所在引腳

void DelayMs(int ms)
{
    while (ms--) {
        volatile int x = 500;//注意:這個數字是估計的,不准確
        while (x--);
    }
}

void main(void)
{
    Key_t key;
    
    //配置P0_1引腳的按鍵1 
    P0SEL &= ~0x02; //普通GPIO模式<0為IO模式,1為外設模式>
    P0DIR &= ~0x02; //輸入功能<0為輸入,1為輸出>
    P0INP &= ~0x02; //上拉或下拉模式<0為上拉或下拉模式,1為三態模式>

    //配置P1_0引腳的LED1
    P1SEL &= ~0x01; //普通GPIO模式<0為IO模式,1為外設模式>
    P1DIR |= 0x01;  //輸出功能<0為輸入,1為輸出>
    P1INP &= ~0x01; //上拉或下拉模式<0為上拉或下拉模式,1為三態模式>
    
    P2INP |= 0xe0;  //P0,P1,P2都設置為上拉模式
    
    Key_Init(&key, 1);
    
    while (1)
    {
        //一般使用定時器來掃描,這里使用延時函數用來測試一下。
        DelayMs(5);     
        Key_Scan(&key);
        
        if (Key_HasPressEvent(&key)) {
            Key_ClearPressEvent(&key);
            LED1 = !LED1;
        } 
    }
}

注意事項

如果要采用這個方法,請仔細測試。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM