Bladeren bron

Add New Tasking I/F and rework GPIO, UART, etc to support it

As with the last commit this rolls up the follwowing, but include the various
review comments on the PR.

-   **Documentation changes**. I've added the taks FAQ as a stub new Extension
developer FAQ, and split the old FAQ into a Lua Developer FAQ and a Hardware
FAQ.

-   **Tasking I/F**.  New `app/task/Makefile`, `app/task/task.c`,
`app/include/task/task.h` and `app/Makefile` as per previous commit.  Cascade
changes to `app/driver/uart.c`, `app/include/driver/uart.h`,
`app/user/user_main.c` and `app/modules/node.c`

-   **GPIO Rework** to `app/modules/gpio.c` and `pin_map.[hc]`, `platform.[hc]`
in `app/platform`

-   **Other Optimisations** Move the `platform_*_exists()` from
`app/platform/common.c` to static inline declarations in `platform.h` as
this generates faster, smaller code. Move lgc.a routines out of iram0.
TerryE 8 jaren geleden
bovenliggende
commit
49733f6f6d

+ 2 - 0
app/Makefile

@@ -32,6 +32,7 @@ SUBDIRS= 					\
 	lwip 					\
 	coap 					\
 	mqtt 					\
+	task 					\
 	u8glib 					\
 	ucglib					\
 	smart 					\
@@ -73,6 +74,7 @@ COMPONENTS_eagle.app.v6 = 			\
 	json/libjson.a 				\
 	upgrade/libupgrade.a 			\
 	platform/libplatform.a 			\
+	task/libtask.a 				\
 	libc/liblibc.a 				\
 	lua/liblua.a 				\
 	lwip/liblwip.a 				\

+ 7 - 8
app/driver/uart.c

@@ -12,6 +12,7 @@
 #include "ets_sys.h"
 #include "osapi.h"
 #include "driver/uart.h"
+#include "task/task.h"
 #include "user_config.h"
 #include "user_interface.h"
 
@@ -27,8 +28,7 @@
 
 
 // For event signalling
-static uint8 task = USER_TASK_PRIO_MAX;
-static os_signal_t sig;
+static task_handle_t sig = 0;
 
 // UartDev is defined and initialized in rom code.
 extern UartDevice UartDev;
@@ -248,7 +248,7 @@ uart0_rx_intr_handler(void *para)
         if (RcvChar == '\r' || RcvChar == '\n' ) {
             pRxBuff->BuffState = WRITE_OVER;
         }
-        
+
         if (pRxBuff->pWritePos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) {
             // overflow ...we may need more error handle here.
             pRxBuff->pWritePos = pRxBuff->pRcvMsgBuff ;
@@ -258,7 +258,7 @@ uart0_rx_intr_handler(void *para)
 
         if (pRxBuff->pWritePos == pRxBuff->pReadPos){   // overflow one byte, need push pReadPos one byte ahead
             if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) {
-                pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; 
+                pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ;
             } else {
                 pRxBuff->pReadPos++;
             }
@@ -267,8 +267,8 @@ uart0_rx_intr_handler(void *para)
         got_input = true;
     }
 
-    if (got_input && task != USER_TASK_PRIO_MAX)
-      system_os_post (task, sig, UART0);
+    if (got_input && sig)
+      task_post_low (sig, false);
 }
 
 /******************************************************************************
@@ -281,9 +281,8 @@ uart0_rx_intr_handler(void *para)
  * Returns      : NONE
 *******************************************************************************/
 void ICACHE_FLASH_ATTR
-uart_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 task_prio, os_signal_t sig_input)
+uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input)
 {
-    task = task_prio;
     sig = sig_input;
 
     // rom use 74880 baut_rate, here reinitialize

+ 1 - 1
app/include/driver/uart.h

@@ -101,7 +101,7 @@ typedef struct {
     int                      buff_uart_no;  //indicate which uart use tx/rx buffer
 } UartDevice;
 
-void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, uint8 task_prio, os_signal_t sig_input);
+void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input);
 void uart0_alt(uint8 on);
 void uart0_sendStr(const char *str);
 void uart0_putc(const char c);

+ 34 - 0
app/include/task/task.h

@@ -0,0 +1,34 @@
+#ifndef _TASK_H_
+#define _TASK_H_
+
+#include "ets_sys.h"
+#include "osapi.h"
+#include "os_type.h"
+
+/* use LOW / MEDIUM / HIGH since it isn't clear from the docs which is higher */
+
+#define TASK_PRIORITY_LOW     0
+#define TASK_PRIORITY_MEDIUM  1
+#define TASK_PRIORITY_HIGH    2
+#define TASK_PRIORITY_COUNT   3
+
+/*
+* Signals are a 32-bit number of the form header:14; count:16, priority:2.  The header
+* is just a fixed fingerprint and the count is allocated serially by the task get_id()
+* function.
+*/
+
+#define task_post_low(handle,param) system_os_post(0, (handle | TASK_PRIORITY_LOW), param)
+#define task_post_medium(handle,param) system_os_post(1, (handle | TASK_PRIORITY_MEDIUM), param)
+#define task_post_high(handle,param) system_os_post(2, (handle | TASK_PRIORITY_HIGH), param)
+
+#define task_handle_t os_signal_t
+#define task_param_t os_param_t
+
+typedef void (*task_callback_t)(task_param_t param, uint8 prio);
+
+bool task_init_handler(uint8 priority, uint8 qlen);
+task_handle_t task_get_id(task_callback_t t);
+
+#endif
+

+ 8 - 1
app/include/user_config.h

@@ -41,12 +41,19 @@
 #define NODE_ERR
 #endif	/* NODE_ERROR */
 
+#define GPIO_INTERRUPT_ENABLE
+// #define GPIO_SAFE_NO_INTR_ENABLE
+
 #define ICACHE_STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
 #define ICACHE_STORE_ATTR __attribute__((aligned(4)))
 #define ICACHE_RAM_ATTR __attribute__((section(".iram0.text")))
+#ifdef  GPIO_SAFE_NO_INTR_ENABLE
+#define NO_INTR_CODE ICACHE_RAM_ATTR __attribute__ ((noinline))
+#else
+#define NO_INTR_CODE inline
+#endif
 
 #define CLIENT_SSL_ENABLE
-#define GPIO_INTERRUPT_ENABLE
 //#define MD2_ENABLE
 #define SHA2_ENABLE
 

+ 98 - 143
app/modules/gpio.c

@@ -2,9 +2,9 @@
 
 #include "module.h"
 #include "lauxlib.h"
+#include "lmem.h"
 #include "platform.h"
-#include "rom.h"
-
+#include "user_interface.h"
 #include "c_types.h"
 #include "c_string.h"
 
@@ -16,139 +16,115 @@
 #define HIGH PLATFORM_GPIO_HIGH
 #define LOW PLATFORM_GPIO_LOW
 
-
 #ifdef GPIO_INTERRUPT_ENABLE
 static int gpio_cb_ref[GPIO_PIN_NUM];
