911 lines
20 KiB
C++
911 lines
20 KiB
C++
#ifndef WS2812B_H
|
|
#define WS2812B_H
|
|
|
|
|
|
#include "../../data/Color.h"
|
|
#include "../../Platforms.h"
|
|
#include "../../Debug.h"
|
|
|
|
|
|
|
|
|
|
#if ESP8266
|
|
|
|
#include "../../io/fastGPIO.h"
|
|
|
|
template <int numLEDs> class WS2812B {
|
|
|
|
static constexpr const char* NAME = "WS2812B";
|
|
|
|
// #define LED_SET_PIN_TO_OUTPUT GPIO5_OUTPUT_SET
|
|
// #define LED_SET_PIN_H GPIO5_H
|
|
// #define LED_SET_PIN_L GPIO5_L
|
|
|
|
//#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) )
|
|
|
|
/** color-value for each attached LED */
|
|
Color colors[numLEDs];
|
|
|
|
/** enable/disable each led */
|
|
bool enabled[numLEDs] = {true};
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
WS2812B() {
|
|
init();
|
|
}
|
|
|
|
void init() {
|
|
LED_SET_PIN_TO_OUTPUT;
|
|
debugMod1(NAME, "init with %d leds", numLEDs);
|
|
}
|
|
|
|
/** set the color for the given LED */
|
|
void setColor(const uint8_t idx, const Color rgb) {
|
|
colors[idx] = rgb;
|
|
}
|
|
|
|
/** set the color for all LEDs */
|
|
void setColor(const Color rgb) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
colors[idx] = rgb;
|
|
}
|
|
}
|
|
|
|
/** enable/disable the given LED */
|
|
void setEnabled(const uint8_t idx, const bool en) {
|
|
enabled[idx] = en;
|
|
}
|
|
|
|
/** enable/disable all LEDs */
|
|
void setEnabled(const bool en) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
enabled[idx] = en;
|
|
}
|
|
}
|
|
|
|
/** is the given LED enabled? */
|
|
bool isEnabled(const uint8_t idx) const {
|
|
return enabled[idx];
|
|
}
|
|
|
|
Color& getColor(const uint8_t idx) {
|
|
return colors[idx];
|
|
}
|
|
|
|
/** flush configured changes */
|
|
void flush() {
|
|
|
|
LED_SET_PIN_TO_OUTPUT;
|
|
|
|
ets_intr_lock();
|
|
|
|
// process each LED
|
|
for (int i = 0; i < numLEDs; ++i) {
|
|
|
|
// send each LEDs 24-bit GRB data
|
|
if (enabled[i]) {
|
|
const Color rgb = colors[i];
|
|
sendByte(rgb.g);
|
|
sendByte(rgb.r);
|
|
sendByte(rgb.b);
|
|
} else {
|
|
sendByte(0);
|
|
sendByte(0);
|
|
sendByte(0);
|
|
}
|
|
|
|
}
|
|
|
|
ets_intr_unlock();
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
/** flush configured changes, including global brightness */
|
|
void flushBrightness(const uint8_t brightness) {
|
|
|
|
LED_SET_PIN_TO_OUTPUT;
|
|
|
|
ets_intr_lock();
|
|
|
|
// process each LED
|
|
for (int i = 0; i < numLEDs; ++i) {
|
|
|
|
// send each LEDs 24-bit GRB data
|
|
if (enabled[i]) {
|
|
const Color rgb = colors[i].brightness(brightness);
|
|
sendByte(rgb.g);
|
|
sendByte(rgb.r);
|
|
sendByte(rgb.b);
|
|
} else {
|
|
sendByte(0);
|
|
sendByte(0);
|
|
sendByte(0);
|
|
}
|
|
|
|
}
|
|
|
|
ets_intr_unlock();
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
inline void sendByte(uint8_t b) {
|
|
// for (uint8_t i = 0; i < 8; ++i) {
|
|
// if (b & 0b10000000) {
|
|
// send1();
|
|
// } else {
|
|
// send0();
|
|
// }
|
|
// b <<= 1;
|
|
// }
|
|
if (b & 0b10000000) {send1();} else {send0();}
|
|
if (b & 0b01000000) {send1();} else {send0();}
|
|
if (b & 0b00100000) {send1();} else {send0();}
|
|
if (b & 0b00010000) {send1();} else {send0();}
|
|
if (b & 0b00001000) {send1();} else {send0();}
|
|
if (b & 0b00000100) {send1();} else {send0();}
|
|
if (b & 0b00000010) {send1();} else {send0();}
|
|
if (b & 0b00000001) {send1();} else {send0();}
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void send1() { // 800ns high, 450ns low
|
|
LED_SET_PIN_H;
|
|
delay800();
|
|
LED_SET_PIN_L;
|
|
delay100();
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void send0() { // 400ns high, 850ns low
|
|
LED_SET_PIN_H;
|
|
delay100();
|
|
LED_SET_PIN_L;
|
|
delay800();
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void reset() {
|
|
LED_SET_PIN_L;
|
|
os_delay_us(50);
|
|
//delayMicroseconds(50);
|
|
}
|
|
|
|
|
|
//#pragma GCC optimize 0
|
|
|
|
__attribute__((always_inline)) inline void delay50() {
|
|
asm volatile("nop");
|
|
asm volatile("nop");
|
|
asm volatile("nop");
|
|
asm volatile("nop");
|
|
}
|
|
|
|
|
|
// 100 ns delay. @80 MHz one nop = 12,5ns
|
|
__attribute__((always_inline)) inline void delay100() {
|
|
delay50();
|
|
delay50();
|
|
}
|
|
|
|
/*
|
|
__attribute__((always_inline)) inline void delay200() {
|
|
delay100();
|
|
delay100();
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void delay400() {
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void delay500() {
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
}
|
|
|
|
__attribute__((always_inline)) inline void delay600() {
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
}
|
|
*/
|
|
|
|
__attribute__((always_inline)) inline void delay800() {
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
delay100();
|
|
}
|
|
|
|
|
|
// #pragma GCC reset_options
|
|
|
|
|
|
|
|
// inline void delayNS(const uint16_t ns) {
|
|
// const uint16_t ticks = ns / NS_PER_TICK / 2;
|
|
// for (uint16_t i = 0; i < ticks; ++i) {asm("nop");}
|
|
// }
|
|
|
|
};
|
|
|
|
#elif false// ESP32aaa
|
|
|
|
//#include <driver/gpio.h>
|
|
#include <rom/ets_sys.h>
|
|
#include <xtensa/core-macros.h>
|
|
#include <driver/rmt.h>
|
|
#include <soc/rmt_struct.h>
|
|
|
|
template <int numLEDs, gpio_num_t outPin> class WS2812B {
|
|
|
|
static constexpr const char* TAG = "WS2812";
|
|
static constexpr rmt_channel_t chan = RMT_CHANNEL_0;
|
|
|
|
//#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
|
|
//#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)
|
|
//#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS()
|
|
//#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
|
|
|
|
static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer!
|
|
//static constexpr int numItems = 256;//(numLEDs*3)*8;
|
|
//static constexpr int numItems = (numLEDs*3)*8;
|
|
rmt_item32_t items[numBits+1]; // + one entry has two bits + 0-terminator (just like within strings)
|
|
|
|
/** enable/disable each led */
|
|
bool enabled[numLEDs] = {true};
|
|
|
|
/** color-value for each attached LED */
|
|
Color colors[numLEDs];
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
WS2812B() {
|
|
init();
|
|
}
|
|
|
|
void init() {
|
|
|
|
ESP_LOGI(TAG, "init()");
|
|
|
|
rmt_config_t cfg;
|
|
cfg.rmt_mode = RMT_MODE_TX;
|
|
cfg.channel = chan;
|
|
cfg.gpio_num = outPin;
|
|
cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;//
|
|
cfg.clk_div = 8;
|
|
cfg.tx_config.loop_en = false;
|
|
cfg.tx_config.carrier_en = false;
|
|
cfg.tx_config.idle_output_en = true;
|
|
cfg.tx_config.idle_level = (rmt_idle_level_t)0;
|
|
cfg.tx_config.carrier_freq_hz = 10000;
|
|
cfg.tx_config.carrier_level = (rmt_carrier_level_t)1;
|
|
cfg.tx_config.carrier_duty_percent = 50;
|
|
|
|
ESP_ERROR_CHECK(rmt_config(&cfg));
|
|
ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0));
|
|
|
|
// setup constant values once
|
|
for (int idx = 0; idx < numBits; ++idx) {
|
|
items[idx].duration0 = 0;
|
|
items[idx].level0 = 1;
|
|
items[idx].duration1 = 0;
|
|
items[idx].level1 = 0;
|
|
}
|
|
items[numBits].val = 0; // 0 terminator
|
|
|
|
ESP_LOGI(TAG, "init OK!");
|
|
|
|
}
|
|
|
|
/** set the color for the given LED */
|
|
void setColor(const uint8_t idx, const Color rgb) {
|
|
colors[idx] = rgb;
|
|
}
|
|
|
|
/** set the color for all LEDs */
|
|
void setColor(const Color rgb) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
colors[idx] = rgb;
|
|
}
|
|
}
|
|
|
|
/** enable/disable the given LED */
|
|
void setEnabled(const uint8_t idx, const bool en) {
|
|
enabled[idx] = en;
|
|
}
|
|
|
|
/** enable/disable all LEDs */
|
|
void setEnabled(const bool en) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
enabled[idx] = en;
|
|
}
|
|
}
|
|
|
|
/** is the given LED enabled? */
|
|
bool isEnabled(const uint8_t idx) const {
|
|
return enabled[idx];
|
|
}
|
|
|
|
Color& getColor(const uint8_t idx) {
|
|
return colors[idx];
|
|
}
|
|
|
|
int nextLED;
|
|
|
|
/** flush configured changes */
|
|
IRAM_ATTR void flush() {
|
|
|
|
nextLED = 0;
|
|
ESP_LOGI(TAG, "sending %d LEDs", numLEDs);
|
|
|
|
// important! otherwise often interrupted within
|
|
portDISABLE_INTERRUPTS();
|
|
|
|
waitForTX();
|
|
|
|
const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator
|
|
|
|
// add null terminator
|
|
items[toWrite-1].val = 0;
|
|
|
|
// process each LED
|
|
for (int i = 0; i < numLEDs; i+=1) {
|
|
|
|
int idx = 0;
|
|
if (enabled[i]) {
|
|
emplaceByte(colors[i].g, idx);
|
|
emplaceByte(colors[i].r, idx);
|
|
emplaceByte(colors[i].b, idx);
|
|
} else {
|
|
emplaceByte(0, idx);
|
|
emplaceByte(0, idx);
|
|
emplaceByte(0, idx);
|
|
}
|
|
|
|
// wait for sending to complete using the RMT Unit's status
|
|
waitForTX();
|
|
|
|
ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0));
|
|
ESP_ERROR_CHECK(rmt_tx_start(this->chan, true));
|
|
|
|
// reset for next round
|
|
//idx = 0;
|
|
|
|
}
|
|
|
|
//waitForTX();
|
|
|
|
portENABLE_INTERRUPTS();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
IRAM_ATTR void waitForTX() {
|
|
|
|
uint32_t status; // status while sending: 16797696 ??
|
|
for (int i = 0; i < 2000; ++i) {
|
|
rmt_get_status(this->chan, &status);
|
|
if (status == 0) {break;}
|
|
//if (status != 16797696) {break;}
|
|
//if ( (status & (1<<24)) == 0 ) {break;} // somewhat OK
|
|
}
|
|
|
|
|
|
|
|
// for (int i = 0; i < 1000; ++i) {
|
|
// asm volatile("nop");
|
|
// }
|
|
|
|
}
|
|
|
|
/** send one whole byte */
|
|
inline void emplaceByte(const uint8_t b, int& idx) {
|
|
if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);}
|
|
}
|
|
|
|
/** emplace a 0 (short 1 pulse, long 0 pulse) */
|
|
inline void emplace0(int& idx) {
|
|
items[idx].duration0 = 2;//1;
|
|
//items[idx].level0 = 1;
|
|
items[idx].duration1 = 8;//7;
|
|
//items[idx].level1 = 0;
|
|
++idx;
|
|
}
|
|
|
|
/** emplace a 1 (long 1 pulse, short 0 pulse) */
|
|
inline void emplace1(int& idx) {
|
|
items[idx].duration0 = 7;//6;
|
|
//items[idx].level0 = 1;
|
|
items[idx].duration1 = 3;//2;
|
|
//items[idx].level1 = 0;
|
|
++idx;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
#elif false //ESP32
|
|
|
|
#include <driver/gpio.h>
|
|
#include <rom/ets_sys.h>
|
|
#include <xtensa/core-macros.h>
|
|
#include "driver/timer.h"
|
|
|
|
static constexpr timer_group_t grp = TIMER_GROUP_0;
|
|
static constexpr timer_idx_t idx = TIMER_0;
|
|
|
|
template <int numLEDs, gpio_num_t outPin> class WS2812B;
|
|
|
|
static IRAM_ATTR void myISR(void* arg) {
|
|
printf("interrupt\n");
|
|
//WS2812B* leds = (WS2812B*) arg;
|
|
//timer_pause(grp, idx);
|
|
//leds->flushOLD();
|
|
}
|
|
|
|
template <int numLEDs, gpio_num_t outPin> class WS2812B {
|
|
|
|
static constexpr const char* TAG = "WS2812";
|
|
|
|
/** enable/disable each led */
|
|
bool enabled[numLEDs] = {true};
|
|
|
|
/** color-value for each attached LED */
|
|
Color colors[numLEDs];
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
WS2812B() {
|
|
init();
|
|
}
|
|
|
|
void init() {
|
|
|
|
ESP_LOGI(TAG, "init()");
|
|
|
|
gpio_set_direction(outPin, GPIO_MODE_OUTPUT);
|
|
|
|
}
|
|
|
|
/** set the color for the given LED */
|
|
void setColor(const uint8_t idx, const Color rgb) {
|
|
colors[idx] = rgb;
|
|
}
|
|
|
|
/** set the color for all LEDs */
|
|
void setColor(const Color rgb) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
colors[idx] = rgb;
|
|
}
|
|
}
|
|
|
|
/** enable/disable the given LED */
|
|
void setEnabled(const uint8_t idx, const bool en) {
|
|
enabled[idx] = en;
|
|
}
|
|
|
|
/** enable/disable all LEDs */
|
|
void setEnabled(const bool en) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
enabled[idx] = en;
|
|
}
|
|
}
|
|
|
|
/** is the given LED enabled? */
|
|
bool isEnabled(const uint8_t idx) const {
|
|
return enabled[idx];
|
|
}
|
|
|
|
Color& getColor(const uint8_t idx) {
|
|
return colors[idx];
|
|
}
|
|
|
|
|
|
timer_config_t config;
|
|
|
|
/** flush configured changes */
|
|
IRAM_ATTR void flush() {
|
|
|
|
printf("flush()\n");
|
|
|
|
config.alarm_en = 1;
|
|
config.auto_reload = 1;
|
|
config.counter_dir = TIMER_COUNT_UP;
|
|
config.divider = 2;
|
|
config.intr_type = TIMER_INTR_LEVEL;
|
|
config.counter_en = TIMER_PAUSE;
|
|
|
|
|
|
|
|
ESP_ERROR_CHECK(timer_init(grp, idx, &config));
|
|
|
|
ESP_ERROR_CHECK(timer_set_counter_value(grp, idx, 0));
|
|
ESP_ERROR_CHECK(timer_set_alarm_value(grp, idx, 2));
|
|
ESP_ERROR_CHECK(timer_enable_intr(grp, idx));
|
|
ESP_ERROR_CHECK(timer_isr_register(grp, idx, myISR, (void*)this, ESP_INTR_FLAG_IRAM, NULL));
|
|
|
|
printf("start()\n");
|
|
ESP_ERROR_CHECK(timer_start(grp, idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
IRAM_ATTR void flushOLD() {
|
|
|
|
ESP_LOGI(TAG, "sending %d LEDs", numLEDs);
|
|
|
|
// important! otherwise often interrupted within
|
|
portDISABLE_INTERRUPTS();
|
|
|
|
//portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
|
|
//portENTER_CRITICAL(&myMutex);
|
|
|
|
|
|
// process each LED
|
|
for (int i = 0; i < numLEDs; ++i) {
|
|
|
|
portDISABLE_INTERRUPTS();
|
|
const volatile Color c = colors[i];
|
|
|
|
if (enabled[i]) {
|
|
sendByte(c.g);
|
|
sendByte(c.r);
|
|
sendByte(c.b);
|
|
} else {
|
|
sendByte(0);
|
|
sendByte(0);
|
|
sendByte(0);
|
|
}
|
|
|
|
}
|
|
|
|
//portEXIT_CRITICAL(&myMutex);
|
|
|
|
portENABLE_INTERRUPTS();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
/** send one whole byte */
|
|
inline void sendByte(const uint8_t b) {
|
|
if (b & 0b10000000) {send1();} else {send0();}
|
|
if (b & 0b01000000) {send1();} else {send0();}
|
|
if (b & 0b00100000) {send1();} else {send0();}
|
|
if (b & 0b00010000) {send1();} else {send0();}
|
|
if (b & 0b00001000) {send1();} else {send0();}
|
|
if (b & 0b00000100) {send1();} else {send0();}
|
|
if (b & 0b00000010) {send1();} else {send0();}
|
|
if (b & 0b00000001) {send1();} else {send0();}
|
|
}
|
|
|
|
/** send a 0 (short 1 pulse, long 0 pulse) */
|
|
inline void send0() {
|
|
gpio_set_level(outPin, 1);
|
|
delayShort();
|
|
gpio_set_level(outPin, 0);
|
|
delayLong();
|
|
}
|
|
|
|
/** send a 1 (long 1 pulse, short 0 pulse) */
|
|
inline void send1() {
|
|
gpio_set_level(outPin, 1);
|
|
delayLong();
|
|
gpio_set_level(outPin, 0);
|
|
delayShort();
|
|
}
|
|
|
|
inline void delayShort() {
|
|
volatile int i;
|
|
i = 0; i = 1; i = 2; i = 3;
|
|
(void) i;
|
|
}
|
|
|
|
inline void delayLong() {
|
|
volatile int j;
|
|
j = 0; j = 1; j = 2; j = 3;
|
|
j = 4; j = 5; j = 6; j = 7;
|
|
j = 8; j = 9; j = 1; j = 2;
|
|
(void) j;
|
|
// for (size_t i = 0; i < 16; ++i) {
|
|
// asm volatile("nop");
|
|
// }
|
|
}
|
|
|
|
|
|
};
|
|
|
|
#elif false //othertest
|
|
|
|
//#include <driver/gpio.h>
|
|
#include <rom/ets_sys.h>
|
|
#include <xtensa/core-macros.h>
|
|
#include <driver/rmt.h>
|
|
#include <soc/rmt_struct.h>
|
|
|
|
template <int numLEDs, gpio_num_t outPin> class WS2812B {
|
|
|
|
static constexpr const char* TAG = "WS2812";
|
|
static constexpr rmt_channel_t chan = RMT_CHANNEL_0;
|
|
|
|
//#define portDISABLE_INTERRUPTS() do { XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL); portbenchmarkINTERRUPT_DISABLE(); } while (0)
|
|
//#define portENABLE_INTERRUPTS() do { portbenchmarkINTERRUPT_RESTORE(0); XTOS_SET_INTLEVEL(0); } while (0)
|
|
//#define ENABLE_INTERRUPTS() portENABLE_INTERRUPTS()
|
|
//#define DISABLE_INTERRUPTS() portDISABLE_INTERRUPTS()
|
|
|
|
static constexpr int numBits = (3*8) * 2; // (r,g,b) each 8 bit NOTE! must be < 64 to fit into the buffer!
|
|
//static constexpr int numItems = 256;//(numLEDs*3)*8;
|
|
//static constexpr int numItems = (numLEDs*3)*8;
|
|
rmt_item32_t items[numBits+1]; // + one entry has two bits + 0-terminator (just like within strings)
|
|
|
|
/** enable/disable each led */
|
|
bool enabled[numLEDs] = {true};
|
|
|
|
/** color-value for each attached LED */
|
|
Color colors[numLEDs];
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
WS2812B() {
|
|
init();
|
|
}
|
|
|
|
void init() {
|
|
|
|
ESP_LOGI(TAG, "init()");
|
|
|
|
rmt_config_t cfg;
|
|
cfg.rmt_mode = RMT_MODE_TX;
|
|
cfg.channel = chan;
|
|
cfg.gpio_num = outPin;
|
|
cfg.mem_block_num = 1;//chan+1;//8-chan;//chan+1;//8 - chan; //chan+1;//
|
|
cfg.clk_div = 8;
|
|
cfg.tx_config.loop_en = false;
|
|
cfg.tx_config.carrier_en = false;
|
|
cfg.tx_config.idle_output_en = true;
|
|
cfg.tx_config.idle_level = (rmt_idle_level_t)0;
|
|
cfg.tx_config.carrier_freq_hz = 10000;
|
|
cfg.tx_config.carrier_level = (rmt_carrier_level_t)1;
|
|
cfg.tx_config.carrier_duty_percent = 50;
|
|
|
|
ESP_ERROR_CHECK(rmt_config(&cfg));
|
|
|
|
// disable when using custom ISR
|
|
//ESP_ERROR_CHECK(rmt_driver_install(chan, 0, 0));
|
|
|
|
// setup constant values once
|
|
for (int idx = 0; idx < numBits; ++idx) {
|
|
items[idx].duration0 = 0;
|
|
items[idx].level0 = 1;
|
|
items[idx].duration1 = 0;
|
|
items[idx].level1 = 0;
|
|
}
|
|
items[numBits].val = 0; // 0 terminator
|
|
|
|
ESP_LOGI(TAG, "init OK!");
|
|
|
|
}
|
|
|
|
/** set the color for the given LED */
|
|
void setColor(const uint8_t idx, const Color rgb) {
|
|
colors[idx] = rgb;
|
|
}
|
|
|
|
/** set the color for all LEDs */
|
|
void setColor(const Color rgb) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
colors[idx] = rgb;
|
|
}
|
|
}
|
|
|
|
/** enable/disable the given LED */
|
|
void setEnabled(const uint8_t idx, const bool en) {
|
|
enabled[idx] = en;
|
|
}
|
|
|
|
/** enable/disable all LEDs */
|
|
void setEnabled(const bool en) {
|
|
for (int idx = 0; idx < numLEDs; ++idx) {
|
|
enabled[idx] = en;
|
|
}
|
|
}
|
|
|
|
/** is the given LED enabled? */
|
|
bool isEnabled(const uint8_t idx) const {
|
|
return enabled[idx];
|
|
}
|
|
|
|
Color& getColor(const uint8_t idx) {
|
|
return colors[idx];
|
|
}
|
|
|
|
int nextLED;
|
|
|
|
/** flush configured changes */
|
|
/*
|
|
void flush() {
|
|
|
|
nextLED = 0;
|
|
ESP_LOGI(TAG, "sending %d LEDs", numLEDs);
|
|
|
|
// important! otherwise often interrupted within
|
|
portDISABLE_INTERRUPTS();
|
|
|
|
for (int i = 0; i < 1024; ++i) {
|
|
asm volatile("nop");
|
|
}
|
|
|
|
//waitForTX();
|
|
|
|
const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator
|
|
|
|
// add null terminator
|
|
items[toWrite-1].val = 0;
|
|
|
|
// process each LED
|
|
for (int i = 0; i < numLEDs; i+=2) {
|
|
|
|
enqueueNext();
|
|
|
|
// add null terminator
|
|
//items[idx].val = 0;
|
|
|
|
// wait for sending to complete using the RMT Unit's status
|
|
waitForTX();
|
|
|
|
ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0));
|
|
ESP_ERROR_CHECK(rmt_tx_start(this->chan, true));
|
|
|
|
// reset for next round
|
|
//idx = 0;
|
|
|
|
}
|
|
|
|
//waitForTX();
|
|
|
|
portENABLE_INTERRUPTS();
|
|
|
|
}
|
|
*/
|
|
|
|
static constexpr int toWrite = 8+8+8+1;
|
|
rmt_isr_handle_t isrHandle;
|
|
|
|
void flush() {
|
|
|
|
nextLED = 0;
|
|
ESP_LOGI(TAG, "sending %d LEDs", numLEDs);
|
|
|
|
enqueueNext();
|
|
|
|
ESP_ERROR_CHECK(rmt_isr_register(onISR, this, ESP_INTR_FLAG_LEVEL1, &isrHandle));
|
|
ESP_ERROR_CHECK(rmt_set_tx_thr_intr_en(this->chan, true, toWrite/2));
|
|
//ESP_ERROR_CHECK(rmt_set_tx_intr_en(this->chan, true));
|
|
|
|
printf("_start\n");
|
|
ESP_ERROR_CHECK(rmt_tx_start(this->chan, true));
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
static IRAM_ATTR void onISR(void* arg) {
|
|
printf(".\n");
|
|
WS2812B* led = (WS2812B*) arg;
|
|
if (led->nextLED < numLEDs) {
|
|
led->enqueueNext();
|
|
} else {
|
|
rmt_tx_stop(led->chan);
|
|
}
|
|
}
|
|
|
|
void IRAM_ATTR enqueueNext() {
|
|
|
|
printf("e\n");
|
|
int idx = 0;
|
|
|
|
if (enabled[nextLED]) {
|
|
emplaceByte(colors[nextLED].g, idx);
|
|
emplaceByte(colors[nextLED].r, idx);
|
|
emplaceByte(colors[nextLED].b, idx);
|
|
} else {
|
|
emplaceByte(0, idx);
|
|
emplaceByte(0, idx);
|
|
emplaceByte(0, idx);
|
|
}
|
|
|
|
ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0));
|
|
|
|
++nextLED;
|
|
|
|
}
|
|
|
|
void waitForTX() {
|
|
|
|
uint32_t status; // status while sending: 16797696 ??
|
|
for (int i = 0; i < 2000; ++i) {
|
|
rmt_get_status(this->chan, &status);
|
|
if (status == 0) {break;}
|
|
//if (status != 16797696) {break;}
|
|
//if ( (status & (1<<24)) == 0 ) {break;} // somewhat OK
|
|
}
|
|
|
|
|
|
|
|
// for (int i = 0; i < 1000; ++i) {
|
|
// asm volatile("nop");
|
|
// }
|
|
|
|
}
|
|
|
|
/** send one whole byte */
|
|
inline void emplaceByte(const uint8_t b, int& idx) {
|
|
if (b & 0b10000000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b01000000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00100000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00010000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00001000) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000100) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000010) {emplace1(idx);} else {emplace0(idx);}
|
|
if (b & 0b00000001) {emplace1(idx);} else {emplace0(idx);}
|
|
}
|
|
|
|
/** emplace a 0 (short 1 pulse, long 0 pulse) */
|
|
inline void emplace0(int& idx) {
|
|
items[idx].duration0 = 2;//1;
|
|
//items[idx].level0 = 1;
|
|
items[idx].duration1 = 8;//7;
|
|
//items[idx].level1 = 0;
|
|
++idx;
|
|
}
|
|
|
|
/** emplace a 1 (long 1 pulse, short 0 pulse) */
|
|
inline void emplace1(int& idx) {
|
|
items[idx].duration0 = 7;//6;
|
|
//items[idx].level0 = 1;
|
|
items[idx].duration1 = 3;//2;
|
|
//items[idx].level1 = 0;
|
|
++idx;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
#endif // WS2812B_H
|