gadget-kspconsole/button.c

253 lines
6.6 KiB
C
Raw Permalink Normal View History

2023-11-27 15:13:55 +01:00
/*
* Copyright (C) 2023 Daniele Lacamera <root@danielinux.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#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);
}