-static lua_State* gL = NULL;
-
-void lua_gpio_unref(unsigned pin){
-  if(gpio_cb_ref[pin] != LUA_NOREF){
-    if(gL!=NULL)
-      luaL_unref(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
-  }
-  gpio_cb_ref[pin] = LUA_NOREF;
-}
 
-void gpio_intr_callback( unsigned pin, unsigned level )
+// This task is scheduled by the ISP if pin_trigger[pin] is true and is used
+// to intitied the Lua-land gpio.trig() callback function
+static void gpio_intr_callback_task (task_param_t param, uint8 priority)
 {
+  unsigned pin = param >> 1;
+  unsigned level = param & 1;
+  UNUSED(priority);
+
   NODE_DBG("pin:%d, level:%d \n", pin, level);
-  if(gpio_cb_ref[pin] == LUA_NOREF)
-    return;
-  if(!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
-  lua_pushinteger(gL, level);
-  lua_call(gL, 1, 0);
+  if(gpio_cb_ref[pin] != LUA_NOREF) {
+    // GPIO callbacks are run in L0 and inlcude the level as a parameter
+    lua_State *L = lua_getstate();
+    NODE_DBG("Calling: %08x\n", gpio_cb_ref[pin]);
+    // The trigger is reset before the callback, as the callback itself might reset the trigger
+    pin_trigger[pin] = true;
+    lua_rawgeti(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+    lua_pushinteger(L, level);
+    lua_call(L, 1, 0);
+  }
 }
 
 // Lua: trig( pin, type, function )
 static int lgpio_trig( lua_State* L )
 {
-  unsigned type;
-  unsigned pin;
-  size_t sl;
-  
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  if(pin==0)
-    return luaL_error( L, "no interrupt for D0" );
-
-  const char *str = luaL_checklstring( L, 2, &sl );
-  if (str == NULL)
-    return luaL_error( L, "wrong arg type" );
-
-  if(sl == 2 && c_strcmp(str, "up") == 0){
-    type = GPIO_PIN_INTR_POSEDGE;
-  }else if(sl == 4 && c_strcmp(str, "down") == 0){
-    type = GPIO_PIN_INTR_NEGEDGE;
-  }else if(sl == 4 && c_strcmp(str, "both") == 0){
-    type = GPIO_PIN_INTR_ANYEDGE;
-  }else if(sl == 3 && c_strcmp(str, "low") == 0){
-    type = GPIO_PIN_INTR_LOLEVEL;
-  }else if(sl == 4 && c_strcmp(str, "high") == 0){
-    type = GPIO_PIN_INTR_HILEVEL;
-  }else{
-    type = GPIO_PIN_INTR_DISABLE;
-  }
-
-  // luaL_checkanyfunction(L, 3);
-  if (lua_type(L, 3) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION){
+  unsigned pin = luaL_checkinteger( L, 1 );
+  static const char * const opts[] = {"", "up", "down", "both", "low", "high"};
+  static const int opts_type[] = {
+    GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_POSEDGE, GPIO_PIN_INTR_NEGEDGE,
+    GPIO_PIN_INTR_ANYEDGE, GPIO_PIN_INTR_LOLEVEL, GPIO_PIN_INTR_HILEVEL
+    };
+  int type = opts_type[luaL_checkoption(L, 2, "", opts)];
+
+  luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, 1, "Invalid interrupt pin");
+
+  if (type != GPIO_PIN_INTR_DISABLE) {
+    int cbtype = lua_type(L, 3);
+    luaL_argcheck(L, cbtype == LUA_TFUNCTION || cbtype == LUA_TLIGHTFUNCTION, 3,
+      "invalid callback type");
     lua_pushvalue(L, 3);  // copy argument (func) to the top of stack
-    if(gpio_cb_ref[pin] != LUA_NOREF)
-      luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
     gpio_cb_ref[pin] = luaL_ref(L, LUA_REGISTRYINDEX);
   }
-
+  NODE_DBG("Pin data: %d %d %08x, %d %d %d %d, %08x\n",
+          pin, type, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], pin_trigger[pin], gpio_cb_ref[pin]);
   platform_gpio_intr_init(pin, type);
-  return 0;  
+  return 0;
 }
 #endif
 
 // Lua: mode( pin, mode, pullup )
 static int lgpio_mode( lua_State* L )
 {
-  unsigned mode, pullup = FLOAT;
-  unsigned pin;
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned mode = luaL_checkinteger( L, 2 );
+  unsigned pullup = luaL_optinteger( L, 3, FLOAT );
+
+  luaL_argcheck(L, platform_gpio_exists(pin) && (mode!=INTERRUPT || pin>0), 1, "Invalid pin");
+  luaL_argcheck(L, mode==OUTPUT || mode==INPUT
+ #ifdef GPIO_INTERRUPT_ENABLE
+                                               || mode==INTERRUPT
+ #endif
+                                                                  , 2,  "wrong arg type" );
+  if(pullup!=FLOAT) pullup = PULLUP;
+
+NODE_DBG("pin,mode,pullup= %d %d %d\n",pin,mode,pullup);
+NODE_DBG("Pin data at mode: %d %08x, %d %d %d %d, %08x\n",
+          pin, pin_mux[pin], pin_num[pin], pin_func[pin], pin_int_type[pin], pin_trigger[pin], gpio_cb_ref[pin]);
 
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  mode = luaL_checkinteger( L, 2 );
-  if ( mode!=OUTPUT && mode!=INPUT && mode!=INTERRUPT)
-    return luaL_error( L, "wrong arg type" );
-  if(pin==0 && mode==INTERRUPT)
-    return luaL_error( L, "no interrupt for D0" );
-  if(lua_isnumber(L, 3))
-    pullup = lua_tointeger( L, 3 );
-  if(pullup!=FLOAT)
-    pullup = PULLUP;
 #ifdef GPIO_INTERRUPT_ENABLE
-  gL = L;   // save to local gL, for callback function
-  if (mode!=INTERRUPT){     // disable interrupt
+  if (mode != INTERRUPT){     // disable interrupt
     if(gpio_cb_ref[pin] != LUA_NOREF){
       luaL_unref(L, LUA_REGISTRYINDEX, gpio_cb_ref[pin]);
+      gpio_cb_ref[pin] = LUA_NOREF;
     }
-    gpio_cb_ref[pin] = LUA_NOREF;
   }
 #endif
-  int r = platform_gpio_mode( pin, mode, pullup );
-  if( r<0 )
+
+  if( platform_gpio_mode( pin, mode, pullup ) < 0 )
     return luaL_error( L, "wrong pin num." );
-  return 0;  
+
+  return 0;
 }
 
 // Lua: read( pin )
 static int lgpio_read( lua_State* L )
 {
-  unsigned pin;
-  
-  pin = luaL_checkinteger( L, 1 );
+  unsigned pin = luaL_checkinteger( L, 1 );
   MOD_CHECK_ID( gpio, pin );
-
-  unsigned level = platform_gpio_read( pin );
-  lua_pushinteger( L, level );
-  return 1; 
+  lua_pushinteger( L, platform_gpio_read( pin ) );
+  return 1;
 }
 
 // Lua: write( pin, level )
 static int lgpio_write( lua_State* L )
 {
-  unsigned level;
-  unsigned pin;
-  
-  pin = luaL_checkinteger( L, 1 );
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned level = luaL_checkinteger( L, 2 );
+
   MOD_CHECK_ID( gpio, pin );
-  level = luaL_checkinteger( L, 2 );
-  if ( level!=HIGH && level!=LOW )
-    return luaL_error( L, "wrong arg type" );
+  luaL_argcheck(L, level==HIGH || level==LOW, 2, "wrong level type" );
+
   platform_gpio_write(pin, level);
-  return 0;  
+  return 0;
 }
 
 #define DELAY_TABLE_MAX_LEN 256
-#define noInterrupts ets_intr_lock
-#define interrupts ets_intr_unlock
 #define delayMicroseconds os_delay_us
-#define DIRECT_WRITE(pin, level)    (GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level))
 // Lua: serout( pin, firstLevel, delay_table, [repeatNum] )
 // -- serout( pin, firstLevel, delay_table, [repeatNum] )
 // gpio.mode(1,gpio.OUTPUT,gpio.PULLUP)
@@ -161,62 +137,41 @@ static int lgpio_write( lua_State* L )
 // gpio.serout(1,1,{8,18},8) -- serial 30% pwm 38k, lasts 8 cycles
 static int lgpio_serout( lua_State* L )
 {
-  unsigned level;
-  unsigned pin;
-  unsigned table_len = 0;
-  unsigned repeat = 0;
-  int delay_table[DELAY_TABLE_MAX_LEN];
-  
-  pin = luaL_checkinteger( L, 1 );
-  MOD_CHECK_ID( gpio, pin );
-  level = luaL_checkinteger( L, 2 );
-  if ( level!=HIGH && level!=LOW )
-    return luaL_error( L, "wrong arg type" );
-  if( lua_istable( L, 3 ) )
-  {
-    table_len = lua_objlen( L, 3 );
-    if (table_len <= 0 || table_len>DELAY_TABLE_MAX_LEN)
-      return luaL_error( L, "wrong arg range" );
-    int i;
-    for( i = 0; i < table_len; i ++ )
-    {
-      lua_rawgeti( L, 3, i + 1 );
-      delay_table[i] = ( int )luaL_checkinteger( L, -1 );
-      lua_pop( L, 1 );
-      if( delay_table[i] < 0 || delay_table[i] > 1000000 )    // can not delay more than 1000000 us
-        return luaL_error( L, "delay must < 1000000 us" );
-    }
-  } else {
-    return luaL_error( L, "wrong arg range" );
-  } 
-
-  if(lua_isnumber(L, 4))
-    repeat = lua_tointeger( L, 4 );
-  if( repeat < 0 || repeat > DELAY_TABLE_MAX_LEN )
-    return luaL_error( L, "delay must < 256" );
-
-  if(repeat==0)
-    repeat = 1;
-  int j;
-  bool skip_loop = true;
-  do
-  {
-    if(skip_loop){    // skip the first loop.
-      skip_loop = false;
+  unsigned clocks_per_us = system_get_cpu_freq();
+  unsigned pin = luaL_checkinteger( L, 1 );
+  unsigned level = luaL_checkinteger( L, 2 );
+  unsigned repeats = luaL_optint( L, 4, 1 );
+  unsigned table_len, i, j;
+
+  luaL_argcheck(L, platform_gpio_exists(pin), 1, "Invalid pin");
+  luaL_argcheck(L, level==HIGH || level==LOW, 2, "Wrong arg type" );
+  luaL_argcheck(L, lua_istable( L, 3 ) &&
+                   ((table_len = lua_objlen( L, 3 )<DELAY_TABLE_MAX_LEN)), 3, "Invalid table" );
+  luaL_argcheck(L, repeats<256, 4, "repeats >= 256" );
+
+  uint32 *delay_table = luaM_newvector(L, table_len*repeats, uint32);
+  for( i = 1; i <= table_len; i++ )  {
+    lua_rawgeti( L, 3, i + 1 );
+    unsigned delay = (unsigned) luaL_checkinteger( L, -1 );
+    if (delay > 1000000) return luaL_error( L, "delay %u must be < 1,000,000 us", i );
+    delay_table[i-1] = delay;
+    lua_pop( L, 1 );
+  }
+
+  for( i = 0; i <= repeats; i++ )  {
+    if (!i)    // skip the first loop (presumably this is some form of icache priming??).
       continue;
-    }
-    for(j=0;j<table_len;j++){
-      noInterrupts();
-      // platform_gpio_write(pin, level);
-      DIRECT_WRITE(pin, level);
-      interrupts();
+
+    for( j = 0;j < table_len; j++ ){
+      /* Direct Write is a ROM function which already disables interrupts for the atomic bit */
+      GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[pin]), level);
       delayMicroseconds(delay_table[j]);
-      level=!level;
+      level = level==LOW ? HIGH : LOW;
     }
-    repeat--;
-  } while (repeat>0); 
+  }
 
-  return 0;  
+  luaM_freearray(L, delay_table, table_len, uint32);
+  return 0;
 }
 #undef DELAY_TABLE_MAX_LEN
 
@@ -245,7 +200,7 @@ int luaopen_gpio( lua_State *L ) {
   for(i=0;i<GPIO_PIN_NUM;i++){
     gpio_cb_ref[i] = LUA_NOREF;
   }
-  platform_gpio_init(gpio_intr_callback);
+  platform_gpio_init(task_get_id(gpio_intr_callback_task));
 #endif
   return 0;
 }

+ 13 - 17
app/modules/node.c

@@ -145,8 +145,6 @@ static int node_heap( lua_State* L )
   return 1;
 }
 
-static lua_State *gL = NULL;
-
 #ifdef DEVKIT_VERSION_0_9
 static int led_high_count = LED_HIGH_COUNT_DEFAULT;
 static int led_low_count = LED_LOW_COUNT_DEFAULT;
@@ -174,27 +172,25 @@ static void default_short_press(void *arg) {
 }
 
 static void key_long_press(void *arg) {
+  lua_State *L = lua_getstate();
   NODE_DBG("key_long_press is called.\n");
   if (long_key_ref == LUA_NOREF) {
     default_long_press(arg);
     return;
   }
-  if (!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, long_key_ref);
-  lua_call(gL, 0, 0);
+  lua_rawgeti(L, LUA_REGISTRYINDEX, long_key_ref);
+  lua_call(L, 0, 0);
 }
 
 static void key_short_press(void *arg) {
+  lua_State *L = lua_getstate();
   NODE_DBG("key_short_press is called.\n");
   if (short_key_ref == LUA_NOREF) {
     default_short_press(arg);
     return;
   }
-  if (!gL)
-    return;
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, short_key_ref);
-  lua_call(gL, 0, 0);
+  lua_rawgeti(L, LUA_REGISTRYINDEX, short_key_ref);
+  lua_call(L, 0, 0);
 }
 
 static void update_key_led (void *p)
