px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
px_kbd_matrix.c
1/* =============================================================================
2 ____ ___ ____ ___ _ _ ___ __ __ ___ __ __ TM
3 | _ \ |_ _| / ___| / _ \ | \ | | / _ \ | \/ | |_ _| \ \/ /
4 | |_) | | | | | | | | | | \| | | | | | | |\/| | | | \ /
5 | __/ | | | |___ | |_| | | |\ | | |_| | | | | | | | / \
6 |_| |___| \____| \___/ |_| \_| \___/ |_| |_| |___| /_/\_\
7
8 Copyright (c) 2008 Pieter Conradie <https://piconomix.com>
9
10 License: MIT
11 https://github.com/piconomix/px-fwlib/blob/master/LICENSE.md
12
13 Title: px_kbd_matrix.h : Matrix Keyboard module
14 Author(s): Pieter Conradie
15 Creation Date: 2008-11-21
16
17============================================================================= */
18
19/* _____STANDARD INCLUDES____________________________________________________ */
20#include <string.h>
21
22/* _____PROJECT INCLUDES_____________________________________________________ */
23#include "px_kbd_matrix.h"
24
25/* _____LOCAL DEFINITIONS____________________________________________________ */
26
27/* _____MACROS_______________________________________________________________ */
28
29/* _____GLOBAL VARIABLES_____________________________________________________ */
30
31/* _____LOCAL VARIABLES______________________________________________________ */
32
33/* _____LOCAL FUNCTION DECLARATIONS__________________________________________ */
34
35/* _____LOCAL FUNCTIONS______________________________________________________ */
36static void kbd_key_state_init(px_kbd_matrix_key_state_t * key_state)
37{
38 memset(key_state, 0, sizeof(px_kbd_matrix_key_state_t));
39}
40
41static void kbd_key_state_copy(px_kbd_matrix_key_state_t * key_state_to,
42 const px_kbd_matrix_key_state_t * key_state_from)
43{
44 memcpy(key_state_to, key_state_from, sizeof(px_kbd_matrix_key_state_t));
45}
46
47static void kbd_key_state_set(px_kbd_matrix_key_state_t * key_state,
48 uint8_t key,
49 uint8_t state)
50{
51 uint8_t byte = key / 8;
52 uint8_t bit = key % 8;
53
54 if (state)
55 {
56 key_state->bit_mask[byte] |= (1 << bit);
57 }
58 else
59 {
60 key_state->bit_mask[byte] &= ~(1 << bit);
61 }
62}
63
64static uint8_t kbd_key_state_get(px_kbd_matrix_key_state_t * key_state,
65 uint8_t key)
66{
67 uint8_t byte = key / 8;
68 uint8_t bit = key % 8;
69
70 uint8_t state = (key_state->bit_mask[byte] >> bit) & 1;
71
72 return state;
73}
74
75static void kbd_key_state_and(px_kbd_matrix_key_state_t * key_x,
77 px_kbd_matrix_key_state_t * key_result)
78{
79 size_t i;
80
81 for (i = 0; i < sizeof(px_kbd_matrix_key_state_t); i++)
82 {
83 key_result->bit_mask[i] = key_x->bit_mask[i] & key_y->bit_mask[i];
84 }
85}
86
87static void kbd_key_state_or(px_kbd_matrix_key_state_t * key_x,
89 px_kbd_matrix_key_state_t * key_result)
90{
91 size_t i;
92
93 for (i = 0; i < sizeof(px_kbd_matrix_key_state_t); i++)
94 {
95 key_result->bit_mask[i] = key_x->bit_mask[i] | key_y->bit_mask[i];
96 }
97}
98
99static void kbd_key_state_xor(px_kbd_matrix_key_state_t * key_x,
101 px_kbd_matrix_key_state_t * key_result)
102{
103 size_t i;
104
105 for (i = 0; i < sizeof(px_kbd_matrix_key_state_t); i++)
106 {
107 key_result->bit_mask[i] = key_x->bit_mask[i] ^ key_y->bit_mask[i];
108 }
109}
110
111static bool px_kbd_matrix_phantom_key(px_link_list_t * key_press_list)
112{
113 px_kbd_matrix_key_t * key_press;
114 px_kbd_matrix_key_t * key_press_compare;
115 bool same_row_flag, same_col_flag;
116
117
118 // Start at first item in the list
119 key_press = (px_kbd_matrix_key_t *)px_link_list_get_item_first(key_press_list);
120
121 while(key_press != NULL)
122 {
123 // Reset flags
124 same_row_flag = false;
125 same_col_flag = false;
126
127 // Compare with other keys in the list
128 key_press_compare = (px_kbd_matrix_key_t *)px_link_list_get_item_first(key_press_list);
129 while(key_press_compare != NULL)
130 {
131 // Do not compare key with itself
132 if(key_press_compare != key_press)
133 {
134 // See if key is in the same row
135 if(key_press->row == key_press_compare->row)
136 {
137 same_row_flag = true;
138 }
139 // See if key is in the same column
140 if(key_press->col == key_press_compare->col)
141 {
142 same_col_flag = true;
143 }
144 // See if a key has been detected in the same row
145 // and another key has been detected in the same column
146 if( (same_row_flag == true) && (same_col_flag == true) )
147 {
148 // Phantom key detected
149 return true;
150 }
151 }
152 // Next item
153 key_press_compare = (px_kbd_matrix_key_t *)px_link_list_get_item_next(key_press_list, &key_press_compare->key_list_item);
154 }
155 // Next item
156 key_press = (px_kbd_matrix_key_t *)px_link_list_get_item_next(key_press_list, &key_press->key_list_item);
157 }
158
159 return false;
160}
161
162static void px_kbd_matrix_on_key_event(px_kbd_matrix_t * matrix,
163 uint8_t row,
164 uint8_t col,
166{
167 px_link_list_t * key_press_list = &(matrix->key_press_list);
168 px_link_list_item_t * key_press_item;
169 px_kbd_matrix_key_t * key_press;
170 uint8_t i;
171
172 if(event == PX_KBD_MATRIX_EVENT_PRESSED)
173 {
174 // See if key pressed list is full
175 if(px_link_list_is_full(key_press_list))
176 {
177 // Ignore extra key presses
178 return;
179 }
180
181 // Get an unused structure to add to the list
182 for(i = 0; i < PX_KBD_MATRIX_MAX_NR_KEYS_PRESSED; i++)
183 {
184 key_press = &(matrix->key_press_array[i]);
185
186 if(px_link_list_has_item(key_press_list, &(key_press->key_list_item)) == false)
187 {
188 // Add key to list
189 key_press->row = row;
190 key_press->col = col;
191 px_link_list_insert_item_end(key_press_list, &(key_press->key_list_item));
192 break;
193 }
194 }
195
196 // See if phantom key detection should be performed
197 if(px_link_list_get_item_count(key_press_list) >= 3)
198 {
199 // See if key forms the third side of a rectangle
200 if(px_kbd_matrix_phantom_key(key_press_list))
201 {
202 // Remove phantom key from the list
203 key_press = (px_kbd_matrix_key_t *)px_link_list_remove_item_last(key_press_list);
204 return;
205 }
206 }
207
208 // Reset counter
210 // Call event handler
211 matrix->on_key_event_handler(row, col, event);
212 }
213
215 {
216 // Start at first item in the list
217 key_press_item = px_link_list_get_item_first(key_press_list);
218 while(key_press_item != NULL)
219 {
220 // Access data of list item
221 key_press = (px_kbd_matrix_key_t *)key_press_item;
222
223 // See if key is in the list
224 if( (key_press->row == row) && (key_press->col == col) )
225 {
226 // Remove key from the list
227 px_link_list_remove_item(key_press_list, key_press_item);
228 // Call event handler
229 matrix->on_key_event_handler(row, col, event);
230 break;
231 }
232 // Next item
233 key_press_item = px_link_list_get_item_next(key_press_list, key_press_item);
234 }
235 }
236}
237
238/**
239 * Computes the new debounced key state and trigger key events if necessary.
240 *
241 * The debouncing is done both when the key is pressed and when it is released.
242 *
243 * @param matrix Pointer to matrix instance
244 *
245 * @retval true Changes have been detected
246 * @retval false No change have been detected
247 */
248static void px_kbd_matrix_debounce(px_kbd_matrix_t *matrix)
249{
250 static px_kbd_matrix_key_state_t pressed;
251 static px_kbd_matrix_key_state_t released;
252 static px_kbd_matrix_key_state_t new;
253 static px_kbd_matrix_key_state_t changed;
254
255 uint8_t i;
256 uint8_t nr_of_keys;
257
258 /*
259 * Debounce pressed keys
260 *
261 * pressed[x] will be 1 if all key_history[x] samples are 1
262 * pressed[x] will be 0 if any key_history[x] sample is 0
263 */
264 kbd_key_state_copy(&pressed, &(matrix->key_history[0]));
265 for (i = 1; i < PX_KBD_MATRIX_NR_SAMPLES; i++)
266 {
267 kbd_key_state_and(&pressed, &(matrix->key_history[i]), &pressed);
268 }
269
270 /*
271 * Debounce released keys
272 *
273 * released[x] will be 0 if all key_history[x] samples are 0
274 * released[x] will be 1 if any key_history[x] sample is 1
275 */
276 kbd_key_state_copy(&released, &(matrix->key_history[0]));
277 for (i = 1; i < PX_KBD_MATRIX_NR_SAMPLES; i++)
278 {
279 kbd_key_state_or(&released, &(matrix->key_history[i]), &released);
280 }
281
282 // Compute new key status
283 kbd_key_state_or(&(matrix->key_state), &pressed, &new);
284 kbd_key_state_and(&new, &released, &new);
285 // Compare with existing status
286 kbd_key_state_xor(&(matrix->key_state), &new, &changed);
287 // Process each pending event
288 nr_of_keys = matrix->nr_of_rows matrix->nr_of_columns;
289 for (i = 0; i < nr_of_keys; i++)
290 {
291 if (kbd_key_state_get(&changed, i))
292 {
293 // Deterimine if a key has been pressed or released
295 if(kbd_key_state_get(&new, i))
296 {
298 }
299 else
300 {
302 }
303 // Handle key event
304 px_kbd_matrix_on_key_event(matrix,
305 i / matrix->nr_of_columns,
306 i % matrix->nr_of_columns,
307 event);
308 }
309 }
310 // Save new key state
311 kbd_key_state_copy(&(matrix->key_state), &new);
312}
313
314/**
315 * Enable all rows and see if any key has been pressed.
316 *
317 * @param matrix Pointer to matrix instance
318 *
319 * @retval true A key has been pressed
320 * @retval false No key has been pressed
321 */
322static bool px_kbd_matrix_key_pressed(px_kbd_matrix_t * matrix)
323{
324 uint8_t row;
325 uint8_t col;
326 bool key_pressed_flag = false;
327
328 // Enable all rows
329 for (row = 0; row < matrix->nr_of_rows; row++)
330 {
331 matrix->set_row(row, true);
332 }
333 // Wait before columns must be sampled
334 matrix->delay(true);
335
336 // Scan each column
337 for (col = 0; col < matrix->nr_of_columns; col++)
338 {
339 if(matrix->get_col(col))
340 {
341 // A Key has been pressed
342 key_pressed_flag = true;
343 break;
344 }
345 }
346 // Disable all rows
347 for (row = 0; row < matrix->nr_of_rows; row++)
348 {
349 matrix->set_row(row, false);
350 }
351 // Wait for columns to return to their inactive state
352 matrix->delay(false);
353
354 return key_pressed_flag;
355}
356
357/**
358 * Retrieves a new sample set by reading the current state of the keyboard.
359 *
360 * @param matrix Pointer to matrix instance
361 */
362static void px_kbd_matrix_sample(px_kbd_matrix_t *matrix)
363{
364 uint8_t row, col, key;
365
366 // Pointer to array where new sample set must be stored
367 px_kbd_matrix_key_state_t *key_state = &(matrix->key_history[matrix->key_history_index]);
368
369 // Clear array
370 kbd_key_state_init(key_state);
371
372 // See if a key has been pressed
373 if(px_kbd_matrix_key_pressed(matrix))
374 {
375 // Determine which key(s) has been pressed
376 key = 0;
377 for (row = 0; row < matrix->nr_of_rows; row++)
378 {
379 // Enable row
380 matrix->set_row(row, true);
381 // Wait before columns must be sampled
382 matrix->delay(true);
383
384 // Scan each column
385 for (col=0; col < matrix->nr_of_columns; col++)
386 {
387 // See if key is being pressed
388 if(matrix->get_col(col))
389 {
390 // Set bit
391 kbd_key_state_set(key_state, key, 1);
392 }
393 // Next key
394 key++;
395 }
396
397 // Disable row
398 matrix->set_row(row, false);
399 // Wait for columns to return to their inactive state
400 matrix->delay(false);
401 }
402 }
403
404 // Update history index
406 {
407 // Wrap index
408 matrix->key_history_index = 0;
409 }
410}
411
412static void px_kbd_matrix_repeat(px_kbd_matrix_t * matrix)
413{
414 px_link_list_t * key_press_list = &(matrix->key_press_list);
415 px_kbd_matrix_key_t * key_press;
416
417 // See if a key(s) must be repeated
418 if(px_link_list_is_empty(key_press_list) == false)
419 {
420 // See if counter has expired
421 if(matrix->key_repeat_counter == 0)
422 {
423 // Reset counter
425 // Get last key pressed
426 key_press = (px_kbd_matrix_key_t *)px_link_list_get_item_last(key_press_list);
427 // Repeat key
428 matrix->on_key_event_handler(key_press->row, key_press->col, PX_KBD_MATRIX_EVENT_PRESSED);
429 }
430 else
431 {
432 // Decrement counter
433 (matrix->key_repeat_counter)--;
434 }
435 }
436 else
437 {
438 // Reset counter
440 }
441}
442
443/* _____GLOBAL FUNCTIONS_____________________________________________________ */
444void px_kbd_matrix_init(px_kbd_matrix_t * matrix,
445 uint8_t nr_of_rows,
446 uint8_t nr_of_columns,
450 px_kbd_matrix_on_key_event_t on_key_event_handler)
451{
452 uint8_t i;
453
454 // Initialise structure
455 matrix->nr_of_rows = nr_of_rows;
456 matrix->nr_of_columns = nr_of_columns;
457 matrix->set_row = set_row;
458 matrix->get_col = get_col;
459 matrix->delay = delay;
460 matrix->on_key_event_handler = on_key_event_handler;
462
463 // Initialise key state array
464 kbd_key_state_init(&(matrix->key_state));
465
466 // Initialise history state array(s)
467 for(i = 0; i < PX_KBD_MATRIX_NR_SAMPLES; i++)
468 {
469 kbd_key_state_init(&(matrix->key_history[i]));
470 }
471
472 // Initialise linked list
474
475 // Initialise items used for linked list
476 for(i = 0; i < PX_KBD_MATRIX_MAX_NR_KEYS_PRESSED; i++)
477 {
478 px_link_list_item_init(&(matrix->key_press_list), &(matrix->key_press_array[i].key_list_item));
479 matrix->key_press_array[i].row = 0;
480 matrix->key_press_array[i].col = 0;
481 }
482}
483
484void px_kbd_matrix_scan(px_kbd_matrix_t * matrix)
485{
486 px_kbd_matrix_sample (matrix);
487 px_kbd_matrix_debounce(matrix);
488 px_kbd_matrix_repeat (matrix);
489}
490
#define NULL
NULL pointer.
Definition: px_defs.h:49
px_kbd_matrix_set_row_t set_row
Function handler to write a row output.
px_kbd_matrix_get_col_t get_col
Function handler to read a column input.
uint8_t nr_of_columns
Number of columns in matrix.
px_kbd_matrix_on_key_event_t on_key_event_handler
Function that will be called if a key is pressed or released.
uint8_t key_history_index
Wrapping index (used as a ring buffer)
px_kbd_matrix_key_t key_press_array[PX_KBD_MATRIX_MAX_NR_KEYS_PRESSED]
Storage for linked list to remember which keys are pressed.
px_kbd_matrix_key_state_t key_history[PX_KBD_MATRIX_NR_SAMPLES]
Array of bit masks that stores the key state history.
px_link_list_t key_press_list
Linked list of pressed keys.
px_kbd_matrix_key_state_t key_state
Bit mask that stores the current state of the keys.
px_kbd_matrix_delay_t delay
Function handler to read a column input.
uint16_t key_repeat_counter
Counter to determine if a pressed key must repeated (typematic)
uint8_t nr_of_rows
Number of rows in matrix.
void(* px_kbd_matrix_on_key_event_t)(uint8_t row, uint8_t col, px_kbd_matrix_event_t event)
Function that will be called when a key event has taken place.
#define PX_KBD_MATRIX_MAX_NR_KEYS_PRESSED
Maximum number of keys that may be pressed simultaneously.
void(* px_kbd_matrix_set_row_t)(uint8_t row, bool active)
Function that will be called to set a row output.
bool(* px_kbd_matrix_get_col_t)(uint8_t col)
Function that will be called to read a column input.
#define KBD_KEY_REPEAT_DELAY
Delay between pressed key repeats.
px_kbd_matrix_event_t
Key event types.
void(* px_kbd_matrix_delay_t)(bool active)
Function that will be called for row column scanning delay.
#define KBD_DELAY_BEFORE_REPEATING_KEY
Delay before a pressed key will be repeated.
#define PX_KBD_MATRIX_NR_SAMPLES
Number of samples that are necessary to debounce a keypress.
@ PX_KBD_MATRIX_EVENT_PRESSED
Key has been pressed.
@ PX_KBD_MATRIX_EVENT_RELEASED
Key has been released.
Key state bit mask (1 = key pressed; 0 = key released)
Linked list item that describes which key has been pressed.
Keyboard matrix definition and state.
bool px_link_list_is_empty(px_link_list_t *list)
See if the list is empty.
Definition: px_link_list.c:53
px_link_list_item_t * px_link_list_get_item_next(px_link_list_t *list, px_link_list_item_t *item)
Get a pointer to the next item in the list (after the specified item).
Definition: px_link_list.c:97
size_t px_link_list_get_item_count(px_link_list_t *list)
Get the number of items in the list.
Definition: px_link_list.c:82
px_link_list_item_t * px_link_list_remove_item_last(px_link_list_t *list)
Remove last item from the list.
Definition: px_link_list.c:281
bool px_link_list_insert_item_end(px_link_list_t *list, px_link_list_item_t *item)
Add item to the end of the list.
Definition: px_link_list.c:138
px_link_list_item_t * px_link_list_get_item_last(px_link_list_t *list)
Get a pointer to the last item in the list.
Definition: px_link_list.c:92
px_link_list_item_t * px_link_list_get_item_first(px_link_list_t *list)
Get a pointer to the first item in the list.
Definition: px_link_list.c:87
bool px_link_list_has_item(px_link_list_t *list, px_link_list_item_t *item)
See if item is in the list.
Definition: px_link_list.c:342
void px_link_list_init(px_link_list_t *list, size_t max_nr_of_items)
Initialises a linked list structure.
Definition: px_link_list.c:37
void px_link_list_remove_item(px_link_list_t *list, px_link_list_item_t *item)
Remove item from the list.
Definition: px_link_list.c:311
bool px_link_list_is_full(px_link_list_t *list)
See if the list is full.
Definition: px_link_list.c:65
void px_link_list_item_init(px_link_list_t *list, px_link_list_item_t *item)
Initialises a list item.
Definition: px_link_list.c:46