├── .gitignore ├── .tmp ├── multi_button copy.h ├── multi_button_config.h └── new_example.c ├── LICENSE ├── Makefile ├── README.md ├── examples ├── advanced_example.c ├── basic_example.c └── poll_example.c ├── multi_button.c └── multi_button.h /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /.tmp/multi_button copy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zibin Zheng 3 | * All rights reserved 4 | */ 5 | 6 | #ifndef _MULTI_BUTTON_H_ 7 | #define _MULTI_BUTTON_H_ 8 | 9 | #include 10 | #include 11 | 12 | // Configuration constants - can be modified according to your needs 13 | #define TICKS_INTERVAL 5 // ms - timer interrupt interval 14 | #define DEBOUNCE_TICKS 3 // MAX 7 (0 ~ 7) - debounce filter depth 15 | #define SHORT_TICKS (300 / TICKS_INTERVAL) // short press threshold 16 | #define LONG_TICKS (1000 / TICKS_INTERVAL) // long press threshold 17 | #define PRESS_REPEAT_MAX_NUM 15 // maximum repeat counter value 18 | 19 | // Button event types 20 | typedef enum 21 | { 22 | BTN_PRESS_DOWN = 0, // button pressed down 23 | BTN_PRESS_UP, // button released 24 | BTN_PRESS_REPEAT, // repeated press detected 25 | BTN_SINGLE_CLICK, // single click completed 26 | BTN_DOUBLE_CLICK, // double click completed 27 | BTN_LONG_PRESS_START, // long press started 28 | BTN_LONG_PRESS_HOLD, // long press holding 29 | BTN_EVENT_COUNT, // total number of events 30 | BTN_NONE_PRESS // no event 31 | } ButtonEvent; 32 | 33 | // Button state machine states 34 | typedef enum 35 | { 36 | BTN_STATE_IDLE = 0, // idle state 37 | BTN_STATE_PRESS, // pressed state 38 | BTN_STATE_RELEASE, // released state waiting for timeout 39 | BTN_STATE_REPEAT, // repeat press state 40 | BTN_STATE_LONG_HOLD // long press hold state 41 | } ButtonState; 42 | 43 | 44 | typedef void (*MultiButtonCallback)(uint16_t buttonId, ButtonEvent event, void *userData); 45 | 46 | typedef struct 47 | { 48 | uint16_t ticks; // tick counter 49 | uint8_t repeat : 4; // repeat counter (0-15) 50 | uint8_t event : 4; // current event (0-15) 51 | uint8_t state : 3; // state machine state (0-7) 52 | uint8_t debounce_cnt : 3; // debounce counter (0-7) 53 | uint8_t active_level : 1; // active GPIO level (0 or 1) 54 | uint8_t button_level : 1; // current button level 55 | uint8_t buttonId; // button identifier 56 | uint8_t (*hal_button_level)(uint16_t buttonId); // HAL function to read GPIO 57 | MultiButtonCallback callback; // callback function array 58 | void *userData; // user data pointer 59 | } MultiButton; 60 | 61 | typedef struct 62 | { 63 | uint16_t ticksInterval; 64 | uint16_t debounceTicks; 65 | uint16_t shortTicks; 66 | uint16_t longTicks; 67 | uint16_t pressRepeatMaxNum; 68 | const MultiButton *const *buttonList; 69 | uint16_t buttonCount; 70 | } MultiButtonConfig; 71 | 72 | typedef struct 73 | { 74 | MultiButtonConfig config; 75 | } MultiButtonContext; 76 | 77 | #ifdef __cplusplus 78 | extern "C" 79 | { 80 | #endif 81 | 82 | int multiButtonInstall(MultiButtonConfig *config); 83 | 84 | #ifdef __cplusplus 85 | } 86 | #endif 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /.tmp/multi_button_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiButton Configuration Header 3 | * This file allows customization of button library behavior 4 | */ 5 | 6 | #ifndef _MULTI_BUTTON_CONFIG_H_ 7 | #define _MULTI_BUTTON_CONFIG_H_ 8 | 9 | // Timing configuration (in milliseconds) 10 | #ifndef TICKS_INTERVAL 11 | #define TICKS_INTERVAL 5 // Timer interrupt interval 12 | #endif 13 | 14 | #ifndef DEBOUNCE_TIME_MS 15 | #define DEBOUNCE_TIME_MS 15 // Debounce time 16 | #endif 17 | 18 | #ifndef SHORT_PRESS_TIME_MS 19 | #define SHORT_PRESS_TIME_MS 300 // Single/double click threshold 20 | #endif 21 | 22 | #ifndef LONG_PRESS_TIME_MS 23 | #define LONG_PRESS_TIME_MS 1000 // Long press threshold 24 | #endif 25 | 26 | // Calculated tick values 27 | #define DEBOUNCE_TICKS (DEBOUNCE_TIME_MS / TICKS_INTERVAL) 28 | #define SHORT_TICKS (SHORT_PRESS_TIME_MS / TICKS_INTERVAL) 29 | #define LONG_TICKS (LONG_PRESS_TIME_MS / TICKS_INTERVAL) 30 | 31 | // Functional configuration 32 | #ifndef PRESS_REPEAT_MAX_NUM 33 | #define PRESS_REPEAT_MAX_NUM 15 // Maximum repeat counter value 34 | #endif 35 | 36 | #ifndef ENABLE_TRIPLE_CLICK 37 | #define ENABLE_TRIPLE_CLICK 0 // Enable triple click detection 38 | #endif 39 | 40 | #ifndef ENABLE_QUADRUPLE_CLICK 41 | #define ENABLE_QUADRUPLE_CLICK 0 // Enable quadruple click detection 42 | #endif 43 | 44 | // Memory optimization 45 | #ifndef BUTTON_USE_STATIC_ALLOCATION 46 | #define BUTTON_USE_STATIC_ALLOCATION 1 // Use static allocation for buttons 47 | #endif 48 | 49 | #ifndef MAX_BUTTON_COUNT 50 | #define MAX_BUTTON_COUNT 8 // Maximum number of buttons (if static allocation) 51 | #endif 52 | 53 | // Debug configuration 54 | #ifndef BUTTON_DEBUG 55 | #define BUTTON_DEBUG 0 // Enable debug output 56 | #endif 57 | 58 | // Validation macros 59 | #if DEBOUNCE_TICKS > 7 60 | #error "DEBOUNCE_TICKS must be <= 7 (limited by 3-bit field)" 61 | #endif 62 | 63 | #if PRESS_REPEAT_MAX_NUM > 15 64 | #error "PRESS_REPEAT_MAX_NUM must be <= 15 (limited by 4-bit field)" 65 | #endif 66 | 67 | #endif // _MULTI_BUTTON_CONFIG_H_ -------------------------------------------------------------------------------- /.tmp/new_example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiButton Library Basic Example 3 | * This example demonstrates basic usage of the optimized MultiButton library 4 | */ 5 | 6 | #include "multi_button.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Button instances 13 | static Button btn1, btn2; 14 | static volatile int running = 1; 15 | 16 | static MultiButton buttonList[] = { 17 | { 18 | .button_id = 1, 19 | .hal_button_level = read_button_gpio, 20 | .active_level = 1, 21 | .button_level = 0, 22 | .callback = { 23 | .single_click = btn1_single_click_handler, 24 | .double_click = btn1_double_click_handler, 25 | .long_press_start = btn1_long_press_start_handler, 26 | .long_press_hold = btn1_long_press_hold_handler, 27 | .press_repeat = btn1_press_repeat_handler, 28 | } 29 | } 30 | } 31 | 32 | // Simulate GPIO state for demonstration 33 | static int btn1_state = 0; 34 | static int btn2_state = 0; 35 | 36 | // Signal handler for graceful exit 37 | void signal_handler(int sig) 38 | { 39 | if (sig == SIGINT) { 40 | printf("\nReceived SIGINT, exiting...\n"); 41 | running = 0; 42 | } 43 | } 44 | 45 | // Hardware abstraction layer function 46 | // This simulates reading GPIO states 47 | uint8_t read_button_gpio(uint8_t button_id) 48 | { 49 | switch (button_id) { 50 | case 1: 51 | return btn1_state; 52 | case 2: 53 | return btn2_state; 54 | default: 55 | return 0; 56 | } 57 | } 58 | 59 | // Callback functions for button 1 60 | void btn1_single_click_handler(void* btn) 61 | { 62 | printf("🔘 Button 1: Single Click\n"); 63 | } 64 | 65 | void btn1_double_click_handler(void* btn) 66 | { 67 | printf("🔘🔘 Button 1: Double Click\n"); 68 | } 69 | 70 | void btn1_long_press_start_handler(void* btn) 71 | { 72 | printf("⏹️ Button 1: Long Press Start\n"); 73 | } 74 | 75 | void btn1_long_press_hold_handler(void* btn) 76 | { 77 | printf("⏸️ Button 1: Long Press Hold...\n"); 78 | } 79 | 80 | void btn1_press_repeat_handler(void* btn) 81 | { 82 | Button* button = (Button*)btn; 83 | printf("🔄 Button 1: Press Repeat (count: %d)\n", button_get_repeat_count(button)); 84 | } 85 | 86 | // Callback functions for button 2 87 | void btn2_single_click_handler(void* btn) 88 | { 89 | printf("🔵 Button 2: Single Click\n"); 90 | } 91 | 92 | void btn2_double_click_handler(void* btn) 93 | { 94 | printf("🔵🔵 Button 2: Double Click\n"); 95 | } 96 | 97 | void btn2_press_down_handler(void* btn) 98 | { 99 | printf("⬇️ Button 2: Press Down\n"); 100 | } 101 | 102 | void btn2_press_up_handler(void* btn) 103 | { 104 | printf("⬆️ Button 2: Press Up\n"); 105 | } 106 | 107 | // Initialize buttons 108 | void buttons_init(void) 109 | { 110 | // Initialize button 1 (active high for simulation) 111 | button_init(&btn1, read_button_gpio, 1, 1); 112 | 113 | // Attach event handlers for button 1 114 | button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler); 115 | button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_handler); 116 | button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_press_start_handler); 117 | button_attach(&btn1, BTN_LONG_PRESS_HOLD, btn1_long_press_hold_handler); 118 | button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_handler); 119 | 120 | // Initialize button 2 (active high for simulation) 121 | button_init(&btn2, read_button_gpio, 1, 2); 122 | 123 | // Attach event handlers for button 2 124 | button_attach(&btn2, BTN_SINGLE_CLICK, btn2_single_click_handler); 125 | button_attach(&btn2, BTN_DOUBLE_CLICK, btn2_double_click_handler); 126 | button_attach(&btn2, BTN_PRESS_DOWN, btn2_press_down_handler); 127 | button_attach(&btn2, BTN_PRESS_UP, btn2_press_up_handler); 128 | 129 | // Start button processing 130 | button_start(&btn1); 131 | button_start(&btn2); 132 | } 133 | 134 | // Simulate button press for demonstration 135 | void simulate_button_press(int button_id, int duration_ms) 136 | { 137 | printf("\n📱 Simulating button %d press for %d ms...\n", button_id, duration_ms); 138 | 139 | if (button_id == 1) { 140 | btn1_state = 1; 141 | } else if (button_id == 2) { 142 | btn2_state = 1; 143 | } 144 | 145 | // Let the button library process the press 146 | for (int i = 0; i < duration_ms / 5; i++) { 147 | button_ticks(); 148 | usleep(5000); // 5ms delay 149 | } 150 | 151 | // Release the button 152 | if (button_id == 1) { 153 | btn1_state = 0; 154 | } else if (button_id == 2) { 155 | btn2_state = 0; 156 | } 157 | 158 | // Process the release 159 | for (int i = 0; i < 10; i++) { 160 | button_ticks(); 161 | usleep(5000); // 5ms delay 162 | } 163 | } 164 | 165 | // Main function 166 | int main(void) 167 | { 168 | printf("🚀 MultiButton Library Basic Example\n"); 169 | printf("=====================================\n\n"); 170 | 171 | // Set up signal handler 172 | signal(SIGINT, signal_handler); 173 | 174 | // Initialize buttons 175 | buttons_init(); 176 | printf("✅ Buttons initialized successfully\n\n"); 177 | 178 | printf("📋 Demo sequence:\n"); 179 | printf("1. Single click simulation\n"); 180 | printf("2. Double click simulation\n"); 181 | printf("3. Long press simulation\n"); 182 | printf("4. Repeat press simulation\n\n"); 183 | 184 | // Demo sequence 185 | printf("--- Single Click Demo ---\n"); 186 | simulate_button_press(1, 100); // Short press 187 | usleep(500000); // Wait 500ms 188 | 189 | printf("\n--- Double Click Demo ---\n"); 190 | simulate_button_press(1, 100); // First click 191 | usleep(50000); // Quick gap 192 | simulate_button_press(1, 100); // Second click 193 | usleep(500000); // Wait for timeout 194 | 195 | printf("\n--- Long Press Demo ---\n"); 196 | simulate_button_press(1, 1500); // Long press 197 | usleep(200000); // Wait 198 | 199 | printf("\n--- Repeat Press Demo ---\n"); 200 | for (int i = 0; i < 3; i++) { 201 | simulate_button_press(2, 80); 202 | usleep(80000); // Quick succession 203 | } 204 | usleep(500000); // Wait for timeout 205 | 206 | printf("\n--- Button State Query Demo ---\n"); 207 | printf("Button 1 pressed: %s\n", button_is_pressed(&btn1) ? "Yes" : "No"); 208 | printf("Button 2 pressed: %s\n", button_is_pressed(&btn2) ? "Yes" : "No"); 209 | printf("Button 1 repeat count: %d\n", button_get_repeat_count(&btn1)); 210 | printf("Button 2 repeat count: %d\n", button_get_repeat_count(&btn2)); 211 | 212 | printf("\n✅ Demo completed successfully!\n"); 213 | printf("💡 In a real application, button_ticks() would be called from a 5ms timer interrupt.\n"); 214 | 215 | return 0; 216 | } 217 | 218 | /* 219 | * Build instructions: 220 | * 221 | * From project root directory: 222 | * make basic_example 223 | * 224 | * Or build all examples: 225 | * make examples 226 | * 227 | * Run the example: 228 | * ./build/bin/basic_example 229 | */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zibin Zheng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # MultiButton Library Makefile 2 | # 支持编译库文件和示例程序 3 | 4 | # Compiler and tools 5 | CC = gcc 6 | AR = ar 7 | RM = rm -f 8 | MKDIR = mkdir -p 9 | 10 | # Project directories 11 | SRC_DIR = . 12 | EXAMPLES_DIR = examples 13 | BUILD_DIR = build 14 | LIB_DIR = $(BUILD_DIR)/lib 15 | BIN_DIR = $(BUILD_DIR)/bin 16 | OBJ_DIR = $(BUILD_DIR)/obj 17 | 18 | # Compiler flags 19 | CFLAGS = -Wall -Wextra -std=c99 -O2 -g 20 | INCLUDES = -I$(SRC_DIR) 21 | LDFLAGS = 22 | LIBS = 23 | 24 | # Source files 25 | LIB_SOURCES = multi_button.c 26 | LIB_OBJECTS = $(addprefix $(OBJ_DIR)/, $(LIB_SOURCES:.c=.o)) 27 | 28 | # Library name 29 | LIB_NAME = libmultibutton 30 | STATIC_LIB = $(LIB_DIR)/$(LIB_NAME).a 31 | SHARED_LIB = $(LIB_DIR)/$(LIB_NAME).so 32 | 33 | # Example programs 34 | EXAMPLES = basic_example advanced_example poll_example 35 | 36 | # Default target 37 | all: library examples 38 | 39 | # Create directories 40 | $(BUILD_DIR): 41 | $(MKDIR) $(BUILD_DIR) 42 | 43 | $(LIB_DIR): $(BUILD_DIR) 44 | $(MKDIR) $(LIB_DIR) 45 | 46 | $(BIN_DIR): $(BUILD_DIR) 47 | $(MKDIR) $(BIN_DIR) 48 | 49 | $(OBJ_DIR): $(BUILD_DIR) 50 | $(MKDIR) $(OBJ_DIR) 51 | 52 | # Build object files 53 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) 54 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ 55 | 56 | $(OBJ_DIR)/%.o: $(EXAMPLES_DIR)/%.c | $(OBJ_DIR) 57 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ 58 | 59 | # Build static library 60 | $(STATIC_LIB): $(LIB_OBJECTS) | $(LIB_DIR) 61 | $(AR) rcs $@ $^ 62 | @echo "Static library created: $@" 63 | 64 | # Build shared library 65 | $(SHARED_LIB): $(LIB_OBJECTS) | $(LIB_DIR) 66 | $(CC) -shared -fPIC $(CFLAGS) $(INCLUDES) $(LIB_SOURCES) -o $@ 67 | @echo "Shared library created: $@" 68 | 69 | # Library target 70 | library: $(STATIC_LIB) 71 | 72 | # Shared library target 73 | shared: $(SHARED_LIB) 74 | 75 | # Example programs 76 | basic_example: $(BIN_DIR)/basic_example 77 | $(BIN_DIR)/basic_example: $(OBJ_DIR)/basic_example.o $(STATIC_LIB) | $(BIN_DIR) 78 | $(CC) $< -L$(LIB_DIR) -lmultibutton -o $@ 79 | @echo "Example program created: $@" 80 | 81 | advanced_example: $(BIN_DIR)/advanced_example 82 | $(BIN_DIR)/advanced_example: $(OBJ_DIR)/advanced_example.o $(STATIC_LIB) | $(BIN_DIR) 83 | $(CC) $< -L$(LIB_DIR) -lmultibutton -o $@ 84 | @echo "Example program created: $@" 85 | 86 | poll_example: $(BIN_DIR)/poll_example 87 | $(BIN_DIR)/poll_example: $(OBJ_DIR)/poll_example.o $(STATIC_LIB) | $(BIN_DIR) 88 | $(CC) $< -L$(LIB_DIR) -lmultibutton -o $@ 89 | @echo "Example program created: $@" 90 | 91 | # Build all examples 92 | examples: $(addprefix $(BIN_DIR)/, $(EXAMPLES)) 93 | 94 | # Test target 95 | test: examples 96 | @echo "Running basic example..." 97 | @cd $(BIN_DIR) && ./basic_example 98 | 99 | # Clean build files 100 | clean: 101 | $(RM) -r $(BUILD_DIR) 102 | @echo "Build directory cleaned" 103 | 104 | # Install library (optional) 105 | install: library 106 | @echo "Installing library to /usr/local/lib..." 107 | sudo cp $(STATIC_LIB) /usr/local/lib/ 108 | sudo cp multi_button.h /usr/local/include/ 109 | sudo ldconfig 110 | 111 | # Uninstall library 112 | uninstall: 113 | sudo $(RM) /usr/local/lib/$(LIB_NAME).a 114 | sudo $(RM) /usr/local/include/multi_button.h 115 | 116 | # Show help 117 | help: 118 | @echo "MultiButton Library Build System" 119 | @echo "" 120 | @echo "Available targets:" 121 | @echo " all - Build library and examples (default)" 122 | @echo " library - Build static library only" 123 | @echo " shared - Build shared library" 124 | @echo " examples - Build all examples" 125 | @echo " basic_example - Build basic example" 126 | @echo " advanced_example - Build advanced example" 127 | @echo " poll_example - Build poll example" 128 | @echo " test - Build and run basic test" 129 | @echo " clean - Remove build directory" 130 | @echo " install - Install library to system" 131 | @echo " uninstall - Remove library from system" 132 | @echo " help - Show this help message" 133 | @echo "" 134 | @echo "Build configuration:" 135 | @echo " CC = $(CC)" 136 | @echo " CFLAGS = $(CFLAGS)" 137 | @echo " BUILD_DIR = $(BUILD_DIR)" 138 | 139 | # Print build info 140 | info: 141 | @echo "Project: MultiButton Library" 142 | @echo "Sources: $(LIB_SOURCES)" 143 | @echo "Examples: $(EXAMPLES)" 144 | @echo "Build directory: $(BUILD_DIR)" 145 | @echo "Compiler: $(CC)" 146 | @echo "Flags: $(CFLAGS)" 147 | 148 | # Phony targets 149 | .PHONY: all library shared examples clean install uninstall help info test basic_example advanced_example poll_example 150 | 151 | # Dependencies 152 | $(OBJ_DIR)/multi_button.o: multi_button.c multi_button.h 153 | $(OBJ_DIR)/basic_example.o: $(EXAMPLES_DIR)/basic_example.c multi_button.h 154 | $(OBJ_DIR)/advanced_example.o: $(EXAMPLES_DIR)/advanced_example.c multi_button.h 155 | $(OBJ_DIR)/poll_example.o: $(EXAMPLES_DIR)/poll_example.c multi_button.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiButton 2 | 3 | 一个高效、灵活的多按键状态机库,支持多种按键事件检测。 4 | 5 | ## 功能特性 6 | 7 | - ✅ **多种按键事件**: 按下、抬起、单击、双击、长按开始、长按保持、重复按下 8 | - ✅ **硬件去抖**: 内置数字滤波,消除按键抖动 9 | - ✅ **状态机驱动**: 清晰的状态转换逻辑,可靠性高 10 | - ✅ **多按键支持**: 支持无限数量的按键实例 11 | - ✅ **回调机制**: 灵活的事件回调函数注册 12 | - ✅ **内存优化**: 紧凑的数据结构,低内存占用 13 | - ✅ **配置灵活**: 可自定义时间参数和功能选项 14 | - ✅ **参数验证**: 完善的错误检查和边界条件处理 15 | 16 | ## 优化改进 17 | 18 | ### 1. 代码结构优化 19 | - 更清晰的枚举命名 (`BTN_PRESS_DOWN` vs `PRESS_DOWN`) 20 | - 增加状态机状态枚举,提高可读性 21 | - 统一的函数命名规范 22 | - 更好的代码注释和文档 23 | 24 | ### 2. 功能增强 25 | - 新增 `button_detach()` - 动态移除事件回调 26 | - 新增 `button_reset()` - 重置按键状态 27 | - 新增 `button_is_pressed()` - 查询当前按键状态 28 | - 新增 `button_get_repeat_count()` - 获取重复按下次数 29 | - 改进的 `button_get_event()` 函数 30 | 31 | ### 3. 安全性提升 32 | - 完善的参数验证 33 | - 空指针检查 34 | - 数组越界保护 35 | - 更好的错误返回值 36 | 37 | ### 4. 性能优化 38 | - 内联函数优化 GPIO 读取 39 | - 更安全的宏定义 40 | - 减少不必要的计算 41 | - 优化的状态机逻辑 42 | 43 | ### 5. 可维护性 44 | - 清晰的状态转换 45 | - 模块化设计 46 | - 配置文件分离 47 | - 详细的使用示例 48 | 49 | ## 编译和构建 50 | 51 | ### 使用 Makefile (推荐) 52 | 53 | ```bash 54 | # 编译所有内容 (库 + 示例) 55 | make 56 | 57 | # 只编译库 58 | make library 59 | 60 | # 只编译示例 61 | make examples 62 | 63 | # 编译特定示例 64 | make basic_example 65 | make advanced_example 66 | make poll_example 67 | 68 | # 运行测试 69 | make test 70 | 71 | # 清理构建文件 72 | make clean 73 | 74 | # 查看帮助 75 | make help 76 | ``` 77 | 78 | ### 使用构建脚本 79 | 80 | ```bash 81 | # 使脚本可执行 82 | chmod +x build.sh 83 | 84 | # 编译所有内容 85 | ./build.sh 86 | 87 | # 只编译库 88 | ./build.sh library 89 | 90 | # 编译特定示例 91 | ./build.sh basic_example 92 | 93 | # 查看帮助 94 | ./build.sh help 95 | ``` 96 | 97 | ### 构建输出 98 | 99 | 编译完成后,文件结构如下: 100 | 101 | ``` 102 | build/ 103 | ├── lib/ 104 | │ └── libmultibutton.a # 静态库 105 | ├── bin/ 106 | │ ├── basic_example # 基础示例 107 | │ ├── advanced_example # 高级示例 108 | │ └── poll_example # 轮询示例 109 | └── obj/ # 目标文件 110 | ``` 111 | 112 | ## 示例程序 113 | 114 | ### 1. 基础示例 (`examples/basic_example.c`) 115 | 116 | 演示基本的按键事件处理: 117 | 118 | ```bash 119 | ./build/bin/basic_example 120 | ``` 121 | 122 | 功能: 123 | - 单击、双击、长按检测 124 | - 重复按下计数 125 | - 按键状态查询 126 | - 自动化演示序列 127 | 128 | ### 2. 高级示例 (`examples/advanced_example.c`) 129 | 130 | 演示高级功能和动态管理: 131 | 132 | ```bash 133 | # 运行完整演示 134 | ./build/bin/advanced_example 135 | 136 | # 详细输出模式 137 | ./build/bin/advanced_example -v 138 | 139 | # 安静模式 (手动测试) 140 | ./build/bin/advanced_example -q 141 | ``` 142 | 143 | 功能: 144 | - 多按键管理 145 | - 动态回调函数添加/移除 146 | - 配置按键 147 | - 运行时状态监控 148 | 149 | ### 3. 轮询示例 (`examples/poll_example.c`) 150 | 151 | 演示轮询模式使用: 152 | 153 | ```bash 154 | ./build/bin/poll_example 155 | ``` 156 | 157 | 功能: 158 | - 无回调函数的轮询模式 159 | - 事件状态查询 160 | - 主循环集成示例 161 | - 预定义按键模式演示 162 | 163 | ## 快速开始 164 | 165 | ### 1. 包含头文件 166 | ```c 167 | #include "multi_button.h" 168 | ``` 169 | 170 | ### 2. 定义按键实例 171 | ```c 172 | static Button btn1; 173 | ``` 174 | 175 | ### 3. 实现 GPIO 读取函数 176 | ```c 177 | uint8_t read_button_gpio(uint8_t button_id) 178 | { 179 | switch (button_id) { 180 | case 1: 181 | return HAL_GPIO_ReadPin(BUTTON1_GPIO_Port, BUTTON1_Pin); 182 | default: 183 | return 0; 184 | } 185 | } 186 | ``` 187 | 188 | ### 4. 初始化按键 189 | ```c 190 | // 初始化按键 (active_level: 0=低电平有效, 1=高电平有效) 191 | button_init(&btn1, read_button_gpio, 0, 1); 192 | ``` 193 | 194 | ### 5. 注册事件回调 195 | ```c 196 | void btn1_single_click_handler(void* btn) 197 | { 198 | printf("Button 1: Single Click\n"); 199 | } 200 | 201 | button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler); 202 | ``` 203 | 204 | ### 6. 启动按键处理 205 | ```c 206 | button_start(&btn1); 207 | ``` 208 | 209 | ### 7. 定时调用处理函数 210 | ```c 211 | // 在 5ms 定时器中断中调用 212 | void timer_5ms_interrupt_handler(void) 213 | { 214 | button_ticks(); 215 | } 216 | ``` 217 | 218 | ## API 参考 219 | 220 | ### 按键事件类型 221 | ```c 222 | typedef enum { 223 | BTN_PRESS_DOWN = 0, // 按键按下 224 | BTN_PRESS_UP, // 按键抬起 225 | BTN_PRESS_REPEAT, // 重复按下检测 226 | BTN_SINGLE_CLICK, // 单击完成 227 | BTN_DOUBLE_CLICK, // 双击完成 228 | BTN_LONG_PRESS_START, // 长按开始 229 | BTN_LONG_PRESS_HOLD, // 长按保持 230 | BTN_NONE_PRESS // 无事件 231 | } ButtonEvent; 232 | ``` 233 | 234 | ### 核心函数 235 | 236 | #### `void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id)` 237 | **功能**: Initialize button instance 238 | **参数**: 239 | - `handle`: 按键句柄 240 | - `pin_level`: GPIO 读取函数指针 241 | - `active_level`: 有效电平 (0 或 1) 242 | - `button_id`: 按键 ID 243 | 244 | #### `void button_attach(Button* handle, ButtonEvent event, BtnCallback cb)` 245 | **功能**: Attach event callback function 246 | **参数**: 247 | - `handle`: 按键句柄 248 | - `event`: 事件类型 249 | - `cb`: 回调函数 250 | 251 | #### `void button_detach(Button* handle, ButtonEvent event)` 252 | **功能**: Detach event callback function 253 | **参数**: 254 | - `handle`: 按键句柄 255 | - `event`: 事件类型 256 | 257 | #### `int button_start(Button* handle)` 258 | **功能**: Start button processing 259 | **返回值**: 0=成功, -1=已存在, -2=参数错误 260 | 261 | #### `void button_stop(Button* handle)` 262 | **功能**: Stop button processing 263 | 264 | #### `void button_ticks(void)` 265 | **功能**: Background processing function (call every 5ms) 266 | 267 | ### 工具函数 268 | 269 | #### `ButtonEvent button_get_event(Button* handle)` 270 | **功能**: Get current button event 271 | 272 | #### `uint8_t button_get_repeat_count(Button* handle)` 273 | **功能**: Get repeat press count 274 | 275 | #### `void button_reset(Button* handle)` 276 | **功能**: Reset button state to idle 277 | 278 | #### `int button_is_pressed(Button* handle)` 279 | **功能**: Check if button is currently pressed 280 | **返回值**: 1=按下, 0=未按下, -1=错误 281 | 282 | ## 配置选项 283 | 284 | 在 `multi_button_config.h` 中可以自定义以下参数: 285 | 286 | ```c 287 | #define TICKS_INTERVAL 5 // 定时器中断间隔 (ms) 288 | #define DEBOUNCE_TIME_MS 15 // 去抖时间 (ms) 289 | #define SHORT_PRESS_TIME_MS 300 // 短按时间阈值 (ms) 290 | #define LONG_PRESS_TIME_MS 1000 // 长按时间阈值 (ms) 291 | #define PRESS_REPEAT_MAX_NUM 15 // 最大重复计数 292 | ``` 293 | 294 | ## 使用注意事项 295 | 296 | 1. **定时器设置**: 必须配置 5ms 定时器中断,在中断中调用 `button_ticks()` 297 | 2. **GPIO 配置**: 按键引脚需配置为输入模式,根据需要启用上拉或下拉电阻 298 | 3. **回调函数**: 回调函数应尽量简短,避免长时间阻塞 299 | 4. **内存管理**: 按键实例可以是全局变量或动态分配 300 | 5. **多按键**: 每个物理按键需要独立的 Button 实例和唯一的 button_id 301 | 302 | ## 状态机说明 303 | 304 | ``` 305 | [IDLE] --按下--> [PRESS] --长按--> [LONG_HOLD] 306 | ^ | | 307 | | 抬起| 抬起| 308 | | v | 309 | | [RELEASE] <----------------+ 310 | | | ^ 311 | | 超时| |快速按下 312 | | | | 313 | +----------+ [REPEAT] 314 | ``` 315 | 316 | ## 项目结构 317 | 318 | ``` 319 | MultiButton/ 320 | ├── multi_button.h # 主头文件 321 | ├── multi_button.c # 主源文件 322 | ├── Makefile # 构建脚本 323 | ├── build.sh # 备用构建脚本 324 | ├── examples/ # 示例目录 325 | │ ├── basic_example.c # 基础示例 326 | │ ├── advanced_example.c # 高级示例 327 | │ └── poll_example.c # 轮询示例 328 | ├── build/ # 构建输出目录 329 | │ ├── lib/ # 库文件 330 | │ ├── bin/ # 可执行文件 331 | │ └── obj/ # 目标文件 332 | └── README.md # 说明文档 333 | ``` 334 | 335 | ## 兼容性 336 | 337 | - C99 标准 338 | - 适用于各种微控制器平台 (STM32, Arduino, ESP32, etc.) 339 | - 支持裸机和 RTOS 环境 340 | - 内存占用小,适合资源受限的系统 341 | -------------------------------------------------------------------------------- /examples/advanced_example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiButton Library Advanced Example 3 | * This example demonstrates advanced features and dynamic button management 4 | */ 5 | 6 | #include "multi_button.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAX_BUTTONS 4 14 | 15 | // Button instances 16 | static Button buttons[MAX_BUTTONS]; 17 | static volatile int running = 1; 18 | static int button_states[MAX_BUTTONS] = {0}; 19 | 20 | // Configuration 21 | static int demo_mode = 1; 22 | static int verbose_mode = 0; 23 | 24 | // Signal handler for graceful exit 25 | void signal_handler(int sig) 26 | { 27 | if (sig == SIGINT) { 28 | printf("\n🛑 Received SIGINT, cleaning up...\n"); 29 | running = 0; 30 | } 31 | } 32 | 33 | // Hardware abstraction layer function 34 | uint8_t read_button_gpio(uint8_t button_id) 35 | { 36 | if (button_id > 0 && button_id <= MAX_BUTTONS) { 37 | return button_states[button_id - 1]; 38 | } 39 | return 0; 40 | } 41 | 42 | // Generic event handler that shows button info 43 | void generic_event_handler(Button* btn, const char* event_name) 44 | { 45 | if (verbose_mode) { 46 | printf("🔘 Button %d: %s (repeat: %d, pressed: %s)\n", 47 | btn->button_id, 48 | event_name, 49 | button_get_repeat_count(btn), 50 | button_is_pressed(btn) ? "Yes" : "No"); 51 | } else { 52 | printf("🔘 Button %d: %s\n", btn->button_id, event_name); 53 | } 54 | } 55 | 56 | // Event handlers 57 | void on_press_down(Button* btn) { generic_event_handler(btn, "Press Down"); } 58 | void on_press_up(Button* btn) { generic_event_handler(btn, "Press Up"); } 59 | void on_single_click(Button* btn) { generic_event_handler(btn, "Single Click"); } 60 | void on_double_click(Button* btn) { generic_event_handler(btn, "Double Click"); } 61 | void on_long_press_start(Button* btn) { generic_event_handler(btn, "Long Press Start"); } 62 | void on_long_press_hold(Button* btn) { generic_event_handler(btn, "Long Press Hold"); } 63 | void on_press_repeat(Button* btn) { generic_event_handler(btn, "Press Repeat"); } 64 | 65 | // Special handler for button configuration 66 | void on_config_button_click(Button* btn) 67 | { 68 | static int config_state = 0; 69 | 70 | printf("⚙️ Config Button %d clicked!\n", btn->button_id); 71 | 72 | switch (config_state) { 73 | case 0: 74 | verbose_mode = !verbose_mode; 75 | printf("📝 Verbose mode: %s\n", verbose_mode ? "ON" : "OFF"); 76 | break; 77 | case 1: 78 | demo_mode = !demo_mode; 79 | printf("🎭 Demo mode: %s\n", demo_mode ? "ON" : "OFF"); 80 | break; 81 | case 2: 82 | printf("🔄 Resetting all buttons...\n"); 83 | for (int i = 0; i < MAX_BUTTONS; i++) { 84 | button_reset(&buttons[i]); 85 | } 86 | break; 87 | case 3: 88 | printf("👋 Stopping demo...\n"); 89 | running = 0; 90 | break; 91 | } 92 | 93 | config_state = (config_state + 1) % 4; 94 | } 95 | 96 | // Initialize a single button with all event handlers 97 | void init_button(int index, uint8_t button_id, int enable_all_events) 98 | { 99 | button_init(&buttons[index], read_button_gpio, 1, button_id); 100 | 101 | if (enable_all_events) { 102 | button_attach(&buttons[index], BTN_PRESS_DOWN, on_press_down); 103 | button_attach(&buttons[index], BTN_PRESS_UP, on_press_up); 104 | button_attach(&buttons[index], BTN_SINGLE_CLICK, on_single_click); 105 | button_attach(&buttons[index], BTN_DOUBLE_CLICK, on_double_click); 106 | button_attach(&buttons[index], BTN_LONG_PRESS_START, on_long_press_start); 107 | button_attach(&buttons[index], BTN_LONG_PRESS_HOLD, on_long_press_hold); 108 | button_attach(&buttons[index], BTN_PRESS_REPEAT, on_press_repeat); 109 | } else { 110 | // Only essential events 111 | button_attach(&buttons[index], BTN_SINGLE_CLICK, on_single_click); 112 | button_attach(&buttons[index], BTN_DOUBLE_CLICK, on_double_click); 113 | button_attach(&buttons[index], BTN_LONG_PRESS_START, on_long_press_start); 114 | } 115 | 116 | button_start(&buttons[index]); 117 | } 118 | 119 | // Initialize all buttons 120 | void buttons_init(void) 121 | { 122 | printf("🔧 Initializing %d buttons...\n", MAX_BUTTONS); 123 | 124 | // Button 1: Full feature set 125 | init_button(0, 1, 1); 126 | printf(" ✅ Button 1: Full feature set\n"); 127 | 128 | // Button 2: Essential events only 129 | init_button(1, 2, 0); 130 | printf(" ✅ Button 2: Essential events only\n"); 131 | 132 | // Button 3: Configuration button with special handler 133 | init_button(2, 3, 0); 134 | button_detach(&buttons[2], BTN_SINGLE_CLICK); 135 | button_attach(&buttons[2], BTN_SINGLE_CLICK, on_config_button_click); 136 | printf(" ✅ Button 3: Configuration button\n"); 137 | 138 | // Button 4: Dynamic configuration demo 139 | init_button(3, 4, 0); 140 | printf(" ✅ Button 4: Dynamic configuration demo\n"); 141 | 142 | printf("🎯 All buttons initialized successfully!\n\n"); 143 | } 144 | 145 | // Simulate button press 146 | void simulate_button_press(int button_id, int duration_ms) 147 | { 148 | if (button_id < 1 || button_id > MAX_BUTTONS) return; 149 | 150 | if (verbose_mode) { 151 | printf("📱 Simulating button %d press (%d ms)\n", button_id, duration_ms); 152 | } 153 | 154 | button_states[button_id - 1] = 1; 155 | 156 | for (int i = 0; i < duration_ms / 5; i++) { 157 | button_ticks(); 158 | usleep(5000); 159 | } 160 | 161 | button_states[button_id - 1] = 0; 162 | 163 | for (int i = 0; i < 10; i++) { 164 | button_ticks(); 165 | usleep(5000); 166 | } 167 | } 168 | 169 | // Dynamic configuration demo 170 | void dynamic_config_demo(void) 171 | { 172 | printf("\n🔄 Dynamic Configuration Demo\n"); 173 | printf("=====================================\n"); 174 | 175 | // Initially button 4 has minimal handlers 176 | printf("1. Testing button 4 with minimal handlers...\n"); 177 | simulate_button_press(4, 100); 178 | 179 | usleep(300000); 180 | 181 | // Add more handlers dynamically 182 | printf("2. Adding more event handlers to button 4...\n"); 183 | button_attach(&buttons[3], BTN_PRESS_DOWN, on_press_down); 184 | button_attach(&buttons[3], BTN_PRESS_UP, on_press_up); 185 | button_attach(&buttons[3], BTN_PRESS_REPEAT, on_press_repeat); 186 | 187 | printf("3. Testing button 4 with full handlers...\n"); 188 | simulate_button_press(4, 100); 189 | 190 | usleep(300000); 191 | 192 | // Remove some handlers 193 | printf("4. Removing press down/up handlers...\n"); 194 | button_detach(&buttons[3], BTN_PRESS_DOWN); 195 | button_detach(&buttons[3], BTN_PRESS_UP); 196 | 197 | printf("5. Testing button 4 with reduced handlers...\n"); 198 | simulate_button_press(4, 100); 199 | } 200 | 201 | // Interactive demo sequence 202 | void run_demo_sequence(void) 203 | { 204 | printf("\n🎭 Interactive Demo Sequence\n"); 205 | printf("=====================================\n"); 206 | 207 | printf("Demo 1: Single clicks on all buttons\n"); 208 | for (int i = 1; i <= MAX_BUTTONS; i++) { 209 | simulate_button_press(i, 100); 210 | usleep(200000); 211 | } 212 | 213 | printf("\nDemo 2: Double click patterns\n"); 214 | simulate_button_press(1, 80); 215 | usleep(50000); 216 | simulate_button_press(1, 80); 217 | usleep(500000); 218 | 219 | printf("\nDemo 3: Long press demonstration\n"); 220 | simulate_button_press(2, 1200); 221 | usleep(300000); 222 | 223 | printf("\nDemo 4: Rapid press sequence\n"); 224 | for (int i = 0; i < 4; i++) { 225 | simulate_button_press(1, 60); 226 | usleep(70000); 227 | } 228 | usleep(500000); 229 | 230 | printf("\nDemo 5: Configuration button test\n"); 231 | for (int i = 0; i < 3; i++) { 232 | simulate_button_press(3, 100); 233 | usleep(200000); 234 | } 235 | } 236 | 237 | // Print button status 238 | void print_button_status(void) 239 | { 240 | printf("\n📊 Button Status Report\n"); 241 | printf("========================\n"); 242 | for (int i = 0; i < MAX_BUTTONS; i++) { 243 | printf("Button %d: ", buttons[i].button_id); 244 | printf("State=%d, ", button_is_pressed(&buttons[i])); 245 | printf("Repeat=%d, ", button_get_repeat_count(&buttons[i])); 246 | ButtonEvent event = button_get_event(&buttons[i]); 247 | printf("Event=%d\n", event); 248 | } 249 | } 250 | 251 | // Main function 252 | int main(int argc, char* argv[]) 253 | { 254 | printf("🚀 MultiButton Library Advanced Example\n"); 255 | printf("==========================================\n"); 256 | 257 | // Parse command line arguments 258 | for (int i = 1; i < argc; i++) { 259 | if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { 260 | verbose_mode = 1; 261 | } else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) { 262 | demo_mode = 0; 263 | } 264 | } 265 | 266 | printf("Configuration: Demo=%s, Verbose=%s\n\n", 267 | demo_mode ? "ON" : "OFF", 268 | verbose_mode ? "ON" : "OFF"); 269 | 270 | // Set up signal handler 271 | signal(SIGINT, signal_handler); 272 | 273 | // Initialize buttons 274 | buttons_init(); 275 | 276 | if (demo_mode) { 277 | // Run demonstration sequence 278 | run_demo_sequence(); 279 | 280 | // Dynamic configuration demo 281 | dynamic_config_demo(); 282 | 283 | // Print final status 284 | print_button_status(); 285 | 286 | printf("\n✅ Advanced demo completed!\n"); 287 | printf("💡 Use Ctrl+C to exit, or run with --quiet for manual testing\n"); 288 | } else { 289 | printf("🎮 Manual test mode - buttons are ready for interaction\n"); 290 | printf("💡 Use Ctrl+C to exit\n"); 291 | } 292 | 293 | // Keep running until interrupted 294 | while (running) { 295 | button_ticks(); 296 | usleep(5000); // 5ms tick 297 | } 298 | 299 | // Cleanup 300 | printf("\n🧹 Cleaning up...\n"); 301 | for (int i = 0; i < MAX_BUTTONS; i++) { 302 | button_stop(&buttons[i]); 303 | } 304 | 305 | printf("👋 Advanced example finished!\n"); 306 | return 0; 307 | } 308 | 309 | /* 310 | * Build and run instructions: 311 | * 312 | * Build: 313 | * make advanced_example 314 | * 315 | * Run with demo: 316 | * ./build/bin/advanced_example 317 | * 318 | * Run with verbose output: 319 | * ./build/bin/advanced_example -v 320 | * 321 | * Run in quiet mode (manual testing): 322 | * ./build/bin/advanced_example -q 323 | */ -------------------------------------------------------------------------------- /examples/basic_example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiButton Library Basic Example 3 | * This example demonstrates basic usage of the optimized MultiButton library 4 | */ 5 | 6 | #include "multi_button.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Button instances 13 | static Button btn1, btn2; 14 | static volatile int running = 1; 15 | 16 | // Simulate GPIO state for demonstration 17 | static int btn1_state = 0; 18 | static int btn2_state = 0; 19 | 20 | // Signal handler for graceful exit 21 | void signal_handler(int sig) 22 | { 23 | if (sig == SIGINT) { 24 | printf("\nReceived SIGINT, exiting...\n"); 25 | running = 0; 26 | } 27 | } 28 | 29 | // Hardware abstraction layer function 30 | // This simulates reading GPIO states 31 | uint8_t read_button_gpio(uint8_t button_id) 32 | { 33 | switch (button_id) { 34 | case 1: 35 | return btn1_state; 36 | case 2: 37 | return btn2_state; 38 | default: 39 | return 0; 40 | } 41 | } 42 | 43 | // Callback functions for button 1 44 | void btn1_single_click_handler(Button* btn) 45 | { 46 | (void)btn; // suppress unused parameter warning 47 | printf("🔘 Button 1: Single Click\n"); 48 | } 49 | 50 | void btn1_double_click_handler(Button* btn) 51 | { 52 | (void)btn; // suppress unused parameter warning 53 | printf("🔘🔘 Button 1: Double Click\n"); 54 | } 55 | 56 | void btn1_long_press_start_handler(Button* btn) 57 | { 58 | (void)btn; // suppress unused parameter warning 59 | printf("⏹️ Button 1: Long Press Start\n"); 60 | } 61 | 62 | void btn1_long_press_hold_handler(Button* btn) 63 | { 64 | (void)btn; // suppress unused parameter warning 65 | printf("⏸️ Button 1: Long Press Hold...\n"); 66 | } 67 | 68 | void btn1_press_repeat_handler(Button* btn) 69 | { 70 | printf("🔄 Button 1: Press Repeat (count: %d)\n", button_get_repeat_count(btn)); 71 | } 72 | 73 | // Callback functions for button 2 74 | void btn2_single_click_handler(Button* btn) 75 | { 76 | (void)btn; // suppress unused parameter warning 77 | printf("🔵 Button 2: Single Click\n"); 78 | } 79 | 80 | void btn2_double_click_handler(Button* btn) 81 | { 82 | (void)btn; // suppress unused parameter warning 83 | printf("🔵🔵 Button 2: Double Click\n"); 84 | } 85 | 86 | void btn2_press_down_handler(Button* btn) 87 | { 88 | (void)btn; // suppress unused parameter warning 89 | printf("⬇️ Button 2: Press Down\n"); 90 | } 91 | 92 | void btn2_press_up_handler(Button* btn) 93 | { 94 | (void)btn; // suppress unused parameter warning 95 | printf("⬆️ Button 2: Press Up\n"); 96 | } 97 | 98 | // Initialize buttons 99 | void buttons_init(void) 100 | { 101 | // Initialize button 1 (active high for simulation) 102 | button_init(&btn1, read_button_gpio, 1, 1); 103 | 104 | // Attach event handlers for button 1 105 | button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler); 106 | button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_handler); 107 | button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_press_start_handler); 108 | button_attach(&btn1, BTN_LONG_PRESS_HOLD, btn1_long_press_hold_handler); 109 | button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_handler); 110 | 111 | // Initialize button 2 (active high for simulation) 112 | button_init(&btn2, read_button_gpio, 1, 2); 113 | 114 | // Attach event handlers for button 2 115 | button_attach(&btn2, BTN_SINGLE_CLICK, btn2_single_click_handler); 116 | button_attach(&btn2, BTN_DOUBLE_CLICK, btn2_double_click_handler); 117 | button_attach(&btn2, BTN_PRESS_DOWN, btn2_press_down_handler); 118 | button_attach(&btn2, BTN_PRESS_UP, btn2_press_up_handler); 119 | 120 | // Start button processing 121 | button_start(&btn1); 122 | button_start(&btn2); 123 | } 124 | 125 | // Simulate button press for demonstration 126 | void simulate_button_press(int button_id, int duration_ms) 127 | { 128 | printf("\n📱 Simulating button %d press for %d ms...\n", button_id, duration_ms); 129 | 130 | if (button_id == 1) { 131 | btn1_state = 1; 132 | } else if (button_id == 2) { 133 | btn2_state = 1; 134 | } 135 | 136 | // Let the button library process the press 137 | for (int i = 0; i < duration_ms / 5; i++) { 138 | button_ticks(); 139 | usleep(5000); // 5ms delay 140 | } 141 | 142 | // Release the button 143 | if (button_id == 1) { 144 | btn1_state = 0; 145 | } else if (button_id == 2) { 146 | btn2_state = 0; 147 | } 148 | 149 | // Process the release 150 | for (int i = 0; i < 10; i++) { 151 | button_ticks(); 152 | usleep(5000); // 5ms delay 153 | } 154 | } 155 | 156 | // Main function 157 | int main(void) 158 | { 159 | printf("🚀 MultiButton Library Basic Example\n"); 160 | printf("=====================================\n\n"); 161 | 162 | // Set up signal handler 163 | signal(SIGINT, signal_handler); 164 | 165 | // Initialize buttons 166 | buttons_init(); 167 | printf("✅ Buttons initialized successfully\n\n"); 168 | 169 | printf("📋 Demo sequence:\n"); 170 | printf("1. Single click simulation\n"); 171 | printf("2. Double click simulation\n"); 172 | printf("3. Long press simulation\n"); 173 | printf("4. Repeat press simulation\n\n"); 174 | 175 | // Demo sequence 176 | printf("--- Single Click Demo ---\n"); 177 | simulate_button_press(1, 100); // Short press 178 | usleep(500000); // Wait 500ms 179 | 180 | printf("\n--- Double Click Demo ---\n"); 181 | simulate_button_press(1, 100); // First click 182 | usleep(50000); // Quick gap 183 | simulate_button_press(1, 100); // Second click 184 | usleep(500000); // Wait for timeout 185 | 186 | printf("\n--- Long Press Demo ---\n"); 187 | simulate_button_press(1, 1500); // Long press 188 | usleep(200000); // Wait 189 | 190 | printf("\n--- Repeat Press Demo ---\n"); 191 | for (int i = 0; i < 3; i++) { 192 | simulate_button_press(2, 80); 193 | usleep(80000); // Quick succession 194 | } 195 | usleep(500000); // Wait for timeout 196 | 197 | printf("\n--- Button State Query Demo ---\n"); 198 | printf("Button 1 pressed: %s\n", button_is_pressed(&btn1) ? "Yes" : "No"); 199 | printf("Button 2 pressed: %s\n", button_is_pressed(&btn2) ? "Yes" : "No"); 200 | printf("Button 1 repeat count: %d\n", button_get_repeat_count(&btn1)); 201 | printf("Button 2 repeat count: %d\n", button_get_repeat_count(&btn2)); 202 | 203 | printf("\n✅ Demo completed successfully!\n"); 204 | printf("💡 In a real application, button_ticks() would be called from a 5ms timer interrupt.\n"); 205 | 206 | return 0; 207 | } 208 | 209 | /* 210 | * Build instructions: 211 | * 212 | * From project root directory: 213 | * make basic_example 214 | * 215 | * Or build all examples: 216 | * make examples 217 | * 218 | * Run the example: 219 | * ./build/bin/basic_example 220 | */ -------------------------------------------------------------------------------- /examples/poll_example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiButton Library Polling Example 3 | * This example demonstrates polling-based button event detection 4 | */ 5 | 6 | #include "multi_button.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Button instance 13 | static Button btn1; 14 | static volatile int running = 1; 15 | 16 | // Signal handler for graceful exit 17 | void signal_handler(int sig) 18 | { 19 | if (sig == SIGINT) { 20 | printf("\n🛑 Exiting polling example...\n"); 21 | running = 0; 22 | } 23 | } 24 | 25 | // Hardware abstraction layer function 26 | uint8_t read_button_gpio(uint8_t button_id) 27 | { 28 | (void)button_id; // suppress unused parameter warning 29 | 30 | // Simulate button state changes for demonstration 31 | static int cycle_count = 0; 32 | static int pattern_index = 0; 33 | 34 | // Predefined button press patterns for demonstration 35 | // Each number represents the state for several cycles 36 | int patterns[] = { 37 | 0, 0, 0, 0, 0, // Idle 38 | 1, 1, 1, 1, 0, // Short press 39 | 0, 0, 0, 0, 0, // Idle 40 | 1, 1, 0, 1, 1, // Double click 41 | 0, 0, 0, 0, 0, // Idle 42 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // Long press 43 | 0, 0, 0, 0, 0, // Idle 44 | 1, 0, 1, 0, 1, 0, 1, 0, // Rapid clicks 45 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // Final idle 46 | }; 47 | 48 | int pattern_size = sizeof(patterns) / sizeof(patterns[0]); 49 | 50 | if (cycle_count++ >= 10) { // Change pattern every 50ms (10 * 5ms) 51 | cycle_count = 0; 52 | pattern_index = (pattern_index + 1) % pattern_size; 53 | } 54 | 55 | return patterns[pattern_index]; 56 | } 57 | 58 | // Initialize button without callbacks (polling mode) 59 | void button_init_polling(void) 60 | { 61 | printf("🔧 Initializing button for polling mode...\n"); 62 | 63 | // Initialize button but don't attach any callbacks 64 | button_init(&btn1, read_button_gpio, 1, 1); 65 | 66 | // Start button processing 67 | button_start(&btn1); 68 | 69 | printf("✅ Button initialized for polling\n\n"); 70 | } 71 | 72 | // Poll button events and handle them 73 | void poll_and_handle_events(void) 74 | { 75 | static ButtonEvent last_event = BTN_NONE_PRESS; 76 | static int event_count = 0; 77 | 78 | // Get current event 79 | ButtonEvent current_event = button_get_event(&btn1); 80 | 81 | // Only process if event changed 82 | if (current_event != last_event && current_event != BTN_NONE_PRESS) { 83 | event_count++; 84 | 85 | printf("📡 [%d] Polled Event: ", event_count); 86 | 87 | switch (current_event) { 88 | case BTN_PRESS_DOWN: 89 | printf("Press Down"); 90 | break; 91 | case BTN_PRESS_UP: 92 | printf("Press Up"); 93 | break; 94 | case BTN_SINGLE_CLICK: 95 | printf("Single Click ✨"); 96 | break; 97 | case BTN_DOUBLE_CLICK: 98 | printf("Double Click ✨✨"); 99 | break; 100 | case BTN_LONG_PRESS_START: 101 | printf("Long Press Start 🔥"); 102 | break; 103 | case BTN_LONG_PRESS_HOLD: 104 | printf("Long Press Hold 🔥🔥"); 105 | break; 106 | case BTN_PRESS_REPEAT: 107 | printf("Press Repeat (count: %d) 🔄", button_get_repeat_count(&btn1)); 108 | break; 109 | default: 110 | printf("Unknown Event ❓"); 111 | break; 112 | } 113 | 114 | printf(" | Pressed: %s", button_is_pressed(&btn1) ? "Yes" : "No"); 115 | printf("\n"); 116 | 117 | last_event = current_event; 118 | } 119 | } 120 | 121 | // Print periodic status 122 | void print_status(void) 123 | { 124 | static int status_counter = 0; 125 | 126 | if (++status_counter >= 200) { // Print every 1 second (200 * 5ms) 127 | status_counter = 0; 128 | 129 | printf("📊 Status - Pressed: %s, Repeat: %d, Event: %d\n", 130 | button_is_pressed(&btn1) ? "Yes" : "No", 131 | button_get_repeat_count(&btn1), 132 | button_get_event(&btn1)); 133 | } 134 | } 135 | 136 | // Main function 137 | int main(void) 138 | { 139 | printf("🚀 MultiButton Library Polling Example\n"); 140 | printf("========================================\n\n"); 141 | 142 | printf("💡 This example demonstrates polling-based event detection\n"); 143 | printf("📡 Events are detected by polling button_get_event() instead of using callbacks\n"); 144 | printf("🎬 A predefined pattern will simulate button presses\n\n"); 145 | 146 | // Set up signal handler 147 | signal(SIGINT, signal_handler); 148 | 149 | // Initialize button for polling 150 | button_init_polling(); 151 | 152 | printf("🎭 Starting simulation with predefined patterns...\n"); 153 | printf(" Pattern includes: short press, double click, long press, rapid clicks\n"); 154 | printf(" Press Ctrl+C to exit\n\n"); 155 | 156 | int tick_count = 0; 157 | 158 | // Main polling loop 159 | while (running) { 160 | // Update button state machine 161 | button_ticks(); 162 | 163 | // Poll for events 164 | poll_and_handle_events(); 165 | 166 | // Print periodic status 167 | print_status(); 168 | 169 | // Simulate 5ms tick interval 170 | usleep(5000); 171 | 172 | // Stop after reasonable demo time 173 | if (++tick_count > 2000) { // 10 seconds 174 | printf("\n🏁 Demo pattern completed!\n"); 175 | break; 176 | } 177 | } 178 | 179 | // Cleanup 180 | printf("\n🧹 Cleaning up...\n"); 181 | button_stop(&btn1); 182 | 183 | printf("✅ Polling example finished!\n"); 184 | printf("\n📚 Key takeaways:\n"); 185 | printf(" • Polling mode allows checking events at your own pace\n"); 186 | printf(" • No callback functions needed\n"); 187 | printf(" • Use button_get_event() to check current event\n"); 188 | printf(" • Still need to call button_ticks() every 5ms\n"); 189 | printf(" • Useful for main loop architectures without interrupts\n"); 190 | 191 | return 0; 192 | } 193 | 194 | /* 195 | * Build and run instructions: 196 | * 197 | * Build: 198 | * make poll_example 199 | * 200 | * Run: 201 | * ./build/bin/poll_example 202 | * 203 | * This example shows: 204 | * - How to use the library without callbacks 205 | * - Polling-based event detection 206 | * - Integration with main loop architecture 207 | * - Status monitoring and reporting 208 | */ -------------------------------------------------------------------------------- /multi_button.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zibin Zheng 3 | * All rights reserved 4 | */ 5 | 6 | #include "multi_button.h" 7 | 8 | // Macro for callback execution with null check 9 | #define EVENT_CB(ev) do { if(handle->cb[ev]) handle->cb[ev](handle); } while(0) 10 | 11 | // Button handle list head 12 | static Button* head_handle = NULL; 13 | 14 | // Forward declarations 15 | static void button_handler(Button* handle); 16 | static inline uint8_t button_read_level(Button* handle); 17 | 18 | /** 19 | * @brief Initialize the button struct handle 20 | * @param handle: the button handle struct 21 | * @param pin_level: read the HAL GPIO of the connected button level 22 | * @param active_level: pressed GPIO level 23 | * @param button_id: the button id 24 | * @retval None 25 | */ 26 | void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id) 27 | { 28 | if (!handle || !pin_level) return; // parameter validation 29 | 30 | memset(handle, 0, sizeof(Button)); 31 | handle->event = (uint8_t)BTN_NONE_PRESS; 32 | handle->hal_button_level = pin_level; 33 | handle->button_level = !active_level; // initialize to opposite of active level 34 | handle->active_level = active_level; 35 | handle->button_id = button_id; 36 | handle->state = BTN_STATE_IDLE; 37 | } 38 | 39 | /** 40 | * @brief Attach the button event callback function 41 | * @param handle: the button handle struct 42 | * @param event: trigger event type 43 | * @param cb: callback function 44 | * @retval None 45 | */ 46 | void button_attach(Button* handle, ButtonEvent event, BtnCallback cb) 47 | { 48 | if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation 49 | handle->cb[event] = cb; 50 | } 51 | 52 | /** 53 | * @brief Detach the button event callback function 54 | * @param handle: the button handle struct 55 | * @param event: trigger event type 56 | * @retval None 57 | */ 58 | void button_detach(Button* handle, ButtonEvent event) 59 | { 60 | if (!handle || event >= BTN_EVENT_COUNT) return; // parameter validation 61 | handle->cb[event] = NULL; 62 | } 63 | 64 | /** 65 | * @brief Get the button event that happened 66 | * @param handle: the button handle struct 67 | * @retval button event 68 | */ 69 | ButtonEvent button_get_event(Button* handle) 70 | { 71 | if (!handle) return BTN_NONE_PRESS; 72 | return (ButtonEvent)(handle->event); 73 | } 74 | 75 | /** 76 | * @brief Get the repeat count of button presses 77 | * @param handle: the button handle struct 78 | * @retval repeat count 79 | */ 80 | uint8_t button_get_repeat_count(Button* handle) 81 | { 82 | if (!handle) return 0; 83 | return handle->repeat; 84 | } 85 | 86 | /** 87 | * @brief Reset button state to idle 88 | * @param handle: the button handle struct 89 | * @retval None 90 | */ 91 | void button_reset(Button* handle) 92 | { 93 | if (!handle) return; 94 | handle->state = BTN_STATE_IDLE; 95 | handle->ticks = 0; 96 | handle->repeat = 0; 97 | handle->event = (uint8_t)BTN_NONE_PRESS; 98 | handle->debounce_cnt = 0; 99 | } 100 | 101 | /** 102 | * @brief Check if button is currently pressed 103 | * @param handle: the button handle struct 104 | * @retval 1: pressed, 0: not pressed, -1: error 105 | */ 106 | int button_is_pressed(Button* handle) 107 | { 108 | if (!handle) return -1; 109 | return (handle->button_level == handle->active_level) ? 1 : 0; 110 | } 111 | 112 | /** 113 | * @brief Read button level with inline optimization 114 | * @param handle: the button handle struct 115 | * @retval button level 116 | */ 117 | static inline uint8_t button_read_level(Button* handle) 118 | { 119 | return handle->hal_button_level(handle->button_id); 120 | } 121 | 122 | /** 123 | * @brief Button driver core function, driver state machine 124 | * @param handle: the button handle struct 125 | * @retval None 126 | */ 127 | static void button_handler(Button* handle) 128 | { 129 | uint8_t read_gpio_level = button_read_level(handle); 130 | 131 | // Increment ticks counter when not in idle state 132 | if (handle->state > BTN_STATE_IDLE) { 133 | handle->ticks++; 134 | } 135 | 136 | /*------------Button debounce handling---------------*/ 137 | if (read_gpio_level != handle->button_level) { 138 | // Continue reading same new level for debounce 139 | if (++(handle->debounce_cnt) >= DEBOUNCE_TICKS) { 140 | handle->button_level = read_gpio_level; 141 | handle->debounce_cnt = 0; 142 | } 143 | } else { 144 | // Level not changed, reset counter 145 | handle->debounce_cnt = 0; 146 | } 147 | 148 | /*-----------------State machine-------------------*/ 149 | switch (handle->state) { 150 | case BTN_STATE_IDLE: 151 | if (handle->button_level == handle->active_level) { 152 | // Button press detected 153 | handle->event = (uint8_t)BTN_PRESS_DOWN; 154 | EVENT_CB(BTN_PRESS_DOWN); 155 | handle->ticks = 0; 156 | handle->repeat = 1; 157 | handle->state = BTN_STATE_PRESS; 158 | } else { 159 | handle->event = (uint8_t)BTN_NONE_PRESS; 160 | } 161 | break; 162 | 163 | case BTN_STATE_PRESS: 164 | if (handle->button_level != handle->active_level) { 165 | // Button released 166 | handle->event = (uint8_t)BTN_PRESS_UP; 167 | EVENT_CB(BTN_PRESS_UP); 168 | handle->ticks = 0; 169 | handle->state = BTN_STATE_RELEASE; 170 | } else if (handle->ticks > LONG_TICKS) { 171 | // Long press detected 172 | handle->event = (uint8_t)BTN_LONG_PRESS_START; 173 | EVENT_CB(BTN_LONG_PRESS_START); 174 | handle->state = BTN_STATE_LONG_HOLD; 175 | } 176 | break; 177 | 178 | case BTN_STATE_RELEASE: 179 | if (handle->button_level == handle->active_level) { 180 | // Button pressed again 181 | handle->event = (uint8_t)BTN_PRESS_DOWN; 182 | EVENT_CB(BTN_PRESS_DOWN); 183 | if (handle->repeat < PRESS_REPEAT_MAX_NUM) { 184 | handle->repeat++; 185 | } 186 | EVENT_CB(BTN_PRESS_REPEAT); 187 | handle->ticks = 0; 188 | handle->state = BTN_STATE_REPEAT; 189 | } else if (handle->ticks > SHORT_TICKS) { 190 | // Timeout reached, determine click type 191 | if (handle->repeat == 1) { 192 | handle->event = (uint8_t)BTN_SINGLE_CLICK; 193 | EVENT_CB(BTN_SINGLE_CLICK); 194 | } else if (handle->repeat == 2) { 195 | handle->event = (uint8_t)BTN_DOUBLE_CLICK; 196 | EVENT_CB(BTN_DOUBLE_CLICK); 197 | } 198 | handle->state = BTN_STATE_IDLE; 199 | } 200 | break; 201 | 202 | case BTN_STATE_REPEAT: 203 | if (handle->button_level != handle->active_level) { 204 | // Button released 205 | handle->event = (uint8_t)BTN_PRESS_UP; 206 | EVENT_CB(BTN_PRESS_UP); 207 | if (handle->ticks < SHORT_TICKS) { 208 | handle->ticks = 0; 209 | handle->state = BTN_STATE_RELEASE; // Continue waiting for more presses 210 | } else { 211 | handle->state = BTN_STATE_IDLE; // End of sequence 212 | } 213 | } else if (handle->ticks > SHORT_TICKS) { 214 | // Held down too long, treat as normal press 215 | handle->state = BTN_STATE_PRESS; 216 | } 217 | break; 218 | 219 | case BTN_STATE_LONG_HOLD: 220 | if (handle->button_level == handle->active_level) { 221 | // Continue holding 222 | handle->event = (uint8_t)BTN_LONG_PRESS_HOLD; 223 | EVENT_CB(BTN_LONG_PRESS_HOLD); 224 | } else { 225 | // Released from long press 226 | handle->event = (uint8_t)BTN_PRESS_UP; 227 | EVENT_CB(BTN_PRESS_UP); 228 | handle->state = BTN_STATE_IDLE; 229 | } 230 | break; 231 | 232 | default: 233 | // Invalid state, reset to idle 234 | handle->state = BTN_STATE_IDLE; 235 | break; 236 | } 237 | } 238 | 239 | /** 240 | * @brief Start the button work, add the handle into work list 241 | * @param handle: target handle struct 242 | * @retval 0: succeed, -1: already exist, -2: invalid parameter 243 | */ 244 | int button_start(Button* handle) 245 | { 246 | if (!handle) return -2; // invalid parameter 247 | 248 | Button* target = head_handle; 249 | while (target) { 250 | if (target == handle) return -1; // already exist 251 | target = target->next; 252 | } 253 | 254 | handle->next = head_handle; 255 | head_handle = handle; 256 | return 0; 257 | } 258 | 259 | /** 260 | * @brief Stop the button work, remove the handle from work list 261 | * @param handle: target handle struct 262 | * @retval None 263 | */ 264 | void button_stop(Button* handle) 265 | { 266 | if (!handle) return; // parameter validation 267 | 268 | Button** curr; 269 | for (curr = &head_handle; *curr; ) { 270 | Button* entry = *curr; 271 | if (entry == handle) { 272 | *curr = entry->next; 273 | entry->next = NULL; // clear next pointer 274 | return; 275 | } else { 276 | curr = &entry->next; 277 | } 278 | } 279 | } 280 | 281 | /** 282 | * @brief Background ticks, timer repeat invoking interval 5ms 283 | * @param None 284 | * @retval None 285 | */ 286 | void button_ticks(void) 287 | { 288 | Button* target; 289 | for (target = head_handle; target; target = target->next) { 290 | button_handler(target); 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /multi_button.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Zibin Zheng 3 | * All rights reserved 4 | */ 5 | 6 | #ifndef _MULTI_BUTTON_H_ 7 | #define _MULTI_BUTTON_H_ 8 | 9 | #include 10 | #include 11 | 12 | // Configuration constants - can be modified according to your needs 13 | #define TICKS_INTERVAL 5 // ms - timer interrupt interval 14 | #define DEBOUNCE_TICKS 3 // MAX 7 (0 ~ 7) - debounce filter depth 15 | #define SHORT_TICKS (300 / TICKS_INTERVAL) // short press threshold 16 | #define LONG_TICKS (1000 / TICKS_INTERVAL) // long press threshold 17 | #define PRESS_REPEAT_MAX_NUM 15 // maximum repeat counter value 18 | 19 | // Forward declaration 20 | typedef struct _Button Button; 21 | 22 | // Button callback function type 23 | typedef void (*BtnCallback)(Button* btn_handle); 24 | 25 | // Button event types 26 | typedef enum { 27 | BTN_PRESS_DOWN = 0, // button pressed down 28 | BTN_PRESS_UP, // button released 29 | BTN_PRESS_REPEAT, // repeated press detected 30 | BTN_SINGLE_CLICK, // single click completed 31 | BTN_DOUBLE_CLICK, // double click completed 32 | BTN_LONG_PRESS_START, // long press started 33 | BTN_LONG_PRESS_HOLD, // long press holding 34 | BTN_EVENT_COUNT, // total number of events 35 | BTN_NONE_PRESS // no event 36 | } ButtonEvent; 37 | 38 | // Button state machine states 39 | typedef enum { 40 | BTN_STATE_IDLE = 0, // idle state 41 | BTN_STATE_PRESS, // pressed state 42 | BTN_STATE_RELEASE, // released state waiting for timeout 43 | BTN_STATE_REPEAT, // repeat press state 44 | BTN_STATE_LONG_HOLD // long press hold state 45 | } ButtonState; 46 | 47 | // Button structure 48 | struct _Button { 49 | uint16_t ticks; // tick counter 50 | uint8_t repeat : 4; // repeat counter (0-15) 51 | uint8_t event : 4; // current event (0-15) 52 | uint8_t state : 3; // state machine state (0-7) 53 | uint8_t debounce_cnt : 3; // debounce counter (0-7) 54 | uint8_t active_level : 1; // active GPIO level (0 or 1) 55 | uint8_t button_level : 1; // current button level 56 | uint8_t button_id; // button identifier 57 | uint8_t (*hal_button_level)(uint8_t button_id); // HAL function to read GPIO 58 | BtnCallback cb[BTN_EVENT_COUNT]; // callback function array 59 | Button* next; // next button in linked list 60 | }; 61 | 62 | #ifdef __cplusplus 63 | extern "C" { 64 | #endif 65 | 66 | // Public API functions 67 | void button_init(Button* handle, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t button_id); 68 | void button_attach(Button* handle, ButtonEvent event, BtnCallback cb); 69 | void button_detach(Button* handle, ButtonEvent event); 70 | ButtonEvent button_get_event(Button* handle); 71 | int button_start(Button* handle); 72 | void button_stop(Button* handle); 73 | void button_ticks(void); 74 | 75 | // Utility functions 76 | uint8_t button_get_repeat_count(Button* handle); 77 | void button_reset(Button* handle); 78 | int button_is_pressed(Button* handle); 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | 84 | #endif 85 | --------------------------------------------------------------------------------