diff --git a/.travis.yml b/.travis.yml index 894f2fd0..5e76c51e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: cpp before_install: - sudo apt-get install -y python-serial srecord install: -- wget https://github.com/GeorgeHahn/nodemcu-firmware/raw/travis/tools/esp-open-sdk.tar.gz -O tools/esp-open-sdk.tar.gz - tar -zxvf tools/esp-open-sdk.tar.gz - export PATH=$PATH:$PWD/esp-open-sdk/sdk:$PWD/esp-open-sdk/xtensa-lx106-elf/bin script: diff --git a/README.md b/README.md index 4e7a4aeb..89794648 100644 --- a/README.md +++ b/README.md @@ -36,44 +36,32 @@ Tencent QQ group: 309957875
- cross compiler (done) # Change log +2015-06-25
+move constants to ROM. Frees up 16k+ of RAM.
+add dhtlib for DHT11/21/22/33/44, port from Arduino.
+add 433MHz transmission.
+add crypto library.
+re-add ws2812.write().
+add wifi.getchannel.
+changed wifi_setip() to allow setting SoftAP gateway to 0.0.0.0.
+added net.dns.setdnsserver and net.dns.getdnsserver.
+add support for lm92 temperature sensor.
+implement getStrWidth() and setFontLineSpacingFactor().
+add -Os flag to release and debug builds.
+changed output format of table that is output by wifi_scan_done.
+added ability to set scan configuration to wifi.sta.getap.
+added function wifi.sta.getconfig().
+allow connecting to unsecured WiFi networks.
+add setphymode and getphymode to wifi module.
+add multicastJoin and multicastLeave to net module.
+add Yeelink Modules.
+ 2015-03-31
polish mqtt module, add queue for mqtt module.
add reconnect option to mqtt.connect api, :connect( host, port, secure, auto_reconnect, function(client) )
move node.readvdd33 to adc.readvdd33.
tools/esptool.py supported NodeMCU devkit automatic flash. -2015-03-18
-update u8glib.
-merge everything to master. - -2015-03-17
-add cjson module, only cjson.encode() and cjson.decode() is implemented.
-read doc [here](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/cjson/manual.txt) - -2015-03-15
-bugs fixed: #239, #273.
-reduce coap module memory usage, add coap module to default built. - -2015-03-11
-fix bugs of spiffs.
-build both float and integer version [latest releases](https://github.com/nodemcu/nodemcu-firmware/releases/latest).
-fix tmr.time().
-fix memory leak when DNS fail. - -2015-03-10
-update to the recent spiffs.
-add file.fsinfo() api, usage: remain, used, total = file.fsinfo().
-add Travis CI. please download the latest firmware from [releases](https://github.com/nodemcu/nodemcu-firmware/releases).
-add math lib, partial api work.
-u8g module, ws2812 module default enabled in dev-branch build. - -2015-02-13
-add node.compile() api to compile lua text file into lua bytecode file.
-this will reduce memory usage noticeably when require modules into NodeMCU.
-raise internal LUA_BUFFERSIZE from 1024 to 4096.
-lua require("mod") will load "mod.lc" file first if exist.
-build latest pre_build bin. - [more change log](https://github.com/nodemcu/nodemcu-firmware/wiki)
##GPIO NEW TABLE ( Build 20141219 and later) diff --git a/app/Makefile b/app/Makefile index 87331c24..8275a264 100644 --- a/app/Makefile +++ b/app/Makefile @@ -99,7 +99,7 @@ LINKFLAGS_eagle.app.v6 = \ -nostdlib \ -T$(LD_FILE) \ -Wl,--no-check-sections \ - -u call_user_start \ + -Wl,--wrap=_xtos_set_exception_handler \ -Wl,-static \ -Wl,--start-group \ -lc \ diff --git a/app/include/rom.h b/app/include/rom.h index 88d7c81d..51d3fbe1 100644 --- a/app/include/rom.h +++ b/app/include/rom.h @@ -37,3 +37,57 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_ // initialized because it uses mem_malloc extern void mem_init(void * start_addr); + + +// Hardware exception handling +struct exception_frame +{ + uint32_t epc; + uint32_t ps; + uint32_t sar; + uint32_t unused; + union { + struct { + uint32_t a0; + // note: no a1 here! + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t a8; + uint32_t a9; + uint32_t a10; + uint32_t a11; + uint32_t a12; + uint32_t a13; + uint32_t a14; + uint32_t a15; + }; + uint32_t a_reg[15]; + }; + uint32_t cause; +}; + +/** + * C-level exception handler callback prototype. + * + * Does not need an RFE instruction - it is called through a wrapper which + * performs state capture & restore, as well as the actual RFE. + * + * @param ef An exception frame containing the relevant state from the + * exception triggering. This state may be manipulated and will + * be applied on return. + * @param cause The exception cause number. + */ +typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause); + +/** + * Sets the exception handler function for a particular exception cause. + * @param handler The exception handler to install. + * If NULL, reverts to the XTOS default handler. + * @returns The previous exception handler, or NULL if none existed prior. + */ +exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler); + diff --git a/app/include/user_version.h b/app/include/user_version.h index 789e5fea..a803dfaa 100644 --- a/app/include/user_version.h +++ b/app/include/user_version.h @@ -7,6 +7,6 @@ #define NODE_VERSION_INTERNAL 0U #define NODE_VERSION "NodeMCU 0.9.6" -#define BUILD_DATE "build 20150618" +#define BUILD_DATE "build 20150625" #endif /* __USER_VERSION_H__ */ diff --git a/app/modules/u8g.c b/app/modules/u8g.c index 3596ab06..b046635e 100644 --- a/app/modules/u8g.c +++ b/app/modules/u8g.c @@ -26,21 +26,6 @@ typedef struct _lu8g_userdata_t lu8g_userdata_t; #define LU8G (&(lud->u8g)) -// function to read 4-byte aligned from program memory AKA irom0 -u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr) -{ - uint32_t iadr = (uint32_t)adr; - // set up pointer to 4-byte aligned memory location - uint32_t *ptr = (uint32_t *)(iadr & ~0x3); - - // read 4-byte aligned - uint32_t pgm_data = *ptr; - - // return the correct byte within the retrieved 32bit word - return pgm_data >> ((iadr % 4) * 8); -} - - // helper function: retrieve and check userdata argument static lu8g_userdata_t *get_lud( lua_State *L ) { @@ -889,22 +874,12 @@ uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void break; case U8G_COM_MSG_WRITE_SEQ: - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - platform_spi_send_recv( 1, *ptr++ ); - arg_val--; - } - } - break; case U8G_COM_MSG_WRITE_SEQ_P: { register uint8_t *ptr = arg_ptr; while( arg_val > 0 ) { - platform_spi_send_recv( 1, u8g_pgm_read(ptr) ); - ptr++; + platform_spi_send_recv( 1, *ptr++ ); arg_val--; } } @@ -957,6 +932,7 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi break; case U8G_COM_MSG_WRITE_SEQ: + case U8G_COM_MSG_WRITE_SEQ_P: //u8g->pin_list[U8G_PI_SET_A0] = 1; if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) return platform_i2c_send_stop( ESP_I2C_ID ), 0; @@ -973,24 +949,6 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi // platform_i2c_send_stop( ESP_I2C_ID ); break; - case U8G_COM_MSG_WRITE_SEQ_P: - //u8g->pin_list[U8G_PI_SET_A0] = 1; - if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 ) - return platform_i2c_send_stop( ESP_I2C_ID ), 0; - { - register uint8_t *ptr = arg_ptr; - while( arg_val > 0 ) - { - // ignore return value -> tolerate missing ACK - if ( platform_i2c_send_byte( ESP_I2C_ID, u8g_pgm_read(ptr) ) == 0 ) - ; //return 0; - ptr++; - arg_val--; - } - } - // platform_i2c_send_stop( ESP_I2C_ID ); - break; - case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ u8g->pin_list[U8G_PI_A0_STATE] = arg_val; u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */ diff --git a/app/u8glib/u8g.h b/app/u8glib/u8g.h index 7aa2845a..e62fb024 100644 --- a/app/u8glib/u8g.h +++ b/app/u8glib/u8g.h @@ -88,7 +88,7 @@ extern "C" { # define U8G_FONT_SECTION(name) U8G_SECTION(".progmem." name) # endif # if defined(__XTENSA__) -# define U8G_FONT_SECTION(name) U8G_SECTION(".u8g_progmem." name) +# define U8G_FONT_SECTION(name) # endif #else # define U8G_NOINLINE @@ -116,10 +116,10 @@ typedef uint8_t u8g_fntpgm_uint8_t; #elif defined(__XTENSA__) # define U8G_PROGMEM -# define PROGMEM U8G_SECTION(".u8g_progmem.data") +# define PROGMEM typedef uint8_t u8g_pgm_uint8_t; typedef uint8_t u8g_fntpgm_uint8_t; - u8g_pgm_uint8_t u8g_pgm_read(const u8g_pgm_uint8_t *adr); +# define u8g_pgm_read(adr) (*(const u8g_pgm_uint8_t *)(adr)) # define U8G_PSTR(s) ((u8g_pgm_uint8_t *)(s)) #else diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c new file mode 100644 index 00000000..fb26e76a --- /dev/null +++ b/app/user/user_exceptions.c @@ -0,0 +1,112 @@ +/* + * Copyright 2015 Dius Computing Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * - Neither the name of the copyright holders nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Johny Mattsson + */ + +#include "user_exceptions.h" + +#define LOAD_MASK 0x00f00fu +#define L8UI_MATCH 0x000002u +#define L16UI_MATCH 0x001002u +#define L16SI_MATCH 0x009002u + + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) +{ + /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */ + (void)cause; + + uint32_t epc1 = ef->epc; + uint32_t excvaddr; + uint32_t insn; + asm ( + "rsr %0, EXCVADDR;" /* read out the faulting address */ + "movi a4, ~3;" /* prepare a mask for the EPC */ + "and a4, a4, %2;" /* apply mask for 32bit aligned base */ + "l32i a5, a4, 0;" /* load part 1 */ + "l32i a6, a4, 4;" /* load part 2 */ + "ssa8l %2;" /* set up shift register for src op */ + "src %1, a6, a5;" /* right shift to get faulting instruction */ + :"=r"(excvaddr), "=r"(insn) + :"r"(epc1) + :"a4", "a5", "a6" + ); + + uint32_t valmask = 0; + uint32_t what = insn & LOAD_MASK; + + if (what == L8UI_MATCH) + valmask = 0xffu; + else if (what == L16UI_MATCH || what == L16SI_MATCH) + valmask = 0xffffu; + else + { +die: + /* Turns out we couldn't fix this, trigger a system break instead + * and hang if the break doesn't get handled. This is effectively + * what would happen if the default handler was installed. */ + asm ("break 1, 1"); + while (1) {} + } + + /* Load, shift and mask down to correct size */ + uint32_t val = (*(uint32_t *)(excvaddr & ~0x3)); + val >>= (excvaddr & 0x3) * 8; + val &= valmask; + + /* Sign-extend for L16SI, if applicable */ + if (what == L16SI_MATCH && (val & 0x8000)) + val |= 0xffff0000; + + int regno = (insn & 0x0000f0u) >> 4; + if (regno == 1) + goto die; /* we can't support loading into a1, just die */ + else if (regno != 0) + --regno; /* account for skipped a1 in exception_frame */ + + ef->a_reg[regno] = val; /* carry out the load */ + ef->epc += 3; /* resume at following instruction */ +} + + +/** + * The SDK's user_main function installs a debugging handler regardless + * of whether there's a proper handler installed for EXCCAUSE_LOAD_STORE_ERROR, + * which of course breaks everything if we allow that to go through. As such, + * we use the linker to wrap that call and stop the SDK from shooting itself in + * its proverbial foot. + */ +exception_handler_fn +__wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) +{ + if (cause != EXCCAUSE_LOAD_STORE_ERROR) + __real__xtos_set_exception_handler (cause, fn); +} diff --git a/app/user/user_exceptions.h b/app/user/user_exceptions.h new file mode 100644 index 00000000..d2cf5166 --- /dev/null +++ b/app/user/user_exceptions.h @@ -0,0 +1,5 @@ +#include "c_types.h" +#include "rom.h" +#include + +void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) TEXT_SECTION_ATTR; diff --git a/app/user/user_main.c b/app/user/user_main.c index 88938da8..c24e0f00 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -16,6 +16,7 @@ #include "flash_fs.h" #include "user_interface.h" +#include "user_exceptions.h" #include "ets_sys.h" #include "driver/uart.h" @@ -25,6 +26,21 @@ #define TASK_QUEUE_LEN 4 os_event_t *taskQueue; + +/* Note: the trampoline *must* be explicitly put into the .text segment, since + * by the time it is invoked the irom has not yet been mapped. This naturally + * also goes for anything the trampoline itself calls. + */ +void user_start_trampoline (void) TEXT_SECTION_ATTR; +void user_start_trampoline (void) +{ + __real__xtos_set_exception_handler ( + EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler); + + 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"); diff --git a/include/c_types.h b/include/c_types.h index 55bffd46..2cd6332a 100644 --- a/include/c_types.h +++ b/include/c_types.h @@ -84,6 +84,9 @@ typedef enum { #define ICACHE_FLASH_ATTR #endif /* ICACHE_FLASH */ +#define TEXT_SECTION_ATTR __attribute__((section(".text"))) +#define RAM_CONST_ATTR __attribute__((section(".text"))) + #ifndef __cplusplus typedef unsigned char bool; #define BOOL bool diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld index a36cb31e..2df6a4ae 100644 --- a/ld/eagle.app.v6.ld +++ b/ld/eagle.app.v6.ld @@ -19,7 +19,7 @@ PHDRS /* Default entry point: */ -ENTRY(call_user_start) +ENTRY(user_start_trampoline) PROVIDE(_memmap_vecbase_reset = 0x40000000); /* Various memory-map dependent cache attribute settings: */ _memmap_cacheattr_wb_base = 0x00000110; @@ -72,8 +72,9 @@ SECTIONS *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text) *(.literal.* .text.*) - /* put font and progmem data into irom0 */ - *(.u8g_progmem.*) + /* Trade some performance for lots of ram. At the time of writing the + * available Lua heap went from 18248 to 34704. */ + *(.rodata*) _irom0_text_end = ABSOLUTE(.); _flash_used_end = ABSOLUTE(.); @@ -124,10 +125,7 @@ SECTIONS .rodata : ALIGN(4) { _rodata_start = ABSOLUTE(.); - *(.rodata) - *(.rodata.*) *(.gnu.linkonce.r.*) - *(.rodata1) __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); *(.xt_except_table) *(.gcc_except_table) diff --git a/tools/esp-open-sdk.tar.gz b/tools/esp-open-sdk.tar.gz new file mode 100644 index 00000000..87af5572 Binary files /dev/null and b/tools/esp-open-sdk.tar.gz differ diff --git a/tools/esptool.py b/tools/esptool.py index 88a4b4ed..53e10041 100755 --- a/tools/esptool.py +++ b/tools/esptool.py @@ -360,6 +360,20 @@ class ELFFile: self._fetch_symbols() return self.symbols[sym] + def get_entry_point(self): + tool_readelf = "xtensa-lx106-elf-readelf" + if os.getenv('XTENSA_CORE')=='lx106': + tool_objcopy = "xt-readelf" + try: + proc = subprocess.Popen([tool_readelf, "-h", self.name], stdout=subprocess.PIPE) + except OSError: + print "Error calling "+tool_nm+", do you have Xtensa toolchain in PATH?" + sys.exit(1) + for l in proc.stdout: + fields = l.strip().split() + if fields[0] == "Entry": + return int(fields[3], 0); + def load_section(self, section): tool_objcopy = "xtensa-lx106-elf-objcopy" if os.getenv('XTENSA_CORE')=='lx106': @@ -586,7 +600,7 @@ if __name__ == '__main__': args.output = args.input + '-' e = ELFFile(args.input) image = ESPFirmwareImage() - image.entrypoint = e.get_symbol_addr("call_user_start") + image.entrypoint = e.get_entry_point() for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")): data = e.load_section(section) image.add_segment(e.get_symbol_addr(start), data)