gadget-tester/button.c
2019-11-22 11:12:54 +01:00

331 lines
8.2 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
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";
// PA8 + PA9 // ROTARY encoder
// PC0 // BUTTON
void pin_exti_init(void)
{
int i;
uint32_t exti_irq;
rcc_periph_clock_enable(RCC_SYSCFG);
rcc_periph_clock_enable(RCC_GPIOA);
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO9 | GPIO8 | GPIO7 | GPIO6 | GPIO5);
nvic_enable_irq(NVIC_EXTI9_5_IRQ);
nvic_set_priority(NVIC_EXTI9_5_IRQ, 1);
exti_select_source(GPIO5, GPIOA);
exti_select_source(GPIO6, GPIOA);
exti_select_source(GPIO7, GPIOA);
exti_select_source(GPIO8, GPIOA);
exti_select_source(GPIO9, GPIOA);
}
void pin_exti_start_read(void)
{
exti_set_trigger(GPIO5|GPIO6|GPIO7|GPIO8|GPIO9, EXTI_TRIGGER_BOTH);
exti_enable_request(GPIO5);
exti_enable_request(GPIO6);
exti_enable_request(GPIO7);
exti_enable_request(GPIO8);
exti_enable_request(GPIO9);
}
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_GPIOC);
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO10);
gpio_mode_setup(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO4);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO0);
nvic_enable_irq(NVIC_EXTI4_IRQ);
nvic_enable_irq(NVIC_EXTI0_IRQ);
nvic_enable_irq(NVIC_EXTI15_10_IRQ);
nvic_set_priority(NVIC_EXTI4_IRQ, 1);
nvic_set_priority(NVIC_EXTI0_IRQ, 1);
nvic_set_priority(NVIC_EXTI15_10_IRQ, 1);
exti_select_source(GPIO10, GPIOA);
exti_select_source(GPIO4, GPIOB);
exti_select_source(GPIO0, GPIOC);
}
enum button_state {
IDLE = 0,
PRESSING,
PRESSED,
HOLD,
RELEASING,
};
static volatile enum button_state button_status = 0;
static volatile uint32_t rot_up = 0;
static volatile uint32_t rot_down = 0;
static void button_start_read(void)
{
exti_set_trigger(GPIO0, EXTI_TRIGGER_RISING);
exti_set_trigger(GPIO10|GPIO4, EXTI_TRIGGER_RISING);
exti_enable_request(GPIO10);
exti_enable_request(GPIO4);
exti_enable_request(GPIO0);
}
void isr_exti_rot1(void)
{
exti_reset_request(GPIO4);
if (gpio_get(GPIOA, GPIO10) == 0)
return;
rot_down++;
}
void isr_exti_rot0(void)
{
exti_reset_request(GPIO10);
if (gpio_get(GPIOB, GPIO4) == 0)
return;
rot_up++;
}
void isr_exti_button(void)
{
button_press_pending = !!(gpio_get(GPIOC, GPIO0));
exti_reset_request(GPIO0);
}
static uint32_t t0s, t0us;
static uint32_t t1s, t1us;
static uint32_t time_diff_ms(uint32_t s_a, uint32_t us_a, uint32_t s_b, uint32_t us_b)
{
uint32_t res = 0;
res = (s_a - s_b) * 1000;
if (us_b > us_a) {
us_a += 1000000;
res -= 1000;
}
res += (us_a - us_b) / 1000;
return res;
}
typedef void (*exti_trigger_callback_fn)(uint32_t pin, uint8_t front, uint32_t s, uint32_t us);
static exti_trigger_callback_fn exti_trigger_callback[5];
void exti_set_callback(uint32_t pin, void (*cb)(uint32_t pin, uint8_t front, uint32_t s, uint32_t us))
{
int i;
for (i = 0; i < 5; i++) {
if (Channel_Pin[i] == pin) {
DBG("Setting callback for EXTI on ch %d\r\n", i);
exti_trigger_callback[i] = cb;
return;
}
}
}
void exti_clear_callback(uint32_t pin)
{
int i;
for (i = 0; i < 5; i++) {
if (Channel_Pin[i] == pin) {
DBG("Clearing callback for EXTI on ch %d\r\n", i);
exti_trigger_callback[i] = NULL;
return;
}
}
}
struct trig_timeval {
int channel;
uint32_t pin;
int front;
uint32_t tv_sec;
uint32_t tv_usec;
};
static struct trig_timeval trig_tv[32] = {};
static volatile uint8_t trig_pending = 0;
void isr_exti_channel(void)
{
uint32_t ts, tus;
gettime(&ts, &tus);
DBG("EXTI\r\n");
if (exti_get_flag_status(GPIO9)) {
DBG("c1\r\n");
if (exti_trigger_callback[1]) {
gettime(&ts, &tus);
trig_tv[trig_pending].channel = 1;
trig_tv[trig_pending].pin = GPIO9;
trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO9);
trig_tv[trig_pending].tv_sec = ts;
trig_tv[trig_pending].tv_usec = tus;
trig_pending++;
}
exti_reset_request(GPIO9);
}
if (exti_get_flag_status(GPIO8)) {
DBG("c0\r\n");
if (exti_trigger_callback[0]) {
gettime(&ts, &tus);
trig_tv[trig_pending].channel = 0;
trig_tv[trig_pending].pin = GPIO8;
trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO8);
trig_tv[trig_pending].tv_sec = ts;
trig_tv[trig_pending].tv_usec = tus;
trig_pending++;
}
exti_reset_request(GPIO8);
}
if (exti_get_flag_status(GPIO7)) {
if (exti_trigger_callback[2]) {
gettime(&ts, &tus);
trig_tv[trig_pending].channel = 2;
trig_tv[trig_pending].pin = GPIO7;
trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO7);
trig_tv[trig_pending].tv_sec = ts;
trig_tv[trig_pending].tv_usec = tus;
trig_pending++;
}
exti_reset_request(GPIO7);
}
if (exti_get_flag_status(GPIO6)) {
if (exti_trigger_callback[3]) {
gettime(&ts, &tus);
trig_tv[trig_pending].channel = 3;
trig_tv[trig_pending].pin = GPIO6;
trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO6);
trig_tv[trig_pending].tv_sec = ts;
trig_tv[trig_pending].tv_usec = tus;
trig_pending++;
}
exti_reset_request(GPIO6);
}
if (exti_get_flag_status(GPIO5)) {
if (exti_trigger_callback[4]) {
gettime(&ts, &tus);
trig_tv[trig_pending].channel = 4;
trig_tv[trig_pending].pin = GPIO5;
trig_tv[trig_pending].front = !!gpio_get(GPIOA,GPIO5);
trig_tv[trig_pending].tv_sec = ts;
trig_tv[trig_pending].tv_usec = tus;
trig_pending++;
}
exti_reset_request(GPIO5);
}
}
void exti_poll(void)
{
int i;
uint32_t pin;
for (i = 0; i < trig_pending; i++) {
if (exti_trigger_callback[trig_tv[i].channel]) {
DBG("EXTI cb!\r\n");
exti_trigger_callback[trig_tv[i].channel](trig_tv[i].pin, trig_tv[i].front, trig_tv[i].tv_sec, trig_tv[i].tv_usec);
}
}
trig_pending = 0;
}
/* 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))
{
int b = 0;
int st;
static uint32_t last_event = 0;
static uint32_t pressed_start = 0;
while(rot_down > 0) {
callback('-',0);
rot_down--;
}
while(rot_up > 0) {
callback('+',0);
rot_up--;
}
if (jiffies - last_event < BUTTON_DEBOUNCE_TIME)
return;
last_event = jiffies;
st = !!gpio_get(GPIOC, GPIO0);
if (!st) {
pressed_start = 0;
}
if ((button_press_pending) && st) {
if ((pressed_start == 0) || ((jiffies - pressed_start) > BUTTON_HOLD_TIME)) {
pressed_start = jiffies;
callback('*', 0);
return 1;
}
}
button_press_pending = 0;
return 0;
}
void button_init(void)
{
memset(Buttons, 0, sizeof(struct user_button) * N_BUTTONS);
button_setup();
button_start_read();
}