/* * Copyright (C) 2023 Daniele Lacamera * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include "system.h" #include "button.h" #include "unicore-mx/stm32/gpio.h" #include "unicore-mx/stm32/exti.h" #include "unicore-mx/stm32/rcc.h" #include "unicore-mx/stm32/adc.h" #include "unicore-mx/stm32/f7/nvic.h" #include "unicore-mx/stm32/f4/adc.h" #include "systick.h" #include "task.h" #include "adc.h" #define BUTTON_DEBOUNCE_TIME 100 #define TS_DEBOUNCE_TIME 0 #define PSEL_0 (1 << 0) #define PSEL_1 (1 << 1) #define PSEL_2 (1 << 2) /* Input lines: PH6, PA8, PB15, PB14 */ #define PLINE_0 (1 << 6) #define PLINE_1 (1 << 8) #define PLINE_2 (1 << 15) #define PLINE_3 (1 << 14) #define TS_PIN (1 << 13) /* GPIO I13 */ 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"; struct task input_task = { .init = input_init, .run = input_run, .events = EV_SYSTICK, .name = button_task_name }; static void button_setup(void) { int i; uint32_t exti_irq; rcc_periph_clock_enable(RCC_SYSCFG); rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOB); rcc_periph_clock_enable(RCC_GPIOH); rcc_periph_clock_enable(RCC_GPIOI); gpio_mode_setup(GPIOI, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLDOWN, PSEL_0 | PSEL_1 | PSEL_2); gpio_clear(GPIOI, PSEL_0 | PSEL_1 | PSEL_2); nvic_enable_irq(NVIC_EXTI15_10_IRQ); nvic_enable_irq(NVIC_EXTI9_5_IRQ); nvic_set_priority(NVIC_EXTI15_10_IRQ, 1); nvic_set_priority(NVIC_EXTI9_5_IRQ, 1); gpio_mode_setup(GPIOH, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, PLINE_0); gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, PLINE_1); gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, PLINE_2 | PLINE_3); /* Set touch screen interrupt line */ gpio_mode_setup(GPIOI, GPIO_MODE_INPUT, GPIO_PUPD_NONE, TS_PIN); exti_select_source(PLINE_0, GPIOH); exti_select_source(PLINE_1, GPIOA); exti_select_source(PLINE_2, GPIOB); exti_select_source(PLINE_3, GPIOB); exti_select_source(TS_PIN, GPIOI); } static void button_start_read(void) { exti_set_trigger(PLINE_0 | PLINE_1 | PLINE_2 | PLINE_3 | TS_PIN, EXTI_TRIGGER_RISING); exti_enable_request(PLINE_0 | PLINE_1 | PLINE_2 | PLINE_3|TS_PIN); } void isr_exti15_10(void) { exti_reset_request(PLINE_2); exti_reset_request(PLINE_3); if (exti_get_flag_status(TS_PIN)) exti_reset_request(TS_PIN); button_press_pending++; } void isr_exti9_5(void) { exti_reset_request(PLINE_1); exti_reset_request(PLINE_0); button_press_pending++; } /*** Event generation interface ***/ struct input_status Input_status = {}; #define TS_TOUCH_NONE 0 #define TS_TOUCH_DETECTED 1 int input_detect_touch(void) { uint8_t n_touch = 0; n_touch = ft5336_TS_DetectTouch(TS_I2C_ADDR); if (n_touch > 0) { uint16_t x, y; ft5336_TS_GetXY(TS_I2C_ADDR, &y, &x); Input_status.ts.Y = y; Input_status.ts.X = x; Input_status.start_touch = jiffies; return TS_TOUCH_DETECTED; } else return TS_TOUCH_NONE; } static int input_process_buttons(void) { int b_pressed = -1; struct user_button *b; uint8_t n_touch = 0; if (button_press_pending) { n_touch = input_detect_touch(); if (n_touch != TS_TOUCH_NONE) { b = &Input_status.b[TOUCHSCREEN]; b->pressed = 1; return 1; } else if (gpio_get(GPIOH, PLINE_0)) b_pressed = 0; else if (gpio_get(GPIOA, PLINE_1)) b_pressed = 1; else if (gpio_get(GPIOB, PLINE_2)) b_pressed = 2; else if (gpio_get(GPIOB, PLINE_3)) b_pressed = 3; else { button_press_pending = 0; return 0; } b_pressed += 4 * button_current_latch; if (b_pressed >= N_BUTTONS) return 0; b = &Input_status.b[b_pressed]; if (b->transition_start_timestamp == 0U) { b->transition_start_timestamp = jiffies; return 0; } if ((jiffies - b->transition_start_timestamp) > BUTTON_DEBOUNCE_TIME) { button_press_pending = 0; b->pressed = 1; return 1; } } else if (jiffies >= button_latch_switchtime) { int i, start = 4 *button_current_latch, end = start + 4; button_latch_switchtime = jiffies + 5; for (i = start; i < end; i++) Input_status.b[i].transition_start_timestamp = 0; switch (button_current_latch) { case 0: gpio_clear(GPIOI, PSEL_0 | PSEL_2); gpio_set(GPIOI, PSEL_1); button_current_latch = 1; break; case 1: gpio_clear(GPIOI, PSEL_0 | PSEL_1); gpio_set(GPIOI, PSEL_2); button_current_latch = 2; break; case 2: default: gpio_clear(GPIOI, PSEL_1 | PSEL_2); gpio_set(GPIOI, PSEL_0); button_current_latch = 0; break; } } return 0; } int input_get_swl(void) { return gpio_get(GPIOF, (1 << 10) ); } int input_get_swr(void) { return gpio_get(GPIOF, (1 << 7) ); } static void adc_setup(void) { int i; rcc_periph_clock_enable(RCC_ADC3); rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOF); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, 0, GPIO0); gpio_mode_setup(GPIOF, GPIO_MODE_ANALOG, 0, GPIO6 | GPIO8 | GPIO9); adc_init(); } void input_init(void) { button_setup(); adc_setup(); button_start_read(); } void input_run(uint32_t ev, void *arg) { int p = input_process_buttons(); if (p > 0) { trigger_event(EV_BUTTON); } } void input_setup(void) { register_task(&input_task); }