按键消抖程序

单片机按键消抖程序,全平台通用的。内存小的片子就不要用了。
需要写一个事件管理的程序。
只支持独立按键。
头文件内容


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

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值