@@ -286,7 +282,6 @@ static int node_key( lua_State* L )
   } else {
     ref = &short_key_ref;
   }
-  gL = L;
   // luaL_checkanyfunction(L, 2);
   if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION) {
     lua_pushvalue(L, 2);  // copy argument (func) to the top of stack
@@ -305,6 +300,7 @@ static int node_key( lua_State* L )
 #endif
 
 extern lua_Load gLoad;
+extern bool user_process_input(bool force);
 // Lua: input("string")
 static int node_input( lua_State* L )
 {
@@ -321,7 +317,7 @@ static int node_input( lua_State* L )
       NODE_DBG("Get command:\n");
       NODE_DBG(load->line); // buggy here
       NODE_DBG("\nResult(if any):\n");
-      system_os_post (LUA_TASK_PRIO, LUA_PROCESS_LINE_SIG, 0);
+      user_process_input(true);
     }
   }
   return 0;
@@ -330,12 +326,13 @@ static int node_input( lua_State* L )
 static int output_redir_ref = LUA_NOREF;
 static int serial_debug = 1;
 void output_redirect(const char *str) {
+  lua_State *L = lua_getstate();
   // if(c_strlen(str)>=TX_BUFF_SIZE){
   //   NODE_ERR("output too long.\n");
   //   return;
   // }
 
-  if (output_redir_ref == LUA_NOREF || !gL) {
+  if (output_redir_ref == LUA_NOREF || !L) {
     uart0_sendStr(str);
     return;
   }
@@ -344,15 +341,14 @@ void output_redirect(const char *str) {
     uart0_sendStr(str);
   }
 
-  lua_rawgeti(gL, LUA_REGISTRYINDEX, output_redir_ref);
-  lua_pushstring(gL, str);
-  lua_call(gL, 1, 0);   // this call back function should never user output.
+  lua_rawgeti(L, LUA_REGISTRYINDEX, output_redir_ref);
+  lua_pushstring(L, str);
+  lua_call(L, 1, 0);   // this call back function should never user output.
 }
 
 // Lua: output(function(c), debug)
 static int node_output( lua_State* L )
 {
-  gL = L;
   // luaL_checkanyfunction(L, 1);
   if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) {
     lua_pushvalue(L, 1);  // copy argument (func) to the top of stack

+ 4 - 80
app/platform/common.c

@@ -10,89 +10,13 @@ void cmn_platform_init(void)
 
 }
 
-// ****************************************************************************
-// GPIO functions
-
-int platform_gpio_exists( unsigned pin )
-{
-  return pin < NUM_GPIO;
-}
-
-// ****************************************************************************
-// CAN functions
-
-int platform_can_exists( unsigned id )
-{
-  return id < NUM_CAN;
-}
-
-// ****************************************************************************
-// SPI functions
-
-
-int platform_spi_exists( unsigned id )
-{
-  return id < NUM_SPI;
-}
-
-// ****************************************************************************
-// PWM functions
-
-int platform_pwm_exists( unsigned id )
-{
-  return ((id < NUM_PWM) && (id > 0));
-}
-
-// ****************************************************************************
-// ADC functions
-
-int platform_adc_exists( unsigned id )
-{
-  return id < NUM_ADC;
-}
-
-// ****************************************************************************
-// UART functions
-
-int platform_uart_exists( unsigned id )
-{
-  return id < NUM_UART;
-}
-
-// ****************************************************************************
-// OneWire functions
-
-int platform_ow_exists( unsigned id )
-{
-  return ((id < NUM_OW) && (id > 0));
-}
-
-// ****************************************************************************
-// Timer functions
-
-int platform_tmr_exists( unsigned id )
-{
-  return id < NUM_TMR;
-}
-
-// ****************************************************************************
-// I2C support
-int platform_i2c_exists( unsigned id )
-{
-#ifndef NUM_I2C
-  return 0;
-#else
-  return id < NUM_I2C;
-#endif
-}
-
 // ****************************************************************************
 // Internal flash support functions
 
 // This symbol must be exported by the linker command file and must reflect the
 // TOTAL size of flash used by the eLua image (not only the code and constants,
 // but also .data and whatever else ends up in the eLua image). FS will start
-// at the next usable (aligned to a flash sector boundary) address after 
+// at the next usable (aligned to a flash sector boundary) address after
 // flash_used_size.
 
 // extern char flash_used_size[];
@@ -151,12 +75,12 @@ uint32_t platform_flash_get_first_free_block_address( uint32_t *psect )
   if(_flash_used_end>0){ // find the used sector
     sect = flashh_find_sector( platform_flash_mapped2phys ( (uint32_t)_flash_used_end - 1), NULL, &end );
     if( psect )
-      *psect = sect + 1;    
+      *psect = sect + 1;
     return end + 1;
   }else{
     sect = flashh_find_sector( 0, &start, NULL ); // find the first free sector
     if( psect )
-      *psect = sect;   
+      *psect = sect;
     return start;
   }
 }
