nodemcu-firmware/app/modules/ws2801.c

135 lines
3.6 KiB
C
Raw Normal View History

#include "module.h"
2015-07-02 00:50:26 +02:00
#include "lauxlib.h"
#include "platform.h"
#include <stdlib.h>
#include <string.h>
Initial pass at switching to RTOS SDK. This compiles, links, and starts the RTOS without crashing and burning. Lua environment does not yet start due to the different task architecture. Known pain points: - task implementation needs to be rewritten for RTOS (next up on my TODO) - secure espconn does not exist, all secure espconn stuff has been #if 0'd - lwip now built from within the RTOS SDK, but does not appear to include MDNS support. Investigation needed. - there is no access to FRC1 NMI, not sure if we ever actually used that however. Also #if 0'd out for now. - new timing constraints introduced by the RTOS, all use of ets_delay_us() and os_delay_us() needs to be reviewed (the tsl2561 driver in particular). - even more confusion with ets_ vs os_ vs c_ vs non-prefixed versions. In the long run everything should be switched to non-prefixed versions. - system_set_os_print() not available, needs to be reimplemented - all the RTOS rodata is loaded into RAM, as it apparently uses some constants while the flash isn't mapped, so our exception handler can't work its magic. This should be narrowed down to the minimum possible at some point. - with each task having its own stack in RTOS, we probably need change flash-page buffers from the stack to the heap in a bunch of places. A single, shared, page buffer *might* be possible if we limit ourselves to running NodeMCU in a single task. - there's a ton of junk in the sdk-overrides now; over time the core code should be updated to not need those shims
2016-05-24 07:05:01 +02:00
#include "esp_misc.h"
#include "rom.h"
2015-07-02 00:50:26 +02:00
/**
* 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(int pin) {
2015-07-02 00:50:26 +02:00
// 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);
ets_intr_lock();
2015-07-02 00:50:26 +02:00
ws2801_strip(buffer, length);
ets_intr_unlock();
2015-07-02 00:50:26 +02:00
return 0;
}
static const LUA_REG_TYPE ws2801_map[] =
2015-07-02 00:50:26 +02:00
{
{ LSTRKEY( "write" ), LFUNCVAL( ws2801_writergb )},
{ LSTRKEY( "init" ), LFUNCVAL( ws2801_init_lua )},
{ LNILKEY, LNILVAL}
};
NODEMCU_MODULE(WS2801, "ws2801", ws2801_map, NULL);