一种可靠的按键消抖方法

方法介绍

按一定的间隔采样,连续多次都处于按下状态才判定为按下状态,可以有效的减少误操作。具体实现如下:

按键头文件

#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;
        } 
    }
}

注意事项

如果要采用这个方法,请仔细测试。

猜你喜欢

转载自www.cnblogs.com/iotplus/p/12693769.html
今日推荐