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

原始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

测试结果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(四)

UIAbility组件与UI的数据同步 基于HarmonyOS的应用模型&#xff0c;可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步。 1.EventHub&#xff1a;基于发布订阅模式来实现&#xff0c;事件需要先订阅后发布&#xff0c;订阅者收到消息后进行处理。 2.globalThis&…

MySQL日常操作记录

1.查看MySQL版本 select version();2.快速复制表结构&#xff0c;不包含相关主键及约束 create table user_test as select * from user where 12;3.uuid select uuid(),uuid_short();4.替换uuid()里的’-‘为’’ select replace(uuid(),-,);5.md5摘要 select md5(uuid()…

HBase(一)HBase v2.2 高可用多节点搭建

最近刚刚完成了HBase相关的一个项目,作为项目的技术负责人,完成了大部分的项目部署,特性调研工作,以此系列文章作为上一阶段工作的总结. 前言 其实目前就大多数做应用的情况来讲,我们并不需要去自己搭建一套HBase的集群,现有的很多云厂商提供的服务已经极大的方便日常的应用使…

rce题目

<?php include "flag.php"; highlight_file(__FILE__); if(isset($_GET[HECTF])) { if (; preg_replace(/[^\W]\((?R)?\)/, NULL, $_GET[HECTF])) { if (!preg_match(/pos|high|op|na|info|dec|hex|oct|pi/i, $_GET[HECTF])) { eval(…

NSSCTF刷web(2)

[NISACTF 2022]bingdundun~ bingdundun处感觉像文件包含,改upload为index 发现确实,猜测会补一个后缀.php 那常规文件包含都不行了,这里还有一个文件上传的功能,考虑phar协议 <?php$phar new Phar("test.phar"); $phar->startBuffering(); $phar->setStu…

C++入门学习(2)

思维导图&#xff1a; 一&#xff0c;缺省参数 如何理解缺省参数呢&#xff1f;简单来说&#xff0c;缺省参数就是一个会找备胎的参数&#xff01;为什么这样子说呢&#xff1f;来看一个缺省参数就知道了&#xff01;代码如下&#xff1a; #include<iostream> using std…

算法与数据结构-排序

文章目录 一、如何分析一个排序算法1.1 排序算法的执行效率1.1.1 最好情况、最坏情况、平均情况时间复杂度1.1.1.1 最好、最坏情况分析1.1.1.2 平均情况分析 1.1.2 时间复杂度的系数、常数 、低阶1.1.3 比较次数和交换&#xff08;或移动&#xff09;次数 1.2 排序算法的内存消…

Go语言之重要数组类型map(映射)类型

通过切片&#xff0c;我们可以动态灵活存储管理学生姓名、年龄等信息&#xff0c;比如 names : []string{"张三","李四","王五"} ages : []int{23,24,25} fmt.Println(names) fmt.Println(ages)但是如果我想获取张三的年龄&#xff0c;这是一个…

C# 同构字符串

205 同构字符串 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符的顺序。不同字符不能映射到同一个字符…

详细介绍MATLAB中的图论算法

MATLAB是一种功能强大的编程语言和环境,提供了许多用于图论算法的工具和函数。图论是研究图及其属性和关系的数学分支,广泛应用于计算机科学、网络分析、社交网络分析等领域。在MATLAB中,我们可以使用图论算法来解决各种问题,如最短路径问题、最小生成树问题、最大流问题等…

国产MCU-CW32F030开发学习-BH1750模块

国产MCU-CW32F030开发学习-BH1750模块 硬件平台 CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件BH1750数字型光照强度传感器 BH1750 BH1750是一款数字型光照强度传感器&#xff0c;能够获取周围环境的光照强度。其测量范围在0~65535 lx。lx勒克斯&#xff0c;是光照强…

【电路效应】信号处理和通信系统模型中的模拟电路效应研究(SimulinkMatlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 在信号处理和通信系统模型中&#xff0c;模拟电路效应研究是指考虑到实际电路的特性对信号进行建模和分析的过程。模拟电路效应…

Kubernetes—集群故障排查

一、用 Kubectl 调试 Kubernetes 节点 准备开始 你必须拥有一个 Kubernetes 的集群&#xff0c;同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 你的 Kubernetes 服务器版本必须不低于版本 1.2. 要获知版…

C#基础--委托

C#基础–委托 C#基础–委托 简单说它就是一个能把方法当参数传递的对象&#xff0c;而且还知道怎么调用这个方法&#xff0c;同时也是粒度更小的“接口”&#xff08;约束了指向方法的签名&#xff09; 一、什么是委托&#xff0c;委托的本质是什么&#xff1f; 跟方法有点类似…

云迁移第二波热潮来袭,你准备好了吗?

最近&#xff0c;云迁移再次被频繁提及&#xff0c;企业对云迁移的需求量有回升趋势&#xff0c;究其根本&#xff0c;主要有以下原因&#xff1a; 企业数字化进程加速&#xff0c;本地上云需求强劲 根据《2021中国企业上云指数洞察报告》&#xff0c;我国实体经济上云渗透率…

深入理解Java虚拟机(二)Java内存区域与内存溢出异常

一、前言 对于Java程序员来说&#xff0c;在虚拟机自动内存管理机制的帮助下&#xff0c;不再需要为每一个new操作去写配对的delete/free代码&#xff0c;不容易出现内存泄漏和内存溢出问题&#xff0c;看起来由虚拟机管理内存一切都很美好。不过&#xff0c;也正是因为Java程序…

kafka接收外部接口的数据,并实现转发

目录 一、什么是kafka 二、kafka接收外部接口数据 三、kafka收到数据后转发 四、kafka总结 一、什么是kafka Kafka是一种分布式流式处理平台&#xff0c;最初由LinkedIn开发。它设计用于高吞吐量、低延迟的数据处理&#xff0c;能够处理大规模的实时数据流。Kafka采用发布…

关系型数据库设计规则

目录 1.1 表、记录、字段 1.2 表的关联关系 1.2.1 一对一关联&#xff08;one-to-one&#xff09; 1.2.2 一对多关系&#xff08;one-to-many&#xff09; 1.2.3 多对多&#xff08;many-to-many&#xff09; 1.2.4 自我引用&#xff08;Self reference&#xff09; 关系…

用Python采用Modbus-Tcp的方式读取485电子水尺数据

README.TXT 2023/6/15 V1.0 实现了单个点位数据通信、数据解析、数据存储 2023/6/17 V2.0 实现了多个点位数据通信、数据解析、数据存储 2023/6/19 V2.1 完善log存储&#xff0c;仅保留近3天的log记录&#xff0c;避免不必要的存储&#xff1b;限制log大小&#xff0c;2MB。架…

基于Redisson的Redis结合布隆过滤器使用

一、场景 缓存穿透问题 一般情况下&#xff0c;先查询Redis缓存&#xff0c;如果Redis中没有&#xff0c;再查询MySQL。当某一时刻访问redis的大量key都在redis中不存在时&#xff0c;所有查询都要访问数据库&#xff0c;造成数据库压力顿时上升&#xff0c;这就是缓存穿透。…