From: Keith Packard Date: Tue, 14 May 2013 05:34:19 +0000 (-0700) Subject: altos: Add debounce helper. Use in button and quadrature drivers for TeleLCO X-Git-Tag: 1.2.9.4~170 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=72b6c699d355fcd41addb9919d846e63105b9db7 altos: Add debounce helper. Use in button and quadrature drivers for TeleLCO Signed-off-by: Keith Packard --- diff --git a/src/core/ao_debounce.h b/src/core/ao_debounce.h new file mode 100644 index 00000000..ebe290e1 --- /dev/null +++ b/src/core/ao_debounce.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2013 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_DEBOUNCE_H_ +#define _AO_DEBOUNCE_H_ + +struct ao_debounce { + struct ao_debounce *next; + + /* time that pin value must be stable before accepting */ + int8_t hold; + + /* last value reported to app; don't report it twice */ + uint8_t value; + + /* current count of intervals pin value has been stable */ + int8_t count; + + /* This pin is running */ + uint8_t running; + + /* Get the current pin value */ + uint8_t (*_get)(struct ao_debounce *debounce); + + /* The stable value has changed */ + void (*_set)(struct ao_debounce *debounce, uint8_t value); +}; + +void +_ao_debounce_start(struct ao_debounce *debounce); + +void +_ao_debounce_stop(struct ao_debounce *debounce); + +void +ao_debounce_init(void); + +#endif /* _AO_DEBOUNCE_H_ */ diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c index a507c909..7b1fb530 100644 --- a/src/drivers/ao_button.c +++ b/src/drivers/ao_button.c @@ -18,6 +18,7 @@ #include #include #include +#include #if AO_EVENT #include #define ao_button_queue(b,v) ao_event_put_isr(AO_EVENT_BUTTON, b, v) @@ -25,55 +26,70 @@ #define ao_button_queue(b,v) #endif -static uint8_t ao_button[AO_BUTTON_COUNT]; -static AO_TICK_TYPE ao_button_time[AO_BUTTON_COUNT]; +#define AO_BUTTON_DEBOUNCE_HOLD 10 -#define AO_DEBOUNCE AO_MS_TO_TICKS(20) +static struct ao_debounce ao_button_debounce[AO_BUTTON_COUNT]; #define port(q) AO_BUTTON_ ## q ## _PORT #define bit(q) AO_BUTTON_ ## q #define pin(q) AO_BUTTON_ ## q ## _PIN -static void -ao_button_do(uint8_t b, uint8_t v) -{ - /* Debounce */ - if ((AO_TICK_SIGNED) (ao_tick_count - ao_button_time[b]) < AO_DEBOUNCE) - return; - - /* pins are inverted */ - v = !v; - if (ao_button[b] != v) { - ao_button[b] = v; - ao_button_time[b] = ao_tick_count; - ao_button_queue(b, v); - ao_wakeup(&ao_button[b]); - } -} +/* pins are inverted */ +#define ao_button_value(b) !ao_gpio_get(port(b), bit(b), pin(b)) -#define ao_button_update(b) ao_button_do(b, ao_gpio_get(port(b), bit(b), pin(b))) - -static void -ao_button_isr(void) +static uint8_t +_ao_button_get(struct ao_debounce *debounce) { + uint8_t b = debounce - ao_button_debounce; + + switch (b) { #if AO_BUTTON_COUNT > 0 - ao_button_update(0); + case 0: return ao_button_value(0); #endif #if AO_BUTTON_COUNT > 1 - ao_button_update(1); + case 1: return ao_button_value(1); #endif #if AO_BUTTON_COUNT > 2 - ao_button_update(2); + case 2: return ao_button_value(2); #endif #if AO_BUTTON_COUNT > 3 - ao_button_update(3); + case 3: return ao_button_value(3); #endif #if AO_BUTTON_COUNT > 4 - ao_button_update(4); + case 4: return ao_button_value(4); #endif + } +} + +static void +_ao_button_set(struct ao_debounce *debounce, uint8_t value) +{ + uint8_t b = debounce - ao_button_debounce; + + ao_button_queue(b, value); +} + + +#define ao_button_update(b) ao_button_do(b, ao_gpio_get(port(b), bit(b), pin(b))) + +static void +ao_button_debounce_init(struct ao_debounce *debounce) { + debounce->hold = AO_BUTTON_DEBOUNCE_HOLD; + debounce->_get = _ao_button_get; + debounce->_set = _ao_button_set; +} + +static void +ao_button_isr(void) +{ + uint8_t b; + + for (b = 0; b < AO_BUTTON_COUNT; b++) + _ao_debounce_start(&ao_button_debounce[b]); } #define init(b) do { \ + ao_button_debounce_init(&ao_button_debounce[b]); \ ao_enable_port(port(b)); \ \ ao_exti_setup(port(b), bit(b), \ @@ -91,4 +107,14 @@ ao_button_init(void) #if AO_BUTTON_COUNT > 1 init(1); #endif +#if AO_BUTTON_COUNT > 2 + init(2); +#endif +#if AO_BUTTON_COUNT > 3 + init(3); +#endif +#if AO_BUTTON_COUNT > 4 + init(4); +#endif + ao_debounce_init(); } diff --git a/src/drivers/ao_event.c b/src/drivers/ao_event.c index 440ef2de..c428125d 100644 --- a/src/drivers/ao_event.c +++ b/src/drivers/ao_event.c @@ -25,11 +25,6 @@ #define ao_event_queue_empty() (ao_event_queue_insert == ao_event_queue_remove) #define ao_event_queue_full() (ao_event_queue_next(ao_event_queue_insert) == ao_event_queue_remove) -/* - * Whether a sequence of events from the same device should be collapsed - */ -#define ao_event_can_collapse(type) ((type) == AO_EVENT_QUADRATURE) - struct ao_event ao_event_queue[AO_EVENT_QUEUE]; uint8_t ao_event_queue_insert; uint8_t ao_event_queue_remove; @@ -48,17 +43,9 @@ ao_event_get(struct ao_event *ev) /* called with interrupts disabled */ void -ao_event_put_isr(uint8_t type, uint8_t unit, uint32_t value) +ao_event_put_isr(uint8_t type, uint8_t unit, int32_t value) { if (!ao_event_queue_full()) { - - if (ao_event_can_collapse(type) && !ao_event_queue_empty()) { - uint8_t prev = ao_event_queue_prev(ao_event_queue_insert); - - if (ao_event_queue[prev].type == type && - ao_event_queue[prev].unit == unit) - ao_event_queue_insert = prev; - } ao_event_queue[ao_event_queue_insert] = (struct ao_event) { .type = type, .unit = unit, @@ -71,7 +58,7 @@ ao_event_put_isr(uint8_t type, uint8_t unit, uint32_t value) } void -ao_event_put(uint8_t type, uint8_t unit, uint32_t value) +ao_event_put(uint8_t type, uint8_t unit, int32_t value) { ao_arch_critical(ao_event_put_isr(type, unit, value);); } diff --git a/src/drivers/ao_event.h b/src/drivers/ao_event.h index 25c49c35..ed9a7433 100644 --- a/src/drivers/ao_event.h +++ b/src/drivers/ao_event.h @@ -26,16 +26,16 @@ struct ao_event { uint8_t type; uint8_t unit; uint16_t tick; - uint32_t value; + int32_t value; }; uint8_t ao_event_get(struct ao_event *ev); void -ao_event_put_isr(uint8_t type, uint8_t unit, uint32_t value); +ao_event_put_isr(uint8_t type, uint8_t unit, int32_t value); void -ao_event_put(uint8_t type, uint8_t unit, uint32_t value); +ao_event_put(uint8_t type, uint8_t unit, int32_t value); #endif /* _AO_EVENT_H_ */ diff --git a/src/drivers/ao_quadrature.c b/src/drivers/ao_quadrature.c index 6cc2467a..cfa58da6 100644 --- a/src/drivers/ao_quadrature.c +++ b/src/drivers/ao_quadrature.c @@ -18,12 +18,14 @@ #include #include #include -#if AO_EVENT +#include #include -#define ao_quadrature_queue(q) ao_event_put_isr(AO_EVENT_QUADRATURE, q, ao_quadrature_count[q]) -#else -#define ao_quadrature_queue(q) -#endif + +#define AO_QUADRATURE_DEBOUNCE_HOLD 3 + +static __xdata struct ao_debounce ao_quadrature_debounce[AO_QUADRATURE_COUNT]; + +#define debounce_id(d) ((d) - ao_quadrature_debounce) __xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT]; @@ -35,41 +37,77 @@ static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT]; #define port(q) AO_QUADRATURE_ ## q ## _PORT #define bita(q) AO_QUADRATURE_ ## q ## _A #define bitb(q) AO_QUADRATURE_ ## q ## _B +#define pina(q) AO_QUADRATURE_ ## q ## _A ## _PIN +#define pinb(q) AO_QUADRATURE_ ## q ## _B ## _PIN -#define ao_quadrature_update(q) do { \ - ao_quadrature_state[q] = ((ao_quadrature_state[q] & 3) << 2); \ - ao_quadrature_state[q] |= ao_gpio_get(port(q), bita(q), 0); \ - ao_quadrature_state[q] |= ao_gpio_get(port(q), bitb(q), 0) << 1; \ - } while (0) - +#define q_case(q) case q: v = (!ao_gpio_get(port(q), bita(q), pina(q))) | ((!ao_gpio_get(port(q), bitb(q), pinb(q))) << 1); break -static void -ao_quadrature_isr(void) -{ - uint8_t q; +uint8_t quad_raw[64]; +uint8_t quad_r; + +static uint8_t +_ao_quadrature_get(struct ao_debounce *debounce) { + uint8_t q = debounce_id(debounce); + uint8_t v = 0; + + switch (q) { #if AO_QUADRATURE_COUNT > 0 - ao_quadrature_update(0); + q_case(0); #endif #if AO_QUADRATURE_COUNT > 1 - ao_quadrature_update(1); + q_case(1); #endif + } + if (q == 0) { + quad_raw[quad_r] = v; + quad_r = (quad_r + 1) & 63; + } + return v; +} - for (q = 0; q < AO_QUADRATURE_COUNT; q++) { - switch (ao_quadrature_state[q]) { - case STATE(0, 1, 0, 0): - ao_quadrature_count[q]++; - break; - case STATE(1, 0, 0, 0): - ao_quadrature_count[q]--; - break; - default: - continue; - } - ao_quadrature_queue(q); - ao_wakeup(&ao_quadrature_count[q]); +static void +_ao_quadrature_queue(uint8_t q, int8_t step) +{ + ao_quadrature_count[q] += step; +#if AO_EVENT + ao_event_put_isr(AO_EVENT_QUADRATURE, q, step); +#endif + ao_wakeup(&ao_quadrature_count[q]); +} + +uint8_t quad_history[64]; +uint8_t quad_h; + +static void +_ao_quadrature_set(struct ao_debounce *debounce, uint8_t value) { + uint8_t q = debounce_id(debounce); + + ao_quadrature_state[q] = ((ao_quadrature_state[q] & 3) << 2); + ao_quadrature_state[q] |= value; + + if (q == 0) { + quad_history[quad_h] = ao_quadrature_state[0]; + quad_h = (quad_h + 1) & 63; + } + + switch (ao_quadrature_state[q]) { + case STATE(0, 1, 0, 0): + _ao_quadrature_queue(q, 1); + break; + case STATE(1, 0, 0, 0): + _ao_quadrature_queue(q, -1); + break; } } +static void +ao_quadrature_isr(void) +{ + uint8_t q; + for (q = 0; q < AO_QUADRATURE_COUNT; q++) + _ao_debounce_start(&ao_quadrature_debounce[q]); +} + int32_t ao_quadrature_poll(uint8_t q) { @@ -107,9 +145,16 @@ static const struct ao_cmds ao_quadrature_cmds[] = { { 0, NULL } }; +static void +ao_quadrature_debounce_init(struct ao_debounce *debounce) { + debounce->hold = AO_QUADRATURE_DEBOUNCE_HOLD; + debounce->_get = _ao_quadrature_get; + debounce->_set = _ao_quadrature_set; +} + #define init(q) do { \ ao_enable_port(port(q)); \ - \ + ao_quadrature_debounce_init(&ao_quadrature_debounce[q]); \ ao_exti_setup(port(q), bita(q), \ AO_QUADRATURE_MODE|AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_MED, \ ao_quadrature_isr); \ @@ -124,6 +169,7 @@ static const struct ao_cmds ao_quadrature_cmds[] = { void ao_quadrature_init(void) { + ao_debounce_init(); #if AO_QUADRATURE_COUNT > 0 init(0); #endif diff --git a/src/stm-demo/Makefile b/src/stm-demo/Makefile index d1f825db..48fa07eb 100644 --- a/src/stm-demo/Makefile +++ b/src/stm-demo/Makefile @@ -36,10 +36,7 @@ ALTOS_SRC = \ ao_data.c \ ao_i2c_stm.c \ ao_usb_stm.c \ - ao_exti_stm.c \ - ao_event.c \ - ao_quadrature.c \ - ao_button.c + ao_exti_stm.c PRODUCT=StmDemo-v0.0 PRODUCT_DEF=-DSTM_DEMO diff --git a/src/stm-demo/ao_demo.c b/src/stm-demo/ao_demo.c index 5677cdf4..58cf651b 100644 --- a/src/stm-demo/ao_demo.c +++ b/src/stm-demo/ao_demo.c @@ -153,6 +153,7 @@ ao_temp (void) printf ("temp: %d\n", temp); } +#if 0 static void ao_event(void) { @@ -168,6 +169,7 @@ ao_event(void) } } +#endif __code struct ao_cmds ao_demo_cmds[] = { { ao_dma_test, "D\0DMA test" }, @@ -175,7 +177,7 @@ __code struct ao_cmds ao_demo_cmds[] = { { ao_spi_read, "R\0SPI read" }, { ao_i2c_write, "i\0I2C write" }, { ao_temp, "t\0Show temp" }, - { ao_event, "e\0Monitor event queue" }, +/* { ao_event, "e\0Monitor event queue" }, */ { 0, NULL } }; diff --git a/src/stm/ao_debounce.c b/src/stm/ao_debounce.c new file mode 100644 index 00000000..22cdf230 --- /dev/null +++ b/src/stm/ao_debounce.c @@ -0,0 +1,187 @@ +/* + * Copyright © 2013 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include + +static uint8_t ao_debounce_initialized; +static uint8_t ao_debounce_running; +static struct ao_debounce *ao_debounce; + +static void +ao_debounce_on(void) +{ + stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) | + (0 << STM_TIM67_CR1_OPM) | + (1 << STM_TIM67_CR1_URS) | + (0 << STM_TIM67_CR1_UDIS) | + (1 << STM_TIM67_CR1_CEN)); +} + +static void +ao_debounce_off(void) +{ + stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) | + (0 << STM_TIM67_CR1_OPM) | + (1 << STM_TIM67_CR1_URS) | + (0 << STM_TIM67_CR1_UDIS) | + (0 << STM_TIM67_CR1_CEN)); +} + +static void +_ao_debounce_set(struct ao_debounce *debounce, uint8_t value) +{ + if (value != debounce->value) { + debounce->value = value; + debounce->_set(debounce, value); + } + _ao_debounce_stop(debounce); +} + +/* + * Get the current value, set the result when we've + * reached the debounce count limit + */ +static void +_ao_debounce_check(struct ao_debounce *debounce) +{ + if (debounce->_get(debounce)) { + if (debounce->count < 0) + debounce->count = 0; + if (debounce->count < debounce->hold) { + if (++debounce->count == debounce->hold) + _ao_debounce_set(debounce, 1); + } + } else { + if (debounce->count > 0) + debounce->count = 0; + if (debounce->count > -debounce->hold) { + if (--debounce->count == -debounce->hold) + _ao_debounce_set(debounce, 0); + } + } +} + +/* + * Start monitoring one pin + */ +void +_ao_debounce_start(struct ao_debounce *debounce) +{ + if (debounce->running) + return; + debounce->running = 1; + + /* Reset the counter */ + debounce->count = 0; + + /* Link into list */ + debounce->next = ao_debounce; + ao_debounce = debounce; + + /* Make sure the timer is running */ + if (!ao_debounce_running++) + ao_debounce_on(); + + /* And go check the current value */ + _ao_debounce_check(debounce); +} + +/* + * Stop monitoring one pin + */ +void +_ao_debounce_stop(struct ao_debounce *debounce) +{ + struct ao_debounce **prev; + if (!debounce->running) + return; + + debounce->running = 0; + + /* Unlink */ + for (prev = &ao_debounce; (*prev); prev = &((*prev)->next)) { + if (*prev == debounce) { + *prev = debounce->next; + break; + } + } + debounce->next = NULL; + + /* Turn off the timer if possible */ + if (!--ao_debounce_running) + ao_debounce_off(); +} + +void stm_tim6_isr(void) +{ + struct ao_debounce *debounce, *next; + if (stm_tim6.sr & (1 << STM_TIM67_SR_UIF)) { + stm_tim6.sr = 0; + + /* Walk the current list, allowing the current + * object to be removed from the list + */ + for (debounce = ao_debounce; debounce; debounce = next) { + next = debounce->next; + _ao_debounce_check(debounce); + } + } +} + +/* + * According to the STM clock-configuration, timers run + * twice as fast as the APB1 clock *if* the APB1 prescaler + * is greater than 1. + */ + +#if AO_APB1_PRESCALER > 1 +#define TIMER_23467_SCALER 2 +#else +#define TIMER_23467_SCALER 1 +#endif + +#define TIMER_100kHz ((AO_PCLK1 * TIMER_23467_SCALER) / 100000) + +void +ao_debounce_init(void) +{ + if (ao_debounce_initialized) + return; + ao_debounce_initialized = 1; + + stm_nvic_set_enable(STM_ISR_TIM6_POS); + stm_nvic_set_priority(STM_ISR_TIM6_POS, AO_STM_NVIC_CLOCK_PRIORITY); + + /* Turn on timer 6 */ + stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM6EN); + + stm_tim6.psc = TIMER_100kHz; + stm_tim6.arr = 9; + stm_tim6.cnt = 0; + + /* Enable update interrupt */ + stm_tim6.dier = (1 << STM_TIM67_DIER_UIE); + + /* Poke timer to reload values */ + stm_tim6.egr |= (1 << STM_TIM67_EGR_UG); + + stm_tim6.cr2 = (STM_TIM67_CR2_MMS_RESET << STM_TIM67_CR2_MMS); + + /* And turn it off (for now) */ + ao_debounce_off(); +} diff --git a/src/telelco-v0.2/Makefile b/src/telelco-v0.2/Makefile index cc6e62c4..42a5a7ee 100644 --- a/src/telelco-v0.2/Makefile +++ b/src/telelco-v0.2/Makefile @@ -21,6 +21,7 @@ INC = \ ao_radio_spi.h \ ao_radio_cmac.h \ ao_cc1120_CC1120.h \ + ao_debounce.h \ stm32l.h # @@ -59,6 +60,7 @@ ALTOS_SRC = \ ao_fec_tx.c \ ao_fec_rx.c \ ao_seven_segment.c \ + ao_debounce.c \ ao_quadrature.c \ ao_button.c \ ao_event.c \ diff --git a/src/telelco-v0.2/ao_lco.c b/src/telelco-v0.2/ao_lco.c index 418c0509..6e490247 100644 --- a/src/telelco-v0.2/ao_lco.c +++ b/src/telelco-v0.2/ao_lco.c @@ -114,11 +114,9 @@ ao_lco_input(void) switch (event.unit) { case AO_QUADRATURE_PAD: if (!ao_lco_armed) { - if (event.value == ao_lco_pad) - break; - dir = ((int8_t) event.value - (int8_t) ao_lco_pad) > 0 ? 1 : -1; - new_pad = event.value; - while (!ao_lco_pad_present(new_pad)) { + dir = (int8_t) event.value; + new_pad = ao_lco_pad; + do { new_pad += dir; if (new_pad > AO_PAD_MAX_CHANNELS) new_pad = 0; @@ -126,21 +124,18 @@ ao_lco_input(void) new_pad = AO_PAD_MAX_CHANNELS - 1; if (new_pad == ao_lco_pad) break; - } + } while (!ao_lco_pad_present(new_pad)); if (new_pad != ao_lco_pad) { ao_lco_pad = new_pad; - ao_quadrature_count[AO_QUADRATURE_PAD] = ao_lco_pad; ao_lco_set_pad(); } } break; case AO_QUADRATURE_BOX: if (!ao_lco_armed) { - if (event.value == ao_lco_box) - break; - dir = ((int8_t) event.value - (int8_t) ao_lco_box) > 0 ? 1 : -1; - new_box = event.value; - while (!ao_lco_box_present(new_box)) { + dir = (int8_t) event.value; + new_box = ao_lco_box; + do { new_box += dir; if (new_box > ao_lco_max_box) new_box = ao_lco_min_box; @@ -148,8 +143,7 @@ ao_lco_input(void) new_box = ao_lco_min_box; if (new_box == ao_lco_box) break; - } - ao_quadrature_count[AO_QUADRATURE_PAD] = new_box; + } while (!ao_lco_box_present(new_box)); if (ao_lco_box != new_box) { ao_lco_box = new_box; ao_lco_got_channels = 0;