239 lines
6.7 KiB
C
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();
|
|
}
|
|
|
|
|