Add support of counting of interrupts (#2149)

* Add support of counting of interrupts

* Update the timestamp when interrupt happens during dispatch. Also
clear out interrupts when setting up a new callback
This commit is contained in:
Philip Gladstone 2017-12-17 15:42:54 -05:00 committed by Arnim Läuger
parent 368c25db8b
commit 9aebc84b72
5 changed files with 65 additions and 16 deletions

View File

@ -53,17 +53,31 @@ static void gpio_intr_callback_task (task_param_t param, uint8 priority)
// GPIO callbacks are run in L0 and include the level as a parameter
lua_State *L = lua_getstate();
NODE_DBG("Calling: %08x\n", gpio_cb_ref[pin]);
//
if (!INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
// Edge triggered -- re-enable the interrupt
platform_gpio_intr_init(pin, pin_int_type[pin]);
}
bool needs_callback = 1;
while (needs_callback) {
// Note that the interrupt level only modifies 'seen' and
// the base level only modifies 'reported'.
// Do the actual callback
lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
lua_pushinteger(L, level);
lua_pushinteger(L, then);
lua_call(L, 2, 0);
uint16_t seen = pin_counter[pin].seen;
lua_pushinteger(L, 0x7fff & (seen - pin_counter[pin].reported));
pin_counter[pin].reported = seen & 0x7fff; // This will cause the next interrupt to trigger a callback
uint16_t diff = (seen ^ pin_counter[pin].seen);
// Needs another callback if seen changed but not if the top bit is set
needs_callback = diff <= 0x7fff && diff > 0;
if (needs_callback) {
// Fake this for next time (this only happens if another interrupt happens since
// we loaded the 'seen' variable.
then = system_get_time() & 0x7fffffff;
}
lua_call(L, 3, 0);
}
if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
// Level triggered -- re-enable the callback
@ -108,6 +122,14 @@ static int lgpio_trig( lua_State* L )
// unreference any overwritten callback
if(old_pin_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, old_pin_ref);
uint16_t seen;
// Make sure that we clear out any queued interrupts
do {
seen = pin_counter[pin].seen;
pin_counter[pin].reported = seen & 0x7fff;
} while (seen != pin_counter[pin].seen);
NODE_DBG("Pin data: %d %d %08x, %d %d %d, %08x\n",
pin, type, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], gpio_cb_ref[pin]);
platform_gpio_intr_init(pin, type);

View File

@ -9,6 +9,7 @@ uint8_t pin_func[GPIO_PIN_NUM];
#ifdef GPIO_INTERRUPT_ENABLE
uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
uint8_t pin_int_type[GPIO_PIN_NUM];
GPIO_INT_COUNTER pin_counter[GPIO_PIN_NUM];
#endif
typedef struct {

View File

@ -15,6 +15,13 @@ extern uint8_t pin_func[GPIO_PIN_NUM];
#ifdef GPIO_INTERRUPT_ENABLE
extern uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
extern uint8_t pin_int_type[GPIO_PIN_NUM];
typedef struct {
// These values have 15 bits of count, and the top bit
// in 'seen' is set if we are missing a task post
volatile uint16_t seen;
volatile uint16_t reported;
} GPIO_INT_COUNTER;
extern GPIO_INT_COUNTER pin_counter[GPIO_PIN_NUM];
#endif
void get_pin_map(void);

View File

@ -14,6 +14,8 @@
#include "driver/uart.h"
#include "driver/sigma_delta.h"
#define INTERRUPT_TYPE_IS_LEVEL(x) ((x) >= GPIO_PIN_INTR_LOLEVEL)
#ifdef GPIO_INTERRUPT_ENABLE
static task_handle_t gpio_task_handle;
@ -226,13 +228,25 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
if (gpio_status&1) {
int i = pin_num_inv[j];
if (pin_int_type[i]) {
uint16_t diff = pin_counter[i].seen ^ pin_counter[i].reported;
pin_counter[i].seen = 0x7fff & (pin_counter[i].seen + 1);
if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[i])) {
//disable interrupt
gpio_pin_intr_state_set(GPIO_ID_PIN(j), GPIO_PIN_INTR_DISABLE);
}
//clear interrupt status
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j));
if (diff == 0 || diff & 0x8000) {
uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level);
// We re-enable the interrupt when we execute the callback
if (!task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level)) {
// If we fail to post, then try on the next interrupt
pin_counter[i].seen |= 0x8000;
}
// We re-enable the interrupt when we execute the callback (if level)
}
}
}
}

View File

@ -125,10 +125,15 @@ This function is not available if GPIO_INTERRUPT_ENABLE was undefined at compile
- `type` "up", "down", "both", "low", "high", which represent *rising edge*, *falling edge*, *both
edges*, *low level*, and *high level* trigger modes respectivey. If the type is "none" or omitted
then the callback function is removed and the interrupt is disabled.
- `callback_function(level, when)` callback function when trigger occurs. The level of the specified pin
- `callback_function(level, when, eventcount)` callback function when trigger occurs. The level of the specified pin
at the interrupt passed as the first parameter to the callback. The timestamp of the event is passed
as the second parameter. This is in microseconds and has the same base as for `tmr.now()`. This timestamp
is grabbed at interrupt level and is more consistent than getting the time in the callback function.
This timestamp is normally of the first interrupt detected, but, under overload conditions, might be a later one.
The eventcount is the number of interrupts that were elided for this callback. This works best for edge triggered
interrupts and enables counting of edges. However, beware
of switch bounces -- you can get multiple pulses for a single switch closure. Counting
works best when the edges are digitally generated.
The previous callback function will be used if the function is omitted.
#### Returns