@@ -231,7 +155,7 @@ uint32_t platform_flash_read( void *to, uint32_t fromaddr, uint32_t size )
     platform_s_flash_read( tmpdata, temp, blksize );
     for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ )
       *pto = tmpdata[ i ];
-    
+
     if( size == 0 )
       return ssize;
     fromaddr = temp + blksize;

+ 50 - 36
app/platform/pin_map.c

@@ -1,45 +1,59 @@
 #include "pin_map.h"
 #include "eagle_soc.h"
-#if 0
-uint32_t pin_mux[GPIO_PIN_NUM] = {PERIPHS_IO_MUX_MTDI_U,  PERIPHS_IO_MUX_MTCK_U,  PERIPHS_IO_MUX_MTMS_U, 	 PERIPHS_IO_MUX_MTDO_U,
-								  PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U, PERIPHS_IO_MUX_SD_DATA3_U,
-								  PERIPHS_IO_MUX_GPIO0_U, PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_GPIO4_U, 	 PERIPHS_IO_MUX_GPIO5_U};
-
-uint8_t pin_num[GPIO_PIN_NUM] = {12, 13, 14, 15,
-								  3,  1,  9, 10,
-								  0,  2,  4,  5};
-
-uint8_t pin_func[GPIO_PIN_NUM] = {FUNC_GPIO12, FUNC_GPIO13, FUNC_GPIO14, FUNC_GPIO15,
-								  FUNC_GPIO3,  FUNC_GPIO1,  FUNC_GPIO9,  FUNC_GPIO10,
-								  FUNC_GPIO0,  FUNC_GPIO2,  FUNC_GPIO4,  FUNC_GPIO5};
+#include "mem.h"
+#include "osapi.h"
 
+uint32_t pin_mux[GPIO_PIN_NUM];
+uint8_t  pin_num[GPIO_PIN_NUM];
+uint8_t  pin_func[GPIO_PIN_NUM];
 #ifdef GPIO_INTERRUPT_ENABLE
-GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = {
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE};
+uint8_t  pin_num_inv[GPIO_PIN_NUM_INV];
+uint8_t  pin_int_type[GPIO_PIN_NUM];
+uint8_t  pin_trigger[GPIO_PIN_NUM];
 #endif
-#else
-uint32_t pin_mux[GPIO_PIN_NUM] = {PAD_XPD_DCDC_CONF,  PERIPHS_IO_MUX_GPIO5_U,  PERIPHS_IO_MUX_GPIO4_U, 	 PERIPHS_IO_MUX_GPIO0_U,
-								  PERIPHS_IO_MUX_GPIO2_U, PERIPHS_IO_MUX_MTMS_U, PERIPHS_IO_MUX_MTDI_U, PERIPHS_IO_MUX_MTCK_U,
-								  PERIPHS_IO_MUX_MTDO_U, PERIPHS_IO_MUX_U0RXD_U, PERIPHS_IO_MUX_U0TXD_U, PERIPHS_IO_MUX_SD_DATA2_U,
-								  PERIPHS_IO_MUX_SD_DATA3_U };
-
-uint8_t pin_num[GPIO_PIN_NUM] = {16, 5, 4, 0,
-								  2,  14,  12, 13,
-								  15,  3,  1, 9,
-								  10};
 
-uint8_t pin_func[GPIO_PIN_NUM] = {0, FUNC_GPIO5, FUNC_GPIO4, FUNC_GPIO0,
-								  FUNC_GPIO2,  FUNC_GPIO14,  FUNC_GPIO12,  FUNC_GPIO13,
-								  FUNC_GPIO15,  FUNC_GPIO3,  FUNC_GPIO1, FUNC_GPIO9,
-								  FUNC_GPIO10};
+typedef struct {
+  int8  mux;
+  uint8 num;
+  uint8 func;
+  uint8 intr_type;
+} pin_rec;
+#define DECLARE_PIN(n,p) { (PERIPHS_IO_MUX_##p##_U - PERIPHS_IO_MUX), n, FUNC_GPIO##n, GPIO_PIN_INTR_DISABLE}
+static const pin_rec pin_map[] = {
+   {PAD_XPD_DCDC_CONF  - PERIPHS_IO_MUX, 16, 0, GPIO_PIN_INTR_DISABLE},
+    DECLARE_PIN( 5, GPIO5),
+    DECLARE_PIN( 4, GPIO4),
+    DECLARE_PIN( 0, GPIO0),
+    DECLARE_PIN( 2, GPIO2),
+    DECLARE_PIN(14, MTMS),
+    DECLARE_PIN(12, MTDI),
+    DECLARE_PIN(13, MTCK),
+    DECLARE_PIN(15, MTDO),
+    DECLARE_PIN( 3, U0RXD),
+    DECLARE_PIN( 1, U0TXD),
+    DECLARE_PIN( 9, SD_DATA2),
+    DECLARE_PIN(10, SD_DATA3)
+};
+void get_pin_map(void) {
+  /*
+   * Flash copy of the pin map.  This has to be copied to RAM to be accessible from the ISR.
+   * Note that the mux field is a signed offset from PERIPHS_IO_MUX to allow the whole struct
+   * to be stored in a single 32-bit record.
+   */
+  int i;
+  /* Take temporary stack copy to avoid unaligned exceptions on Flash version */
+  pin_rec pin[GPIO_PIN_NUM];
+  os_memcpy(pin, pin_map, sizeof(pin_map) );
 
+  for (i=0; i<GPIO_PIN_NUM; i++) {
+    pin_mux[i]  = pin[i].mux + PERIPHS_IO_MUX;
+    pin_func[i] = pin[i].func;
+    pin_num[i]  = pin[i].num;
 #ifdef GPIO_INTERRUPT_ENABLE
-GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM] = {
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
-								GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE, GPIO_PIN_INTR_DISABLE,
-								GPIO_PIN_INTR_DISABLE};
-#endif
+    pin_num_inv[pin_num[i]] = i;
+    pin_int_type[i]         = pin[i].intr_type;
+    pin_trigger[i]          = false;
 #endif
+  }
+}
+

+ 9 - 3
app/platform/pin_map.h

@@ -7,11 +7,17 @@
 #include "gpio.h"
 
 #define GPIO_PIN_NUM 13
+#define GPIO_PIN_NUM_INV 17
 
-extern uint8_t pin_num[GPIO_PIN_NUM];
-extern uint8_t pin_func[GPIO_PIN_NUM];
 extern uint32_t pin_mux[GPIO_PIN_NUM];
+extern uint8_t  pin_num[GPIO_PIN_NUM];
+extern uint8_t  pin_func[GPIO_PIN_NUM];
 #ifdef GPIO_INTERRUPT_ENABLE
-extern GPIO_INT_TYPE pin_int_type[GPIO_PIN_NUM];
+extern uint8_t  pin_num_inv[GPIO_PIN_NUM_INV];
+extern uint8_t  pin_int_type[GPIO_PIN_NUM];
+extern uint8_t  pin_trigger[GPIO_PIN_NUM];
 #endif
+
+void get_pin_map(void);
+
 #endif // #ifndef __PIN_MAP_H__

+ 104 - 65
app/platform/platform.c

@@ -1,17 +1,17 @@
-// Platform-dependent functions
+// Platform-dependent functions and includes
 
 #include "platform.h"
 #include "common.h"
 #include "c_stdio.h"
 #include "c_string.h"
 #include "c_stdlib.h"
