STM32之按键驱动的使用和自定义(MultiButton)

news2024/10/6 12:33:41

原始Github地址

Github地址

修改后

调整内容

  1. 将宏定义转换成配置结构体

头文件

#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)

#define BUTTON_DEFAULT_CONF {5,3,60,200}


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;
    uint8_t button_level: 1;

    uint8_t (*hal_button_Level)(void);

    BtnCallback cb[number_of_event];
    struct Button *next;
} Button;
struct Button_Conf {
    uint8_t interval_ticks; /*定时执行次数*/
    uint8_t debounce_ticks; /*消抖次数*/
    uint16_t short_press_timeout_ticks; /*短按超时次数*/
    uint16_t long_press_timeout_ticks; /*长按超时次数*/
};
#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, struct Button_Conf *conf);

void button_stop(struct Button *handle);

void button_ticks(void);

#ifdef __cplusplus
}
#endif

#endif

源文件

/*
 * 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;
static struct Button_Conf *button_conf = NULL;

/**
  * @brief  Initializes the button struct handle.
  * @param  handle:     按钮句柄.
  * @param  pin_level: 读取io状态回调.
  * @param  active_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  按钮驱动核心功能,驱动状态机。
  * @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++;

    /*------------button debounce handle---------------*/
    if (read_gpio_level != handle->button_level) { //not equal to prev one
        //continue read 3 times same new level change
        if (++(handle->debounce_cnt) >= button_conf->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) {    //start press down
                handle->event = (uint8_t) PRESS_DOWN;
                EVENT_CB(PRESS_DOWN);
                handle->ticks = 0;
                handle->repeat = 1;
                handle->state = 1;
            } else {
                handle->event = (uint8_t) NONE_PRESS;
            }
            break;

        case 1:
            if (handle->button_level != handle->active_level) { //released press up
                handle->event = (uint8_t) PRESS_UP;
                EVENT_CB(PRESS_UP);
                handle->ticks = 0;
                handle->state = 2;

            } else if (handle->ticks > button_conf->long_press_timeout_ticks) {
                handle->event = (uint8_t) LONG_PRESS_START;
                EVENT_CB(LONG_PRESS_START);
                handle->state = 5;
            }
            break;

        case 2:
            if (handle->button_level == handle->active_level) { //press down again
                handle->event = (uint8_t) PRESS_DOWN;
                EVENT_CB(PRESS_DOWN);
                handle->repeat++;
                EVENT_CB(PRESS_REPEAT); // repeat hit
                handle->ticks = 0;
                handle->state = 3;
            } else if (handle->ticks > button_conf->short_press_timeout_ticks) { //released timeout
                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;
            }
            break;

        case 3:
            if (handle->button_level != handle->active_level) { //released press up
                handle->event = (uint8_t) PRESS_UP;
                EVENT_CB(PRESS_UP);
                if (handle->ticks < button_conf->short_press_timeout_ticks) {
                    handle->ticks = 0;
                    handle->state = 2; //repeat press
                } else {
                    handle->state = 0;
                }
            } else if (handle->ticks > button_conf->short_press_timeout_ticks) { // long press up
                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 { //releasd
                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.
  * @param  conf: 对应配置
  * @retval 0: succeed. -1: already exist.
  */
int button_start(struct Button *handle, struct Button_Conf *conf) {
    struct Button *target = head_handle;
    button_conf = conf;
    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;
    if (button_conf == NULL)return;
    for (target = head_handle; target; target = target->next) {
        button_handler(target);
    }
}


测试

驱动使用

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
    @brief 此为按键配置
 ******************************************************************************/
#include "app_conf.h"

#define DBG_ENABLE
#define DBG_SECTION_NAME "btn_cnf"
#define DBG_LEVEL DBG_LOG

#include "sys_dbg.h"
#include "dr_button.h"
#include "multi_button.h"

struct Button button; /*按钮结构体*/
struct Button_Conf btn_cnf = BUTTON_DEFAULT_CONF;
#define BUTTON_PIN stm_port_define(A,0)

static uint8_t read_button_pin() {
    return stm_pin_read(BUTTON_PIN);
}

static void btn_cb(void *arg) {
    struct Button *handle = (struct Button *) arg;

    PressEvent event = get_button_event(handle);

    switch (event) {
        case PRESS_DOWN:
            LOG_D("BUTTON EVENT:PRESS_DOWN");
            break;
        case PRESS_UP:
            LOG_D("BUTTON EVENT:PRESS_UP");
            break;
        case PRESS_REPEAT:
            LOG_D("BUTTON EVENT:PRESS_REPEAT");
            break;
        case SINGLE_CLICK:
            LOG_D("BUTTON EVENT:SINGLE_CLICK");
            break;
        case DOUBLE_CLICK:
            LOG_D("BUTTON EVENT:DOUBLE_CLICK");
            break;
        case LONG_PRESS_START:
            LOG_D("BUTTON EVENT:LONG_PRESS_START");
            break;
        case LONG_PRESS_HOLD:
            LOG_D("BUTTON EVENT:LONG_PRESS_HOLD");
            break;
        case number_of_event:
            LOG_D("BUTTON EVENT:number_of_event");
            break;
        case NONE_PRESS:
            LOG_D("BUTTON EVENT:NONE_PRESS");
            break;
    }

}

static void btn_cnf_pre_init() {
    btn_cnf.debounce_ticks = 4;
    // 初始化
    button_init(&button, read_button_pin, GPIO_PIN_SET);
    // 注册按键事件
//    button_attach(&button, SINGLE_CLICK, btn_cb);
//    button_attach(&button, DOUBLE_CLICK, btn_cb);
//    button_attach(&button, LONG_PRESS_START, btn_cb);
//    button_attach(&button, LONG_PRESS_HOLD, btn_cb);
//    button_attach(&button, PRESS_REPEAT, btn_cb);
    button_attach(&button, PRESS_DOWN, btn_cb);
    button_attach(&button, PRESS_UP, btn_cb);

    //启动按键
    button_start(&button, &btn_cnf);
}

sys_pre_init_export(btn_cnf, btn_cnf_pre_init);

static void btn_cnf_init() {
    stm32_pin_mode(BUTTON_PIN, pin_mode_input);
}

sys_init_export(btn_cnf, btn_cnf_init);

static void btn_cnf_after_init() {

}

sys_after_init_export(btn_cnf, btn_cnf_after_init);

执行循环逻辑 (在RTOS的定时器中执行)

/*******************************************************************************
 Copyright (c) [scl]。保留所有权利。
 ******************************************************************************/
#include "app_task.h"

#if OS_TIMER_LED
#define DBG_ENABLE /*日志是否启用*/
#define DBG_SECTION_NAME "led" /*日志模块名*/
#define DBG_LEVEL DBG_LOG /*日志等级*/

#include "sys_dbg.h"
#include "dr_led.h"
#include "multi_button.h"
/****************************************内部文件使用宏定义*************************************/
#define led_TIMER_TRIGGER 10 /*定时触发:ms(单位)*/
/****************************************外部变量引入****************************************************/
/****************************************变量定义****************************************************/
osTimerId led_timer;

/***************************************************************************************************/

static void led_run(void const *argument);

static void led_timer_create() {
    osTimerDef(led_timer, led_run);
    led_timer = osTimerCreate(osTimer(led_timer), osTimerPeriodic, NULL);

    osTimerStart(led_timer, led_TIMER_TRIGGER);
}

OS_TIMERS_EXPORT(led, led_timer_create);

/****************************************业务逻辑****************************************************/

static void led_run(void const *argument) {
//    LED_Toggle(LED_G);
    button_ticks(); // 执行按键循环检测
}

#endif

测试结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/768058.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构--图的存储邻接表法

数据结构–图的存储邻接表法 邻接矩阵&#xff1a; 数组实现的顺序存储&#xff0c;空间复杂度高&#xff0c;不适合存储稀疏图 邻接表&#xff1a; 顺序链式存储 邻接表法&#xff08;顺序链式存储&#xff09; //边/弧 typedef struct ArcNode {int adjvex; //边/弧指向哪个…

MVX-Net Multimodal VoxelNet for 3D Object Detection 论文学习

论文链接&#xff1a;MVX-Net Multimodal VoxelNet for 3D Object Detection 1. 解决了什么问题&#xff1f; 2D 目标检测取得了显著成效&#xff0c;但由于输入模态的本质区别&#xff0c;CNN 无法直接应用在 3D 检测任务。LiDAR 能准确地定位到 3D 空间的物体&#xff0c;基…

根据对象数组的key进行分组

简单版&#xff1a; const arr [{key: aaa,tableName: bbbbb},{key: aaa,tableName: bbbbb},{key: www,tableName: bbbbb},{key: www,tableName: bbbbb},{key: mysql_ytr,tableName: bbbbb} ]// 把arr按key進行分組&#xff0c; 輸出結果是對象&#xff0c;對象裡面用key做鍵…

logback自定义调用以及文件输出

1、logback 首先五大日志等级是不可更改的&#xff0c;我们所定义的日志输出和调用也是用的这五大等级&#xff0c;这个就不多说了&#xff0c;没啥用。 2、效果 调用 String msg "测试日志"; MyLoger.myloger(msg); 输出 2023-07-18 10:55:05 [main] INFO m…

[element-ui] el-select,虚拟滚动(vue-virtual-scroll-list)

一、问题描述 表单中某下拉框&#xff0c;由于数据过多&#xff0c;选择的时候会因为数据量过大导致页面卡顿&#xff0c;于是对于el-select进行二次封装&#xff0c;实现虚拟滚动。 二、实现如下&#xff1a; 看起来是加载了全部数据&#xff0c;实际上只加载了自己设定的1…

单例模式类设计|什么是饿汉模式和懒汉模式

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量干货博客汇总https://blog.csdn.net/yu_cblog/c…

汇编实现1-100累加(ARMv7)

汇编实现1-100累加 代码.text .globl _start _start:mov r0,#0 summationmov r1,#0 autoIncrementbl funadd funadd:cmp r1,#100addccs r0,r1,r0addccs r1,r1,#1mov pc,lr stop:b stop .end运行效果

请问如何用oracle触发器实现不允许新增/删除表/增加/减少/修改字段类型

请问如何用oracle触发器实现不允许新增/删除表/增加/减少/修改字段类型 给本帖投票 56211打赏收藏 分享 转发到动态举报 写回复 性能测试中发现oracle11g数据库每天22点,oralce进程CPU占用率突增>> 11 条回复 切换为时间正序 请发表友善的回复… 发表回复 microsof…

UWB天线设计之一种优化扁平超宽带单极的新方法

文章亮点 一种新颖的方法提出了扁平超宽带单极天线。 通过应用收缩,可以设计具有相同性能的更小的天线作为平面单极天线。 优化结果表明该方法能够设计非常小的天线。 在这项研究中,提出了一种优化扁平超宽带单极天线的新方法。在该方法中,一般性地描述了天线的形状。这种一…

Http 接口测试框架

目录 前言&#xff1a; 实际效果 框架的下一步 最新框架图&#xff08;红色部分未完成&#xff09; 部分代码 你需要做的 前言&#xff1a; 在进行HTTP接口测试时&#xff0c;使用一个可靠的测试框架可以提高测试效率和质量。HTTP接口测试框架是一种用于自动化测试HTTP接…

STM32数字小键盘

基于STM32的数字小键盘 自己的键盘小键盘数字键坏了几个&#xff0c;准备自己用STM32做一个数字键盘。 硬件 找了一些资料&#xff0c;感谢知乎老哥。 原理图 采用的是主控是STM32F103RBT6&#xff0c;上拉1.5K接高速USB。按键采用的是矩阵按键。轴位可以自己选择。还接了一…

OpenCv之图像轮廓

目录 一、图像轮廓定义 二、绘制轮廓 三、计算轮廓面积与周长 一、图像轮廓定义 图像轮廓是具有相同颜色或灰度的连续带你的曲线.轮廓在形状分析和物体的检测和识别中很有用 轮廓的作用: 用于图形分析物体的识别与检测 注意点: 为了检测的准确性&#xff0c;需要先对图像…

AI 救不了好莱坞,16 万演员联合编剧上演史诗级「罢工大戏」

内容一览&#xff1a; 近日&#xff0c;美国演员工会正式加入编剧工会行列&#xff0c;开始举行罢工&#xff0c;由此&#xff0c;被多家媒体称为好莱坞「末日」时刻来临。值得关注的是&#xff0c;本次罢工的原因&#xff0c;除了老生常谈的薪资问题&#xff0c;还有一个重要的…

多用户商城系统Dokan评测优点与缺点(2023)

目录 多用户商城系统Dokan优点 多用户商城系统Dokan缺点 您应该开始使用多供应商市场吗&#xff1f; 多用户商城系统Dokan评论 为什么选择Dokan&#xff1f; 用户界面 用户友好的前端 仪表板和后端 管理员后台 供应商仪表板 第三方兼容性 Dokan 可以卖什么&…

c#调用cpp库,debug时不进入cpp函数

选中c#的项目&#xff0c;右击属性&#xff0c;进入属性页&#xff0c;点击调试&#xff0c;点击打开调试启动配置文件UI&#xff0c;打开启用本机代码调试。

Scrapy和Selenium整合(一文搞定)

文章目录 前言一、开始准备1. 包管理和安装chrome驱动2. 爬虫项目的创建&#xff08;举个栗子&#xff09;3. setting.py的配置 二、代码演示1. 主爬虫程序2. 中间件的配置3. 定义item对象4. 定义管道 总结 前言 scrapy和selenium的整合使用 先定个小目标实现万物皆可爬&#…

Loadrunner结合Fiddler实现脚本的录制

Loadrunner一直被业内认为是最好用的性能测试工具&#xff0c;行业大哥大, 但是用过Loadrunner的朋友都知道&#xff0c;工具功能的确牛&#xff0c;但实际使用过程中总会有一些困扰新手的问题&#xff0c;无法录制脚本&#xff0c; 如遇到Loadrunner不支持的IE版本、对Chrome、…

【C语言】Sleep()函数----详解

&#x1f341; 博客主页:江池俊的博客 &#x1f341;收录专栏&#xff1a;C语言——探索高效编程的基石 &#x1f341; 如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏&#x1f31f; 三连支持一下博主&#x1f49e; 目录 前言 &#x1f4cc;C语言sleep函…

一本通1919:【02NOIP普及组】选数

这道题感觉很好玩。 正文&#xff1a; 先放题目&#xff1a; 信息学奥赛一本通&#xff08;C版&#xff09;在线评测系统 (ssoier.cn)http://ybt.ssoier.cn:8088/problem_show.php?pid1919 描述 已知 n 个整数 x1,x2,…,xn&#xff0c;以及一个整数 k&#xff08;k&#…

Docker 容器生命周期:创建、启动、暂停与停止----从创建到停止多角度分析

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…