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