LuatOS ESP32C3 > 按键驱动编写

1.创建新的组件

创建新的组件,命名为"swl_button",功能包括单击,多击,长按(起始,周期触发,结束)

swl_button.h

/**
 * @file swl_button.h
 * @author JohnWay (1973372075@qq.com)
 * @brief 
 * @version 0.1
 * @date 2023-11-25
 * 
 * @copyright Copyright (c) 2023 JohnWay
 * SPDX-License-Identifier: Apache-2.0
 * encoding: utf-8
 * 
 */
#ifndef SWL_BUTTON_H
#define SWL_BUTTON_H

#ifdef __cplusplus
extern "C" {
#endif

#include "../../swl_comm.h"

#if defined(SWL_USING_BUTTON) || defined(CONFIG_SWL_USING_BUTTON)

typedef enum swl_button_event swl_button_event_t;
enum swl_button_event {
    eSwlButtonEventRelease = 0,
    eSwlButtonEventPress,
    eSwlButtonEventClick,
    eSwlButtonEventMultipleClick,
    eSwlButtonEventHoldBegin,
    eSwlButtonEventHoldCycle,
    eSwlButtonEventHoldEnd,
    
    eSwlButtonEventUnknow = 255,
};

typedef struct swl_button_public swl_button_public_t;
struct swl_button_public {
    void (*start)(swl_button_public_t *self, uint32_t tick);
    uint32_t (*get_tick)(swl_button_public_t *self);
    swl_button_event_t (*get_event)(swl_button_public_t *self);
    uint8_t (*get_multiple_click)(swl_button_public_t *self);
    uint32_t (*get_release_count)(swl_button_public_t *self);
    uint32_t (*get_press_count)(swl_button_public_t *self);
};

swl_button_public_t *swl_button_create(void (*hw_init)(void), uint8_t (*read_io_status)(void), uint8_t trigger_level);

#endif /* SWL_USING_BUTTON */

#ifdef __cplusplus
}
#endif

#endif /* SWL_BUTTON_H */

swl_button.c

/**
 * @file swl_button.c
 * @author JohnWay (1973372075@qq.com)
 * @brief 
 * @version 0.1
 * @date 2023-11-25
 * 
 * @copyright Copyright (c) 2023 JohnWay
 * SPDX-License-Identifier: Apache-2.0
 * encoding: utf-8
 * 
 */
#include "swl_button.h"

#if defined(SWL_USING_BUTTON) || defined(CONFIG_SWL_USING_BUTTON)

typedef struct swl_button_private swl_button_private_t;
struct swl_button_private {
    swl_button_public_t pub;
    uint32_t tick;
    uint32_t press_count;
    uint32_t release_count;
    uint8_t multiple_click_count;
    swl_button_event_t event;
    uint8_t trigger_level;
    uint8_t io_status;
    uint8_t (*read_io_status)(void);
};

SWL_METHOD(swl_button_public_t, get_tick, uint32_t, swl_button_private_t *self)
{
    return self->tick;
}

SWL_METHOD(swl_button_public_t, start, void, swl_button_private_t *self, uint32_t tick)
{
    if (self->tick != tick) {
        self->tick = tick;
        
        /* 读取IO状态 */
        self->io_status = self->read_io_status();
        /* 判断IO状态 */
        if (self->io_status == self->trigger_level) {
            /* 按下按键 */
            /* 按键按下计数 */
            self->press_count++;
            /* 重置释放计数 */
            self->release_count = 0;
            /* 判断按下计数*/
            if (self->press_count == SWL_BUTTON_HOLD_TIME) {
                /* 计数等于长按时间,设置长按事件 */
                self->event = eSwlButtonEventHoldBegin;
            } else if ((self->press_count > SWL_BUTTON_HOLD_TIME) && ((self->press_count % SWL_BUTTON_HOLD_CYCLE_TIME) == 0)) {
                /* 计数大于长按时间,并且每SWL_BUTTON_HOLD_CYCLE_TIME设置长按事件(周期) */
                self->event = eSwlButtonEventHoldCycle;
            } else {
                /* 否则,设置按键按下事件 */
                self->event = eSwlButtonEventPress;
            }
        } else {
            /* 释放按键 */
            /* 按键释放计数,用于重置连击计数 */
            self->release_count++;
            /* 判断按下的计数是否小于长按时间 */
            if (self->press_count < SWL_BUTTON_HOLD_TIME) {
                if (self->press_count > 0) {
                    /* 否则,设置连击计数 */
                    self->multiple_click_count++;
                    /* 判断连击计数 */
                    if (self->multiple_click_count <= 1) {
                        /* 计数小于等于1,设置单击事件 */
                        self->event = eSwlButtonEventClick;
                    } else {
                        /* 计数大于1,设置多击事件 */
                        self->event = eSwlButtonEventMultipleClick;
                    }
                } else {
                    /* 设置按键释放事件 */
                    self->event = eSwlButtonEventRelease;
                }
            } else {
                /* 计数大于长按时间,设置长按事件结束 */
                self->event = eSwlButtonEventHoldEnd;
            }
            /* 重置计数器,以便下次按键计数时从0开始 */
            self->press_count = 0;
            if (self->release_count >= SWL_BUTTON_MULTIPLE_CLICK_RESET_TIME) {
                self->release_count = 0;
                self->multiple_click_count = 0;
            }
        }
    }
}

