Use a translator callback to convert GRB data to RMT pulses.
This commit is contained in:
parent
e5892a7286
commit
b4af563bc3
|
@ -11,6 +11,13 @@
|
||||||
#define SHIFT_LOGICAL 0
|
#define SHIFT_LOGICAL 0
|
||||||
#define SHIFT_CIRCULAR 1
|
#define SHIFT_CIRCULAR 1
|
||||||
|
|
||||||
|
// The default bit H & L durations in multiples of 100ns.
|
||||||
|
#define WS2812_DURATION_T0H 4
|
||||||
|
#define WS2812_DURATION_T0L 7
|
||||||
|
#define WS2812_DURATION_T1H 8
|
||||||
|
#define WS2812_DURATION_T1L 6
|
||||||
|
// The default reset duration in multiples of 100ns.
|
||||||
|
#define WS2812_DURATION_RESET 500
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int size;
|
int size;
|
||||||
|
@ -33,6 +40,7 @@ static void ws2812_cleanup( lua_State *L, int pop )
|
||||||
// ws2812.write({pin = 4, data = string.char(255, 0, 0, 255, 255, 255)}) first LED green, second LED white.
|
// ws2812.write({pin = 4, data = string.char(255, 0, 0, 255, 255, 255)}) first LED green, second LED white.
|
||||||
static int ws2812_write( lua_State* L )
|
static int ws2812_write( lua_State* L )
|
||||||
{
|
{
|
||||||
|
int type;
|
||||||
int top = lua_gettop( L );
|
int top = lua_gettop( L );
|
||||||
|
|
||||||
for (int stack = 1; stack <= top; stack++) {
|
for (int stack = 1; stack <= top; stack++) {
|
||||||
|
@ -56,6 +64,106 @@ static int ws2812_write( lua_State* L )
|
||||||
int gpio_num = luaL_checkint( L, -1 );
|
int gpio_num = luaL_checkint( L, -1 );
|
||||||
lua_pop( L, 1 );
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// retrieve reset
|
||||||
|
// This is an optional parameter which defaults to WS2812_DURATION_RESET.
|
||||||
|
//
|
||||||
|
int reset = WS2812_DURATION_RESET;
|
||||||
|
type = lua_getfield( L, stack, "reset" );
|
||||||
|
if (type!=LUA_TNIL )
|
||||||
|
{
|
||||||
|
if (!lua_isnumber( L, -1 )) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "invalid reset" );
|
||||||
|
}
|
||||||
|
reset = luaL_checkint( L, -1 );
|
||||||
|
if ((reset<0) || (reset>0xfffe)) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "reset must be 0<=reset<=65534" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// retrieve t0h
|
||||||
|
// This is an optional parameter which defaults to WS2812_DURATION_T0H.
|
||||||
|
//
|
||||||
|
int t0h = WS2812_DURATION_T0H;
|
||||||
|
type = lua_getfield( L, stack, "t0h" );
|
||||||
|
if (type!=LUA_TNIL )
|
||||||
|
{
|
||||||
|
if (!lua_isnumber( L, -1 )) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "invalid t0h" );
|
||||||
|
}
|
||||||
|
t0h = luaL_checkint( L, -1 );
|
||||||
|
if ((t0h<1) || (t0h>0x7fff)) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "t0h must be 1<=t0h<=32767" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// retrieve t0l
|
||||||
|
// This is an optional parameter which defaults to WS2812_DURATION_T0L.
|
||||||
|
//
|
||||||
|
int t0l = WS2812_DURATION_T0L;
|
||||||
|
type = lua_getfield( L, stack, "t0l" );
|
||||||
|
if (type!=LUA_TNIL )
|
||||||
|
{
|
||||||
|
if (!lua_isnumber( L, -1 )) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "invalid t0l" );
|
||||||
|
}
|
||||||
|
t0l = luaL_checkint( L, -1 );
|
||||||
|
if ((t0l<1) || (t0l>0x7fff)) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "t0l must be 1<=t0l<=32767" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// retrieve t1h
|
||||||
|
// This is an optional parameter which defaults to WS2812_DURATION_T1H.
|
||||||
|
//
|
||||||
|
int t1h = WS2812_DURATION_T1H;
|
||||||
|
type = lua_getfield( L, stack, "t1h" );
|
||||||
|
if (type!=LUA_TNIL )
|
||||||
|
{
|
||||||
|
if (!lua_isnumber( L, -1 )) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "invalid t1h" );
|
||||||
|
}
|
||||||
|
t1h = luaL_checkint( L, -1 );
|
||||||
|
if ((t1h<1) || (t1h>0x7fff)) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "t1h must be 1<=t1h<=32767" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
//
|
||||||
|
// retrieve t1l
|
||||||
|
// This is an optional parameter which defaults to WS2812_DURATION_T1L.
|
||||||
|
//
|
||||||
|
int t1l = WS2812_DURATION_T1L;
|
||||||
|
type = lua_getfield( L, stack, "t1l" );
|
||||||
|
if (type!=LUA_TNIL )
|
||||||
|
{
|
||||||
|
if (!lua_isnumber( L, -1 )) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "invalid t1l" );
|
||||||
|
}
|
||||||
|
t1l = luaL_checkint( L, -1 );
|
||||||
|
if ((t1l<1) || (t1l>0x7fff)) {
|
||||||
|
ws2812_cleanup( L, 1 );
|
||||||
|
return luaL_argerror( L, stack, "t1l must be 1<=t1l<=32767" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
//
|
//
|
||||||
// retrieve data
|
// retrieve data
|
||||||
//
|
//
|
||||||
|
@ -83,7 +191,7 @@ static int ws2812_write( lua_State* L )
|
||||||
lua_pop( L, 1 );
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
// prepare channel
|
// prepare channel
|
||||||
if (platform_ws2812_setup( gpio_num, 1, (const uint8_t *)data, length ) != PLATFORM_OK) {
|
if (platform_ws2812_setup( gpio_num, reset, t0h, t0l, t1h, t1l, (const uint8_t *)data, length ) != PLATFORM_OK) {
|
||||||
ws2812_cleanup( L, 0 );
|
ws2812_cleanup( L, 0 );
|
||||||
return luaL_argerror( L, stack, "can't set up chain" );
|
return luaL_argerror( L, stack, "can't set up chain" );
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ int platform_dht_read( uint8_t gpio_num, uint8_t wakeup_ms, uint8_t *data );
|
||||||
// WS2812 platform interface
|
// WS2812 platform interface
|
||||||
|
|
||||||
void platform_ws2812_init( void );
|
void platform_ws2812_init( void );
|
||||||
int platform_ws2812_setup( uint8_t gpio_num, uint8_t num_mem, const uint8_t *data, size_t len );
|
int platform_ws2812_setup( uint8_t gpio_num, uint32_t reset, uint32_t bit0h, uint32_t bit0l, uint32_t bit1h, uint32_t bit1l, const uint8_t *data, size_t len );
|
||||||
int platform_ws2812_release( void );
|
int platform_ws2812_release( void );
|
||||||
int platform_ws2812_send( void );
|
int platform_ws2812_send( void );
|
||||||
|
|
||||||
|
|
|
@ -30,122 +30,183 @@
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "soc/periph_defs.h"
|
#include "soc/periph_defs.h"
|
||||||
|
#include "soc/rmt_reg.h"
|
||||||
|
|
||||||
#undef WS2812_DEBUG
|
#undef WS2812_DEBUG
|
||||||
|
|
||||||
|
|
||||||
// divider to generate 100ns base period from 80MHz APB clock
|
// divider to generate 100ns base period from 80MHz APB clock
|
||||||
#define WS2812_CLKDIV (100 * 80 /1000)
|
#define WS2812_CLKDIV (100 * 80 /1000)
|
||||||
// bit H & L durations in multiples of 100ns
|
|
||||||
#define WS2812_DURATION_T0H 4
|
|
||||||
#define WS2812_DURATION_T0L 7
|
|
||||||
#define WS2812_DURATION_T1H 8
|
|
||||||
#define WS2812_DURATION_T1L 6
|
|
||||||
#define WS2812_DURATION_RESET (50000 / 100)
|
|
||||||
|
|
||||||
// 0 bit in rmt encoding
|
|
||||||
const rmt_item32_t ws2812_rmt_bit0 = {
|
|
||||||
.level0 = 1,
|
|
||||||
.duration0 = WS2812_DURATION_T0H,
|
|
||||||
.level1 = 0,
|
|
||||||
.duration1 = WS2812_DURATION_T0L
|
|
||||||
};
|
|
||||||
// 1 bit in rmt encoding
|
|
||||||
const rmt_item32_t ws2812_rmt_bit1 = {
|
|
||||||
.level0 = 1,
|
|
||||||
.duration0 = WS2812_DURATION_T1H,
|
|
||||||
.level1 = 0,
|
|
||||||
.duration1 = WS2812_DURATION_T1L
|
|
||||||
};
|
|
||||||
|
|
||||||
#define ws2812_rmt_reset {.level0 = 0, .duration0 = 4, .level1 = 0, .duration1 = 4}
|
|
||||||
// reset signal, spans one complete buffer block
|
|
||||||
const rmt_item32_t ws2812_rmt_reset_block[64] = { [0 ... 63] = ws2812_rmt_reset };
|
|
||||||
|
|
||||||
|
|
||||||
// descriptor for a ws2812 chain
|
// descriptor for a ws2812 chain
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool valid;
|
bool valid;
|
||||||
uint8_t gpio;
|
uint8_t gpio;
|
||||||
|
bool sendreset;
|
||||||
|
rmt_item32_t reset;
|
||||||
|
rmt_item32_t bits[2];
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t tx_offset;
|
uint8_t bitpos;
|
||||||
} ws2812_chain_t;
|
} ws2812_chain_t;
|
||||||
|
|
||||||
|
|
||||||
// chain descriptor array
|
// chain descriptor array
|
||||||
static ws2812_chain_t ws2812_chains[RMT_CHANNEL_MAX];
|
static ws2812_chain_t ws2812_chains[RMT_CHANNEL_MAX];
|
||||||
|
|
||||||
// interrupt handler for ws2812 ISR
|
// Callback function to convert GRB data to pulse lengths.
|
||||||
static intr_handle_t ws2812_intr_handle;
|
static void ws2812_convert(const void *src, rmt_item32_t *dest, size_t src_size, size_t wanted_num, size_t *translated_size, size_t *item_num)
|
||||||
|
|
||||||
|
|
||||||
static void ws2812_fill_memory_encoded( rmt_channel_t channel, const uint8_t *data, size_t len, size_t tx_offset )
|
|
||||||
{
|
{
|
||||||
for (size_t idx = 0; idx < len; idx++) {
|
size_t cnt_in;
|
||||||
uint8_t byte = data[idx];
|
size_t cnt_out;
|
||||||
|
const uint8_t *pucData;
|
||||||
|
uint8_t ucData;
|
||||||
|
uint8_t ucBitPos;
|
||||||
|
esp_err_t tStatus;
|
||||||
|
void *pvContext;
|
||||||
|
ws2812_chain_t *ptContext;
|
||||||
|
uint8_t ucBit;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
cnt_in = 0;
|
||||||
RMTMEM.chan[channel].data32[tx_offset + idx*8 + i].val = byte & 0x80 ? ws2812_rmt_bit1.val : ws2812_rmt_bit0.val;
|
cnt_out = 0;
|
||||||
|
if( dest!=NULL && wanted_num>0 )
|
||||||
byte <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void ws2812_isr(void *arg)
|
|
||||||
{
|
{
|
||||||
uint32_t intr_st = RMT.int_st.val;
|
tStatus = rmt_translator_get_context(item_num, &pvContext);
|
||||||
|
if( tStatus==ESP_OK )
|
||||||
|
{
|
||||||
|
ptContext = (ws2812_chain_t *)pvContext;
|
||||||
|
|
||||||
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX; channel++) {
|
if( ptContext->sendreset==true )
|
||||||
|
{
|
||||||
if ((intr_st & (BIT(channel+24))) && (ws2812_chains[channel].valid)) {
|
dest[cnt_out++] = ptContext->reset;
|
||||||
RMT.int_clr.val = BIT(channel+24);
|
ptContext->sendreset = false;
|
||||||
|
|
||||||
ws2812_chain_t *chain = &(ws2812_chains[channel]);
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
|
||||||
uint32_t data_sub_len = RMT.tx_lim_ch[channel].limit/8;
|
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
||||||
uint32_t data_sub_len = RMT.chn_tx_lim[channel].tx_lim_chn/8;
|
|
||||||
#else
|
|
||||||
uint32_t data_sub_len = RMT.tx_lim[channel].limit/8;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (chain->len >= data_sub_len) {
|
|
||||||
ws2812_fill_memory_encoded( channel, chain->data, data_sub_len, chain->tx_offset );
|
|
||||||
chain->data += data_sub_len;
|
|
||||||
chain->len -= data_sub_len;
|
|
||||||
} else if (chain->len == 0) {
|
|
||||||
RMTMEM.chan[channel].data32[chain->tx_offset].val = 0;
|
|
||||||
} else {
|
|
||||||
ws2812_fill_memory_encoded( channel, chain->data, chain->len, chain->tx_offset );
|
|
||||||
RMTMEM.chan[channel].data32[chain->tx_offset + chain->len*8].val = 0;
|
|
||||||
chain->data += chain->len;
|
|
||||||
chain->len = 0;
|
|
||||||
}
|
|
||||||
if (chain->tx_offset == 0) {
|
|
||||||
chain->tx_offset = data_sub_len * 8;
|
|
||||||
} else {
|
|
||||||
chain->tx_offset = 0;
|
|
||||||
}
|
}
|
||||||
|
if( src!=NULL && src_size>0 )
|
||||||
|
{
|
||||||
|
ucBitPos = ptContext->bitpos;
|
||||||
|
|
||||||
|
/* Each bit of the input data is converted into one RMT item. */
|
||||||
|
|
||||||
|
pucData = (const uint8_t*)src;
|
||||||
|
/* Get the current byte. */
|
||||||
|
ucData = pucData[cnt_in] << ucBitPos;
|
||||||
|
|
||||||
|
while( cnt_in<src_size && cnt_out<wanted_num )
|
||||||
|
{
|
||||||
|
/* Get the current bit. */
|
||||||
|
ucBit = (ucData & 0x80U) >> 7U;
|
||||||
|
/* Translate the bit to a WS2812 input code. */
|
||||||
|
dest[cnt_out++] = ptContext->bits[ucBit];
|
||||||
|
/* Move to the next bit. */
|
||||||
|
++ucBitPos;
|
||||||
|
if( ucBitPos<8U )
|
||||||
|
{
|
||||||
|
ucData <<= 1;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ucBitPos = 0U;
|
||||||
|
++cnt_in;
|
||||||
|
ucData = pucData[cnt_in];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ptContext->bitpos = ucBitPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*translated_size = cnt_in;
|
||||||
|
*item_num = cnt_out;
|
||||||
|
}
|
||||||
|
|
||||||
int platform_ws2812_setup( uint8_t gpio_num, uint8_t num_mem, const uint8_t *data, size_t len )
|
int platform_ws2812_setup( uint8_t gpio_num, uint32_t reset, uint32_t t0h, uint32_t t0l, uint32_t t1h, uint32_t t1l, const uint8_t *data, size_t len )
|
||||||
{
|
{
|
||||||
int channel;
|
int channel;
|
||||||
|
|
||||||
if ((channel = platform_rmt_allocate( num_mem, RMT_MODE_TX )) >= 0) {
|
if ((channel = platform_rmt_allocate( 1, RMT_MODE_TX )) >= 0) {
|
||||||
ws2812_chain_t *chain = &(ws2812_chains[channel]);
|
ws2812_chain_t *chain = &(ws2812_chains[channel]);
|
||||||
|
rmt_item32_t tRmtItem;
|
||||||
|
uint32_t half;
|
||||||
|
|
||||||
chain->valid = true;
|
chain->valid = true;
|
||||||
chain->gpio = gpio_num;
|
chain->gpio = gpio_num;
|
||||||
chain->len = len;
|
chain->len = len;
|
||||||
chain->data = data;
|
chain->data = data;
|
||||||
chain->tx_offset = 0;
|
chain->bitpos = 0;
|
||||||
|
|
||||||
|
// Send a reset if "reset" is not 0.
|
||||||
|
chain->sendreset = (reset != 0);
|
||||||
|
|
||||||
|
// Construct the RMT item for a reset.
|
||||||
|
tRmtItem.level0 = 0;
|
||||||
|
tRmtItem.level1 = 0;
|
||||||
|
// The reset duration must fit into one RMT item. This leaves 2*15 bit,
|
||||||
|
// which results in a maximum of 0xfffe .
|
||||||
|
if (reset>0xfffe)
|
||||||
|
{
|
||||||
|
reset = 0xfffe;
|
||||||
|
}
|
||||||
|
if (reset>0x7fff)
|
||||||
|
{
|
||||||
|
tRmtItem.duration0 = 0x7fff;
|
||||||
|
tRmtItem.duration1 = reset - 0x7fff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
half = reset >> 1U;
|
||||||
|
tRmtItem.duration0 = half;
|
||||||
|
tRmtItem.duration1 = reset - half;
|
||||||
|
}
|
||||||
|
chain->reset = tRmtItem;
|
||||||
|
|
||||||
|
// Limit the bit times to the available 15 bits.
|
||||||
|
// The values must not be 0.
|
||||||
|
if( t0h==0 )
|
||||||
|
{
|
||||||
|
t0h = 1;
|
||||||
|
}
|
||||||
|
else if( t0h>0x7fffU )
|
||||||
|
{
|
||||||
|
t0h = 0x7fffU;
|
||||||
|
}
|
||||||
|
if( t0l==0 )
|
||||||
|
{
|
||||||
|
t0l = 1;
|
||||||
|
}
|
||||||
|
else if( t0l>0x7fffU )
|
||||||
|
{
|
||||||
|
t0l = 0x7fffU;
|
||||||
|
}
|
||||||
|
if( t1h==0 )
|
||||||
|
{
|
||||||
|
t1h = 1;
|
||||||
|
}
|
||||||
|
else if( t1h>0x7fffU )
|
||||||
|
{
|
||||||
|
t1h = 0x7fffU;
|
||||||
|
}
|
||||||
|
if( t1l==0 )
|
||||||
|
{
|
||||||
|
t1l = 1;
|
||||||
|
}
|
||||||
|
else if( t1l>0x7fffU )
|
||||||
|
{
|
||||||
|
t1l = 0x7fffU;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the RMT item for a 0 bit.
|
||||||
|
tRmtItem.level0 = 1;
|
||||||
|
tRmtItem.duration0 = t0h;
|
||||||
|
tRmtItem.level1 = 0;
|
||||||
|
tRmtItem.duration1 = t0l;
|
||||||
|
chain->bits[0] = tRmtItem;
|
||||||
|
|
||||||
|
// Construct the RMT item for a 1 bit.
|
||||||
|
tRmtItem.level0 = 1;
|
||||||
|
tRmtItem.duration0 = t1h;
|
||||||
|
tRmtItem.level1 = 0;
|
||||||
|
tRmtItem.duration1 = t1l;
|
||||||
|
chain->bits[1] = tRmtItem;
|
||||||
|
|
||||||
#ifdef WS2812_DEBUG
|
#ifdef WS2812_DEBUG
|
||||||
ESP_LOGI("ws2812", "Setup done for gpio %d on RMT channel %d", gpio_num, channel);
|
ESP_LOGI("ws2812", "Setup done for gpio %d on RMT channel %d", gpio_num, channel);
|
||||||
|
@ -192,6 +253,7 @@ int platform_ws2812_send( void )
|
||||||
rmt_tx.tx_config.idle_level = 0;
|
rmt_tx.tx_config.idle_level = 0;
|
||||||
rmt_tx.tx_config.idle_output_en = true;
|
rmt_tx.tx_config.idle_output_en = true;
|
||||||
rmt_tx.rmt_mode = RMT_MODE_TX;
|
rmt_tx.rmt_mode = RMT_MODE_TX;
|
||||||
|
rmt_tx.flags = 0;
|
||||||
|
|
||||||
// configure selected RMT channels
|
// configure selected RMT channels
|
||||||
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) {
|
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) {
|
||||||
|
@ -202,26 +264,38 @@ int platform_ws2812_send( void )
|
||||||
res = PLATFORM_ERR;
|
res = PLATFORM_ERR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rmt_driver_install( channel, 0, PLATFORM_RMT_INTR_FLAGS ) != ESP_OK) {
|
if (rmt_driver_install( channel, 0, 0 /*PLATFORM_RMT_INTR_FLAGS*/ ) != ESP_OK) {
|
||||||
|
res = PLATFORM_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rmt_translator_init( channel, ws2812_convert) != ESP_OK) {
|
||||||
|
res = PLATFORM_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (rmt_translator_set_context( channel, &(ws2812_chains[channel])) != ESP_OK) {
|
||||||
res = PLATFORM_ERR;
|
res = PLATFORM_ERR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SOC_RMT_SUPPORT_TX_SYNCHRO
|
||||||
// hook-in our shared ISR
|
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) {
|
||||||
esp_intr_alloc( ETS_RMT_INTR_SOURCE, PLATFORM_RMT_INTR_FLAGS, ws2812_isr, NULL, &ws2812_intr_handle );
|
if (ws2812_chains[channel].valid) {
|
||||||
|
if (rmt_add_channel_to_group( channel ) != ESP_OK) {
|
||||||
|
res = PLATFORM_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// start selected channels one by one
|
// start selected channels one by one
|
||||||
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) {
|
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX && res == PLATFORM_OK; channel++) {
|
||||||
if (ws2812_chains[channel].valid) {
|
if (ws2812_chains[channel].valid) {
|
||||||
// we just feed a single block for generating the reset to the rmt driver
|
if (rmt_write_sample( channel,
|
||||||
// the actual payload data is handled by our private shared ISR
|
ws2812_chains[channel].data,
|
||||||
if (rmt_write_items( channel,
|
ws2812_chains[channel].len,
|
||||||
(rmt_item32_t *)ws2812_rmt_reset_block,
|
|
||||||
64,
|
|
||||||
false ) != ESP_OK) {
|
false ) != ESP_OK) {
|
||||||
res = PLATFORM_ERR;
|
res = PLATFORM_ERR;
|
||||||
break;
|
break;
|
||||||
|
@ -236,10 +310,16 @@ int platform_ws2812_send( void )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SOC_RMT_SUPPORT_TX_SYNCHRO
|
||||||
// un-hook our ISR
|
for (rmt_channel_t channel = 0; channel < RMT_CHANNEL_MAX; channel++) {
|
||||||
esp_intr_free( ws2812_intr_handle );
|
if (ws2812_chains[channel].valid) {
|
||||||
|
if (rmt_remove_channel_from_group( channel ) != ESP_OK) {
|
||||||
|
res = PLATFORM_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# WS2812 Module
|
# WS2812 Module
|
||||||
| Since | Origin / Contributor | Maintainer | Source |
|
| Since | Origin / Contributor | Maintainer | Source |
|
||||||
| :----- | :-------------------- | :---------- | :------ |
|
| :----- | :-------------------- | :---------- | :------ |
|
||||||
| 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel), [Thomas Soëte](https://github.com/Alkorin) | [Arnim Läuger](https://github.com/devsaurus) | [ws2812.c](../../components/modules/ws2812.c)|
|
| 2015-02-05 | [Till Klocke](https://github.com/dereulenspiegel), [Thomas Soëte](https://github.com/Alkorin), [Christoph Thelen](https://github.com/docbacardi) | [Arnim Läuger](https://github.com/devsaurus) | [ws2812.c](../../components/modules/ws2812.c)|
|
||||||
|
|
||||||
ws2812 is a library to handle ws2812-like led strips.
|
ws2812 is a library to handle ws2812-like led strips.
|
||||||
It works at least on WS2812, WS2812b, APA104, SK6812 (RGB or RGBW).
|
It works at least on WS2812, WS2812b, APA104, SK6812 (RGB or RGBW).
|
||||||
|
@ -22,6 +22,14 @@ Variable number of tables, each describing a single strip. Required elements are
|
||||||
- `pin` IO index, see [GPIO Overview](gpio.md#gpio-overview)
|
- `pin` IO index, see [GPIO Overview](gpio.md#gpio-overview)
|
||||||
- `data` payload to be sent to one or more WS2812 like leds through GPIO2
|
- `data` payload to be sent to one or more WS2812 like leds through GPIO2
|
||||||
|
|
||||||
|
Optional elements are:
|
||||||
|
|
||||||
|
- `reset` duration of the reset signal in multiples of 100ns. A duration of 0 generates no reset. The minimum possible value is 0. The maximum is 65534. The default value is 500 which generates a reset of 50us.
|
||||||
|
- `t0h` duration of the high period for a 0 code in multiples of 100ns. The minimum possible value is 1. The maximum is 32767. The default value is 4 which results in a high period of 400ns for each 0 code.
|
||||||
|
- `t0l` duration of the low period for a 0 code in multiples of 100ns. The minimum possible value is 1. The maximum is 32767. The default value is 7 which results in a low period of 700ns for each 0 code.
|
||||||
|
- `t1h` duration of the high period for a 1 code in multiples of 100ns. The minimum possible value is 1. The maximum is 32767. The default value is 8 which results in a high period of 800ns for each 1 code.
|
||||||
|
- `t1l` duration of the low period for a 1 code in multiples of 100ns. The minimum possible value is 1. The maximum is 32767. The default value is 6 which results in a low period of 600ns for each 1 code.
|
||||||
|
|
||||||
Payload type could be:
|
Payload type could be:
|
||||||
|
|
||||||
- `string` representing bytes to send
|
- `string` representing bytes to send
|
||||||
|
@ -44,6 +52,10 @@ ws2812.write({pin = 4, data = string.char(255, 0, 0, 255, 0, 0)},
|
||||||
{pin = 14, data = string.char(0, 255, 0, 0, 255, 0)}) -- turn the two first RGB leds to green on the first strip and red on the second strip
|
{pin = 14, data = string.char(0, 255, 0, 0, 255, 0)}) -- turn the two first RGB leds to green on the first strip and red on the second strip
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ws2812.write({pin = 8, reset = 800, t0h = 3, t0l = 9, t1h = 6, t1l = 6, data = string.char(1, 0, 0)}) -- turn the SK6812 RGB led on the ESP32-C3-DevKitM-1 to green
|
||||||
|
```
|
||||||
|
|
||||||
# Buffer module
|
# Buffer module
|
||||||
For more advanced animations, it is useful to keep a "framebuffer" of the strip,
|
For more advanced animations, it is useful to keep a "framebuffer" of the strip,
|
||||||
interact with it and flush it to the strip.
|
interact with it and flush it to the strip.
|
||||||
|
|
Loading…
Reference in New Issue