单片机按键消抖程序,全平台通用的。内存小的片子就不要用了。
需要写一个事件管理的程序。
只支持独立按键。
头文件内容
typedef unsigned char u8;
typedef unsigned long u32;
/* 扫描时间 */
#define ScanTimeMs 10
/* 多长时间判断为长按 */
#define LongDownTime (4 * (1000 / ScanTimeMs))
/*
消抖时间 - 1的个数 * 扫描周期
如果扫描时间是10ms,那么07(0000 0111 三个1)表示消抖时间是10 * 3 = 30ms
*/
#define KeyStateNumBuf 0x07
enum KeyLevel{
low_level = 0,
high_level = 1,
};
enum KeyStatus_E
{
PRESS_DOWN_ACTION = 0xAA, /* 按下 */
LONG_HOLD_ACTION = 0xBB, /* 长按 */
RELEAS_UP_ACTION = 0xCC, /* 抬起 */
};
typedef u8 (*GpioInput)(void);
typedef void (*KeyEvent)(enum KeyStatus_E);
typedef void (*GpioInit)(void);
struct KeyNode {
u32 LongPressTheTime_ms; /* 长按时间(ms) */
u32 LongPressTheTime_cnt; /* 长按计数器,这个对外不可见 */
u8 LongPressTheTime_lock; /* 用来保证发生长按后只执行一次长按函数 */
enum KeyLevel ActiveLevel; /* 按下的时候是什么电平,在初始化的时候需要赋值 */
enum KeyStatus_E SteadyState; /* 按键最终判断的状态 */
enum KeyStatus_E SteadyStateBuf; /* 与 SteadyState 配合实现检测边沿 */
u8 KeyStatus; /* 按键状态缓存 */
KeyEvent EventCallback; /* 指向按键回调,做具体的功能处理 */
// GpioInit KeyInit; /* 指向按键初始化函数 */
GpioInput GetValue; /* 读取IO电平 */
struct KeyNode *next;
};
typedef struct KeyNode KeyNode_S;
struct SetKeyNodeMsg {
u32 LongPressTheTime_ms; /* 长按时间(ms) */
enum KeyLevel ActiveLevel; /* 按下的时候是什么电平,在初始化的时候需要赋值 */
KeyEvent EventCallback; /* 指向按键回调,做具体的功能处理 */
GpioInit KeyInit; /* 指向按键初始化函数 */
GpioInput GetValue; /* 读取IO电平 */
};
typedef struct SetKeyNodeMsg SetKeyNodeMsg_S;
extern void InitKeyDriver(u32 scan_time_ms);
extern char CreateKeyNode(KeyNode_S *node, SetKeyNodeMsg_S *msg);
源文件
static TimerEvent KeyDriverEvent_node;
static void KeyScan(void);
static void KeyDriver(void);
static void KeyDriverEvent(void);
/* 头尾节点指针 */
static KeyNode_S *HeadNode = NULL;
static KeyNode_S *TailNode = NULL;
static u32 ScanTime = 0;
/*
* @description : 创建一个按键节点
* @param : *node - 指向一个按键节点,这个必须是不会释放的全局变量
* *msg - 设置按键的一些信息
* @return : -1 - 形参为空
* -1 - IO操作函数不完整
* 0 - 正常
*/
char CreateKeyNode(KeyNode_S *node, SetKeyNodeMsg_S *msg)
{
if( NULL == node ) {
return -1;
}
if( NULL == msg ) {
return -1;
}
if( NULL == msg->EventCallback ) {
return -2;
}
if( NULL == msg->KeyInit ) {
return -2;
}
if( NULL == msg->GetValue ) {
return -2;
}
/* 执行按键初始化 */
msg->KeyInit();
node->LongPressTheTime_ms = msg->LongPressTheTime_ms;
node->ActiveLevel = msg->ActiveLevel;
node->EventCallback = msg->EventCallback;
// node->KeyInit = msg->KeyInit;
node->GetValue = msg->GetValue;
node->next = NULL;
node->KeyStatus = 0x00;
/* 这里不都设置成弹起的话上电会出问题 */
node->SteadyState = RELEAS_UP_ACTION;
node->SteadyStateBuf = RELEAS_UP_ACTION;
if( NULL == HeadNode ) {
HeadNode = node;
TailNode = node;
} else {
TailNode->next = node;
/* 重新指向最后一个节点 */
TailNode = TailNode->next;
/* 尾结点的下一个节点是NULL */
TailNode->next = NULL;
}
return 0;
}
/*
* @description : 初始换按键驱动
* @param : scan_time_ms - 扫描时间(ms)推荐10ms
* @return : 无
*/
void InitKeyDriver(u32 scan_time_ms)
{
HeadNode = NULL;
TailNode = NULL;
ScanTime = scan_time_ms;
CreatEventTimer(&KeyDriverEvent_node, KeyDriverEvent);
SetEventTimeout(&KeyDriverEvent_node, ScanTime);
}
/*
* @description : 每隔一个 ScanTime 时间片就执行一次
* @param : 无
* @return : 无
*/
static void KeyDriverEvent(void)
{
KeyScan();
KeyDriver();
SetEventTimeout(&KeyDriverEvent_node, ScanTime);
}
/*
* @description : 按键驱动
* @param : 无
* @return : 无
*/
static void KeyDriver(void)
{
KeyNode_S *Nonius = NULL;
if( NULL == HeadNode ) {
return ;
}
Nonius = HeadNode;
for(;;) {
if( NULL == Nonius ) {
break;
}
if( Nonius->SteadyStateBuf != Nonius->SteadyState ) {
Nonius->LongPressTheTime_cnt = 0;
Nonius->LongPressTheTime_lock = 0;
Nonius->EventCallback(Nonius->SteadyState);
Nonius->SteadyStateBuf = Nonius->SteadyState;
}
if( Nonius->LongPressTheTime_ms <= Nonius->LongPressTheTime_cnt ) {
if( !Nonius->LongPressTheTime_lock ) {
Nonius->LongPressTheTime_lock = 1;
Nonius->SteadyState = LONG_HOLD_ACTION;
Nonius->EventCallback(Nonius->SteadyState);
}
}
Nonius = Nonius->next;
}
}
/*
* @description : 按键扫描,要求10ms执行一次
* @param : 无
* @return : 无
*/
static void KeyScan(void)
{
KeyNode_S *Nonius = NULL;
if( NULL == HeadNode ) {
return ;
}
Nonius = HeadNode;
for(;;) {
if( NULL == Nonius ) {
break;
}
Nonius->KeyStatus = ( Nonius->KeyStatus << 1 ) | ( Nonius->GetValue() & 0x01 );
if( low_level == Nonius->ActiveLevel ) {
if( 0x00 == ( Nonius->KeyStatus & KeyStateNumBuf ) ) {
Nonius->SteadyState = PRESS_DOWN_ACTION;
if( Nonius->LongPressTheTime_cnt <= Nonius->LongPressTheTime_ms ) {
Nonius->LongPressTheTime_cnt++;
}
} else if ( KeyStateNumBuf == ( Nonius->KeyStatus & KeyStateNumBuf )) {
Nonius->SteadyState = RELEAS_UP_ACTION;
}
}
else if( high_level == Nonius->ActiveLevel ) {
if( KeyStateNumBuf == ( Nonius->KeyStatus & KeyStateNumBuf ) ) {
Nonius->SteadyState = PRESS_DOWN_ACTION;
if( Nonius->LongPressTheTime_cnt <= Nonius->LongPressTheTime_ms ) {
Nonius->LongPressTheTime_cnt++;
}
}
else if ( 0x00 == ( Nonius->KeyStatus & KeyStateNumBuf ) ) {
Nonius->SteadyState = RELEAS_UP_ACTION;
}
}
Nonius = Nonius->next;
}
}