gadget-securevault/button.c
2019-11-02 14:44:45 +01:00

239 lines
6.7 KiB
C

/*
* (c) danielinux 2019
* GPLv.2
*
* See LICENSE for details
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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();
}