GPS Device
Loading...
Searching...
No Matches
button.c
Go to the documentation of this file.
1
11
12#include "button.h"
14#include "cmd.h"
15#include "common.h"
16#include "err.h"
17#include "stm32f401re_gpio.h"
18#include "configuration.h"
19#include "log.h"
20#include "modules.h"
21#include "init.h"
22
36 char name[CONFIG_BUTTON_MAX_NAME_LEN];
37
40
43
46
49
51 uint32_t held_limit_ms;
52
55
58
61
64
66 bool pushed;
67
69 bool held;
70
72 bool in_use;
73};
74
84struct internal_state_s {
86 struct button_handle_s buttons[CONFIG_BUTTON_MAX_OBJECTS];
87
89 uint8_t button_num;
90
93
96
98 bool initialized;
99
102};
103
106
107/* ---- Forward declarations for command handlers ---- */
108static ERR_te button_getpushed_handler(uint32_t argc, char **argv);
109static ERR_te button_getheld_handler(uint32_t argc, char **argv);
110static ERR_te button_cmd_info_handler(uint32_t argc, char **argv);
111
121 {
122 .name = "getpushed",
123 .help = "Gets the pushed state, usage: button getpushed <button>",
124 .handler = button_getpushed_handler
125 },
126 {
127 .name = "getheld",
128 .help = "Gets the held state, usage: button getheld <button>",
129 .handler = button_getheld_handler
130 },
131 {
132 .name = "info",
133 .help = "Shows button information, usage: button info",
134 .handler = button_cmd_info_handler
135 }
136};
137
147 .cmds_ptr = button_cmds,
148 .num_cmds = sizeof(button_cmds) / sizeof(button_cmds[0]),
149 .name = "button",
150 .log_level_ptr = &internal_state.log_level
151};
152
157
160 ERR_te err;
161
162 if(internal_state.initialized) {
164 }
165
166 internal_state = (struct internal_state_s){ 0 };
167
168 internal_state.log_level = LOG_LEVEL_INFO;
170 internal_state.initialized = true;
171 internal_state.started = false;
172
173 init_log();
174 init_systick();
175
177 if(err != ERR_OK) {
178 LOG_ERROR(
179 internal_state.subsys,
180 internal_state.log_level,
181 "button_init_subsys: cmd_register error"
182 );
183
184 return err;
185 }
186 LOG_INFO(
187 internal_state.subsys,
188 internal_state.log_level,
189 "button_init_subsys: subsys initialized"
190 );
191
192 return ERR_OK;
193}
194
197 if(internal_state.initialized && !internal_state.started) {
198 internal_state = (struct internal_state_s){ 0 };
199
201 }
202 else {
203 LOG_ERROR(
204 internal_state.subsys,
205 internal_state.log_level,
206 "button_deinit_subsys: subsys is not initialized or not stopped"
207 );
208
210 }
211 LOG_INFO(
212 internal_state.subsys,
213 internal_state.log_level,
214 "button_deinit_subsys: subsys deinitialized"
215 );
216
217 return ERR_OK;
218}
219
222 if(internal_state.initialized && !internal_state.started) {
223 internal_state.started = true;
224
225 LOG_INFO(
226 internal_state.subsys,
227 internal_state.log_level,
228 "button_start_subsys: subsys started"
229 );
230 }
231 else {
232 LOG_ERROR(
233 internal_state.subsys,
234 internal_state.log_level,
235 "button_start_subsys: subsys not initialized or already started"
236 );
237
238 return ERR_UNKNOWN;
239 }
240
241 return ERR_OK;
242}
243
246 if(internal_state.initialized && internal_state.started) {
247 internal_state.started = false;
248
249 LOG_INFO(
250 internal_state.subsys,
251 internal_state.log_level,
252 "button_stop_subsys: subsys stopped"
253 );
254 }
255 else {
256 LOG_ERROR(
257 internal_state.subsys,
258 internal_state.log_level,
259 "button_stop_subsys: subsys not initialized or already already stopped"
260 );
261
262 return ERR_UNKNOWN;
263 }
264
265 return ERR_OK;
266}
267
270 if(!internal_state.initialized) {
271 LOG_INFO(
272 internal_state.subsys,
273 internal_state.log_level,
274 "button_init_handle: subsys not initialized"
275 );
276
278 }
279
280 if(internal_state.button_num == CONFIG_BUTTON_MAX_OBJECTS) {
281 LOG_ERROR(
282 internal_state.subsys,
283 internal_state.log_level,
284 "button_init_handle: subsystem out of memory space"
285 );
286
288 }
289
290 if(!button_cfg) {
291 LOG_ERROR(
292 internal_state.subsys,
293 internal_state.log_level,
294 "button_init_handle: invalid argument"
295 );
297 }
298
299 GPIO_CFG_ts gpio = { 0 };
300 gpio.port = button_cfg->gpio_port;
301 gpio.pin = button_cfg->gpio_pin;
303 gpio.mode = GPIO_MODE_INPUT;
304
305 gpio_init(&gpio);
306
307 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
308 if(internal_state.buttons[i].in_use == false) {
309 str_cpy(
310 internal_state.buttons[i].name,
311 button_cfg->name,
312 get_str_len(button_cfg->name) + 1
313 );
314
315 internal_state.buttons[i].debounce_limit_ms = button_cfg->debounce_limit_ms;
316 internal_state.buttons[i].gpio_port = button_cfg->gpio_port;
317 internal_state.buttons[i].gpio_pin = button_cfg->gpio_pin;
318 internal_state.buttons[i].pushed_type = button_cfg->pushed_type;
319 internal_state.buttons[i].held_limit_ms = button_cfg->held_limit_ms;
320 internal_state.buttons[i].held = false;
321 internal_state.buttons[i].held_started_ms = 0;
322 internal_state.buttons[i].debounce_started = false;
323 internal_state.buttons[i].held_started = false;
324 internal_state.buttons[i].pushed = false;
325 internal_state.buttons[i].debounce_started_ms = 0;
326 internal_state.buttons[i].in_use = true;
327
328 *button_handle_o = &internal_state.buttons[i];
329
330 internal_state.button_num++;
331
332 LOG_INFO(
333 internal_state.subsys,
334 internal_state.log_level,
335 "button_init_handle: button handle %s initialized",
336 internal_state.buttons[i].name
337 );
338
339 break;
340 }
341 }
342
343 return ERR_OK;
344}
345
348 if(internal_state.started) {
349 LOG_INFO(
350 internal_state.subsys,
351 internal_state.log_level,
352 "button_deinit_handle: subsys not stopped"
353 );
354 return ERR_ILLEGAL_ACTION;
355 }
356
357 if(internal_state.button_num == 0) {
358 LOG_ERROR(
359 internal_state.subsys,
360 internal_state.log_level,
361 "button_deinit_handle: no such handle to deinitialize"
362 );
363
365 }
366
367 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
368 if(&internal_state.buttons[i] == button_handle) {
369 uint8_t name_len = get_str_len(internal_state.buttons[i].name) + 1;
370 char name[name_len];
371 str_cpy(name, internal_state.buttons[i].name, name_len);
372
373 internal_state.buttons[i] = (BUTTON_HANDLE_ts){ 0 };
374
375 internal_state.button_num--;
376
377 LOG_INFO(
378 internal_state.subsys,
379 internal_state.log_level,
380 "button_deinit_handle: io handle %s deinitialized",
381 name
382 );
383
384 break;
385 }
386
387 if(i == CONFIG_BUTTON_MAX_OBJECTS - 1) {
388 LOG_ERROR(
389 internal_state.subsys,
390 internal_state.log_level,
391 "button_deinit_handle: no such handle to deinitialize"
392 );
393
395 }
396 }
397
398 return ERR_OK;
399}
400
403 if(!internal_state.initialized || !internal_state.started) {
404 LOG_ERROR(
405 internal_state.subsys,
406 internal_state.log_level,
407 "button_run_handle: subsystem not initialized or started"
408 );
409
410 return ERR_UNKNOWN;
411 }
412
413 PIN_STATUS_te pin_status = gpio_read(button_handle->gpio_port, button_handle->gpio_pin);
414
415 // If the debounce has not yet been completed, complete it (if it's enabled)
416 if(button_handle->pushed == false) {
417 if((button_handle->pushed_type == BUTTON_PUSHED_TYPE_HIGH &&
418 pin_status == HIGH) ||
419 (button_handle->pushed_type == BUTTON_PUSHED_TYPE_LOW &&
420 pin_status == LOW)) {
421 uint32_t current_ms = systick_get_ms();
422
423 if(!button_handle->debounce_started) {
424 button_handle->debounce_started = true;
425 button_handle->debounce_started_ms = current_ms;
426 }
427
428 if(button_handle->debounce_started &&
429 current_ms - button_handle->debounce_started_ms >= button_handle->debounce_limit_ms) {
430 button_handle->pushed = true;
431 button_handle->debounce_started = false;
432 }
433 }
434 else {
435 if(button_handle->debounce_started) {
436 button_handle->debounce_started = false;
437 button_handle->debounce_started_ms = 0;
438 }
439 }
440 }
441
442 // If the held property is enabled, check if button is held for long enough
443 if(button_handle->pushed == true && button_handle->held == false) {
444 if((button_handle->pushed_type == BUTTON_PUSHED_TYPE_HIGH &&
445 pin_status == HIGH) ||
446 (button_handle->pushed_type == BUTTON_PUSHED_TYPE_LOW &&
447 pin_status == LOW)) {
448 uint32_t current_ms = systick_get_ms();
449
450 if(!button_handle->held_started) {
451 button_handle->held_started = true;
452 button_handle->held_started_ms = current_ms;
453 }
454
455 if(button_handle->held_started &&
456 current_ms - button_handle->held_started_ms >= button_handle->held_limit_ms) {
457 button_handle->held = true;
458 button_handle->held_started = false;
459 }
460 }
461 else {
462 if(button_handle->held_started) {
463 button_handle->held_started = false;
464 button_handle->held_started_ms = 0;
465 }
466 }
467 }
468
469 // If the button has been pushed, check for button release then restore states and counters
470 if(button_handle->pushed == true) {
471 if((button_handle->pushed_type == BUTTON_PUSHED_TYPE_HIGH &&
472 pin_status == LOW) ||
473 (button_handle->pushed_type == BUTTON_PUSHED_TYPE_LOW &&
474 pin_status == HIGH)) {
475 uint32_t current_ms = systick_get_ms();
476
477 if(!button_handle->debounce_started) {
478 button_handle->debounce_started = true;
479 button_handle->debounce_started_ms = current_ms;
480 }
481
482 if(button_handle->debounce_started &&
483 current_ms - button_handle->debounce_started_ms >= button_handle->debounce_limit_ms) {
484 button_handle->pushed = false;
485 button_handle->held = false;
486 button_handle->debounce_started = false;
487 button_handle->held_started = false;
488 }
489 }
490 else {
491 if(button_handle->debounce_started) {
492 button_handle->debounce_started = false;
493 button_handle->debounce_started_ms = 0;
494 }
495 }
496 }
497
498 return ERR_OK;
499}
500
503 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
504 if(internal_state.buttons[i].in_use == true) {
505 ERR_te err = button_run_handle(&internal_state.buttons[i]);
506 if(err != ERR_OK) {
507 return err;
508 }
509 }
510 }
511
512 return ERR_OK;
513}
514
516ERR_te button_get_pushed_state(BUTTON_HANDLE_ts const *button_handle, bool *pushed_state_o) {
517 if(!internal_state.initialized || !internal_state.started) {
518 LOG_ERROR(
519 internal_state.subsys,
520 internal_state.log_level,
521 "button_get_pushed_state: handle not initialized or subsystem not started"
522 );
523
524 return ERR_ILLEGAL_ACTION;
525 }
526
527 *pushed_state_o = button_handle->pushed;
528
529 return ERR_OK;
530}
531
533ERR_te button_get_held_state(BUTTON_HANDLE_ts const *button_handle, bool *held_state_o) {
534 if(!internal_state.initialized || !internal_state.started) {
535 LOG_ERROR(
536 internal_state.subsys,
537 internal_state.log_level,
538 "button_get_held_state: handle not initialized or subsystem not started"
539 );
540
541 return ERR_ILLEGAL_ACTION;
542 }
543
544 *held_state_o = button_handle->held;
545
546 return ERR_OK;
547}
548
550
555
574static ERR_te button_getpushed_handler(uint32_t argc, char **argv) {
575 if(argc != 3) {
576 LOG_ERROR(
577 internal_state.subsys,
578 internal_state.log_level,
579 "button_getpushed_handler: invalid arguments"
580 );
582 }
583
584 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
585 if(str_cmp(argv[2], internal_state.buttons[i].name) == true) {
586 bool pushed_state;
587 ERR_te err = button_get_pushed_state(&internal_state.buttons[i], &pushed_state);
588 if(err != ERR_OK) {
589 return err;
590 }
591
592 LOG_INFO(
593 internal_state.subsys,
594 internal_state.log_level,
595 "button_getpushed_handler: %s pushed state: %d",
596 internal_state.buttons[i].name,
597 pushed_state
598 );
599
600 return ERR_OK;
601 }
602
603 if(i == internal_state.button_num - 1) {
604 LOG_ERROR(
605 internal_state.subsys,
606 internal_state.log_level,
607 "button_getpushed_handler: invalid arguments"
608 );
610 }
611 }
612
613 return ERR_OK;
614}
615
634static ERR_te button_getheld_handler(uint32_t argc, char **argv) {
635 if(argc != 3) {
636 LOG_ERROR(
637 internal_state.subsys,
638 internal_state.log_level,
639 "button_getheld_handler: invalid arguments"
640 );
642 }
643
644 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
645 if(str_cmp(argv[2], internal_state.buttons[i].name) == true) {
646 bool held_state;
647 ERR_te err = button_get_held_state(&internal_state.buttons[i], &held_state);
648 if(err != ERR_OK) {
649 return err;
650 }
651
652 LOG_INFO(
653 internal_state.subsys,
654 internal_state.log_level,
655 "button_getheld_handler: %s held state: %d",
656 internal_state.buttons[i].name,
657 held_state
658 );
659
660 return ERR_OK;
661 }
662
663 if(i == internal_state.button_num - 1) {
664 LOG_ERROR(
665 internal_state.subsys,
666 internal_state.log_level,
667 "button_getheld_handler: invalid arguments"
668 );
670 }
671 }
672
673 return ERR_OK;
674}
675
692static ERR_te button_cmd_info_handler(uint32_t argc, char **argv) {
693 if(argc != 2) {
694 LOG_ERROR(
695 internal_state.subsys,
696 internal_state.log_level,
697 "button_cmd_info_handler: invalid arguments"
698 );
699 return ERR_INVALID_ARGUMENT;
700 }
701
702 LOG_INFO(internal_state.subsys, internal_state.log_level, "Printing button information:");
703
704 for(uint32_t i = 0; i < CONFIG_BUTTON_MAX_OBJECTS; i++) {
705 if(internal_state.buttons[i].in_use == true) {
706 LOG_INFO(
707 internal_state.subsys,
708 internal_state.log_level,
709 "name: %s",
710 internal_state.buttons[i].name
711 );
712 }
713 }
714
715 return ERR_OK;
716}
717
static struct internal_state_s internal_state
Singleton instance of the SysTick driver internal state.
Arm Cortex-M4 SysTick driver public API.
static CMD_CLIENT_INFO_ts button_cmd_client_info
Registration descriptor passed to the command subsystem.
Definition button.c:146
static CMD_INFO_ts button_cmds[]
Table of CLI commands registered by the button subsystem.
Definition button.c:120
Button module public API.
Command subsystem public API.
Common utility module public API.
System-wide error code definitions.
static ERR_te button_cmd_info_handler(uint32_t argc, char **argv)
CLI handler for the "info" command. Logs the names of all registered buttons.
Definition button.c:692
static ERR_te button_getpushed_handler(uint32_t argc, char **argv)
CLI handler for the "getpushed" command. Reports the debounced pushed state of a button.
Definition button.c:574
static ERR_te button_getheld_handler(uint32_t argc, char **argv)
CLI handler for the "getheld" command. Reports the held state of a button.
Definition button.c:634
ERR_te button_init_subsys(void)
Initializes the button subsystem.
Definition button.c:159
ERR_te button_run_handle_all(void)
Runs the state machine for all registered button handles.
Definition button.c:502
ERR_te button_deinit_handle(BUTTON_HANDLE_ts const *button_handle)
Deinitializes a button handle.
Definition button.c:347
ERR_te button_stop_subsys(void)
Stops the button subsystem.
Definition button.c:245
ERR_te button_get_pushed_state(BUTTON_HANDLE_ts const *button_handle, bool *pushed_state_o)
Retrieves the pushed state of a button.
Definition button.c:516
ERR_te button_init_handle(BUTTON_CFG_ts *button_cfg, BUTTON_HANDLE_ts **button_handle_o)
Initializes and registers a button handle.
Definition button.c:269
ERR_te button_get_held_state(BUTTON_HANDLE_ts const *button_handle, bool *held_state_o)
Retrieves the held state of a button.
Definition button.c:533
ERR_te button_run_handle(BUTTON_HANDLE_ts *button_handle)
Runs the state machine for a single button handle.
Definition button.c:402
ERR_te button_start_subsys(void)
Starts the button subsystem.
Definition button.c:221
ERR_te button_deinit_subsys(void)
Deinitializes the button subsystem.
Definition button.c:196
BUTTON_PUSHED_TYPE_te
Defines the active level of a button press.
Definition button.h:58
struct button_handle_s BUTTON_HANDLE_ts
Opaque handle representing a button instance.
Definition button.h:110
@ BUTTON_PUSHED_TYPE_HIGH
Definition button.h:64
@ BUTTON_PUSHED_TYPE_LOW
Definition button.h:61
ERR_te cmd_deregister(CMD_CLIENT_INFO_ts const *cmd_client_info)
Deregisters a client from the command subsystem.
Definition cmd.c:66
ERR_te cmd_register(CMD_CLIENT_INFO_ts *cmd_client_info)
Registers a client with the command subsystem.
Definition cmd.c:51
bool str_cmp(const char *str1, const char *str2)
Compares two null-terminated strings for equality.
Definition common.c:248
int str_cpy(char *str_to, const char *str_from, uint32_t len)
Copies a null-terminated string into a destination buffer.
Definition common.c:333
uint32_t get_str_len(char const *str)
Returns the length of a string, excluding the null terminator.
Definition common.c:22
PIN_STATUS_te
Represents the logical level of a GPIO pin.
Definition common.h:84
@ HIGH
Definition common.h:89
@ LOW
Definition common.h:86
uint32_t systick_get_ms(void)
Returns the number of milliseconds elapsed since SysTick was initialized.
ERR_te
Standard return type used by all public API functions.
Definition err.h:35
@ ERR_UNINITIALZIED_OBJECT
Definition err.h:42
@ ERR_ILLEGAL_ACTION
Definition err.h:50
@ ERR_DEINITIALIZATION_FAILURE
Definition err.h:44
@ ERR_UNKNOWN
Definition err.h:37
@ ERR_OK
Definition err.h:36
@ ERR_NOT_ENOUGH_SPACE
Definition err.h:49
@ ERR_INITIALIZATION_FAILURE
Definition err.h:43
@ ERR_INVALID_ARGUMENT
Definition err.h:38
ERR_te init_log(void)
Initializes the logging subsystem.
Definition init.c:25
ERR_te init_systick(void)
Initializes the SysTick timer.
Definition init.c:40
#define LOG_ERROR(subsys, lvl, fmt,...)
Definition log.h:258
#define LOG_INFO(subsys, lvl, fmt,...)
Definition log.h:255
LOG_LEVEL_te
Log severity levels, in ascending order of severity.
Definition log.h:63
@ LOG_LEVEL_INFO
Definition log.h:64
MODULES_te
Identifies a subsystem for use in logging and CLI output.
Definition modules.h:42
@ MODULES_BUTTON
Definition modules.h:47
PIN_STATUS_te gpio_read(GPIO_REGDEF_ts const *gpio_port, uint8_t gpio_pin)
Reads the current logic level of a GPIO input pin.
void gpio_init(GPIO_CFG_ts *gpio_cfg)
Initializes a GPIO pin according to the given configuration.
GPIO_PIN_te
GPIO pin number within a port (0–15).
@ GPIO_OUTPUT_TYPE_PUSHPULL
@ GPIO_MODE_INPUT
Common initialization public API.
Log subsystem public API.
System module identifier definitions.
STM32F401RE GPIO driver public API.
Configuration structure for initializing a button instance.
Definition button.h:75
uint32_t debounce_limit_ms
Definition button.h:94
uint32_t held_limit_ms
Definition button.h:97
char name[CONFIG_BUTTON_MAX_NAME_LEN]
Definition button.h:78
GPIO_PIN_te gpio_pin
Definition button.h:84
GPIO_REGDEF_ts * gpio_port
Definition button.h:81
BUTTON_PUSHED_TYPE_te pushed_type
Definition button.h:91
Describes a subsystem client registering with the command module.
Definition cmd.h:92
Describes a single command exposed by a client.
Definition cmd.h:72
Configuration structure for initializing a GPIO pin.
GPIO_REGDEF_ts * port
GPIO_OUTPUT_TYPE_te output_type
GPIO_PIN_te pin
GPIO_MODE_te mode
GPIO peripheral register map.
Definition stm32f401re.h:95
Internal structure representing a single button instance.
Definition button.c:34
bool held_started
Definition button.c:63
BUTTON_PUSHED_TYPE_te pushed_type
Definition button.c:45
uint32_t debounce_started_ms
Definition button.c:54
GPIO_REGDEF_ts * gpio_port
Definition button.c:39
uint32_t held_started_ms
Definition button.c:57
bool debounce_started
Definition button.c:60
GPIO_PIN_te gpio_pin
Definition button.c:42
uint32_t debounce_limit_ms
Definition button.c:48
char name[CONFIG_BUTTON_MAX_NAME_LEN]
Definition button.c:36
uint32_t held_limit_ms
Definition button.c:51
Internal state of the SysTick driver.
MODULES_te subsys
Definition button.c:95
struct button_handle_s buttons[CONFIG_BUTTON_MAX_OBJECTS]
Definition button.c:86
uint8_t button_num
Definition button.c:89
LOG_LEVEL_te log_level
Definition button.c:92