+#include "llimits.h"
 #include "gpio.h"
 #include "user_interface.h"
-#include "driver/uart.h"
 #include "driver/gpio16.h"
 #include "driver/i2c_master.h"
 #include "driver/spi.h"
-// Platform specific includes
+#include "driver/uart.h"
 
 static void pwms_init();
 
@@ -39,72 +39,90 @@ uint8_t platform_key_led( uint8_t level){
 
 // ****************************************************************************
 // GPIO functions
+
+/*
+ * Set GPIO mode to output. Optionally in RAM helper because interrupts are dsabled
+ */
+static void NO_INTR_CODE set_gpio_no_interrupt(uint8 pin) {
+  unsigned pnum = pin_num[pin];
+  ETS_GPIO_INTR_DISABLE();
+#ifdef GPIO_INTERRUPT_ENABLE
+  pin_trigger[pin] = false;
+  pin_int_type[pin] = GPIO_PIN_INTR_DISABLE;
+#endif
+  PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
+  //disable interrupt
+  gpio_pin_intr_state_set(GPIO_ID_PIN(pnum), GPIO_PIN_INTR_DISABLE);
+  //clear interrupt status
+  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pnum));
+  GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pnum)),
+                 GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pnum))) &
+                 (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain;
+  ETS_GPIO_INTR_ENABLE();
+}
+
+/*
+ * Set GPIO mode to interrupt. Optionally RAM helper because interrupts are dsabled
+ */
 #ifdef GPIO_INTERRUPT_ENABLE
-extern void lua_gpio_unref(unsigned pin);
+static void NO_INTR_CODE set_gpio_interrupt(uint8 pin) {
+  ETS_GPIO_INTR_DISABLE();
+  PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
+  GPIO_DIS_OUTPUT(pin_num[pin]);
+  gpio_register_set(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])),
+                    GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)
+                    | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE)
+                    | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
+  pin_trigger[pin] = true;
+  ETS_GPIO_INTR_ENABLE();
+}
 #endif
+
 int platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull )
 {
-  // NODE_DBG("Function platform_gpio_mode() is called. pin_mux:%d, func:%d\n",pin_mux[pin],pin_func[pin]);
+  NODE_DBG("Function platform_gpio_mode() is called. pin_mux:%d, func:%d\n", pin_mux[pin], pin_func[pin]);
   if (pin >= NUM_GPIO)
     return -1;
+
   if(pin == 0){
     if(mode==PLATFORM_GPIO_INPUT)
       gpio16_input_conf();
     else
       gpio16_output_conf();
+
     return 1;
   }
 
   platform_pwm_close(pin);    // closed from pwm module, if it is used in pwm
 
-  switch(pull){
-    case PLATFORM_GPIO_PULLUP:
-      PIN_PULLUP_EN(pin_mux[pin]);
-      break;
-    case PLATFORM_GPIO_FLOAT:
-      PIN_PULLUP_DIS(pin_mux[pin]);
-      break;
-    default:
-      PIN_PULLUP_DIS(pin_mux[pin]);
-      break;
+  if (pull == PLATFORM_GPIO_PULLUP) {
+    PIN_PULLUP_EN(pin_mux[pin]);
+  } else {
+    PIN_PULLUP_DIS(pin_mux[pin]);
   }
 
   switch(mode){
+
     case PLATFORM_GPIO_INPUT:
-#ifdef GPIO_INTERRUPT_ENABLE
-      lua_gpio_unref(pin);    // unref the lua ref call back.
-#endif
       GPIO_DIS_OUTPUT(pin_num[pin]);
+      /* run on */
     case PLATFORM_GPIO_OUTPUT:
-      ETS_GPIO_INTR_DISABLE();
-#ifdef GPIO_INTERRUPT_ENABLE
-      pin_int_type[pin] = GPIO_PIN_INTR_DISABLE;
-#endif
-      PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
-      //disable interrupt
-      gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_DISABLE);
-      //clear interrupt status
-      GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin]));
-      GPIO_REG_WRITE(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin]))) & (~ GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE))); //disable open drain; 
-      ETS_GPIO_INTR_ENABLE();
+      set_gpio_no_interrupt(pin);
       break;
+
 #ifdef GPIO_INTERRUPT_ENABLE
     case PLATFORM_GPIO_INT:
-      ETS_GPIO_INTR_DISABLE();
-      PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]);
-      GPIO_DIS_OUTPUT(pin_num[pin]);
-      gpio_register_set(GPIO_PIN_ADDR(GPIO_ID_PIN(pin_num[pin])), GPIO_PIN_INT_TYPE_SET(GPIO_PIN_INTR_DISABLE)
-                        | GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_DISABLE)
-                        | GPIO_PIN_SOURCE_SET(GPIO_AS_PIN_SOURCE));
-      ETS_GPIO_INTR_ENABLE();
+      set_gpio_interrupt(pin);
       break;
 #endif
+
     default:
       break;
   }
   return 1;
 }
 
+
 int platform_gpio_write( unsigned pin, unsigned level )
 {
   // NODE_DBG("Function platform_gpio_write() is called. pin:%d, level:%d\n",GPIO_ID_PIN(pin_num[pin]),level);
@@ -135,41 +153,62 @@ int platform_gpio_read( unsigned pin )
 }
 
 #ifdef GPIO_INTERRUPT_ENABLE
-static void platform_gpio_intr_dispatcher( void *arg) {
-  platform_gpio_intr_handler_fn_t cb = arg;
-  uint8 i, level;
+static task_handle_t gpio_task_handle;
+
+static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
+  uint32 j=0;
   uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
-  for (i = 0; i < GPIO_PIN_NUM; i++) {
-    if (pin_int_type[i] && (gpio_status & BIT(pin_num[i])) ) {
-      //disable interrupt
-      gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), GPIO_PIN_INTR_DISABLE);
-      //clear interrupt status
-      GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(pin_num[i]));
-      level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[i]));
-      if(cb){
-        cb(i, level);
+  UNUSED(dummy);
+  /*
+   * gpio_status is a bit map where bit 0 is set if unmapped gpio pin 0 (pin3) has
+   * triggered the ISR. bit 1 if unmapped gpio pin 1 (pin10=U0TXD), etc.  Since this
+   * in the ISR, it makes sense to optimize this by doing a fast scan of the status
+   * and reverse mapping any set bits.
+   */
+   for (j = 0; gpio_status>0; j++, gpio_status >>= 1) {
+    if (gpio_status&1) {
+      int i = pin_num_inv[j];
+      if (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));
+        uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
+        if (pin_trigger[i]) {
+          /* the task is only posted if a trigger callback is defined */
+          pin_trigger[i] = false;
+          task_post_high (gpio_task_handle, (i<<1) + level);
+        }
+       // Interrupts are re-enabled but any interrupt occuring before pin_trigger[i] is reset will be ignored.
+      gpio_pin_intr_state_set(GPIO_ID_PIN(j), pin_int_type[i]);
       }
-      gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[i]), pin_int_type[i]);
     }
   }
 }
 
-void platform_gpio_init( platform_gpio_intr_handler_fn_t cb )
+void platform_gpio_init( task_handle_t gpio_task )
 {
-  ETS_GPIO_INTR_ATTACH(platform_gpio_intr_dispatcher, cb);
-}
+  int i;
+  gpio_task_handle = gpio_task;
 
-int platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type )
+  get_pin_map();
+  ETS_GPIO_INTR_ATTACH(platform_gpio_intr_dispatcher, NULL);
+}
+/*
+ * Initialise GPIO interrupt mode. Optionally in RAM because interrupts are dsabled
+ */
+void NO_INTR_CODE platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type )
 {
-  if (pin >= NUM_GPIO)
-    return -1;
-  ETS_GPIO_INTR_DISABLE();
-  //clear interrupt status
-  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin]));
-  pin_int_type[pin] = type;
-  //enable interrupt
-  gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), type);
-  ETS_GPIO_INTR_ENABLE();
+  if (platform_gpio_exists(pin)) {
+    ETS_GPIO_INTR_DISABLE();
+    //clear interrupt status
+    GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(pin_num[pin]));
+    pin_int_type[pin] = type;
+    pin_trigger[pin] = true;
+    //enable interrupt
+    gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), type);
+    ETS_GPIO_INTR_ENABLE();
+  }
 }
 #endif
 
