Finalize work on ws2812 module

* Fix bug on first write
	Pin is 'HIGH' at reset, so we need to pull it down and generate a
	reset.

* Move init code to flash section, not needed to be in iram.

* Remove pin choice in API

* Remove lock in ws2812_buffer_write

* Remove naked malloc

* Drop ws2812_writergb

* Drop support of ws2812.buffers to ws2812_writegrb should use ws2812.buffers:write

* Add support for <>3 colors per leds strips (RGBW)

* Remove ICACHE_FLASH_ATTR

* Add static const on _uartData to avoid initialization penalty
This commit is contained in:
Thomas Soëte 2016-05-09 07:21:50 +02:00 committed by Marcel Stör
parent 216c69f18b
commit 0577c8af0f
2 changed files with 240 additions and 164 deletions

View File

@ -1,5 +1,6 @@
#include "module.h" #include "module.h"
#include "lauxlib.h" #include "lauxlib.h"
#include "lmem.h"
#include "platform.h" #include "platform.h"
#include "c_stdlib.h" #include "c_stdlib.h"
#include "c_string.h" #include "c_string.h"
@ -11,33 +12,51 @@
typedef struct { typedef struct {
int canary; int canary;
int size; int size;
uint8_t colorsPerLed;
uint8_t values[0]; uint8_t values[0];
} ws2812_buffer; } ws2812_buffer;
// Stream data using UART1 routed to GPIO2 // Init UART1 to be able to stream WS2812 data
// NODE_DEBUG should not be activated because it also uses UART1 // We use GPIO2 as output pin
static void ICACHE_RAM_ATTR ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t length) { static void ws2812_init() {
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
// 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
// 0b00110100 => 110100 => [0]001011[1] => 11101000 => 10
// 0b00000100 => 000100 => [0]001000[1] => 11101110 => 11
uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
// Configure UART1 // Configure UART1
// Set baudrate of UART1 to 3200000 // Set baudrate of UART1 to 3200000
WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000); WRITE_PERI_REG(UART_CLKDIV(1), UART_CLK_FREQ / 3200000);
// Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX // Set UART Configuration No parity / 6 DataBits / 1 StopBits / Invert TX
WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S)); WRITE_PERI_REG(UART_CONF0(1), UART_TXD_INV | (1 << UART_STOP_BIT_NUM_S) | (1 << UART_BIT_NUM_S));
// Pull GPIO2 down
platform_gpio_mode(4, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(4, 0);
// Waits 10us to simulate a reset
os_delay_us(10);
// Redirect UART1 to GPIO2 // Redirect UART1 to GPIO2
// Disable GPIO2 // Disable GPIO2
GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, BIT2); GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, BIT2);
// Enable Function 2 for GPIO2 (U1TXD) // Enable Function 2 for GPIO2 (U1TXD)
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK); PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
}
// Stream data using UART1 routed to GPIO2
// ws2812.init() should be called first
//
// NODE_DEBUG should not be activated because it also uses UART1
static void ICACHE_RAM_ATTR ws2812_write(uint8_t *pixels, uint32_t length) {
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
// 0b00000111 => 000111 => [0]111000[1] => 10001110 => 01
// 0b00110100 => 110100 => [0]001011[1] => 11101000 => 10
// 0b00000100 => 000100 => [0]001000[1] => 11101110 => 11
// Array declared as static const to avoid runtime generation
// But declared in ".data" section to avoid read penalty from FLASH
static const __attribute__((section(".data._uartData"))) uint8_t _uartData[4] = { 0b00110111, 0b00000111, 0b00110100, 0b00000100 };
uint8_t *end = pixels + length; uint8_t *end = pixels + length;
do { do {
uint8_t value = *pixels++; uint8_t value = *pixels++;
@ -52,94 +71,37 @@ static void ICACHE_RAM_ATTR ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t
WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]); WRITE_PERI_REG(UART_FIFO(1), _uartData[(value >> 0) & 3]);
} while(pixels < end); } while(pixels < end);
} }
// Lua: ws2812.writergb(pin, "string") // Lua: ws2812.write("string")
// Byte triples in the string or buffer are interpreted as R G B values and sent to the hardware as G R B. // Byte triples in the string are interpreted as G R B values.
// //
// ws2812.writergb(4, string.char(255, 0, 0)) uses GPIO2 and sets the first LED red. // ws2812.init() should be called first
// ws2812.writergb(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue. //
// ws2812.writergb(4, string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white. // ws2812.write(string.char(0, 255, 0)) sets the first LED red.
static int ICACHE_FLASH_ATTR ws2812_writergb(lua_State* L) // ws2812.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
{ // ws2812.write(string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
const uint8_t pin = luaL_checkinteger(L, 1); static int ws2812_writegrb(lua_State* L) {
size_t length; size_t length;
const char *rgb; const char *values = luaL_checklstring(L, 1, &length);
// Buffer or string
if(lua_isuserdata(L, 2)) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
rgb = &buffer->values[0];
length = 3*buffer->size;
} else {
rgb = luaL_checklstring(L, 2, &length);
}
// dont modify lua-internal lstring - make a copy instead
char *buffer = (char *)c_malloc(length);
c_memcpy(buffer, rgb, length);
// Ignore incomplete Byte triples at the end of buffer:
length -= length % 3;
// Rearrange R G B values to G R B order needed by WS2812 LEDs:
size_t i;
for (i = 0; i < length; i += 3) {
const char r = buffer[i];
const char g = buffer[i + 1];
buffer[i] = g;
buffer[i + 1] = r;
}
// Send the buffer // Send the buffer
ws2812_write(pin_num[pin], (uint8_t*) buffer, length); ws2812_write((uint8_t*) values, length);
c_free(buffer);
return 0;
}
// Lua: ws2812.write(pin, "string")
// Lua: ws2812.write(pin, ws2812.buffer)
// Byte triples in the string or buffer are interpreted as G R B values.
//
// ws2812.write(4, string.char(0, 255, 0)) uses GPIO2 and sets the first LED red.
// ws2812.write(3, string.char(0, 0, 255):rep(10)) uses GPIO0 and sets ten LEDs blue.
// ws2812.write(4, string.char(255, 0, 0, 255, 255, 255)) first LED green, second LED white.
static int ICACHE_FLASH_ATTR ws2812_writegrb(lua_State* L) {
const uint8_t pin = luaL_checkinteger(L, 1);
size_t length;
const char *values;
// Buffer or string
if(lua_isuserdata(L, 2)) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 2);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 2, "ws2812.buffer expected");
values = &buffer->values[0];
length = 3*buffer->size;
} else {
values = luaL_checklstring(L, 2, &length);
}
// Send the buffer
ws2812_write(pin_num[pin], (uint8_t*) values, length);
return 0; return 0;
} }
// Handle a buffer where we can store led values // Handle a buffer where we can store led values
static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) { static int ws2812_new_buffer(lua_State *L) {
const int leds = luaL_checkint(L, 1); const int leds = luaL_checkint(L, 1);
const int colorsPerLed = luaL_checkint(L, 2);
luaL_argcheck(L, leds > 0, 1, "should be a positive integer"); luaL_argcheck(L, leds > 0, 1, "should be a positive integer");
luaL_argcheck(L, colorsPerLed > 0, 2, "should be a positive integer");
// Allocate memory // Allocate memory
size_t size = sizeof(ws2812_buffer) + 3*leds*sizeof(uint8_t); size_t size = sizeof(ws2812_buffer) + colorsPerLed*leds*sizeof(uint8_t);
ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size); ws2812_buffer * buffer = (ws2812_buffer*)lua_newuserdata(L, size);
// Associate its metatable // Associate its metatable
@ -148,6 +110,7 @@ static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) {
// Save led strip size // Save led strip size
buffer->size = leds; buffer->size = leds;
buffer->colorsPerLed = colorsPerLed;
// Store canary for future type checks // Store canary for future type checks
buffer->canary = CANARY_VALUE; buffer->canary = CANARY_VALUE;
@ -155,27 +118,37 @@ static int ICACHE_FLASH_ATTR ws2812_new_buffer(lua_State *L) {
return 1; return 1;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_fill(lua_State* L) { static int ws2812_buffer_fill(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
const int g = luaL_checkinteger(L, 2); // Grab colors
const int r = luaL_checkinteger(L, 3); int i, j;
const int b = luaL_checkinteger(L, 4); int * colors = luaM_malloc(L, buffer->colorsPerLed * sizeof(int));
uint8_t * p = &buffer->values[0]; for (i = 0; i < buffer->colorsPerLed; i++)
int i; {
for(i = 0; i < buffer->size; i++) { colors[i] = luaL_checkinteger(L, 2+i);
*p++ = g;
*p++ = r;
*p++ = b;
} }
// Fill buffer
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++)
{
for (j = 0; j < buffer->colorsPerLed; j++)
{
*p++ = colors[j];
}
}
// Free memory
luaM_free(L, colors);
return 0; return 0;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_fade(lua_State* L) { static int ws2812_buffer_fade(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int fade = luaL_checkinteger(L, 2); const int fade = luaL_checkinteger(L, 2);
@ -184,32 +157,33 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_fade(lua_State* L) {
uint8_t * p = &buffer->values[0]; uint8_t * p = &buffer->values[0];
int i; int i;
for(i = 0; i < buffer->size; i++) { for(i = 0; i < buffer->size * buffer->colorsPerLed; i++)
*p++ /= fade; {
*p++ /= fade;
*p++ /= fade; *p++ /= fade;
} }
return 0; return 0;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_get(lua_State* L) { static int ws2812_buffer_get(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int led = luaL_checkinteger(L, 2); const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
lua_pushnumber(L, buffer->values[3*led+0]); int i;
lua_pushnumber(L, buffer->values[3*led+1]); for (i = 0; i < buffer->colorsPerLed; i++)
lua_pushnumber(L, buffer->values[3*led+2]); {
lua_pushnumber(L, buffer->values[buffer->colorsPerLed*led+i]);
}
return 3; return buffer->colorsPerLed;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) { static int ws2812_buffer_set(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const int led = luaL_checkinteger(L, 2); const int led = luaL_checkinteger(L, 2) - 1;
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range"); luaL_argcheck(L, led >= 0 && led < buffer->size, 2, "index out of range");
@ -217,18 +191,18 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) {
int type = lua_type(L, 3); int type = lua_type(L, 3);
if(type == LUA_TTABLE) if(type == LUA_TTABLE)
{ {
// Get g,r,b values and push them on stash int i;
lua_rawgeti(L, 3, 1); for (i = 0; i < buffer->colorsPerLed; i++)
lua_rawgeti(L, 3, 2); {
lua_rawgeti(L, 3, 3); // Get value and push it on stack
lua_rawgeti(L, 3, i+1);
// Convert them as int and store them in buffer // Convert it as int and store them in buffer
buffer->values[3*led+0] = lua_tonumber(L, -3); buffer->values[buffer->colorsPerLed*led+i] = lua_tonumber(L, -1);
buffer->values[3*led+1] = lua_tonumber(L, -2); }
buffer->values[3*led+2] = lua_tonumber(L, -1);
// Clean up the stack // Clean up the stack
lua_pop(L, 3); lua_pop(L, buffer->colorsPerLed);
} }
else if(type == LUA_TSTRING) else if(type == LUA_TSTRING)
{ {
@ -236,24 +210,26 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_set(lua_State* L) {
const char * buf = lua_tolstring(L, 3, &len); const char * buf = lua_tolstring(L, 3, &len);
// Overflow check // Overflow check
if( 3*led + len > 3*buffer->size ) if( buffer->colorsPerLed*led + len > buffer->colorsPerLed*buffer->size )
{ {
return luaL_error(L, "string size will exceed strip length"); return luaL_error(L, "string size will exceed strip length");
} }
c_memcpy(&buffer->values[3*led], buf, len); c_memcpy(&buffer->values[buffer->colorsPerLed*led], buf, len);
} }
else else
{ {
buffer->values[3*led+0] = luaL_checkinteger(L, 3); int i;
buffer->values[3*led+1] = luaL_checkinteger(L, 4); for (i = 0; i < buffer->colorsPerLed; i++)
buffer->values[3*led+2] = luaL_checkinteger(L, 5); {
buffer->values[buffer->colorsPerLed*led+i] = luaL_checkinteger(L, 3+i);
}
} }
return 0; return 0;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_size(lua_State* L) { static int ws2812_buffer_size(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
@ -263,20 +239,13 @@ static int ICACHE_FLASH_ATTR ws2812_buffer_size(lua_State* L) {
return 1; return 1;
} }
static int ICACHE_FLASH_ATTR ws2812_buffer_write(lua_State* L) { static int ws2812_buffer_write(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1); ws2812_buffer * buffer = (ws2812_buffer*)lua_touserdata(L, 1);
const uint8_t pin = luaL_checkinteger(L, 2);
luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected"); luaL_argcheck(L, buffer && buffer->canary == CANARY_VALUE, 1, "ws2812.buffer expected");
// Initialize the output pin
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
platform_gpio_write(pin, 0);
// Send the buffer // Send the buffer
ets_intr_lock(); ws2812_write(buffer->values, buffer->colorsPerLed*buffer->size);
ws2812_write(pin_num[pin], &buffer->values[0], 3*buffer->size);
ets_intr_unlock();
return 0; return 0;
} }
@ -295,9 +264,9 @@ static const LUA_REG_TYPE ws2812_buffer_map[] =
static const LUA_REG_TYPE ws2812_map[] = static const LUA_REG_TYPE ws2812_map[] =
{ {
{ LSTRKEY( "writergb" ), LFUNCVAL( ws2812_writergb )},
{ LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )}, { LSTRKEY( "write" ), LFUNCVAL( ws2812_writegrb )},
{ LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )}, { LSTRKEY( "newBuffer" ), LFUNCVAL( ws2812_new_buffer )},
{ LSTRKEY( "init" ), LFUNCVAL( ws2812_init )},
{ LNILKEY, LNILVAL} { LNILKEY, LNILVAL}
}; };

View File

@ -1,57 +1,164 @@
# WS2812 Module # WS2812 Module
| Since | Origin / Contributor | Maintainer | Source | | Since | Origin / Contributor | Maintainer | Source |
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel) | [Till Klocke](https://github.com/dereulenspiegel) | [ws2812.c](../../../app/modules/ws2812.c)| | 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel), [Thomas Soëte](https://github.com/Alkorin) | [Till Klocke](https://github.com/dereulenspiegel) | [ws2812.c](../../../app/modules/ws2812.c)|
ws2812 is a library to handle ws2812-like led strips.
It works at least on WS2812, WS2812b, APA104, SK6812 (RGB or RGBW).
## ws2812.write() The library uses UART1 routed on GPIO2 (Pin D4 on NodeMCU DEVKIT) to
Send GRB data in 8 bits to a WS2812 chain. generate the bitstream.
#### Syntax ## ws2812.init()
`ws2812.writegrb(pin, string)` Initialize UART1 and GPIO2, should be called once and before write()
#### Parameters #### Parameters
- `pin` is ignored. Only works with GPIO2. none
- `string` payload to be sent to one or more WS2812 LEDs.
It should be composed from a GRB triplet per element.
- `G1` the first pixel's Green channel (0-255)
- `R1` the first pixel's Red channel (0-255)
- `B1` the first pixel's Blue channel (0-255)<br />
... You can connect a lot of WS2812 ...
- `G2`, `R2`, `B2` are the next WS2812's Green, Red, and Blue channel parameters
#### Returns #### Returns
`nil` `nil`
```lua ## ws2812.write()
g = 0 Send data to a led strip using its native format which is generally Green,Red,Blue for RGB strips
r = 255 and Green,Red,Blue,White for RGBW strips.
b = 0
leds_grb = string.char(g,r,b, g,r,b)
ws2812.write(2, leds_grb) -- turn two WS2812Bs to red, connected to pin GPIO2
```
## ws2812.writergb()
Send GRB data in 8bits to a WS2812 chain.
#### Syntax #### Syntax
`ws2812.writergb(pin, string)` `ws2812.write(string)`
#### Parameters #### Parameters
- `pin` is ignored. Only works with GPIO2. - `string` payload to be sent to one or more WS2812 like leds.
- `string` payload to be sent to one or more WS2812 LEDs.
It should be composed from an RGB triplet per element.
- `R1` the first pixel's Red channel (0-255)
- `G1` the first pixel's Green channel (0-255)
- `B1` the first pixel's Blue channel (0-255)<br />
... You can connect a lot of WS2812 ...
- `R2`, `G2`, `B2` are the next WS2812's Red, Green, and Blue channel parameters
#### Returns #### Returns
`nil` `nil`
#### Example #### Example
```lua ```lua
leds_rgb = string.char(255,0,0, 0,255,0, 0,0,255) ws2812.init()
ws2812.writergb(2, leds_rgb) -- turn three WS2812Bs to red, green, and blue respectively ws2812.write(string.char(255,0,0,255,0,0) -- turn the two first RGB leds to green
``` ```
```lua
ws2812.init()
ws2812.write(string.char(0,0,0,255,0,0,0,255) -- turn the two first RGBW leds to white
```
# Buffer module
For more advanced animations, it is useful to keep a "framebuffer" of the strip,
interact with it and flush it to the strip.
For this purpose, the ws2812 library offers a read/write buffer.
#### Example
Led chaser with a RGBW strip
```lua
local i, b = 0, ws2812.newBuffer(300, 4); b:fill(0,0,0,0); tmr.alarm(0, 50, 1, function()
i=i+1
b:fade(2)
b:set(i%b:size()+1, 0, 0, 0, 255)
b:write()
end)
```
## ws2812.newBuffer()
Allocate a new memory buffer to store led values.
#### Syntax
`ws2812.newBuffer(numberOfLeds, bytesPerLed)`
#### Parameters
- `numberOfLeds` length of the led strip
- `bytesPerLed` 3 for RGB strips and 4 for RGBW strips
#### Returns
`ws2812.buffer`
## ws2812.buffer:get()
Return the value at the given position
#### Syntax
`buffer:get(index)`
#### Parameters
- `index` position in the buffer (1 for first led)
#### Returns
`(color)`
#### Example
```lua
buffer:get(2) -- return the color of the second led
```
## ws2812.buffer:set()
Set the value at the given position
#### Syntax
`buffer:set(index, color)`
#### Parameters
- `index` position in the buffer (1 for the first led)
- `color` bytes of the color
#### Returns
`nil`
#### Example
```lua
buffer:set(1, 255, 0, 0) -- set the first led green for a RGB strip
```
## ws2812.buffer:size()
Return the size of the buffer in number of leds
#### Syntax
`buffer:size()`
#### Parameters
none
#### Returns
`int`
## ws2812.buffer:fill()
Fill the buffer with the given color.
The number of given bytes must match the number of bytesPerLed of the buffer
#### Syntax
`buffer:fill(color)`
#### Parameters
- `color` bytes of the color
#### Returns
`nil`
#### Example
```lua
buffer:fill(0, 0, 0) -- fill the buffer with black for a RGB strip
```
## ws2812.buffer:fade()
Divide each byte of each led by the given value. Useful for a fading effect
#### Syntax
`buffer:fade(value)`
#### Parameters
- `value` value by which divide each byte
#### Returns
`nil`
#### Example
```lua
buffer:fade(2)
```
## ws2812.buffer:write()
Output the buffer to the led strip
#### Syntax
`buffer:write()`
#### Parameters
none
#### Returns
`nil`