123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*guys, srsly, turn on warnings in the makefile*/
- #if defined(__GNUC__)
- #pragma GCC diagnostic warning "-Wall"
- #pragma GCC diagnostic warning "-Wextra"
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- /*-------------------------------------
- NEW TIMER API
- ---------------------------------------
- tmr.wdclr() -- not changed
- tmr.now() -- not changed
- tmr.time() -- not changed
- tmr.delay() -- not changed
- tmr.alarm() -- not changed
- tmr.stop() -- changed, see below. use tmr.unregister for old functionality
- tmr.register(id, interval, mode, function)
- bind function with timer and set the interval in ms
- the mode can be:
- tmr.ALARM_SINGLE for a single run alarm
- tmr.ALARM_SEMI for a multiple single run alarm
- tmr.ALARM_AUTO for a repating alarm
- tmr.register does NOT start the timer
- tmr.alarm is a tmr.register & tmr.start macro
- tmr.unregister(id)
- stop alarm, unbind function and clean up memory
- not needed for ALARM_SINGLE, as it unregisters itself
- tmr.start(id)
- ret: bool
- start a alarm, returns true on success
- tmr.stop(id)
- ret: bool
- stops a alarm, returns true on success
- this call dose not free any memory, to do so use tmr.unregister
- stopped alarms can be started with start
- tmr.interval(id, interval)
- set alarm interval, running alarm will be restarted
- tmr.state(id)
- ret: (bool, int) or nil
- returns alarm status (true=started/false=stopped) and mode
- nil if timer is unregistered
- tmr.softwd(int)
- set a negative value to stop the timer
- any other value starts the timer, when the
- countdown reaches zero, the device restarts
- the timer units are seconds
- */
- #include "module.h"
- #include "lauxlib.h"
- #include "platform.h"
- #include "c_types.h"
- #include "user_interface.h"
- #define TIMER_MODE_OFF 3
- #define TIMER_MODE_SINGLE 0
- #define TIMER_MODE_SEMI 2
- #define TIMER_MODE_AUTO 1
- #define TIMER_IDLE_FLAG (1<<7)
- #define STRINGIFY_VAL(x) #x
- #define STRINGIFY(x) STRINGIFY_VAL(x)
- // assuming system_timer_reinit() has *not* been called
- #define MAX_TIMEOUT_DEF 6870947 //SDK 1.5.3 limit (0x68D7A3)
- static const uint32 MAX_TIMEOUT=MAX_TIMEOUT_DEF;
- static const char* MAX_TIMEOUT_ERR_STR = "Range: 1-"STRINGIFY(MAX_TIMEOUT_DEF);
- typedef struct{
- os_timer_t os;
- sint32_t lua_ref;
- uint32_t interval;
- uint8_t mode;
- }timer_struct_t;
- typedef timer_struct_t* timer_t;
- // The previous implementation extended the rtc counter to 64 bits, and then
- // applied rtc2sec with the current calibration value to that 64 bit value.
- // This means that *ALL* clock ticks since bootup are counted with the *current*
- // clock period. In extreme cases (long uptime, sudden temperature change), this
- // could result in tmr.time() going backwards....
- // This implementation instead applies rtc2usec to short time intervals only (the
- // longest being around 1 second), and then accumulates the resulting microseconds
- // in a 64 bit counter. That's guaranteed to be monotonic, and should be a lot closer
- // to representing an actual uptime.
- static uint32_t rtc_time_cali=0;
- static uint32_t last_rtc_time=0;
- static uint64_t last_rtc_time_us=0;
- static sint32_t soft_watchdog = -1;
- static timer_struct_t alarm_timers[NUM_TMR];
- static os_timer_t rtc_timer;
- static void alarm_timer_common(void* arg){
- timer_t tmr = &alarm_timers[(uint32_t)arg];
- lua_State* L = lua_getstate();
- if(tmr->lua_ref == LUA_NOREF)
- return;
- lua_rawgeti(L, LUA_REGISTRYINDEX, tmr->lua_ref);
- //if the timer was set to single run we clean up after it
- if(tmr->mode == TIMER_MODE_SINGLE){
- luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
- tmr->lua_ref = LUA_NOREF;
- tmr->mode = TIMER_MODE_OFF;
- }else if(tmr->mode == TIMER_MODE_SEMI){
- tmr->mode |= TIMER_IDLE_FLAG;
- }
- lua_call(L, 0, 0);
- }
- // Lua: tmr.delay( us )
- static int tmr_delay( lua_State* L ){
- sint32_t us = luaL_checkinteger(L, 1);
- if(us <= 0)
- return luaL_error(L, "wrong arg range");
- while(us >= 1000000){
- us -= 1000000;
- os_delay_us(1000000);
- system_soft_wdt_feed ();
- }
- if(us>0){
- os_delay_us(us);
- system_soft_wdt_feed ();
- }
- return 0;
- }
- // Lua: tmr.now() , return system timer in us
- static int tmr_now(lua_State* L){
- uint32_t now = 0x7FFFFFFF & system_get_time();
- lua_pushinteger(L, now);
- return 1;
- }
- // Lua: tmr.register( id, interval, mode, function )
- static int tmr_register(lua_State* L){
- uint32_t id = luaL_checkinteger(L, 1);
- uint32_t interval = luaL_checkinteger(L, 2);
- uint8_t mode = luaL_checkinteger(L, 3);
- //Check if provided parameters are valid
- MOD_CHECK_ID(tmr, id);
- luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
- luaL_argcheck(L, (mode == TIMER_MODE_SINGLE || mode == TIMER_MODE_SEMI || mode == TIMER_MODE_AUTO), 3, "Invalid mode");
- luaL_argcheck(L, (lua_type(L, 4) == LUA_TFUNCTION || lua_type(L, 4) == LUA_TLIGHTFUNCTION), 4, "Must be function");
- //get the lua function reference
- lua_pushvalue(L, 4);
- sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
- timer_t tmr = &alarm_timers[id];
- if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
- ets_timer_disarm(&tmr->os);
- //there was a bug in this part, the second part of the following condition was missing
- if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref)
- luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
- tmr->lua_ref = ref;
- tmr->mode = mode|TIMER_IDLE_FLAG;
- tmr->interval = interval;
- ets_timer_setfn(&tmr->os, alarm_timer_common, (void*)id);
- return 0;
- }
- // Lua: tmr.start( id )
- static int tmr_start(lua_State* L){
- uint8_t id = luaL_checkinteger(L, 1);
- MOD_CHECK_ID(tmr,id);
- timer_t tmr = &alarm_timers[id];
- //we return false if the timer is not idle
- if(!(tmr->mode&TIMER_IDLE_FLAG)){
- lua_pushboolean(L, 0);
- }else{
- tmr->mode &= ~TIMER_IDLE_FLAG;
- ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1);
- lua_pushboolean(L, 1);
- }
- return 1;
- }
- // Lua: tmr.alarm( id, interval, repeat, function )
- static int tmr_alarm(lua_State* L){
- tmr_register(L);
- return tmr_start(L);
- }
- // Lua: tmr.stop( id )
- static int tmr_stop(lua_State* L){
- uint8_t id = luaL_checkinteger(L, 1);
- MOD_CHECK_ID(tmr,id);
- timer_t tmr = &alarm_timers[id];
- //we return false if the timer is idle (of not registered)
- if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){
- tmr->mode |= TIMER_IDLE_FLAG;
- ets_timer_disarm(&tmr->os);
- lua_pushboolean(L, 1);
- }else{
- lua_pushboolean(L, 0);
- }
- return 1;
- }
- // Lua: tmr.unregister( id )
- static int tmr_unregister(lua_State* L){
- uint8_t id = luaL_checkinteger(L, 1);
- MOD_CHECK_ID(tmr,id);
- timer_t tmr = &alarm_timers[id];
- if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
- ets_timer_disarm(&tmr->os);
- if(tmr->lua_ref != LUA_NOREF)
- luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
- tmr->lua_ref = LUA_NOREF;
- tmr->mode = TIMER_MODE_OFF;
- return 0;
- }
- // Lua: tmr.interval( id, interval )
- static int tmr_interval(lua_State* L){
- uint8_t id = luaL_checkinteger(L, 1);
- MOD_CHECK_ID(tmr,id);
- timer_t tmr = &alarm_timers[id];
- uint32_t interval = luaL_checkinteger(L, 2);
- luaL_argcheck(L, (interval > 0 && interval <= MAX_TIMEOUT), 2, MAX_TIMEOUT_ERR_STR);
- if(tmr->mode != TIMER_MODE_OFF){
- tmr->interval = interval;
- if(!(tmr->mode&TIMER_IDLE_FLAG)){
- ets_timer_disarm(&tmr->os);
- ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1);
- }
- }
- return 0;
- }
- // Lua: tmr.state( id )
- static int tmr_state(lua_State* L){
- uint8_t id = luaL_checkinteger(L, 1);
- MOD_CHECK_ID(tmr,id);
- timer_t tmr = &alarm_timers[id];
- if(tmr->mode == TIMER_MODE_OFF){
- lua_pushnil(L);
- return 1;
- }
- lua_pushboolean(L, (tmr->mode&TIMER_IDLE_FLAG)==0);
- lua_pushinteger(L, tmr->mode&(~TIMER_IDLE_FLAG));
- return 2;
- }
- /*I left the led comments 'couse I don't know
- why they are here*/
- // extern void update_key_led();
- // Lua: tmr.wdclr()
- static int tmr_wdclr( lua_State* L ){
- system_soft_wdt_feed ();
- // update_key_led();
- return 0;
- }
- //system_rtc_clock_cali_proc() returns
- //a fixed point value (12 bit fraction part)
- //it tells how many rtc clock ticks represent 1us.
- //the high 64 bits of the uint64_t multiplication
- //are unnedded (I did the math)
- static uint32_t rtc2usec(uint64_t rtc){
- return (rtc*rtc_time_cali)>>12;
- }
- // This returns the number of microseconds uptime. Note that it relies on the rtc clock,
- // which is notoriously temperature dependent
- inline static uint64_t rtc_timer_update(bool do_calibration){
- if (do_calibration || rtc_time_cali==0)
- rtc_time_cali=system_rtc_clock_cali_proc();
- uint32_t current = system_get_rtc_time();
- uint32_t since_last=current-last_rtc_time; // This will transparently deal with wraparound
- uint32_t us_since_last=rtc2usec(since_last);
- uint64_t now=last_rtc_time_us+us_since_last;
- // Only update if at least 100ms has passed since we last updated.
- // This prevents the rounding errors in rtc2usec from accumulating
- if (us_since_last>=100000)
- {
- last_rtc_time=current;
- last_rtc_time_us=now;
- }
- return now;
- }
- void rtc_callback(void *arg){
- rtc_timer_update(true);
- if(soft_watchdog > 0){
- soft_watchdog--;
- if(soft_watchdog == 0)
- system_restart();
- }
- }
- // Lua: tmr.time() , return rtc time in second
- static int tmr_time( lua_State* L ){
- uint64_t us=rtc_timer_update(false);
- lua_pushinteger(L, us/1000000);
- return 1;
- }
- // Lua: tmr.softwd( value )
- static int tmr_softwd( lua_State* L ){
- soft_watchdog = luaL_checkinteger(L, 1);
- return 0;
- }
- // Module function map
- static const LUA_REG_TYPE tmr_map[] = {
- { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
- { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
- { LSTRKEY( "wdclr" ), LFUNCVAL( tmr_wdclr ) },
- { LSTRKEY( "softwd" ), LFUNCVAL( tmr_softwd ) },
- { LSTRKEY( "time" ), LFUNCVAL( tmr_time ) },
- { LSTRKEY( "register" ), LFUNCVAL( tmr_register ) },
- { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
- { LSTRKEY( "start" ), LFUNCVAL( tmr_start ) },
- { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
- { LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) },
- { LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
- { LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) },
- { LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
- { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) },
- { LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) },
- { LNILKEY, LNILVAL }
- };
- int luaopen_tmr( lua_State *L ){
- int i;
- for(i=0; i<NUM_TMR; i++){
- alarm_timers[i].lua_ref = LUA_NOREF;
- alarm_timers[i].mode = TIMER_MODE_OFF;
- ets_timer_disarm(&alarm_timers[i].os);
- }
- last_rtc_time=system_get_rtc_time(); // Right now is time 0
- last_rtc_time_us=0;
- ets_timer_disarm(&rtc_timer);
- ets_timer_setfn(&rtc_timer, rtc_callback, NULL);
- ets_timer_arm_new(&rtc_timer, 1000, 1, 1);
- return 0;
- }
- NODEMCU_MODULE(TMR, "tmr", tmr_map, luaopen_tmr);
|