@@ -269,7 +308,7 @@ void platform_uart_alt( int set )
 
 
 // Send: version with and without mux
-void platform_uart_send( unsigned id, u8 data ) 
+void platform_uart_send( unsigned id, u8 data )
 {
   uart_tx_one_char(id, data);
 }
@@ -353,7 +392,7 @@ uint32_t platform_pwm_setup( unsigned pin, uint32_t frequency, unsigned duty )
   if ( pin < NUM_PWM)
   {
     platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);  // disable gpio interrupt first
-    if(!pwm_add(pin)) 
+    if(!pwm_add(pin))
       return 0;
     // pwm_set_duty(DUTY(duty), pin);
     pwm_set_duty(0, pin);
@@ -410,7 +449,7 @@ uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t spe
 
   // platform_pwm_close(sda);
   // platform_pwm_close(scl);
-  
+
   // disable gpio interrupt first
   platform_gpio_mode(sda, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);   // inside this func call platform_pwm_close
   platform_gpio_mode(scl, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);    // disable gpio interrupt first

+ 26 - 12
app/platform/platform.h

@@ -7,6 +7,8 @@
 
 #include "c_types.h"
 #include "driver/pwm.h"
+#include "task/task.h"
+
 // Error / status codes
 enum
 {
@@ -35,14 +37,12 @@ uint8_t platform_key_led( uint8_t level);
 #define PLATFORM_GPIO_HIGH 1
 #define PLATFORM_GPIO_LOW 0
 
-/* GPIO interrupt handler */
-typedef void (* platform_gpio_intr_handler_fn_t)( unsigned pin, unsigned level );
-
+static inline int platform_gpio_exists( unsigned pin ) { return pin < NUM_GPIO; }
 int platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull );
 int platform_gpio_write( unsigned pin, unsigned level );
 int platform_gpio_read( unsigned pin );
-void platform_gpio_init( platform_gpio_intr_handler_fn_t cb );
-int platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type );
+void platform_gpio_init( task_handle_t gpio_task );
+void platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type );
 // *****************************************************************************
 // Timer subsection
 
@@ -62,7 +62,7 @@ enum
   ELUA_CAN_ID_EXT
 };
 
-int platform_can_exists( unsigned id );
+static inline int platform_can_exists( unsigned id ) { return id < NUM_CAN; }
 uint32_t platform_can_setup( unsigned id, uint32_t clock );
 int platform_can_send( unsigned id, uint32_t canid, uint8_t idtype, uint8_t len, const uint8_t *data );
 int platform_can_recv( unsigned id, uint32_t *canid, uint8_t *idtype, uint8_t *len, uint8_t *data );
@@ -95,7 +95,7 @@ int platform_can_recv( unsigned id, uint32_t *canid, uint8_t *idtype, uint8_t *l
 typedef uint32_t spi_data_type;
 
 // The platform SPI functions
-int platform_spi_exists( unsigned id );
+static inline int platform_spi_exists( unsigned id ) { return id < NUM_SPI; }
 uint32_t platform_spi_setup( uint8_t id, int mode, unsigned cpol, unsigned cpha, uint32_t clock_div);
 int platform_spi_send( uint8_t id, uint8_t bitlen, spi_data_type data );
 spi_data_type platform_spi_send_recv( uint8_t id, uint8_t bitlen, spi_data_type data );
@@ -140,7 +140,7 @@ enum
 #define PLATFORM_UART_FLOW_CTS                2
 
 // The platform UART functions
-int platform_uart_exists( unsigned id );
+static inline int platform_uart_exists( unsigned id ) { return id < NUM_UART; }
 uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits );
 int platform_uart_set_buffer( unsigned id, unsigned size );
 void platform_uart_send( unsigned id, uint8_t data );
@@ -163,7 +163,7 @@ void platform_uart_alt( int set );
 #define DUTY(d) ((uint16_t)( ((unsigned)(d)*PWM_DEPTH) / NORMAL_PWM_DEPTH) )
 
 // The platform PWM functions
-int platform_pwm_exists( unsigned id );
+static inline int platform_pwm_exists( unsigned id ) { return ((id < NUM_PWM) && (id > 0)); }
 uint32_t platform_pwm_setup( unsigned id, uint32_t frequency, unsigned duty );
 void platform_pwm_close( unsigned id );
 void platform_pwm_start( unsigned id );
@@ -185,7 +185,7 @@ uint32_t  platform_adc_set_clock( unsigned id, uint32_t frequency);
 int  platform_adc_check_timer_id( unsigned id, unsigned timer_id );
 
 // ADC Common Functions
-int  platform_adc_exists( unsigned id );
+static inline int platform_adc_exists( unsigned id ) { return id < NUM_ADC; }
 uint32_t  platform_adc_get_maxval( unsigned id );
 uint32_t  platform_adc_set_smoothing( unsigned id, uint32_t length );
 void platform_adc_set_blocking( unsigned id, uint32_t mode );
@@ -193,6 +193,16 @@ void platform_adc_set_freerunning( unsigned id, uint32_t mode );
 uint32_t  platform_adc_is_done( unsigned id );
 void platform_adc_set_timer( unsigned id, uint32_t timer );
 
+// ****************************************************************************
+// OneWire functions
+
+static inline int platform_ow_exists( unsigned id ) { return ((id < NUM_OW) && (id > 0)); }
+
+// ****************************************************************************
+// Timer functions
+
+static inline int platform_tmr_exists( unsigned id ) { return id < NUM_TMR; }
+
 // *****************************************************************************
 // I2C platform interface
 
@@ -210,7 +220,11 @@ enum
   PLATFORM_I2C_DIRECTION_RECEIVER
 };
 
-int platform_i2c_exists( unsigned id );
+#ifdef NUM_I2C
+static inline int platform_i2c_exists( unsigned id ) { return id < NUM_I2C; }
+#else
+static inline int platform_i2c_exists( unsigned id ) { return 0; }
+#endif
 uint32_t platform_i2c_setup( unsigned id, uint8_t sda, uint8_t scl, uint32_t speed );
 void platform_i2c_send_start( unsigned id );
 void platform_i2c_send_stop( unsigned id );
@@ -271,7 +285,7 @@ int platform_tmr_exists( unsigned id );
 #define MOD_CHECK_TIMER( id )\
   if( id == PLATFORM_TIMER_SYS_ID && !platform_timer_sys_available() )\
     return luaL_error( L, "the system timer is not available on this platform" );\
-  if( !platform_timer_exists( id ) )\
+  if( !platform_tmr_exists( id ) )\
     return luaL_error( L, "timer %d does not exist", ( unsigned )id )\
 
 #define MOD_CHECK_RES_ID( mod, id, resmod, resid )\

+ 44 - 0
app/task/Makefile

@@ -0,0 +1,44 @@
+
+#############################################################
+# Required variables for each makefile
+# Discard this section from all parent makefiles
+# Expected variables (with automatic defaults):
+#   CSRCS (all "C" files in the dir)
+#   SUBDIRS (all subdirs with a Makefile)
+#   GEN_LIBS - list of libs to be generated ()
+#   GEN_IMAGES - list of images to be generated ()
+#   COMPONENTS_xxx - a list of libs/objs in the form
+#     subdir/lib to be extracted and rolled up into
+#     a generated lib/image xxx.a ()
+#
+ifndef PDIR
+GEN_LIBS = libtask.a
+endif
+
+#############################################################
+# Configuration i.e. compile options etc.
+# Target specific stuff (defines etc.) goes in here!
+# Generally values applying to a tree are captured in the
+#   makefile at its root level - these are then overridden
+#   for a subtree within the makefile rooted therein
+#
+#DEFINES += 
+
+#############################################################
+# Recursion Magic - Don't touch this!!
+#
+# Each subtree potentially has an include directory
+#   corresponding to the common APIs applicable to modules
+#   rooted at that subtree. Accordingly, the INCLUDE PATH
+#   of a module can only contain the include directories up
+#   its parent path, and not its siblings
+#
+# Required for each makefile to inherit from the parent
+#
+
+INCLUDES := $(INCLUDES) -I $(PDIR)include
+INCLUDES += -I ./
+INCLUDES += -I ../libc
+PDIR := ../$(PDIR)
+sinclude $(PDIR)Makefile
+

+ 72 - 0
app/task/task.c

