IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 单片机系统一个非常实用的按键处理框架MultiButton -> 正文阅读

[嵌入式]单片机系统一个非常实用的按键处理框架MultiButton


前言

在嵌入式开发中,对于输入设备有这几种,屏幕、按键、编码器等。
按键是我们使用最常见的一种。但是处理按键的时候也有一些麻烦之处,比方说我们对于一个按键进行程序消抖、区分按键单击、双击、长按、短按,如果没有一个按键驱动框架是很难实现的 ,在这里推荐一个非常好用的按键处理框架MultiButton


一、MultiButton 是什么?

MultiButton 就是这样一个按键管理框架,它不仅占用内存小,而且实现了按键操作的诸多功能(具体功能如下),使用回调函数进行业务的处理,是一个非常实用的按键框架。
请添加图片描述
本篇文章对 MultiButton 进行简略的介绍,并且对业务处理代码进行了解读

GitHub仓库 https://github.com/0x1abin/MultiButton


2. 使用步骤和代码的问题

2.1 使用步骤

  • 代码如下
// 1. 申请按键结构体
struct Button key2;
// 2. 初始化按键  
	// handle 按键结构体指针
	// uint8_t(*pin_level)() 获取按键值回调函数
	// active_level 触发电平
void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level)
button_init(&key2,Read_Key2_Pin , 1);
// 3. 注册回调函数
	// handle 按键结构体指针
	// event 触发条件,看下面表格
	// BtnCallback 触发回调函数
void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
// 4. 启动 按键
button_start(&key2);

// 5. 在循环或者使用定时器,每5ms调用一次次函数
button_ticks();

2.2 按键库的问题

  • 因为处理函数设计的问题,如果只有一个按键结构体,就会导致段错误
void button_ticks()
{
   struct Button* target;
   for(target=head_handle; target; target=target->next) {
       button_handler(target);
   }
}

2.3 按键库一些默认参数的配置

#define TICKS_INTERVAL    5	//ms                    // 心跳时钟
#define DEBOUNCE_TICKS    3	//MAX 8                 // 防抖次数
#define SHORT_TICKS       (300 /TICKS_INTERVAL)     // 短按时间
#define LONG_TICKS        (1000 /TICKS_INTERVAL)    // 长按时间

