打印
[技术问答]

一种可靠的按键消抖方法

[复制链接]
822|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
余三水|  楼主 | 2020-4-27 14:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沙发
余三水|  楼主 | 2020-4-27 14:38 | 只看该作者
按键头文件
#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__

使用特权

评论回复
板凳
余三水|  楼主 | 2020-4-27 14:38 | 只看该作者
按键检测具体实现
#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;
}

使用特权

评论回复
地板
余三水|  楼主 | 2020-4-27 14:48 | 只看该作者
测试代码
#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;
        }
    }
}

使用特权

评论回复
5
ayb_ice| | 2020-5-19 17:14 | 只看该作者
不需要多次,定时(10~100MS)调用,与上次值比较就可以了,不同就是按下或弹起

使用特权

评论回复
6
詹求实| | 2020-5-28 10:29 | 只看该作者
支持下,谢谢分享!

使用特权

评论回复
7
yzq13246068880| | 2020-7-9 10:17 | 只看该作者
顶下,顶下,顶下,

使用特权

评论回复
8
スモモ| | 2020-8-23 18:47 | 只看该作者
感谢分享

使用特权

评论回复
9
张さん| | 2020-8-23 19:11 | 只看该作者
学习了

使用特权

评论回复
10
weifeng90| | 2020-9-18 19:07 | 只看该作者
复杂化了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

356

帖子

1

粉丝