diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-01-26 14:27:55 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2018-06-30 14:28:51 -0400 |
commit | 38d7b9ada0d61971fdaf3e853892e7fb4051b36a (patch) | |
tree | fc629b0ec267cab79219afe7554b6e34d14595cb /src | |
parent | 4061026c2548ff84873b06a9943d9d6ea3e2ddee (diff) | |
download | kutter-38d7b9ada0d61971fdaf3e853892e7fb4051b36a.tar.gz kutter-38d7b9ada0d61971fdaf3e853892e7fb4051b36a.tar.xz kutter-38d7b9ada0d61971fdaf3e853892e7fb4051b36a.zip |
buttons: Add initial support for detecting button presses
Add mcu support for periodically polling for a button press. Add host
code support for registering buttons and invoking callbacks for them.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/buttons.c | 160 |
2 files changed, 161 insertions, 1 deletions
diff --git a/src/Makefile b/src/Makefile index 92667b2a..22db35ea 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,4 +5,4 @@ src-$(CONFIG_HAVE_GPIO) += gpiocmds.c stepper.c endstop.c src-$(CONFIG_HAVE_GPIO_ADC) += adccmds.c src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c -src-$(CONFIG_HAVE_USER_INTERFACE) += lcd_st7920.c lcd_hd44780.c +src-$(CONFIG_HAVE_USER_INTERFACE) += lcd_st7920.c lcd_hd44780.c buttons.c diff --git a/src/buttons.c b/src/buttons.c new file mode 100644 index 00000000..7f6e1ee7 --- /dev/null +++ b/src/buttons.c @@ -0,0 +1,160 @@ +// Report on user interface buttons +// +// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "basecmd.h" // oid_alloc +#include "board/gpio.h" // struct gpio_in +#include "board/irq.h" // irq_disable +#include "command.h" // DECL_COMMAND +#include "sched.h" // struct timer + +struct buttons { + struct timer time; + uint32_t rest_ticks; + uint8_t pressed, last_pressed; + uint8_t report_count, reports[8]; + uint8_t ack_count, retransmit_state, retransmit_count; + uint8_t button_count; + struct gpio_in pins[0]; +}; + +enum { BF_NO_RETRANSMIT = 0x80, BF_PENDING = 0xff, BF_ACKED = 0xfe }; + +static struct task_wake buttons_wake; + +static uint_fast8_t +buttons_event(struct timer *t) +{ + struct buttons *b = container_of(t, struct buttons, time); + + // Read pins + uint8_t i, bit, status = 0; + for (i = 0, bit = 1; i < b->button_count; i++, bit <<= 1) { + uint8_t val = gpio_in_read(b->pins[i]); + if (val) + status |= bit; + } + + // Check if any pins have changed since last time + uint8_t diff = status ^ b->pressed; + if (diff) { + // At least one pin has changed - do button debouncing + uint8_t debounced = ~(status ^ b->last_pressed); + if (diff & debounced) { + // Pin has been consistently different - report it + b->pressed = (b->pressed & ~debounced) | (status & debounced); + if (b->report_count < sizeof(b->reports)) { + b->reports[b->report_count++] = b->pressed; + sched_wake_task(&buttons_wake); + b->retransmit_state = BF_PENDING; + } + } + } + b->last_pressed = status; + + // Check if a retransmit is needed + uint8_t retransmit_state = b->retransmit_state; + if (!(retransmit_state & BF_NO_RETRANSMIT)) { + retransmit_state--; + if (retransmit_state & BF_NO_RETRANSMIT) + // timeout - do retransmit + sched_wake_task(&buttons_wake); + b->retransmit_state = retransmit_state; + } + + // Reschedule timer + b->time.waketime += b->rest_ticks; + return SF_RESCHEDULE; +} + +void +command_config_buttons(uint32_t *args) +{ + uint8_t button_count = args[1]; + if (button_count > 8) + shutdown("Max of 8 buttons"); + struct buttons *b = oid_alloc( + args[0], command_config_buttons + , sizeof(*b) + sizeof(b->pins[0]) * button_count); + b->button_count = button_count; + b->time.func = buttons_event; +} +DECL_COMMAND(command_config_buttons, "config_buttons oid=%c button_count=%c"); + +void +command_buttons_add(uint32_t *args) +{ + struct buttons *b = oid_lookup(args[0], command_config_buttons); + uint8_t pos = args[1]; + if (pos >= b->button_count) + shutdown("Set button past maximum button count"); + b->pins[pos] = gpio_in_setup(args[2], args[3]); +} +DECL_COMMAND(command_buttons_add, "buttons_add oid=%c pos=%c pin=%u pull_up=%c"); + +void +command_buttons_query(uint32_t *args) +{ + struct buttons *b = oid_lookup(args[0], command_config_buttons); + sched_del_timer(&b->time); + b->time.waketime = args[1]; + b->rest_ticks = args[2]; + b->ack_count = b->report_count = 0; + b->retransmit_state = BF_ACKED; + b->retransmit_count = args[3]; + if (b->retransmit_count >= BF_NO_RETRANSMIT) + shutdown("Invalid buttons retransmit count"); + if (! b->rest_ticks) + return; + sched_add_timer(&b->time); +} +DECL_COMMAND(command_buttons_query, + "buttons_query oid=%c clock=%u rest_ticks=%u retransmit_count=%c"); + +void +command_buttons_ack(uint32_t *args) +{ + struct buttons *b = oid_lookup(args[0], command_config_buttons); + uint8_t count = args[1]; + b->ack_count += count; + irq_disable(); + if (count >= b->report_count) { + b->report_count = 0; + b->retransmit_state = BF_ACKED; + } else { + uint8_t pending = b->report_count - count, i; + for (i=0; i<pending; i++) + b->reports[i] = b->reports[i+count]; + b->report_count = pending; + } + irq_enable(); +} +DECL_COMMAND(command_buttons_ack, "buttons_ack oid=%c count=%c"); + +void +buttons_task(void) +{ + if (!sched_check_wake(&buttons_wake)) + return; + uint8_t oid; + struct buttons *b; + foreach_oid(oid, b, command_config_buttons) { + // See if need to transmit buttons_state + if (b->retransmit_state != BF_PENDING) + continue; + // Generate message + irq_disable(); + uint8_t report_count = b->report_count; + if (!report_count) { + irq_enable(); + continue; + } + b->retransmit_state = b->retransmit_count; + irq_enable(); + sendf("buttons_state oid=%c ack_count=%c state=%*s" + , oid, b->ack_count, report_count, b->reports); + } +} +DECL_TASK(buttons_task); |