/* * (c) danielinux 2019 * GPLv.2 * * See LICENSE for details */ #include #include #include #include #include "system.h" #include "button.h" #include "systick.h" #include "unicore-mx/stm32/gpio.h" #include "unicore-mx/stm32/exti.h" #include "unicore-mx/stm32/rcc.h" #include "unicore-mx/stm32/f4/rcc.h" #include "unicore-mx/stm32/adc.h" #include "unicore-mx/stm32/f4/adc.h" #include "unicore-mx/stm32/f4/nvic.h" // Uncomment to enable debug //#define BUTTON_DEBUG #ifdef BUTTON_DEBUG # define DBG printf #else # define DBG(...) do {} while (0) #endif #define BUTTON_DEBOUNCE_TIME 50 #define BUTTON_HOLD_TIME 1500 /* Selectors: PC4, PC5, PC6 */ #define PSEL_0 (1 << 5) #define PSEL_1 (1 << 4) #define PSEL_2 (1 << 6) /* Input lines: PB12, PB13, PB14, PB15 */ #define PLINE_0 (1 << 12) #define PLINE_1 (1 << 13) #define PLINE_2 (1 << 14) #define PLINE_3 (1 << 15) #define PSEL_MASK (PSEL_0 | PSEL_1 | PSEL_2) #define PLINE_MASK (PLINE_0 | PLINE_1 | PLINE_2 | PLINE_3) enum button_state { IDLE = 0, PRESSING, PRESSED, HOLD, RELEASING, }; static volatile int button_current_latch = 0; static uint32_t button_latch_switchtime; static volatile int button_press_pending = 0; static void input_run(uint32_t ev, void *arg); static void input_init(void); const char button_task_name[] = "Input"; static void button_setup(void) { int i; uint32_t exti_irq; rcc_periph_clock_enable(RCC_SYSCFG); rcc_periph_clock_enable(RCC_GPIOC); rcc_periph_clock_enable(RCC_GPIOB); gpio_mode_setup(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLDOWN, PSEL_MASK); gpio_clear(GPIOC, PSEL_MASK); gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, PLINE_MASK); nvic_enable_irq(NVIC_EXTI15_10_IRQ); nvic_set_priority(NVIC_EXTI15_10_IRQ, 1); exti_select_source(PLINE_0, GPIOB); exti_select_source(PLINE_1, GPIOB); exti_select_source(PLINE_2, GPIOB); exti_select_source(PLINE_3, GPIOB); } static void button_start_read(void) { exti_set_trigger(PLINE_MASK, EXTI_TRIGGER_RISING); exti_enable_request(PLINE_0); exti_enable_request(PLINE_1); exti_enable_request(PLINE_2); exti_enable_request(PLINE_3); } void isr_exti(void) { button_press_pending = (GPIO_ODR(GPIOC) & PSEL_MASK); exti_reset_request(PLINE_0); exti_reset_request(PLINE_1); exti_reset_request(PLINE_2); exti_reset_request(PLINE_3); } /* Button interface */ struct user_button { enum button_state state; uint32_t transition_start_timestamp; }; static struct user_button Buttons[N_BUTTONS]; int button_poll(void (*callback)(uint8_t press, int hold)) { if (button_press_pending) { volatile uint32_t line_status = (gpio_get(GPIOB, PLINE_MASK)); volatile uint32_t latch_status = button_press_pending; int i; if (line_status) { int b = -1; int latch = -1; if (latch_status & PSEL_0) latch = 0; else if (latch_status & PSEL_1) latch = 1; else if (latch_status & PSEL_2) latch = 2; else { return 0; } if (latch != button_current_latch) return 0; if (line_status & PLINE_0) b = 0; else if (line_status & PLINE_1) b = 1; else if (line_status & PLINE_2) b = 2; else if (line_status & PLINE_3) b = 3; else { return 0; } b += latch * 4; if((Buttons[b].state == IDLE) || (Buttons[b].state == RELEASING)) { Buttons[b].state = PRESSING; Buttons[b].transition_start_timestamp = jiffies; DBG("%06u: ST: PRESSING\r\n", jiffies); button_press_pending = 0; return 0; } else { uint32_t p_interval = jiffies - Buttons[b].transition_start_timestamp; if (p_interval > BUTTON_HOLD_TIME) { if (Buttons[b].state < HOLD) { callback(b, 1); Buttons[b].state = HOLD; Buttons[b].transition_start_timestamp = jiffies; button_press_pending = 0; DBG("%06u: ST: HOLD\r\n", jiffies); } } else if (p_interval > BUTTON_DEBOUNCE_TIME) { if (Buttons[b].state == PRESSING) { callback(b, 0); Buttons[b].state = PRESSED; DBG("%06u: ST: PRESSED\r\n", jiffies); button_press_pending = 0; } if (Buttons[b].state == HOLD) { callback(b, 1); Buttons[b].transition_start_timestamp = jiffies; } } return 1; } } else { int base = button_current_latch * 4; int i; for (i = base; i < base + 4; i++) { if (Buttons[i].state == RELEASING) { if ((jiffies - Buttons[i].transition_start_timestamp) > (BUTTON_DEBOUNCE_TIME)) { Buttons[i].state = IDLE; Buttons[i].transition_start_timestamp = 0; DBG("%06u: ST: IDLE\r\n", jiffies); } } else if (Buttons[i].state != IDLE) { Buttons[i].state = RELEASING; Buttons[i].transition_start_timestamp = jiffies; DBG("%06u: ST:0\r\n", jiffies); } } } } if (jiffies >= button_latch_switchtime) { uint32_t latch = button_current_latch + 1; uint32_t selected_pin = PSEL_0; gpio_clear(GPIOC, PSEL_0); gpio_clear(GPIOC, PSEL_1); gpio_clear(GPIOC, PSEL_2); switch(latch){ case 1: selected_pin = PSEL_1; break; case 2: selected_pin = PSEL_2; break; case 3: latch = 0; selected_pin = PSEL_0; break; default: latch = 0; } asm volatile("cpsid i"); gpio_set(GPIOC, selected_pin); button_current_latch = latch; while ((GPIO_ODR(GPIOC) & PSEL_MASK) != selected_pin) ; asm volatile("cpsie i"); button_latch_switchtime = jiffies + 5; } return 0; } void button_init(void) { memset(Buttons, 0, sizeof(struct user_button) * N_BUTTONS); button_setup(); button_start_read(); }