1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
#include "badge/gpio.h"
#include "badge/config.h"
#include "badge/log.h"
#include <Arduino.h>
#if UVOK_EPAP_BOARD == BOARD_ESP32_CROWPANEL
// Elecrow
#define EXIT_KEY 1
#endif
#define GPIO_STACK 2048
#define GPIO_QUEUE_LEN 32
typedef struct
{
uint32_t timestamp;
uint32_t pin : 8;
uint32_t state : 1;
} pin_notification_t;
_Static_assert(sizeof(uint32_t) >= sizeof(unsigned long), "return type of millis");
//! Keep track of inputs via interrupt
typedef struct
{
// uint32_t last_change;
uint32_t last_level;
uint32_t pressed;
uint32_t released;
// ... already forgot why I wanted this
uint32_t last_handled;
} InputInfo_t;
//! Keep track of inputs via interrupt
static volatile InputInfo_t inputs[NUM_DIGITAL_PINS];
//! FreeRTOS handles, storages, etc.
static struct
{
StaticTask_t task;
StackType_t task_stack[GPIO_STACK / sizeof(StackType_t)];
StaticQueue_t queue;
uint8_t queue_storage[GPIO_QUEUE_LEN * sizeof(pin_notification_t)];
QueueHandle_t queue_handle;
} gpio_task_stuff;
static ARDUINO_ISR_ATTR void gpio_pin_irq(void *);
static void gpio_loop(void *);
//! Create task, initialize pins...
void de::uvok::badge::gpio_init(void)
{
xTaskCreateStatic(gpio_loop, "gpio", GPIO_STACK, NULL, configMAX_PRIORITIES - 1, gpio_task_stuff.task_stack,
&gpio_task_stuff.task);
gpio_task_stuff.queue_handle =
xQueueCreateStatic(4, sizeof(pin_notification_t), &(gpio_task_stuff.queue_storage[0]), &gpio_task_stuff.queue);
#if UVOK_EPAP_BOARD == BOARD_ESP32_CROWPANEL
uint8_t inPins[] = {HOME_KEY, EXIT_KEY, HOME_KEY, NEXT_KEY, OK_KEY, PRV_KEY};
for (uint8_t p : inPins)
{
pinMode(p, GPIO_MODE_INPUT);
// simply use pin number as arg
attachInterruptArg(digitalPinToInterrupt(p), gpio_pin_irq, (void *)(ptrdiff_t)p, CHANGE);
inputs[p].last_level = digitalRead(p);
}
#endif
}
static volatile uint32_t event;
// "Poll task", for button 0.
de::uvok::badge::gpio_poll_result_t de::uvok::badge::gpio_poll(void)
{
if (event)
{
uint32_t r = event;
event = 0;
return {.duration_ms = (uint16_t)(r >> 8), .pin_number = (uint8_t)(r & 0xff), .has_data = true};
}
return {0};
}
//! GPIO task loop, read / handle queue.
static void gpio_loop(void *ctx)
{
Serial.println("Starting GPIO loop");
while (1)
{
pin_notification_t received;
if (xQueueReceive(gpio_task_stuff.queue_handle, &received, portMAX_DELAY) == pdTRUE)
{
const bool pressed = !received.state;
const uint8_t pin = received.pin;
LOG_F("(%u) Pin %u was %s\n", received.timestamp, pin, pressed ? "pressed" : "released");
LOG_F(" pressed: %u, released: %u\n", inputs[pin].pressed, inputs[pin].released);
if (
// ignore weird stuff
inputs[pin].pressed == 0
// Ignore overflows - badge won't be powered always anyway
|| inputs[pin].pressed >= inputs[pin].released
// Ignore "bouncing" or very short presses.
|| inputs[pin].released - inputs[pin].pressed < 50)
continue;
LOG_F(" Handling\n");
event = ((inputs[pin].released - inputs[pin].pressed) << 8) | (pin & 0xff);
}
}
}
//! GPIO interrupt handler
static ARDUINO_ISR_ATTR IRAM_ATTR void gpio_pin_irq(void *arg)
{
/*
... Ugh.
Pins 4, 5, 6, the selector thingy, is very dirty, or the IRQ is too slow.
Even with that code, I sometimes get multiples pressed events directly after each other,
or multiple released events.
Need to use some filtering, maybe missing some dirty events as a result.
*/
const uint8_t pin_no = (uint8_t)(ptrdiff_t)arg;
const int pinLevel = digitalRead(pin_no);
const uint32_t now = millis();
// Only remember pressed events, don't post.
if (!pinLevel)
{
inputs[pin_no].pressed = now;
return;
}
// Only post released events
inputs[pin_no].released = now;
// do any filtering in the main task loop
const pin_notification_t send = {.timestamp = now, .pin = pin_no, .state = (pinLevel == HIGH)};
BaseType_t wakeup = false;
xQueueSendFromISR(gpio_task_stuff.queue_handle, &send, &wakeup);
if (wakeup)
{
portYIELD_FROM_ISR();
}
}
|