123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- #include "module.h"
- #include "lauxlib.h"
- #include "lmem.h"
- #include "platform.h"
- #include "user_interface.h"
- #include <stdint.h>
- #include <string.h>
- #include "gpio.h"
- #include "hw_timer.h"
- #include "pin_map.h"
- #include "driver/gpio16.h"
- #define TIMER_OWNER (('P' << 8) + 'U')
- #define xstr(s) str(s)
- #define str(s) #s
- // Maximum delay in microseconds
- #define DELAY_LIMIT 64000000
- typedef struct {
- uint32_t gpio_set;
- uint32_t gpio_clr;
- int32_t delay;
- int32_t delay_min;
- int32_t delay_max;
- uint32_t count;
- uint32_t count_left;
- uint16_t loop;
- } pulse_entry_t;
- typedef struct {
- uint32_t entry_count;
- volatile uint32_t steps;
- volatile uint16_t entry_pos;
- volatile uint16_t active_pos;
- volatile int16_t stop_pos; // -1 is stop nowhere, -2 is stop everywhere, otherwise is stop point
- pulse_entry_t *entry;
- volatile uint32_t expected_end_time;
- volatile uint32_t desired_end_time;
- volatile int32_t next_adjust;
- volatile int32_t pending_delay;
- volatile int cb_ref;
- } pulse_t;
- static int active_pulser_ref;
- static pulse_t *active_pulser;
- static platform_task_handle_t tasknumber;
- static int gpio_pulse_push_state(lua_State *L, pulse_t *pulser) {
- uint32_t now;
- uint32_t expected_end_time;
- uint32_t active_pos;
- uint32_t steps;
- do {
- now = 0x7FFFFFFF & system_get_time();
- expected_end_time = pulser->expected_end_time;
- active_pos = pulser->active_pos;
- steps = pulser->steps;
- } while (expected_end_time != pulser->expected_end_time ||
- active_pos != pulser->active_pos ||
- steps != pulser->steps);
- if (active_pos >= pulser->entry_count) {
- lua_pushnil(L);
- } else {
- lua_pushinteger(L, active_pos + 1); // Lua is 1 offset
- }
- lua_pushinteger(L, steps);
- int32_t diff = (expected_end_time - now) & 0x7fffffff;
- lua_pushinteger(L, (diff << 1) >> 1);
- lua_pushinteger(L, now);
- return 4;
- }
- static int gpio_pulse_getstate(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- return gpio_pulse_push_state(L, pulser);
- }
- static int gpio_pulse_stop(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- if (pulser != active_pulser) {
- return 0;
- }
- int argno = 2;
- int32_t stop_pos = -2;
- if (lua_type(L, argno) == LUA_TNUMBER) {
- stop_pos = luaL_checkinteger(L, 2);
- if (stop_pos != -2) {
- if (stop_pos < 1 || stop_pos > pulser->entry_count) {
- return luaL_error( L, "bad stop position: %d (valid range 1 - %d)", stop_pos, pulser->entry_count );
- }
- stop_pos = stop_pos - 1;
- }
- argno++;
- }
- if (lua_isfunction(L, argno)) {
- lua_pushvalue(L, argno);
- } else {
- return luaL_error( L, "missing callback" );
- }
- int new_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
- int cb_ref = pulser->cb_ref;
- pulser->cb_ref = LUA_NOREF;
- pulser->stop_pos = -1;
- pulser->cb_ref = new_cb_ref;
- pulser->stop_pos = stop_pos;
- luaL_unref(L, LUA_REGISTRYINDEX, cb_ref);
- lua_pushboolean(L, 1);
- return 1;
- }
- static int gpio_pulse_delete(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- if (pulser == active_pulser) {
- return 0;
- }
- luaL_unref(L, LUA_REGISTRYINDEX, pulser->cb_ref);
- return 0;
- }
- static void fill_entry_from_table(lua_State *L, pulse_entry_t *entry) {
- if (lua_type(L, -1) != LUA_TTABLE) {
- luaL_error(L, "All entries must be tables");
- }
- lua_pushnil(L); // key position
- while (lua_next(L, -2)) {
- // stack now contains: -1 => value; -2 => key; -3 => table
- if (lua_type(L, -2) == LUA_TNUMBER) {
- int pin = luaL_checkint(L, -2);
- int value = luaL_checkint(L, -1);
- if (pin < 0 || pin >= GPIO_PIN_NUM) {
- luaL_error(L, "pin number %d must be in range 0 .. %d", pin, GPIO_PIN_NUM - 1);
- }
- if (value) {
- entry->gpio_set |= BIT(pin_num[pin]);
- } else {
- entry->gpio_clr |= BIT(pin_num[pin]);
- }
- } else {
- const char *str = luaL_checkstring(L, -2);
- if (strcmp(str, "delay") == 0) {
- entry->delay = luaL_checkint(L, -1);
- if (entry->delay < 0 || entry->delay > DELAY_LIMIT) {
- luaL_error(L, "delay of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay);
- }
- } else if (strcmp(str, "min") == 0) {
- entry->delay_min = luaL_checkint(L, -1);
- if (entry->delay_min < 0 || entry->delay_min > DELAY_LIMIT) {
- luaL_error(L, "delay minimum of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay_min);
- }
- } else if (strcmp(str, "max") == 0) {
- entry->delay_max = luaL_checkint(L, -1);
- if (entry->delay_max < 0 || entry->delay_max > DELAY_LIMIT) {
- luaL_error(L, "delay maximum of %d must be in the range 0 .. " xstr(DELAY_LIMIT) " microseconds", entry->delay_max);
- }
- } else if (strcmp(str, "count") == 0) {
- entry->count = luaL_checkint(L, -1);
- } else if (strcmp(str, "loop") == 0) {
- entry->loop = luaL_checkint(L, -1);
- } else {
- luaL_error(L, "Unrecognized key found: %s", str);
- }
- }
- lua_pop(L, 1);
- }
- if (entry->delay_min != -1 || entry->delay_max != -1) {
- if (entry->delay_min == -1) {
- entry->delay_min = 0;
- }
- if (entry->delay_min > entry->delay ||
- entry->delay_max < entry->delay) {
- luaL_error(L, "Delay of %d must be between min and max", entry->delay);
- }
- }
- lua_pop(L, 1);
- }
- static int gpio_pulse_build(lua_State *L) {
- // Take a table argument
- luaL_checktype(L, 1, LUA_TTABLE);
- // First figure out how big we need the block to be
- size_t size = lua_objlen(L, 1);
- if (size > 100) {
- return luaL_error(L, "table is too large: %d entries is more than 100", size);
- }
- size_t memsize = sizeof(pulse_t) + size * sizeof(pulse_entry_t);
- pulse_t *pulser = (pulse_t *) lua_newuserdata(L, memsize);
- memset(pulser, 0, memsize);
- //
- // Associate its metatable
- luaL_getmetatable(L, "gpio.pulse");
- lua_setmetatable(L, -2);
- pulser->entry = (pulse_entry_t *) (pulser + 1);
- pulser->entry_count = size;
- size_t i;
- for (i = 0; i < size; i++) {
- pulse_entry_t *entry = pulser->entry + i;
- entry->delay_min = -1;
- entry->delay_max = -1;
- lua_rawgeti(L, 1, i + 1);
- fill_entry_from_table(L, entry);
- }
- return 1;
- }
- static int gpio_pulse_update(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- int entry_pos = luaL_checkinteger(L, 2);
- if (entry_pos < 1 || entry_pos > pulser->entry_count) {
- return luaL_error(L, "entry number must be in range 1 .. %d", pulser->entry_count);
- }
- pulse_entry_t *entry = pulser->entry + entry_pos - 1;
- pulse_entry_t new_entry = *entry;
- lua_pushvalue(L, 3);
- fill_entry_from_table(L, &new_entry);
- // Now do the update
- ETS_FRC1_INTR_DISABLE();
- *entry = new_entry;
- ETS_FRC1_INTR_ENABLE();
- return 0;
- }
- static int gpio_pulse_adjust(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- if (active_pulser != pulser) {
- return 0;
- }
- int offset = luaL_checkinteger(L, 2);
- // This will alter the next adjustable
- pulser->next_adjust = offset;
- int rc = gpio_pulse_push_state(L, active_pulser);
- return rc;
- }
- static int gpio_pulse_cancel(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- if (active_pulser != pulser) {
- return 0;
- }
- // Shut off the timer
- platform_hw_timer_close(TIMER_OWNER);
- int rc = gpio_pulse_push_state(L, active_pulser);
- active_pulser = NULL;
- int pulser_ref = active_pulser_ref;
- active_pulser_ref = LUA_NOREF;
- luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref);
- return rc;
- }
- static void ICACHE_RAM_ATTR gpio_pulse_timeout(os_param_t p) {
- (void) p;
- uint32_t now = system_get_time();
- int delay;
- if (active_pulser) {
- delay = active_pulser->pending_delay;
- if (delay > 0) {
- if (delay > 1200000) {
- delay = 1000000;
- }
- active_pulser->pending_delay -= delay;
- platform_hw_timer_arm_us(TIMER_OWNER, delay);
- return;
- }
- }
- do {
- active_pulser->active_pos = active_pulser->entry_pos;
- if (!active_pulser || active_pulser->entry_pos >= active_pulser->entry_count) {
- if (active_pulser) {
- active_pulser->steps++;
- }
- platform_hw_timer_close(TIMER_OWNER);
- platform_post_low(tasknumber, 0);
- return;
- }
- active_pulser->steps++;
- pulse_entry_t *entry = active_pulser->entry + active_pulser->entry_pos;
- // Yes, this means that there is more skew on D0 than on other pins....
- if (entry->gpio_set & 0x10000) {
- gpio16_output_set(1);
- }
- GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, entry->gpio_set);
- GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, entry->gpio_clr);
- if (entry->gpio_clr & 0x10000) {
- gpio16_output_set(0);
- }
- int16_t stop = active_pulser->stop_pos;
- if (stop == -2 || stop == active_pulser->entry_pos) {
- platform_hw_timer_close(TIMER_OWNER);
- platform_post_low(tasknumber, 0);
- return;
- }
- if (entry->loop) {
- if (entry->count_left == 0) {
- entry->count_left = entry->count + 1;
- }
- if (--entry->count_left >= 1) {
- active_pulser->entry_pos = entry->loop - 1; // zero offset
- } else {
- active_pulser->entry_pos++;
- }
- } else {
- active_pulser->entry_pos++;
- }
- delay = entry->delay;
- int delay_offset = 0;
- if (entry->delay_min != -1) {
- int offset = active_pulser->next_adjust;
- active_pulser->next_adjust = 0;
- delay_offset = ((0x7fffffff & (now - active_pulser->desired_end_time)) << 1) >> 1;
- delay -= delay_offset;
- delay += offset;
- //dbg_printf("%d(et %d diff %d): Delay was %d us, offset = %d, delay_offset = %d, new delay = %d, range=%d..%d\n",
- // now, active_pulser->desired_end_time, now - active_pulser->desired_end_time,
- // entry->delay, offset, delay_offset, delay, entry->delay_min, entry->delay_max);
- if (delay < entry->delay_min) {
- // we can't delay as little as 'delay', so we need to adjust
- // the next period as well.
- active_pulser->next_adjust = (entry->delay - entry->delay_min) + offset;
- delay = entry->delay_min;
- } else if (delay > entry->delay_max) {
- // we can't delay as much as 'delay', so we need to adjust
- // the next period as well.
- active_pulser->next_adjust = (entry->delay - entry->delay_max) + offset;
- delay = entry->delay_max;
- }
- }
- active_pulser->desired_end_time += delay + delay_offset;
- active_pulser->expected_end_time = system_get_time() + delay;
- } while (delay < 3);
- if (delay > 1200000) {
- active_pulser->pending_delay = delay - 1000000;
- delay = 1000000;
- }
- platform_hw_timer_arm_us(TIMER_OWNER, delay);
- }
- static int gpio_pulse_start(lua_State *L) {
- pulse_t *pulser = luaL_checkudata(L, 1, "gpio.pulse");
- if (active_pulser) {
- return luaL_error(L, "pulse operation already in progress");
- }
- int argno = 2;
- int initial_adjust;
- if (lua_type(L, argno) == LUA_TNUMBER) {
- initial_adjust = luaL_checkinteger(L, argno);
- argno++;
- }
- if (lua_isfunction(L, argno)) {
- lua_pushvalue(L, argno);
- } else {
- return luaL_error( L, "missing callback" );
- }
- if (pulser->cb_ref) {
- luaL_unref(L, LUA_REGISTRYINDEX, pulser->cb_ref);
- }
- pulser->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
- active_pulser = pulser;
- lua_pushvalue(L, 1);
- active_pulser_ref = luaL_ref(L, LUA_REGISTRYINDEX);
- size_t i;
- for (i = 0; i < pulser->entry_count; i++) {
- pulser->entry[i].count_left = 0;
- }
- pulser->entry_pos = 0;
- pulser->active_pos = 0;
- pulser->steps = 0;
- pulser->stop_pos = -1;
- pulser->next_adjust = initial_adjust;
- // Now start things up
- if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, FALSE)) {
- // Failed to init the timer
- luaL_error(L, "Unable to initialize timer");
- }
- active_pulser->expected_end_time = 0x7fffffff & system_get_time();
- platform_hw_timer_set_func(TIMER_OWNER, gpio_pulse_timeout, 0);
- gpio_pulse_timeout(0);
- return 0;
- }
- static void gpio_pulse_task(os_param_t param, uint8_t prio)
- {
- (void) param;
- (void) prio;
- if (active_pulser) {
- lua_State *L = lua_getstate();
- // Invoke the callback
- lua_rawgeti(L, LUA_REGISTRYINDEX, active_pulser->cb_ref);
- int rc = gpio_pulse_push_state(L, active_pulser);
- active_pulser = NULL;
- int pulser_ref = active_pulser_ref;
- active_pulser_ref = LUA_NOREF;
- luaL_unref(L, LUA_REGISTRYINDEX, pulser_ref);
- luaL_pcallx(L, rc, 0);
- }
- }
- LROT_BEGIN(pulse, NULL, LROT_MASK_GC_INDEX)
- LROT_FUNCENTRY( __gc, gpio_pulse_delete )
- LROT_TABENTRY( __index, pulse )
- LROT_FUNCENTRY( getstate, gpio_pulse_getstate )
- LROT_FUNCENTRY( stop, gpio_pulse_stop )
- LROT_FUNCENTRY( cancel, gpio_pulse_cancel )
- LROT_FUNCENTRY( start, gpio_pulse_start )
- LROT_FUNCENTRY( adjust, gpio_pulse_adjust )
- LROT_FUNCENTRY( update, gpio_pulse_update )
- LROT_END(pulse, NULL, LROT_MASK_GC_INDEX)
- LROT_BEGIN(gpio_pulse, NULL, 0)
- LROT_FUNCENTRY( build, gpio_pulse_build )
- LROT_END(gpio_pulse, NULL, 0)
- int gpio_pulse_init(lua_State *L)
- {
- luaL_rometatable(L, "gpio.pulse", LROT_TABLEREF(pulse));
- tasknumber = platform_task_get_id(gpio_pulse_task);
- return 0;
- }
- NODEMCU_MODULE(GPIOPULSE, "gpiopulse", gpio_pulse, gpio_pulse_init);
|