@@ -0,0 +1,72 @@
+/**
+  This file encapsulates the SDK-based task handling for the NodeMCU Lua firmware.
+ */
+#include "task/task.h"
+#include "mem.h"
+#include "c_stdio.h"
+
+#define TASK_HANDLE_MONIKER 0x68680000
+#define TASK_HANDLE_MASK    0xFFF80000
+#define TASK_HANDLE_UNMASK  (~TASK_HANDLE_MASK)
+#define TASK_HANDLE_SHIFT   2
+#define TASK_HANDLE_ALLOCATION_BRICK 4   // must be a power of 2
+#define TASK_DEFAULT_QUEUE_LEN 8
+#define TASK_PRIORITY_MASK  3
+
+#define CHECK(p,v,msg) if (!(p)) { NODE_DBG ( msg ); return (v); }
+
+/*
+ * Private arrays to hold the 3 event task queues and the dispatch callbacks
+ */
+LOCAL os_event_t *task_Q[TASK_PRIORITY_COUNT];
+LOCAL task_callback_t *task_func;
+LOCAL int task_count;
+
+LOCAL void task_dispatch (os_event_t *e) {
+  task_handle_t handle = e->sig;
+  if ( (handle & TASK_HANDLE_MASK) == TASK_HANDLE_MONIKER) {
+    uint16 entry    = (handle & TASK_HANDLE_UNMASK) >> TASK_HANDLE_SHIFT;
+    uint8  priority = handle & TASK_PRIORITY_MASK;
+    if ( priority <= TASK_PRIORITY_HIGH && task_func && entry < task_count ){
+      /* call the registered task handler with the specified parameter and priority */
+      task_func[entry](e->par, priority);
+      return;
+    }
+  }
+  /* Invalid signals are ignored */
+  NODE_DBG ( "Invalid signal issued: %08x",  handle);
+}
+
+/*
+ * Initialise the task handle callback for a given priority.  This doesn't need
+ * to be called explicitly as the get_id function will call this lazily.
+ */
+bool task_init_handler(uint8 priority, uint8 qlen) {
+  if (priority <= TASK_PRIORITY_HIGH && task_Q[priority] == NULL) {
+    task_Q[priority] = (os_event_t *) os_malloc( sizeof(os_event_t)*qlen );
+    os_memset (task_Q[priority], 0, sizeof(os_event_t)*qlen);
+    if (task_Q[priority]) {
+      return system_os_task( task_dispatch, priority, task_Q[priority], qlen );
+    }
+  }
+  return false;
+}
+
+task_handle_t task_get_id(task_callback_t t) {
+  int p = TASK_PRIORITY_COUNT;
+  /* Initialise and uninitialised Qs with the default Q len */
+    while(p--) if (!task_Q[p]) {
+    CHECK(task_init_handler( p, TASK_DEFAULT_QUEUE_LEN ), 0, "Task initialisation failed");
+  }
+
+  if ( (task_count & (TASK_HANDLE_ALLOCATION_BRICK - 1)) == 0 ) {
+    /* With a brick size of 4 this branch is taken at 0, 4, 8 ... and the new size is +4 */
+    task_func =(task_callback_t *) os_realloc(task_func,
+                        sizeof(task_callback_t)*(task_count+TASK_HANDLE_ALLOCATION_BRICK));
+    CHECK(task_func, 0 , "Malloc failure in task_get_id");
+    os_memset (task_func+task_count, 0, sizeof(task_callback_t)*TASK_HANDLE_ALLOCATION_BRICK);
+  }
+
+  task_func[task_count++] = t;
+  return TASK_HANDLE_MONIKER + ((task_count-1)  << TASK_HANDLE_SHIFT);
+}

+ 21 - 54
app/user/user_main.c

@@ -12,8 +12,6 @@
 #include "platform.h"
 #include "c_string.h"
 #include "c_stdlib.h"
-#include "c_stdio.h"
-
 #include "flash_fs.h"
 #include "user_interface.h"
 #include "user_exceptions.h"
@@ -21,6 +19,7 @@
 
 #include "ets_sys.h"
 #include "driver/uart.h"
+#include "task/task.h"
 #include "mem.h"
 
 #ifdef LUA_USE_MODULES_RTCTIME
@@ -81,35 +80,27 @@ void TEXT_SECTION_ATTR user_start_trampoline (void)
   call_user_start ();
 }
 