SWL_METHOD(swl_button_public_t, get_event, swl_button_event_t, swl_button_private_t *self)
{
    return self->event;
}

SWL_METHOD(swl_button_public_t, get_multiple_click, uint8_t, swl_button_private_t *self)
{
    return self->multiple_click_count;
}

SWL_METHOD(swl_button_public_t, get_release_count, uint32_t, swl_button_private_t *self)
{
    return self->release_count;
}

SWL_METHOD(swl_button_public_t, get_press_count, uint32_t, swl_button_private_t *self)
{
    return self->press_count;
}

swl_button_public_t *swl_button_create(void (*hw_init)(void), uint8_t (*read_io_status)(void), uint8_t trigger_level)
{
    SWL_ASSERT(hw_init != NULL);
    SWL_ASSERT(read_io_status != NULL);

    swl_button_private_t *self = (swl_button_private_t *)malloc(sizeof(swl_button_private_t));
    SWL_ASSERT(self != NULL);

    self->pub.start = _start;
    self->pub.get_tick = _get_tick;
    self->pub.get_event = _get_event;
    self->pub.get_multiple_click = _get_multiple_click;
    self->pub.get_release_count = _get_release_count;
    self->pub.get_press_count = _get_press_count;

    self->read_io_status = read_io_status;
    self->trigger_level = trigger_level;
    self->io_status = !trigger_level;

    self->tick = 0;
    self->press_count = 0;
    self->release_count = 0;
    self->multiple_click_count = 0;
    self->event = eSwlButtonEventUnknow;

    hw_init();

    return &self->pub;
}

#endif /* SWL_USING_BUTTON */

swl_button/kconfig

menu "swl button configuration"
    config SWL_USING_BUTTON
        bool "swl using button"
        default y

    if SWL_USING_BUTTON
        config SWL_BUTTON_HOLD_TIME
            int "swl button hold time"
            range 0 65535
            default 2000

        config SWL_BUTTON_HOLD_CYCLE_TIME
            int "swl button hold cycle time"
            range 0 65535
            default 500

        config SWL_BUTTON_MULTIPLE_CLICK_TIME
            int "swl button multiple click time"
            range 0 65535
            default 200

        config SWL_BUTTON_MULTIPLE_CLICK_RESET_TIME
            int "swl button multiple click reset time"
            range 0 65535
            default 500
    endif
endmenu

kconfig编写后可以在 "ESP-IDF Terminal" 中输入 "idf.py menuconfig" (这里 idf.py 的路径已经在系统环境变量值配置)

Component config ---> 下,往下拉到底即可看

2.使用 

main.c

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "assert.h"
#include "swl_io_toggle.h"
#include "swl_button.h"
#include "air101_lcd.h"

static const char *TAG = "[main.c]";

#define D4_PIN GPIO_NUM_12

#define UPKEY GPIO_NUM_5
#define DWKEY GPIO_NUM_9
#define LKEY GPIO_NUM_13
#define RKEY GPIO_NUM_8
#define CENTER GPIO_NUM_4

swl_io_toggle_public_t *swl_io_toggle;
swl_button_public_t *swl_button_up, *swl_button_down, *swl_button_left, *swl_button_right, *swl_button_center;
air101_lcd_public_t *air101_lcd;

static void periodic_timer_callback(void* arg);
static void swl_io_toggle_hw_init(void);
static void swl_io_toggle_on(void);
static void swl_io_toggle_off(void);
static void swl_button_up_hw_init(void);
static uint8_t swl_button_up_read(void);
static void swl_button_down_hw_init(void);
static uint8_t swl_button_down_read(void);
static void swl_button_left_hw_init(void);
static uint8_t swl_button_left_read(void);
static void swl_button_right_hw_init(void);
static uint8_t swl_button_right_read(void);
static void swl_button_center_hw_init(void);
static uint8_t swl_button_center_read(void);

