一种可靠的按键消抖方法
方法介绍
按一定的间隔采样,连续多次都处于按下状态才判定为按下状态,可以有效的减少误操作。具体实现如下:
按键头文件
#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;
}
}
}
注意事项
如果要采用这个方法,请仔细测试。