Left for archival purposes.

User avatar
By Markus Gritsch
#6941
IMG_4293_1024x768_75.jpg

I have a working implementation which I'd like to share. Unfortunately the code is not yet ready to be included in the official distribution, because it is a pain to get consistent timing when the code is compiled with -O2.

However, here it is:
Code: Select all#define WSGPIO 0

static void send_ws_0()
{
  uint8_t i;
  i = 4; while(i--) WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 1);
  i = 9; while(i--) WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 0);
}

static void send_ws_1()
{
  uint8_t i;
  i = 8; while(i--) WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 1);
  i = 5; while(i--) WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_ID_PIN(WSGPIO), 0);
}

// Lua: ws2812("string")
// Byte triples are interpreted as G R B values.
// gpio.ws2812(string.char(0, 255, 0)) sets the first LED red.
// gpio.ws2812(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
// gpio.ws2812(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
static int lgpio_ws2812( lua_State* L )
{
  size_t length;
  const char *buffer = luaL_checklstring(L, 1, &length);

  GPIO_OUTPUT_SET(GPIO_ID_PIN(WSGPIO), 0);

  os_intr_lock();
  const char *end = buffer + length;
  while( buffer != end ) {
    uint8_t mask = 0x80;
    while (mask) {
      (*buffer & mask) ? send_ws_1() : send_ws_0();
      mask >>= 1;
    }
    ++buffer;
  }
  os_intr_unlock();

  return 0;
}

I added it to the gpio.c file and to make it available from Lua the gpio_map[] array needs this additional entry:
Code: Select all   { LSTRKEY( "ws2812" ), LFUNCVAL( lgpio_ws2812 ) },

The output pin is hardcoded to GPIO0.
I also changed the default baud rate to 115200.

So connect your WS2812 LEDs to GPIO0 and controll them like this:
// Lua: ws2812("string")
// Byte triples are interpreted as G R B values.
// gpio.ws2812(string.char(0, 255, 0)) sets the first LED red.
// gpio.ws2812(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
// gpio.ws2812(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.

A precompiled firmware binary, which can be flashed in the usual way is attached, together with a slightly more complicated example which does a simple color animation.

Enjoy,
Markus
Attachments
(246.83 KiB) Downloaded 385 times
User avatar
By Markus Gritsch
#7283
IMG_4318_1024x768_75.jpg

Hi, I fixed the remaining issues (correct timing with -O2, correct width of the very first pulse) and also any GPIO pin can be used now :)

Usage:
// Lua: ws2812(pin, "string")
// Byte triples in the string are interpreted as G R B values.
// gpio.ws2812(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red.
// gpio.ws2812(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
// gpio.ws2812(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.

A precompiled firmware with WS2812 support and 115200 default baud rate is attached.

To compile it yourself, place this in gpio.c
Code: Select all// ----------------------------------------------------------------------------
// -- This WS2812 code must be compiled with -O2 to get the timing right.
// -- http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/

// The ICACHE_FLASH_ATTR is there to trick the compiler and get the very first pulse width correct.
static void ICACHE_FLASH_ATTR send_ws_0(uint8_t gpio)
{
  uint8_t i;
  i = 4; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio);
  i = 9; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio);
}

static void ICACHE_FLASH_ATTR send_ws_1(uint8_t gpio)
{
  uint8_t i;
  i = 8; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, 1 << gpio);
  i = 6; while (i--) GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, 1 << gpio);
}

// Lua: ws2812(pin, "string")
// Byte triples in the string are interpreted as G R B values.
// gpio.ws2812(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red.
// gpio.ws2812(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
// gpio.ws2812(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
static int ICACHE_FLASH_ATTR lgpio_ws2812(lua_State* L)
{
  const uint8_t pin = luaL_checkinteger(L, 1);
  size_t length;
  const char *buffer = luaL_checklstring(L, 2, &length);

  platform_gpio_mode(pin, OUTPUT, FLOAT);
  platform_gpio_write(pin, 0);
  os_delay_us(10);

  os_intr_lock();
  const char * const end = buffer + length;
  while (buffer != end) {
    uint8_t mask = 0x80;
    while (mask) {
      (*buffer & mask) ? send_ws_1(pin_num[pin]) : send_ws_0(pin_num[pin]);
      mask >>= 1;
    }
    ++buffer;
  }
  os_intr_unlock();

  return 0;
}

// ----------------------------------------------------------------------------

And add the following entry to the gpio_map[]:
Code: Select all  { LSTRKEY( "ws2812" ), LFUNCVAL( lgpio_ws2812 ) },

Have fun,
Markus
Attachments
(246.75 KiB) Downloaded 358 times