-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathButtons.cpp
208 lines (193 loc) · 7.11 KB
/
Buttons.cpp
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*------------------------------------------------------/
/ Copyright (c) 2023, Elehobica
/ Released under the BSD-2-Clause
/ refer to https://opensource.org/licenses/BSD-2-Clause
/------------------------------------------------------*/
#include "Buttons.h"
uint32_t button_t::max_id = 0;
inline uint8_t trailing_zeros64(uint64_t u64)
{
return static_cast<uint8_t>(__builtin_ctzll(u64));
}
inline uint8_t ones_count64(uint64_t u64)
{
return static_cast<uint8_t>(__builtin_popcountll(u64));
}
Buttons::Buttons(button_t* buttons, int size, uint8_t scan_skip)
: _buttons(buttons), _size(size), _scan_skip(scan_skip), _scan_count(0)
{
// button event queue
queue_init(&btn_evt_queue, sizeof(button_event_t), EVENT_QUEUE_LENGTH);
}
void Buttons::scan_periodic()
{
if (_scan_count < _scan_skip) {
_scan_count++;
return;
}
for (int i = 0; i < _size; i++) {
// what to get (default values)
uint8_t repeat_cnt = 0;
uint8_t count_rise = 0;
uint8_t count_fall = 0;
bool detect_long = false;
bool detect_long_long = false;
// alias
button_t* button = &_buttons[i];
const button_config_t* config = button->config;
// === get raw status of pin ===
bool raw_sts = gpio_get(button->pin) == config->active_high;
// === unshift history ===
unshift_history(button->history, raw_sts);
uint8_t recent_stay_pushed_counts = get_recent_stay_pushed_counts(button->history);
uint8_t recent_stay_released_counts = get_recent_stay_released_counts(button->history);
if (config->is_button) {
// === Detect Repeated (by non-filtered) ===
if (config->long_detect_cnt == 0 && config->long_long_detect_cnt == 0) {
if ((_scan_count % (config->repeat_skip + 1)) == 0) {
if (config->repeat_detect_cnt > 0 && recent_stay_pushed_counts >= config->repeat_detect_cnt) {
if (button->rpt_cnt < 255) {
button->rpt_cnt++;
}
repeat_cnt = button->rpt_cnt;
} else {
button->rpt_cnt = 0;
}
}
}
// === Detect Long (by non-filtered) ===
if (config->repeat_detect_cnt == 0) {
if (recent_stay_pushed_counts > 0) {
if (recent_stay_pushed_counts == config->long_detect_cnt) {
detect_long = true;
} else if (recent_stay_pushed_counts == config->long_long_detect_cnt) {
detect_long_long = true;
}
}
}
}
// === unshift Filter ===
if (recent_stay_pushed_counts >= config->filter_size) {
unshift_history(button->filtered, true);
} else if (recent_stay_released_counts >= config->filter_size) {
unshift_history(button->filtered, false);
} else {
unshift_history(button->filtered, get_pos_history(button->filtered, 0));
}
uint8_t recent_stay_released_counts_filtered = get_recent_stay_released_counts(button->filtered);
// === Check Action finished (only if multiClicks) ===
bool act_finished = recent_stay_released_counts_filtered >= config->act_finish_cnt;
// === Then, Count rising edge ===
if (repeat_cnt > 0) { // if repeatCnt,countRise could be 0
count_rise = 1;
} else if (act_finished) {
count_rise = count_rising_edge(button->filtered, !config->multi_clicks);
}
if (!config->is_button) {
count_fall = count_falling_edge(button->filtered, !config->multi_clicks);
} else {
count_fall = 0;
}
// Clear all once detected, initialize all as true to avoid repeated detection
if (detect_long || count_rise > 0) {
clear_history(button->filtered, true);
} else if (count_fall > 0) {
clear_history(button->filtered, false);
}
// === Send event ===
button_event_type_t event_type = EVT_NONE;
if (count_rise > 1) {
event_type = EVT_MULTI;
} else if (count_rise > 0) {
if (config->is_button) {
event_type = EVT_SINGLE;
} else {
event_type = EVT_ON;
}
} else if (count_fall > 0) {
event_type = EVT_OFF;
} else if (detect_long) {
event_type = EVT_LONG;
} else if (detect_long_long) {
event_type = EVT_LONG_LONG;
}
if (event_type != EVT_NONE && queue_get_level(&btn_evt_queue) < EVENT_QUEUE_LENGTH) {
button_event_t button_event = {
button->id, // button_id
button->name, // button_name
event_type, // event_type
count_rise, // click_count
repeat_cnt // repeat_count;
};
if (!queue_try_add(&btn_evt_queue, &button_event)) {
//printf("Queue was full\n");
}
}
}
_scan_count++;
}
bool Buttons::get_button_event(button_event_t& button_event)
{
int count = queue_get_level(&btn_evt_queue);
if (count) {
queue_remove_blocking(&btn_evt_queue, &button_event);
return true;
}
return false;
}
void Buttons::clear_history(button_history_t& history, bool flag)
{
history = flag ? ~0LL : 0LL;
}
bool Buttons::get_pos_history(button_history_t& history, int i)
{
uint64_t mask = 1LL << i;
return (static_cast<uint64_t>(history) & mask) != 0LL;
}
void Buttons::unshift_history(button_history_t& history, bool sts)
{
history = (history << 1) | static_cast<uint64_t>(sts);
}
uint8_t Buttons::get_recent_stay_pushed_counts(button_history_t& history)
{
// shortcut for very usual history case
if (history == 0) {
return static_cast<uint8_t>(0);
}
return trailing_zeros64(~static_cast<uint64_t>(history));
}
uint8_t Buttons::get_recent_stay_released_counts(button_history_t& history)
{
return trailing_zeros64(static_cast<uint64_t>(history));
}
uint8_t Buttons::count_rising_edge_core(uint64_t u64, bool single)
{
const uint64_t even_mask = 0x5555555555555555LL;
const uint64_t odd_mask = 0xaaaaaaaaaaaaaaaaLL;
// check rising at even column
uint64_t u64e = u64 ^ even_mask;
u64e = (u64e >> 1) | u64e;
u64e = u64e & even_mask;
// check rising at odd column
uint64_t u64o = u64 ^ odd_mask;
u64o = (u64o >> 1) | u64o;
u64o = u64o & odd_mask;
// merge even and odd (ignore MSB)
u64 = ~(u64o | u64e | (1LL << 63));
// shortcuts
if (u64 == 0LL) {
return 0;
} else if (single) {
return 1;
}
// now '1' indicates where rising edge is
return ones_count64(u64);
}
uint8_t Buttons::count_rising_edge(button_history_t& history, bool single)
{
return count_rising_edge_core(static_cast<uint64_t>(history), single);
}
uint8_t Buttons::count_falling_edge(button_history_t& history, bool single)
{
return count_rising_edge_core(~static_cast<uint64_t>(history), single);
}