3. 代码解读

  • 按键的处理流程为下图,按照下图看代码会更加清晰
    请添加图片描述

  • C文件

    /*
     * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
     * All rights reserved
     */
    
    #include "multi_button.h"
    
    #define EVENT_CB(ev)   if(handle->cb[ev])handle->cb[ev]((Button*)handle)
    
    //button handle list head.
    static struct Button* head_handle = NULL;
    
    /**
      * @brief  Initializes the button struct handle.
      * @param  handle: the button handle strcut.
      * @param  pin_level: read the HAL GPIO of the connet button level.
      * @param  active_level: pressed GPIO level.
      * @retval None
      */
    void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level)
    {
        memset(handle, 0, sizeof(struct Button));
        handle->event = (uint8_t)NONE_PRESS;
        handle->hal_button_Level = pin_level;
        handle->button_level = handle->hal_button_Level();
        handle->active_level = active_level;
    }
    
    /**
      * @brief  Attach the button event callback function.
      * @param  handle: the button handle strcut.
      * @param  event: trigger event type.
      * @param  cb: callback function.
      * @retval None
      */
    void button_attach(struct Button* handle, PressEvent event, BtnCallback cb)
    {
        handle->cb[event] = cb;
    }
    
    /**
      * @brief  Inquire the button event happen.
      * @param  handle: the button handle strcut.
      * @retval button event.
      */
    PressEvent get_button_event(struct Button* handle)
    {
        return (PressEvent)(handle->event);
    }
    
    /**
      * @brief  Button driver core function, driver state machine.
      * @param  handle: the button handle strcut.
      * @retval None
      */
    void button_handler(struct Button* handle)
    {
        uint8_t read_gpio_level = handle->hal_button_Level();
    
        //ticks counter working..
        /* 判断定时器是否需要工作 */
        if((handle->state) > 0) handle->ticks++;
    
        /*------------防抖处理---------------*/
        if(read_gpio_level != handle->button_level) { //not equal to prev one
            //continue read 3 times same new level change
            // 默认防抖次数为三次,也就是此函数循环第三次才会进行按键判断
            if(++(handle->debounce_cnt) >= DEBOUNCE_TICKS) {
                handle->button_level = read_gpio_level;     // 最后一个读取到的实际值
                handle->debounce_cnt = 0;
            }
        } else { //leved not change ,counter reset. 如果读取不到变化,清空计数器的值
            handle->debounce_cnt = 0;
        }
    
        /*-----------------State machine-------------------*/
        switch (handle->state) {
            case 0:
                if(handle->button_level == handle->active_level) {	// 按键按键
                    handle->event = (uint8_t)PRESS_DOWN;            // 设置按键为按下
                    EVENT_CB(PRESS_DOWN);                           // 调用相应的回调函数
                    handle->ticks = 0;                              // 时钟重置为0
                    handle->repeat = 1;                             // 设置按键为第一次按下
                    handle->state = 1;                              // 状态机设置为1
                } else {
                    handle->event = (uint8_t)NONE_PRESS;            // 状态设置为没有按下
                }
                break;
    
            case 1:
                if(handle->button_level != handle->active_level) {  // 释放按钮
                    handle->event = (uint8_t)PRESS_UP;              // 设置按键状态为松开
                    EVENT_CB(PRESS_UP);                             // 调用松开回调函数
                    handle->ticks = 0;                              // 重置时钟
                    handle->state = 2;                              // 状态机设置为2
    
                } else if(handle->ticks > LONG_TICKS) {             // 长按不放
                    handle->event = (uint8_t)LONG_PRESS_START;      // 长按状态
                    EVENT_CB(LONG_PRESS_START);                     // 调用回调函数
                    handle->state = 5;                              // 状态机设置为5
                }
                break;
    
            case 2:
                if(handle->button_level == handle->active_level) {  // 再次按下
                    handle->event = (uint8_t)PRESS_DOWN;            // 设置按钮为按下
                    EVENT_CB(PRESS_DOWN);                           // 调用相应回调函数
                    handle->repeat++;                               // 连按次数累加
                    EVENT_CB(PRESS_REPEAT); // repeat hit           // 重复按下出发
                    handle->ticks = 0;                              // 清空时钟
                    handle->state = 3;                              // 状态机设置为3
                } else if(handle->ticks > SHORT_TICKS) {            // 按键时间超时
                    if(handle->repeat == 1) {                       // 如果按键只按了一次
                        handle->event = (uint8_t)SINGLE_CLICK;  // 设置为单次点击
                        EVENT_CB(SINGLE_CLICK);                 // 调用单次点击回调函数
                    } else if(handle->repeat == 2) {            // 如果按键按了两次
                        handle->event = (uint8_t)DOUBLE_CLICK;  // 设置为双击状态
                        EVENT_CB(DOUBLE_CLICK); // repeat hit   // 调用双击回调函数
                    }
                    handle->state = 0;                          // 状态机设置为0
                }
                break;
    
            case 3:
                if(handle->button_level != handle->active_level) {  // 释放按钮
                    handle->event = (uint8_t)PRESS_UP;              // 按键标记为释放状态
                    EVENT_CB(PRESS_UP);                             // 调用释放状态回调函数
                    if(handle->ticks < SHORT_TICKS) {               // 时钟如果小于短按时间
                        handle->ticks = 0;                          // 时钟清零
                        handle->state = 2; //repeat press           // 状态机设置为2
                    } else {
                        handle->state = 0;
                    }
                }else if(handle->ticks > SHORT_TICKS){              // 长按松开
                    handle->state = 0;
                }
                break;
    
            case 5:
                if(handle->button_level == handle->active_level) {
                    //continue hold trigger
                    handle->event = (uint8_t)LONG_PRESS_HOLD;       // 长按状态
                    EVENT_CB(LONG_PRESS_HOLD);                      // 调用长按回调函数
    
                } else {                                            // 长按松开
                    handle->event = (uint8_t)PRESS_UP;
                    EVENT_CB(PRESS_UP);
                    handle->state = 0; //reset
                }
                break;
        }
    }
    
    /**
      * @brief  Start the button work, add the handle into work list.
      * @param  handle: target handle strcut.
      * @retval 0: succeed. -1: already exist.
      */
    int button_start(struct Button* handle)
    {
        struct Button* target = head_handle;
        while(target) {
            if(target == handle) return -1;	//already exist.
            target = target->next;
        }
        handle->next = head_handle;
        head_handle = handle;
        return 0;
    }
    
    /**
      * @brief  Stop the button work, remove the handle off work list.
      * @param  handle: target handle strcut.
      * @retval None
      */
    void button_stop(struct Button* handle)
    {
        struct Button** curr;
        for(curr = &head_handle; *curr; ) {
            struct Button* entry = *curr;
            if (entry == handle) {
                *curr = entry->next;
    //			free(entry);
                return;//glacier add 2021-8-18
            } else
                curr = &entry->next;
        }
    }
    
    /**
      * @brief  background ticks, timer repeat invoking interval 5ms.
      * @param  None.
      * @retval None
      */
    void button_ticks()
    {
        struct Button* target;
        for(target=head_handle; target; target=target->next) {
            button_handler(target);
        }
    }
    
  • 头文件

    /*
     * Copyright (c) 2016 Zibin Zheng <znbin@qq.com>
     * All rights reserved
     */
    
    #ifndef _MULTI_BUTTON_H_
    #define _MULTI_BUTTON_H_
    
    #include "stdint.h"
    #include "string.h"
    
    //According to your need to modify the constants.
    #define TICKS_INTERVAL    5	//ms                    // 心跳时钟
    #define DEBOUNCE_TICKS    3	//MAX 8                 // 防抖次数
    #define SHORT_TICKS       (300 /TICKS_INTERVAL)     // 短按时间
    #define LONG_TICKS        (1000 /TICKS_INTERVAL)    // 长按时间
    
    typedef void (*BtnCallback)(void*);
    
    typedef enum {
        PRESS_DOWN = 0,
        PRESS_UP,
        PRESS_REPEAT,
        SINGLE_CLICK,
        DOUBLE_CLICK,
        LONG_PRESS_START,
        LONG_PRESS_HOLD,
        number_of_event,
        NONE_PRESS
    }PressEvent;
    
    typedef struct Button {
        uint16_t ticks;                         // 时钟
        uint8_t  repeat : 4;                    // 按键按下次数
        uint8_t  event : 4;                     // 按键当前状态
        uint8_t  state : 3;                     // 状态
        uint8_t  debounce_cnt : 3;              // 消抖计数
        uint8_t  active_level : 1;              // GPIO触发电平
        uint8_t  button_level : 1;              // 读取到的按键的状态 0 or 1
        uint8_t  (*hal_button_Level)(void);     // 读取按键函数
        BtnCallback  cb[number_of_event];
        struct Button* next;
    }Button;
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void button_init(struct Button* handle, uint8_t(*pin_level)(), uint8_t active_level);
    void button_attach(struct Button* handle, PressEvent event, BtnCallback cb);
    PressEvent get_button_event(struct Button* handle);
    int  button_start(struct Button* handle);
    void button_stop(struct Button* handle);
    void button_ticks(void);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-10-17 12:09:12  更:2021-10-17 12:09:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:23:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码