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 
 * 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

测试结果

STM32之按键驱动的使用和自定义(MultiButton)_第1张图片

你可能感兴趣的:(stm32,驱动开发,嵌入式硬件)