-
-void task_lua(os_event_t *e){
-    char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
-    NODE_DBG("Task task_lua started.\n");
-    switch(e->sig){
-        case SIG_LUA:
-            NODE_DBG("SIG_LUA received.\n");
-            lua_main( 2, lua_argv );
-            break;
-        case SIG_UARTINPUT:
-            lua_handle_input (false);
-            break;
-        case LUA_PROCESS_LINE_SIG:
-            lua_handle_input (true);
-            break;
-        default:
-            break;
-    }
+// +================== New task interface ==================+
+static void start_lua(task_param_t param, uint8 priority) {
+  char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
+  NODE_DBG("Task task_lua started.\n");
+  lua_main( 2, lua_argv );
 }
 
-void task_init(void){
-    taskQueue = (os_event_t *)os_malloc(sizeof(os_event_t) * TASK_QUEUE_LEN);
-    system_os_task(task_lua, USER_TASK_PRIO_0, taskQueue, TASK_QUEUE_LEN);
+static void handle_input(task_param_t flag, uint8 priority) {
+//  c_printf("HANDLE_INPUT: %u %u\n", flag, priority);          REMOVE
+  lua_handle_input (flag);
 }
 
-// extern void test_spiffs();
-// extern int test_romfs();
+static task_handle_t input_sig;
 
-// extern uint16_t flash_get_sec_num();
+task_handle_t user_get_input_sig(void) {
+  return input_sig;
+}
+
+bool user_process_input(bool force) {
+    return task_post_low(input_sig, force);
+}
 
 void nodemcu_init(void)
 {
@@ -153,34 +144,13 @@ void nodemcu_init(void)
         system_restart ();
     }
 
-#if defined( BUILD_WOFS )
-    romfs_init();
-
-    // if( !wofs_format() )
-    // {
-    //     NODE_ERR( "\ni*** ERROR ***: unable to erase the flash. WOFS might be compromised.\n" );
-    //     NODE_ERR( "It is advised to re-flash the NodeWifi image.\n" );
-    // }
-    // else
-    //     NODE_ERR( "format done.\n" );
-
-    // test_romfs();
-#elif defined ( BUILD_SPIFFS )
+#if defined ( BUILD_SPIFFS )
     fs_mount();
     // test_spiffs();
 #endif
     // endpoint_setup();
 
-    // char* lua_argv[] = { (char *)"lua", (char *)"-e", (char *)"print(collectgarbage'count');ttt={};for i=1,100 do table.insert(ttt,i*2 -1);print(i);end for k, v in pairs(ttt) do print('<'..k..' '..v..'>') end print(collectgarbage'count');", NULL };
-    // lua_main( 3, lua_argv );
-    // char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
-    // lua_main( 2, lua_argv );
-    // char* lua_argv[] = { (char *)"lua", (char *)"-e", (char *)"pwm.setup(0,100,50) pwm.start(0) pwm.stop(0)", NULL };
-    // lua_main( 3, lua_argv );
-    // NODE_DBG("Flash sec num: 0x%x\n", flash_get_sec_num());
-
-    task_init();
-    system_os_post(LUA_TASK_PRIO,SIG_LUA,'s');
+    task_post_low(task_get_id(start_lua),'s');
 }
 
 /******************************************************************************
@@ -194,14 +164,11 @@ void user_init(void)
 #ifdef LUA_USE_MODULES_RTCTIME
     rtctime_late_startup ();
 #endif
-    // NODE_DBG("SDK version:%s\n", system_get_sdk_version());
-    // system_print_meminfo();
-    // os_printf("Heap size::%d.\n",system_get_free_heap_size());
-    // os_delay_us(50*1000);   // delay 50ms before init uart
 
     UartBautRate br = BIT_RATE_DEFAULT;
 
-    uart_init (br, br, USER_TASK_PRIO_0, SIG_UARTINPUT);
+    input_sig = task_get_id(handle_input);
+    uart_init (br, br, input_sig);
 
     #ifndef NODE_DEBUG
     system_set_os_print(0);

+ 96 - 0
docs/en/extn-developer-faq.md

@@ -0,0 +1,96 @@
+# Extension Developer FAQ
+
+**# # # Work in Progress # # #**
+
+## How does the non-OS SDK structure execution
+
+Details of the execution model for the **non-OS SDK** is not well documented by 
+Espressif. This section summarises the project's understanding of how this execution
+model works based on the Espressif-supplied examples and SDK documentation, plus
+various posts on the Espressif BBS and other forums, and an examination of the
+BootROM code.
+
+The ESP8266 boot ROM contains a set of primitive tasking and dispatch functions
+which are also used by the SDK. In this model, execution units are either:
+
+-   **INTERRUPT SERVICE ROUTINES (ISRs)** which are declared and controlled
+    through the `ets_isr_attach()` and other `ets_isr_*` and `ets_intr_*`
+    functions. ISRs can be defined on a range of priorities, where a higher
+    priority ISR is able to interrupt a lower priority one. ISRs are time
+    critical and should complete in no more than 50 µSec.
+
+    ISR code and data constants should be run out of RAM or ROM, for two reasons:
+    if an ISR interrupts a flash I/O operation (which must disable the Flash 
+    instruction cache) and a cache miss occurs, then the ISR will trigger a
+    fatal exception; secondly, the
+    execution time for Flash memory (that is located in the `irom0` load section)
+    is indeterminate: whilst cache-hits can run at full memory bandwidth, any
+    cache-misses require the code to be read from Flash; and even though
+    H/W-based, this is at roughly 26x slower than memory bandwidth (for DIO
+    flash); this will cause ISR execution to fall outside the require time
+    guidelines. (Note that any time critical code within normal execution and that
+    is bracketed by interrupt lock / unlock guards should also follow this 50
+    µSec guideline.)<br/><br/>
+    
+-   **TASKS**. A task is a normal execution unit running at a non-interrupt priority.
+    Tasks can be executed from Flash memory. An executing task can be interrupted
+    by one or more ISRs being delivered, but it won't be preempted by another
+    queued task. The Espressif guideline is that no individual task should run for
+    more than 15 mSec, before returning control to the SDK.
+
+    The ROM will queue up to 32 pending tasks at priorities 0..31 and will
+    execute the highest priority queued task next (or wait on interrupt if none
+    is runnable). The SDK tasking system is layered on this ROM dispatcher and
+    it reserves 29 of these task priorities for its own use, including the
+    implementation of the various SDK timer, WiFi and other callback mechanisms
+    such as the software WDT.
+
+    Three of these task priorities are allocated for and exposed directly at an
+    application level. The application can declare a single task handler for each
+    level, and associate a task queue with the level. Tasks can be posted to this
+    queue. (The post will fail is the queue is full). Tasks are then delivered
+    FIFO within task priority.
+
+    How the three user task priorities USER0 .. USER2 are positioned relative to
+    the SDK task priorities is undocumented, but some SDK tasks definitely run at
+    a lower priority than USER0. As a result if you always have a USER task queued
+    for execution, then you can starve SDK housekeeping tasks and you will start
+    to get WiFi and other failures. Espressif therefore recommends that you don't
+    stay computable for more than 500 mSec to avoid such timeouts.
+
+Note that the 50µS, 15mSec and 500mSec limits are guidelines
+and not hard constraints -- that is if you break them (slightly) then your code
+may (usually) work, but you can get very difficult to diagnose and intermittent
+failures. Also running ISRs from Flash may work until there is a collision with
+SPIFFS I/O which will then a cause CPU exception.
+
+Also note that the SDK API function `system_os_post()`, and the `task_post_*()`
+macros which generate this can be safely called from an ISR.
+
+The Lua runtime is NOT reentrant, and hence any code which calls any Lua API
+must run within a task context. Any such task is what we call a _Lua-Land Task_
+(or **LLT**). _ISRs must not access the Lua API or Lua resources._ LLTs can be
+executed as SDK API callbacks or OS tasks. They can also, of course, call the
+Lua execution system to execute Lua code (e.g. `luaL_dofile()` and related
+calls).
+
+Also since the application has no control over the relative time ordering of
+tasks and SDK API callbacks, LLTs can't make any assumptions about whether a
+task and any posted successors will run consecutively.
+
+This API is designed to complement the Lua library model, so that a library can
+declare one or more task handlers and that both ISPs and LLTs can then post a
+message for delivery to a task handler. Each task handler has a unique message
+associated with it, and may bind a single uint32 parameter. How this parameter
+is interpreted is left to the task poster and task handler to coordinate.
+
+The interface is exposed through `#include "task/task.h"` and involves two API
+calls. Any task handlers are declared, typically in the module_init function by
+assigning `task_get_id(some_task_callback)` to a (typically globally) accessible
+handle variable, say `XXX_callback_handle`. This can then be used in an ISR or
+normal LLT to execute a `task_post_YYY(XXX_callback_handle,param)` where YYY is
+one of `low`, `medium`, `high`. The callback will then be executed when the SDK
+delivers the task.
+
+_Note_: `task_post_YYY` can fail with a false return if the task Q is full. 
+

+ 45 - 0
docs/en/hardware-faq.md

@@ -0,0 +1,45 @@
+# Hardware FAQ
+ 
+## What is this FAQ for?
+
+This FAQ addresses hardware-specific issues relating to the NodeMcu firmware on
+NoceMCU Inc Devkits and other ESP-8266 modules.
+
+## Hardware Specifics
+
+## Why file writes fail all the time on DEVKIT V1.0?
+
+NodeMCU DEVKIT V1.0 uses ESP12-E-DIO(ESP-12-D) module. This module runs the
+Flash memory in [Dual IO SPI](#whats-the-different-between-dio-and-qio-mode)
+(DIO) mode. This firmware will not be correctly loaded if you use old flashtool
+versions, and the filesystem will not work if you used a pre 0.9.6 firmware
+version (<0.9.5) or old. The easiest way to resolve this problem s update all
+the firmware and flash tool to current version.
+
+-   Use the latest [esptool.py](https://github.com/themadinventor/esptool) with
+DIO support and command option to flash firmware, or 
+
+-   Use the latest [NodeMCU flasher](https://github.com/NodeMCU/NodeMCU-flasher) 
+with default option. (You must select the `restore to default` option in advanced
+menu tab), or 
+
+-   Use the latest Espressif's flash tool -- see [this Espressif forum
+topic](http://bbs.espressif.com/viewtopic.php?f=5&t=433) (without auto download
+support). Use DIO mode and 32M flash size option, and flash latest firmware to
+0x00000. Before flashing firmware, remember to hold FLASH button, and press RST
+button once. Note that the new NodeMCU our firmware download tool, when
+released, will be capable of flashing firmware automatically without any button
+presses.
+
+## What's the different between DIO and QIO mode?
+Whether DIO or QIO modes are available depends on the physical connection
+between the ESP8266 CPU and its onboard flash chip. QIO connects to the flash
+using 5 data pins as compared to DIO's 3. This frees up an extra 2 IO pins for
+GPIO use, but this also halves the read/write data-rate to Flash compared to 
+QIO modules. 
+
+## How to use DEVKIT V0.9 on Mac OS X?
+<TODO>
+
+### How does DEVKIT use DTR and RTS enter download mode?
+<TODO>

File diff suppressed because it is too large
+ 0 - 0
docs/en/lua-developer-faq.md


+ 1 - 1
ld/nodemcu.ld

@@ -90,7 +90,7 @@ SECTIONS
     LONG(0) LONG(0) /* Null-terminate the array */
 
     /* SDK doesn't use libc functions, and are therefore safe to put in flash */
-    */libc.a(.text* .literal*)
+    */libc.a:*.o(.text* .literal*)
     /* end libc functions */
 
     _irom0_text_end = ABSOLUTE(.);

+ 4 - 1
mkdocs.yml

@@ -27,7 +27,10 @@ pages:
     - Building the firmware: 'en/build.md'
     - Flashing the firmware: 'en/flash.md'
     - Uploading code: 'en/upload.md'
-    - FAQ: 'en/faq.md'
+    - FAQs:
+        - Lua Developer FAQ: 'en/lua-developer-faq.md'
+        - Extension Developer FAQ: 'en/extn-developer-faq.md'
+        - Hardware FAQ: 'en/hardware-faq.md'
     - Support: 'en/support.md'
     - Modules:
         - 'adc': 'en/modules/adc.md'

Some files were not shown because too many files changed in this diff