方法介紹
按一定的間隔采樣,連續多次都處於按下狀態才判定為按下狀態,可以有效的減少誤操作。具體實現如下:
按鍵頭文件
#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;
}
}
}
注意事項
如果要采用這個方法,請仔細測試。