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:
parent
368c25db8b
commit
9aebc84b72
|
@ -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
|
// GPIO callbacks are run in L0 and include the level as a parameter
|
||||||
lua_State *L = lua_getstate();
|
lua_State *L = lua_getstate();
|
||||||
NODE_DBG("Calling: %08x\n", gpio_cb_ref[pin]);
|
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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the actual callback
|
bool needs_callback = 1;
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
|
|
||||||
lua_pushinteger(L, level);
|
while (needs_callback) {
|
||||||
lua_pushinteger(L, then);
|
// Note that the interrupt level only modifies 'seen' and
|
||||||
lua_call(L, 2, 0);
|
// 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);
|
||||||
|
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])) {
|
if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[pin])) {
|
||||||
// Level triggered -- re-enable the callback
|
// Level triggered -- re-enable the callback
|
||||||
|
@ -108,6 +122,14 @@ static int lgpio_trig( lua_State* L )
|
||||||
// unreference any overwritten callback
|
// unreference any overwritten callback
|
||||||
if(old_pin_ref != LUA_NOREF) luaL_unref(L, LUA_REGISTRYINDEX, old_pin_ref);
|
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",
|
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]);
|
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);
|
platform_gpio_intr_init(pin, type);
|
||||||
|
|
|
@ -9,6 +9,7 @@ uint8_t pin_func[GPIO_PIN_NUM];
|
||||||
#ifdef GPIO_INTERRUPT_ENABLE
|
#ifdef GPIO_INTERRUPT_ENABLE
|
||||||
uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
|
uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
|
||||||
uint8_t pin_int_type[GPIO_PIN_NUM];
|
uint8_t pin_int_type[GPIO_PIN_NUM];
|
||||||
|
GPIO_INT_COUNTER pin_counter[GPIO_PIN_NUM];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -15,6 +15,13 @@ extern uint8_t pin_func[GPIO_PIN_NUM];
|
||||||
#ifdef GPIO_INTERRUPT_ENABLE
|
#ifdef GPIO_INTERRUPT_ENABLE
|
||||||
extern uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
|
extern uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
|
||||||
extern uint8_t pin_int_type[GPIO_PIN_NUM];
|
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
|
#endif
|
||||||
|
|
||||||
void get_pin_map(void);
|
void get_pin_map(void);
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "driver/uart.h"
|
#include "driver/uart.h"
|
||||||
#include "driver/sigma_delta.h"
|
#include "driver/sigma_delta.h"
|
||||||
|
|
||||||
|
#define INTERRUPT_TYPE_IS_LEVEL(x) ((x) >= GPIO_PIN_INTR_LOLEVEL)
|
||||||
|
|
||||||
#ifdef GPIO_INTERRUPT_ENABLE
|
#ifdef GPIO_INTERRUPT_ENABLE
|
||||||
static task_handle_t gpio_task_handle;
|
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) {
|
if (gpio_status&1) {
|
||||||
int i = pin_num_inv[j];
|
int i = pin_num_inv[j];
|
||||||
if (pin_int_type[i]) {
|
if (pin_int_type[i]) {
|
||||||
//disable interrupt
|
uint16_t diff = pin_counter[i].seen ^ pin_counter[i].reported;
|
||||||
gpio_pin_intr_state_set(GPIO_ID_PIN(j), GPIO_PIN_INTR_DISABLE);
|
|
||||||
|
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
|
//clear interrupt status
|
||||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j));
|
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j));
|
||||||
uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
|
|
||||||
task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level);
|
if (diff == 0 || diff & 0x8000) {
|
||||||
// We re-enable the interrupt when we execute the callback
|
uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
- `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
|
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.
|
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
|
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
|
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.
|
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.
|
The previous callback function will be used if the function is omitted.
|
||||||
|
|
||||||
#### Returns
|
#### Returns
|
||||||
|
|
Loading…
Reference in New Issue