253 lines
6.6 KiB
C
253 lines
6.6 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
|