void app_main(void)
{
    swl_io_toggle = swl_io_toggle_create(swl_io_toggle_hw_init, swl_io_toggle_on, swl_io_toggle_off, 50, 50);
    swl_button_up = swl_button_create(swl_button_up_hw_init, swl_button_up_read, 0);
    swl_button_down = swl_button_create(swl_button_down_hw_init, swl_button_down_read, 0);
    swl_button_left = swl_button_create(swl_button_left_hw_init, swl_button_left_read, 0);
    swl_button_right = swl_button_create(swl_button_right_hw_init, swl_button_right_read, 0);
    swl_button_center = swl_button_create(swl_button_center_hw_init, swl_button_center_read, 0);

    const esp_timer_create_args_t periodic_timer_args = {
            .callback = &periodic_timer_callback,
            .name = "periodic"
    };
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 10000)); // 500000us

    while (1) {
        switch (swl_button_up->get_event(swl_button_up)) {
            case eSwlButtonEventClick:
                ESP_LOGI(TAG, "up key clicked");
                break;
            case eSwlButtonEventMultipleClick:
                ESP_LOGI(TAG, "up key multiple click: %d", swl_button_up->get_multiple_click(swl_button_up));
                break;
            case eSwlButtonEventHoldBegin:
                ESP_LOGI(TAG, "up key hold begin");
                break;
            case eSwlButtonEventHoldCycle:
                ESP_LOGI(TAG, "up key hold cycle");
                break;
            case eSwlButtonEventHoldEnd:
                ESP_LOGI(TAG, "up key hold end");
                break;
            default: break;
        }

        switch (swl_button_down->get_event(swl_button_down)) {
            case eSwlButtonEventClick:
                ESP_LOGI(TAG, "down key clicked");
                break;
            case eSwlButtonEventMultipleClick:
                ESP_LOGI(TAG, "down key multiple click: %d", swl_button_down->get_multiple_click(swl_button_down));
                break;
            case eSwlButtonEventHoldBegin:
                ESP_LOGI(TAG, "down key hold begin");
                break;
            case eSwlButtonEventHoldCycle:
                ESP_LOGI(TAG, "down key hold cycle");
                break;
            case eSwlButtonEventHoldEnd:
                ESP_LOGI(TAG, "down key hold end");
                break;
            default: break;
        }

        switch (swl_button_left->get_event(swl_button_left)) {
            case eSwlButtonEventClick:
                ESP_LOGI(TAG, "left key clicked");
                break;
            case eSwlButtonEventMultipleClick:
                ESP_LOGI(TAG, "left key multiple click: %d", swl_button_left->get_multiple_click(swl_button_left));
                break;
            case eSwlButtonEventHoldBegin:
                ESP_LOGI(TAG, "left key hold begin");
                break;
            case eSwlButtonEventHoldCycle:
                ESP_LOGI(TAG, "left key hold cycle");
                break;
            case eSwlButtonEventHoldEnd:
                ESP_LOGI(TAG, "left key hold end");
                break;
            default: break;
        }

        switch (swl_button_right->get_event(swl_button_right)) {
            case eSwlButtonEventClick:
                ESP_LOGI(TAG, "right key clicked");
                break;
            case eSwlButtonEventMultipleClick:
                ESP_LOGI(TAG, "right key multiple click: %d", swl_button_right->get_multiple_click(swl_button_right));
                break;
            case eSwlButtonEventHoldBegin:
                ESP_LOGI(TAG, "right key hold begin");
                break;
            case eSwlButtonEventHoldCycle:
                ESP_LOGI(TAG, "right key hold cycle");
                break;
            case eSwlButtonEventHoldEnd:
                ESP_LOGI(TAG, "right key hold end");
                break;
            default: break;
        }

        switch (swl_button_center->get_event(swl_button_center)) {
            case eSwlButtonEventClick:
                ESP_LOGI(TAG, "center key clicked");
                break;
            case eSwlButtonEventMultipleClick:
                ESP_LOGI(TAG, "center key multiple click: %d", swl_button_center->get_multiple_click(swl_button_center));
                break;
            case eSwlButtonEventHoldBegin:
                ESP_LOGI(TAG, "center key hold begin");
                break;
            case eSwlButtonEventHoldCycle:
                ESP_LOGI(TAG, "center key hold cycle");
                break;
            case eSwlButtonEventHoldEnd:
                ESP_LOGI(TAG, "center key hold end");
                break;
            default: break;
        }

        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

static void periodic_timer_callback(void* arg)
{
    int64_t time_since_boot = esp_timer_get_time();
    swl_io_toggle->toggle(swl_io_toggle, time_since_boot);

    swl_button_up->start(swl_button_up, time_since_boot);
    swl_button_down->start(swl_button_down, time_since_boot);
    swl_button_left->start(swl_button_left, time_since_boot);
    swl_button_right->start(swl_button_right, time_since_boot);
    swl_button_center->start(swl_button_center, time_since_boot);
}

static void swl_io_toggle_hw_init(void)
{
    gpio_reset_pin(D4_PIN);
    gpio_set_direction(D4_PIN, GPIO_MODE_OUTPUT);
}

static void swl_io_toggle_on(void)
{
    gpio_set_level(D4_PIN, 1);
}

static void swl_io_toggle_off(void)
{
    gpio_set_level(D4_PIN, 0);
}

static void swl_button_up_hw_init(void)
{
    gpio_reset_pin(UPKEY);
    gpio_set_direction(UPKEY, GPIO_MODE_INPUT);
}
static uint8_t swl_button_up_read(void)
{
    return gpio_get_level(UPKEY);
}

static void swl_button_down_hw_init(void)
{
    gpio_reset_pin(DWKEY);
    gpio_set_direction(DWKEY, GPIO_MODE_INPUT);
}
static uint8_t swl_button_down_read(void)
{
    return gpio_get_level(DWKEY);
}

static void swl_button_left_hw_init(void)
{
    gpio_reset_pin(LKEY);
    gpio_set_direction(LKEY, GPIO_MODE_INPUT);
}
static uint8_t swl_button_left_read(void)
{
    return gpio_get_level(LKEY);
}

static void swl_button_right_hw_init(void)
{
    gpio_reset_pin(RKEY);
    gpio_set_direction(RKEY, GPIO_MODE_INPUT);
}
static uint8_t swl_button_right_read(void)
{
    return gpio_get_level(RKEY);
}

static void swl_button_center_hw_init(void)
{
    gpio_reset_pin(CENTER);
    gpio_set_direction(CENTER, GPIO_MODE_INPUT);
}
static uint8_t swl_button_center_read(void)
{
    return gpio_get_level(CENTER);
}

#define UPKEY GPIO_NUM_5
#define DWKEY GPIO_NUM_9
#define LKEY GPIO_NUM_13
#define RKEY GPIO_NUM_8
#define CENTER GPIO_NUM_4

这些按键引脚的定义是根据下图方向定义的

 3.测试结果

4.配置参数

components/swl_comm.h

/**
 * @file comm.h
 * @author JohnWay (1973372075@qq.com)
 * @brief 
 * @version 0.1
 * @date 2024-01-15
 * 
 * @copyright Copyright (c) 2024 JohnWay
 * SPDX-License-Identifier: Apache-2.0
 * encoding: utf-8
 * 
 */

#ifndef COMM_H
#define COMM_H

#include "../build/config/sdkconfig.h"
#include <assert.h>
#include <stdint.h>

#ifdef __cplusplus
    #define TEMPORARY decltype
#else
    #define TEMPORARY __typeof__
#endif

/**
 * Method declaration/definition macro, providing private and public interface.
 *
 * Defines a method name with this as first parameter and a return value ret,
 * and an alias for this method with a _ prefix, having the this argument
 * safely casted to the public interface iface.
 * _name is provided a function pointer, but will get optimized out by GCC.
 */
#define METHOD(iface, name, ret, this, ...)                    \
                                                               \
    static ret name(                                           \
        union {iface *_public; this; }                         \
        __attribute__((transparent_union)),                    \
        ##__VA_ARGS__);                                        \
                                                               \
    static TEMPORARY(name) *_##name = (TEMPORARY(name) *)name; \
                                                               \
    static ret name(this, ##__VA_ARGS__)

#define SWL_METHOD METHOD
#define SWL_ASSERT assert

#define SWL_BUTTON_HOLD_TIME CONFIG_SWL_BUTTON_HOLD_TIME
#define SWL_BUTTON_HOLD_CYCLE_TIME CONFIG_SWL_BUTTON_HOLD_CYCLE_TIME
#define SWL_BUTTON_MULTIPLE_CLICK_TIME CONFIG_SWL_BUTTON_MULTIPLE_CLICK_TIME
#define SWL_BUTTON_MULTIPLE_CLICK_RESET_TIME CONFIG_SWL_BUTTON_MULTIPLE_CLICK_RESET_TIME

#endif

因为定时器配置了10ms的计时

const esp_timer_create_args_t periodic_timer_args = {
            .callback = &periodic_timer_callback,
            .name = "periodic"
    };
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, 10000)); // 10000us

所以参数(ms)为单位,如:按键的长按触发时间 200*10 = 2000ms

以上五项分别是

- 使用按键组件的宏

- 按键长按的触发时间

- 按键长按的周期触发时间

- 按键多击的间隔触发时间

- 按键多击计数的清除时间

5.之前blink组件的重写

# IO Toggle

- 用于一些单 IO 翻转的设备

- 如:LED闪烁,蜂鸣器等

所以改为 io_toggle

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值