-
Notifications
You must be signed in to change notification settings - Fork 0
/
keypad.h
341 lines (301 loc) · 14.8 KB
/
keypad.h
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/**
* @file Keypad.h
* @author Aditya Agarwal (aditya.agarwal@dumblebots.com)
*
* @brief Simple Library to use a 4x4 Keypad with MBed OS asynchronously
*
* @copyright Copyright (c) 2023
*
*/
#ifndef __KEYPAD_H__
#define __KEYPAD_H__
#include "mbed.h"
#include "platform/CircularBuffer.h"
/** Maximum number of presses/releases/long-presses to queue up before overwriting */
constexpr auto KEYPAD_BUFFER_LEN = 16;
/** Number of rows on a keypad */
constexpr auto KEYPAD_NUM_ROWS = 4;
/** Number of columns on a keypad */
constexpr auto KEYPAD_NUM_COLS = 4;
/**
* @brief Class that provides a simple interface to use a 4x4 keypad asynchronously
*
* @remark At a time, only a single button on the keypad can be pressed, pressing multiple buttons
* at the same time will only cause the earliest to be accepted, while all others are rejected
*/
class Keypad {
/**
* @brief Enumeration consisting of all the possible states of the keypad state machine
*
* @todo Add link to state machine diagram in README
*/
enum ButtonState {
RELEASED,
PRESS_BOUNCING,
PRESSED,
RELEASE_BOUNCING,
LONG_PRESSED
};
/** Pins connected to the Keypad's rows */
DigitalOut row[KEYPAD_NUM_ROWS];
/** Pins connected to the Keypad's cols */
InterruptIn col[KEYPAD_NUM_COLS];
/** Thread used to execute callback functions */
Thread *threadHandle {nullptr};
/** Event Queue on which calls to the callback functions will be posted */
EventQueue queue;
/** Whether the Callback on a button press is registered or not */
bool pressCbEnabled {false};
/** Callback function that is called when a button is pressed */
Callback<void(uint32_t, uint32_t)> onPress;
/** Whether the Callback on a button release is registered or not */
bool releaseCbEnabled {false};
/** Callback function that is called when a button is released */
Callback<void(uint32_t, uint32_t)> onRelease;
/** Whether the Callback on a button being long-pressed is registered or not */
bool longpressCbEnabled {false};
/** Callback function that is called when a button is long-pressed */
Callback<void(uint32_t, uint32_t)> onLongpress;
/** State of the button in the state machine */
ButtonState state {ButtonState::RELEASED};
/** Timeout to indicate when to switch from PRESS_BOUNCING (button bouncing after being pressed) to PRESSED/RELEASED */
Timeout toRowScan;
/** Timeout to indicate when to switch from PRESSED/LONG_PRESSED to RELEASE_BOUNCING (button bouncing after being released) */
Timeout toButtonScan;
/** Timeout to indicate when to switch from PRESSED to LONG_PRESSED */
Timeout toLongPressed;
/** Confirmed Row on which the button was pressed (after row-scanning) */
uint32_t pressedRow {};
/** Confirmed Column on which the button was pressed (after row-scanning) */
uint32_t pressedCol {};
public:
Keypad() = delete;
/**
* @brief Construct a new keypad object
*
* @param r0 Microcontroller Pin to which Row Pin 0 of the keypad is connected
* @param r1 Microcontroller Pin to which Row Pin 1 of the keypad is connected
* @param r2 Microcontroller Pin to which Row Pin 2 of the keypad is connected
* @param r3 Microcontroller Pin to which Row Pin 3 of the keypad is connected
* @param c0 Microcontroller Pin to which Column Pin 0 of the keypad is connected
* @param c1 Microcontroller Pin to which Column Pin 1 of the keypad is connected
* @param c2 Microcontroller Pin to which Column Pin 2 of the keypad is connected
* @param c3 Microcontroller Pin to which Column Pin 3 of the keypad is connected
*
*/
Keypad(PinName r0, PinName r1, PinName r2, PinName r3, PinName c0, PinName c1, PinName c2, PinName c3);
/**
* @brief Initializes the object by allocating and starting a thread to dispatch callbacks on
*
* @remark If the thread was already initialized, then the Keypad::finalize() method must be called
* before trying to re-initialize
*
* @attention Can not call this method from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
* @return true if the thread was successfully allocated and started, false otherwise
*
*/
bool initialize();
/**
* @brief Finalizes the object by stopping and freeing the thread on which callbacks are dispatched
*
* @remark If the thread was not initialized or finalized before, then the Keypad::initialize()
* method must be called before trying to re-finalize it
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention Can not call this method from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
* @return true if the thread was successfully stopped and freed, false otherwise
*
*/
bool finalize();
/**
* @brief Checks if the object was initialized and callbacks can be dispatched correctly
*
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
*
* @return true if the thread is running, false otherwise
*
*/
bool is_initialized() const;
/**
* @brief Register a callback function to be called whenever a button is pressed
*
* @remark If a callback was already registered, then the current one replaces it
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
*
* @param cb Callback when a button is pressed
* , the first argument to the callback is the row which the pressed button belongs to
* , the second argument to the callback is the column which the pressed button belongs to
*
*/
void register_onpress(Callback<void(uint32_t, uint32_t)> cb);
/**
* @brief Remove the previously registered callback function for when a button is pressed
*
* @remark Has no effect if no callback was registered
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
*/
void remove_onpress();
/**
* @brief Checks if a callback function is registered for when a button is pressed
*
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @return true if a callback is currently registered, false otherwise
*
*/
bool is_onpress_registered() const;
/**
* @brief Register a callback function to be called whenever a button is released
*
* @remark If a callback was already registered, then the current one replaces it
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
* @param cb Callback when a button is released
* , the first argument to the callback is the row which the released button belongs to
* , the second argument to the callback is the column which the released button belongs to
*
*/
void register_onrelease(Callback<void(uint32_t, uint32_t)> cb);
/**
* @brief Remove the previously registered callback function for when a button is released
*
* @remark Has no effect if no callback was registered
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
*/
void remove_onrelease();
/**
* @brief Checks if a callback function is registered for when a button is released
*
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @return true if a callback is currently registered, false otherwise
*
*/
bool is_onrelease_registered() const;
/**
* @brief Register a callback function to be called whenever a button is long-pressed
*
* @remark If a callback was already registered, then the current one replaces it
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
* @param cb Callback when a button is long-pressed
* , the first argument to the callback is the row which the long-pressed button belongs to
* , the second argument to the callback is the column which the long-pressed button belongs to
*
*/
void register_onlongpress(Callback<void(uint32_t, uint32_t)> cb);
/**
* @brief Remove the previously registered callback function for when a button was long-pressed
*
* @remark Has no effect if no callback was registered
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @attention This function can be called from ISR context
* @attention It is unsafe to call this method from multiple threads concurrently
*
*/
void remove_onlongpress();
/**
* @brief Checks if a callback function is registered for when a button is long-pressed
*
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @remark Any registered callbacks are preserved when the Keypad::initialize() and Keypad::finalize()
* methods are called, they must be explicitly disabled by calling their respective remove
* methods
*
* @return true if a callback is currently registered, false otherwise
*
*/
bool is_onlongpress_registered() const;
private:
/**
* @brief Handler for when a fall interrupt is received on a column pin
*
* @tparam curCol The column of the keypad that this handler should cater to
*
*/
template <uint32_t curCol>
void fall_handler ();
/**
* @brief Handler for a when a rise interrupt is received on a column pin
*
* @tparam curCol The column of the keypad that this handler should cater to
*
*/
template <uint32_t curCol>
void rise_handler ();
/**
* @brief Scans the rows after a button press on a column was detected to determine its row
*
* @tparam curCol The column of the keypad that this handler should cater to
*
*/
template <uint32_t curCol>
void row_scan_handler ();
/**
* @brief Scans the previously pressed button after a button release was detected to confirm
*
*/
void button_scan_handler ();
/**
* @brief Transitions a button to the long-pressed state onec it has been held down
* past the threshold duration
*
*/
void long_press_handler ();
/**
* @brief Function for queue to run callbacks on
*
* @remark Calling break_dispatch() on the queue will cause the thread to wait for graceful termination
*
*/
void dispatch_events ();
};
#endif //__KEYPAD_H__