diff --git a/app/include/user_modules.h b/app/include/user_modules.h index 9f10f8f3..00ec5ede 100644 --- a/app/include/user_modules.h +++ b/app/include/user_modules.h @@ -29,6 +29,7 @@ #define LUA_USE_MODULES_MQTT #define LUA_USE_MODULES_COAP #define LUA_USE_MODULES_U8G +#define LUA_USE_MODULES_WS2801 #define LUA_USE_MODULES_WS2812 #define LUA_USE_MODULES_CJSON #define LUA_USE_MODULES_CRYPTO diff --git a/app/modules/modules.h b/app/modules/modules.h index c9e55193..ad0c4cfa 100644 --- a/app/modules/modules.h +++ b/app/modules/modules.h @@ -133,6 +133,14 @@ #define ROM_MODULES_BIT #endif +#if defined(LUA_USE_MODULES_WS2801) +#define MODULES_WS2801 "ws2801" +#define ROM_MODULES_WS2801 \ + _ROM(MODULES_WS2801, luaopen_ws2801, ws2801_map) +#else +#define ROM_MODULES_WS2801 +#endif + #if defined(LUA_USE_MODULES_WS2812) #define MODULES_WS2812 "ws2812" #define ROM_MODULES_WS2812 \ @@ -190,6 +198,7 @@ ROM_MODULES_UART \ ROM_MODULES_OW \ ROM_MODULES_BIT \ + ROM_MODULES_WS2801 \ ROM_MODULES_WS2812 \ ROM_MODULES_CJSON \ ROM_MODULES_CRYPTO \ diff --git a/app/modules/ws2801.c b/app/modules/ws2801.c new file mode 100644 index 00000000..640eb066 --- /dev/null +++ b/app/modules/ws2801.c @@ -0,0 +1,140 @@ +#include "lualib.h" +#include "lauxlib.h" +#include "platform.h" +#include "auxmods.h" +#include "lrotable.h" +#include "c_stdlib.h" +#include "c_string.h" + +/** + * Code is based on https://github.com/CHERTS/esp8266-devkit/blob/master/Espressif/examples/EspLightNode/user/ws2801.c + * and provides a similar api as the ws2812 module. + * The current implementation allows the caller to use + * any combination of GPIO0, GPIO2, GPIO4, GPIO5 as clock and data. + */ + +#define PIN_CLK_DEFAULT 0 +#define PIN_DATA_DEFAULT 2 + +static uint32_t ws2801_bit_clk; +static uint32_t ws2801_bit_data; + +static void ws2801_byte(uint8_t n) { + uint8_t bitmask; + for (bitmask = 0x80; bitmask !=0 ; bitmask >>= 1) { + if (n & bitmask) { + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, ws2801_bit_data); + } else { + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_data); + } + GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, ws2801_bit_clk); + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_clk); + } +} + +static void ws2801_color(uint8_t r, uint8_t g, uint8_t b) { + ws2801_byte(r); + ws2801_byte(g); + ws2801_byte(b); +} + +static void ws2801_strip(uint8_t const * data, uint16_t len) { + while (len--) { + ws2801_byte(*(data++)); + } + GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_data); +} + +static void enable_pin_mux(pin) { + // The API only supports setting PERIPHS_IO_MUX on GPIO 0, 2, 4, 5 + switch (pin) { + case 0: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); + break; + case 2: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); + break; + case 4: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4); + break; + case 5: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5); + break; + default: + break; + } +} + +/* Lua: ws2801.init(pin_clk, pin_data) + * Sets up the GPIO pins + * + * ws2801.init(0, 2) uses GPIO0 as clock and GPIO2 as data. + * This is the default behavior. + */ +static int ICACHE_FLASH_ATTR ws2801_init_lua(lua_State* L) { + uint32_t pin_clk; + uint32_t pin_data; + uint32_t func_gpio_clk; + uint32_t func_gpio_data; + + if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { + // Use default pins if the input is omitted + pin_clk = PIN_CLK_DEFAULT; + pin_data = PIN_DATA_DEFAULT; + } else { + pin_clk = luaL_checkinteger(L, 1); + pin_data = luaL_checkinteger(L, 2); + } + + ws2801_bit_clk = 1 << pin_clk; + ws2801_bit_data = 1 << pin_data; + + os_delay_us(10); + + //Set GPIO pins to output mode + enable_pin_mux(pin_clk); + enable_pin_mux(pin_data); + + //Set both GPIOs low low + gpio_output_set(0, ws2801_bit_clk | ws2801_bit_data, ws2801_bit_clk | ws2801_bit_data, 0); + + os_delay_us(10); +} + +/* Lua: ws2801.write(pin, "string") + * Byte triples in the string are interpreted as R G B values. + * This function does not corrupt your buffer. + * + * ws2801.write(string.char(255, 0, 0)) sets the first LED red. + * ws2801.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue. + * ws2801.write(string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white. + */ +static int ICACHE_FLASH_ATTR ws2801_writergb(lua_State* L) { + size_t length; + const char *buffer = luaL_checklstring(L, 1, &length); + + os_delay_us(10); + + os_intr_lock(); + + ws2801_strip(buffer, length); + + os_intr_unlock(); + + return 0; +} + +#define MIN_OPT_LEVEL 2 +#include "lrodefs.h" +const LUA_REG_TYPE ws2801_map[] = +{ + { LSTRKEY( "write" ), LFUNCVAL( ws2801_writergb )}, + { LSTRKEY( "init" ), LFUNCVAL( ws2801_init_lua )}, + { LNILKEY, LNILVAL} +}; + +LUALIB_API int luaopen_ws2801(lua_State *L) { + LREGISTER(L, "ws2801", ws2801_map); + return 1; +} + diff --git a/tools/esptool.py b/tools/esptool.py index 53e10041..944a59b7 100755 --- a/tools/esptool.py +++ b/tools/esptool.py @@ -128,38 +128,32 @@ class ESPROM: def connect(self): print 'Connecting...' - # RTS = CH_PD (i.e reset) - # DTR = GPIO0 - # self._port.setRTS(True) - # self._port.setDTR(True) - # self._port.setRTS(False) - # time.sleep(0.1) - # self._port.setDTR(False) + for _ in xrange(4): + # issue reset-to-bootloader: + # RTS = either CH_PD or nRESET (both active low = chip in reset) + # DTR = GPIO0 (active low = boot to flasher) + self._port.setDTR(False) + self._port.setRTS(True) + time.sleep(0.05) + self._port.setDTR(True) + self._port.setRTS(False) + time.sleep(0.05) + self._port.setDTR(False) - # NodeMCU devkit - self._port.setRTS(True) - self._port.setDTR(True) - time.sleep(0.1) - self._port.setRTS(False) - self._port.setDTR(False) - time.sleep(0.1) - self._port.setRTS(True) - time.sleep(0.1) - self._port.setDTR(True) - self._port.setRTS(False) - time.sleep(0.3) - self._port.setDTR(True) - - self._port.timeout = 0.5 - for i in xrange(10): - try: - self._port.flushInput() - self._port.flushOutput() - self.sync() - self._port.timeout = 5 - return - except: - time.sleep(0.1) + self._port.timeout = 0.3 # worst-case latency timer should be 255ms (probably <20ms) + for _ in xrange(4): + try: + self._port.flushInput() + self._port.flushOutput() + self.sync() + self._port.timeout = 5 + return + except: + time.sleep(0.05) + # this is a workaround for the CH340 serial driver on current versions of Linux, + # which seems to sometimes set the serial port up with wrong parameters + self._port.close() + self._port.open() raise Exception('Failed to connect') """ Read memory address in target """ @@ -268,6 +262,15 @@ class ESPROM: return data + """ Abuse the loader protocol to force flash to be left in write mode """ + def flash_unlock_dio(self): + # Enable flash write mode + self.flash_begin(0, 0) + # Reset the chip rather than call flash_finish(), which would have + # write protected the chip again (why oh why does it do that?!) + self.mem_begin(0,0,0,0x40100000) + self.mem_finish(0x40000080) + """ Perform a chip erase of SPI flash """ def flash_erase(self): # Trick ROM to initialize SFlash @@ -566,7 +569,10 @@ if __name__ == '__main__': seq += 1 print print '\nLeaving...' - esp.flash_finish(False) + if args.flash_mode == 'dio': + esp.flash_unlock_dio() + else: + esp.flash_finish(False) elif args.operation == 'run': esp.run()