开源按键组件MultiButton介绍
GaoSheng Lv4

本文简要介绍一个开源的按键组件项目MultiButton
https/github.com/0x1abin/MultiButton
可以避免重复造轮子,不需要去翻来覆去的去考虑单击/双击/长按…的逻辑。不用去考虑消抖的问题,让开发者专注于自己的业务逻辑

MultiButton的优势

✅多种按键事件: 按下、抬起、单击、双击、长按开始、长按保持、重复按下
✅硬件去抖: 内置数字滤波,消除按键抖动
✅状态机驱动: 清晰的状态转换逻辑,可靠性高
✅多按键支持: 支持无限数量的按键实例
✅回调机制: 灵活的事件回调函数注册
✅内存优化: 紧凑的数据结构,低内存占用
✅配置灵活: 可自定义时间参数和功能选项
✅参数验证: 完善的错误检查和边界条件处理

如何移植到自己的项目(宝宝级教程)

MultiButton耦合性很低,不依赖任何硬件与操作系统.所以移植起来相对容易

  1. Github上面的工程download到本地后,将multi_button.c、multi_button.h添加到工程
    图片1

  2. 在keil的魔术棒(Options for Target…)栏选择C/C++,增加multi_button.h的路径到工程
    图片2

  3. 配置读取GPIO状态的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
// Hardware abstraction layer function
// This simulates reading GPIO states
uint8_t read_button_gpio(uint8_t button_id)
{
switch (button_id) {
case 1:
return (GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_15) == Bit_SET);
case 2:
return btn2_state;
default:
return 0;
}
}
  1. 初始化按键(第三个参数:active_level: 0=低电平有效, 1=高电平有效
1
button_init(&btn1, read_button_gpio, 0, 1);
  1. 注册回调函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Callback functions for button 1
void btn1_single_click_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Single Click\n");
}

void btn1_double_click_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Double Click\n");
}

void btn1_long_press_start_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Long Press Start\n");
}

void btn1_long_press_hold_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Long Press Hold...\n");
}
// Attach event handlers for button 1
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler);
button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_handler);
button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_press_start_handler);
button_attach(&btn1, BTN_LONG_PRESS_HOLD, btn1_long_press_hold_handler);
button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_handler);
  1. 启动按键处理
1
2
// Start button processing
button_start(&btn1);
  1. 周期性调用button_ticks()函数,给MultiButton配置一个 5ms的定时器,用到TIMER或者 Systick都可以
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @brief Systick Config
*/
void Systick_MS_Config(uint32_t sys_freq)
{
/* Setup SysTick Timer for 5 msec interrupts */
if (SysTick_Config(sys_freq / 200))
{
/* Capture error */
while (1);
}
}
  1. 在Systick中断中调用button_ticks
1
2
3
4
void SysTick_Handler(void)
{
button_ticks();
}

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "main.h"
#include <stdint.h>
#include "User_Systick_Config.h"
#include "multi_button.h"
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include "SEGGER_RTT.h"

void SysTick_Handler(void)
{
button_ticks();
}


/**
* @brief Inserts a delay time.
* @param count specifies the delay time length.
*/
void Delay(uint32_t count)
{
for (; count > 0; count--)
;
}


// Button instances
static Button btn1, btn2;
static volatile int running = 1;

// Simulate GPIO state for demonstration
static int btn1_state = 0;
static int btn2_state = 0;

// Signal handler for graceful exit
void signal_handler(int sig)
{
if (sig == SIGINT) {
printf("\nReceived SIGINT, exiting...\n");
running = 0;
}
}

// Hardware abstraction layer function
// This simulates reading GPIO states
uint8_t read_button_gpio(uint8_t button_id)
{
switch (button_id) {
case 1:
return (GPIO_ReadInputDataBit(GPIOA, GPIO_PIN_15) == Bit_SET);
case 2:
return btn2_state;
default:
return 0;
}
}

// Callback functions for button 1
void btn1_single_click_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Single Click\n");
}

void btn1_double_click_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Double Click\n");
}

void btn1_long_press_start_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Long Press Start\n");
}

void btn1_long_press_hold_handler(Button* btn)
{
(void)btn; // suppress unused parameter warning
printf("Button 1: Long Press Hold...\n");
}

void btn1_press_repeat_handler(Button* btn)
{
printf("Button 1: Press Repeat (count: %d)\n", button_get_repeat_count(btn));
}

// Initialize buttons
void buttons_init(void)
{
// Initialize button 1 (active high for simulation)
button_init(&btn1, read_button_gpio, 0, 1);

// Attach event handlers for button 1
button_attach(&btn1, BTN_SINGLE_CLICK, btn1_single_click_handler);
button_attach(&btn1, BTN_DOUBLE_CLICK, btn1_double_click_handler);
button_attach(&btn1, BTN_LONG_PRESS_START, btn1_long_press_start_handler);
button_attach(&btn1, BTN_LONG_PRESS_HOLD, btn1_long_press_hold_handler);
button_attach(&btn1, BTN_PRESS_REPEAT, btn1_press_repeat_handler);


// Start button processing
button_start(&btn1);

}

void KeyInputExtiInit(GPIO_Module* GPIOx, uint16_t Pin)
{
GPIO_InitType GPIO_InitStructure;
EXTI_InitType EXTI_InitStructure;
NVIC_InitType NVIC_InitStructure;

/* Enable the GPIO Clock */

RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_AFIO, ENABLE);

GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = Pin;
GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);

}

/**
* @brief Main program.
*/
int main(void)
{
/*SystemInit() function has been called by startup file startup_n32wb43x.s*/
buttons_init();

Systick_MS_Config(SystemCoreClock);
//make the timer invoking the button_ticks() interval 5ms.
//This function is implemented by yourself.

/*Initialize key as external line interrupt*/
KeyInputExtiInit(GPIOA, GPIO_PIN_15);


while (1)
{

}
}
/**
* @}
*/
//int fputc(int ch, FILE* f)

//{

// if (ch == '\n') {

// SEGGER_RTT_PutChar(0, '\r');

// }

// SEGGER_RTT_PutChar(0, ch);

// return ch;

//}

下面是移植后的测试情况
图片3

本站由 提供部署服务