diff --git a/data/Color.h b/data/Color.h new file mode 100644 index 0000000..bb7878b --- /dev/null +++ b/data/Color.h @@ -0,0 +1,99 @@ +struct Color { + + uint8_t r; + uint8_t g; + uint8_t b; + + /** no-init */ + Color() { + ; + } + +private: + + /** Hidden ctor. RGB */ + Color(const uint8_t r, const uint8_t g, const uint8_t b) : r(r), g(g), b(b) { + ; + } + +public: + + /** get X-percent [0:100] of this color = darker/brighter */ + Color inline getPercent(const int percent) const { + return Color(r*percent/100, g*percent/100, b*percent/100); + } + + /** mix the two given colors */ + static Color mix(const Color c1, const Color c2, int percentC1) { + return Color( + ((c1.r * percentC1) + (c2.r * (100-percentC1))) / 100, + ((c1.g * percentC1) + (c2.g * (100-percentC1))) / 100, + ((c1.b * percentC1) + (c2.b * (100-percentC1))) / 100 + ); + } + + static inline Color fromRGB(const uint8_t r, const uint8_t g, const uint8_t b) { + return Color(r,g,b); + } + + static inline Color fromHSV(const uint8_t h, const uint8_t s, const uint8_t v) { + Color c; c.setHSV(h,s,v); return c; + } + + void setHSV(const uint8_t h, const uint8_t s, const uint8_t v) { + + uint8_t region, remainder, p, q, t; + + region = h / 43; + remainder = (h - (region * 43)) * 6; + + p = (v * (255 - s)) >> 8; + q = (v * (255 - ((s * remainder) >> 8))) >> 8; + t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; + + switch (region) { + case 0: + r = v; g = t; b = p; + break; + case 1: + r = q; g = v; b = p; + break; + case 2: + r = p; g = v; b = t; + break; + case 3: + r = p; g = q; b = v; + break; + case 4: + r = t; g = p; b = v; + break; + default: + r = v; g = p; b = q; + break; + } + + } + + void setRGB(const uint8_t r, const uint8_t g, const uint8_t b) { + this->r = r; + this->g = g; + this->b = b; + } + + void setOff() { + this->r = 0; + this->g = 0; + this->b = 0; + } + + + /** add two colors */ + Color operator + (const Color o) const { + return Color(r+o.r, g+o.g, b+o.g); + } + + Color operator * (const float f) const { + return Color(r*f, g*f, b*f); + } + +}; diff --git a/ext/led/WS2812B.h b/ext/led/WS2812B.h new file mode 100644 index 0000000..dafc0c2 --- /dev/null +++ b/ext/led/WS2812B.h @@ -0,0 +1,223 @@ +#ifndef WS2812B_H +#define WS2812B_H + +#include +#include + +//#include +#include +#include + +#include "../../data/Color.h" + +template class WS2812B { + + static constexpr const char* TAG = "WS2812"; + static constexpr rmt_channel_t chan = RMT_CHANNEL_0; + static constexpr gpio_num_t outPin = GPIO_NUM_27; + +//#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]; // + a 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; + + // allocate one block for sending (64 bits) + //ESP_ERROR_CHECK(rmt_set_mem_block_num(chan, 1)); + + 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 = 1; + items[idx].level0 = 1; + items[idx].duration1 = 1; + 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]; + } + + + + + + + /** flush configured changes */ + void flush() { + + ESP_LOGI(TAG, "sending %d LEDs", numLEDs); + + int idx = 0; + + // process each LED + for (int i = 0; i < numLEDs; ++i) { + + if (enabled[i]) { + const Color c = colors[i]; + emplaceByte(c.g, idx); + emplaceByte(c.r, idx); + emplaceByte(c.b, idx); + } else { + emplaceByte(0, idx); + emplaceByte(0, idx); + emplaceByte(0, idx); + } + + if (idx >= numBits) { + + // wait for pervious transmission to finish + //rmt_wait_tx_done(this->chan, (TickType_t)1); + + items[idx].val = 0; // 0 terminator + ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, numBits, 0)); + ESP_ERROR_CHECK(rmt_tx_start(this->chan, true)); + + rmt_wait_tx_done(this->chan, (TickType_t)1); + + // send within the background + //const bool blockingWait = true; + //ESP_ERROR_CHECK(rmt_write_items(this->chan, items, numItems, blockingWait)); + //rmt_wait_tx_done(this->chan, (TickType_t)1); + idx = 0; + + } + + } + + /* + // bit counter + int idx = 0; + + // process each LED + for (int i = 0; i < numLEDs; ++i) { + + if (enabled[i]) { + const Color c = colors[i]; + emplaceByte(c.g, idx); + emplaceByte(c.r, idx); + emplaceByte(c.b, idx); + } else { + emplaceByte(0, idx); + emplaceByte(0, idx); + emplaceByte(0, idx); + } + + } + + // blocking send all bits via RMT + const bool blockingWait = true; + ESP_ERROR_CHECK(rmt_write_items(this->chan, items, numItems, blockingWait)); + + ESP_LOGI(TAG, "%d bits sent", idx); + */ + + } + +private: + + /** 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; + //items[idx].level0 = 1; + items[idx].duration1 = 8; + //items[idx].level1 = 0; + ++idx; + } + + /** emplace a 1 (long 1 pulse, short 0 pulse) */ + inline void emplace1(int& idx) { + items[idx].duration0 = 7; + //items[idx].level0 = 1; + items[idx].duration1 = 3; + //items[idx].level1 = 0; + ++idx; + } + + +}; + +#endif // WS2812B_H diff --git a/ext/led/WS2812B_OLD.h b/ext/led/WS2812B_OLD.h new file mode 100644 index 0000000..331151e --- /dev/null +++ b/ext/led/WS2812B_OLD.h @@ -0,0 +1,195 @@ +#ifndef WS2812B_H +#define WS2812B_H + +#include "driver/gpio.h" +#include "rom/ets_sys.h" +#include "xtensa/core-macros.h" + +#include "../../data/Color.h" + +template class WS2812B { + + #define WS2812B_PIN GPIO_NUM_27 + #define LED_SET_PIN_TO_OUTPUT gpio_set_direction(WS2812B_PIN, GPIO_MODE_OUTPUT); + #define LED_SET_PIN_TO_INPUT gpio_set_direction(WS2812B_PIN, GPIO_MODE_INPUT); + //#define LED_SET_PIN_H gpio_set_level(WS2812B_PIN, 1) + //#define LED_SET_PIN_L gpio_set_level(WS2812B_PIN, 0) + //#define ENABLE_INTERRUPTS XTOS_RESTORE_INTLEVEL(ilevel); + //#define DISABLE_INTERRUPTS uint32_t volatile register ilevel = XTOS_DISABLE_ALL_INTERRUPTS + + + #define LED_SET_PIN_H GPIO.out_w1ts |= (1 << WS2812B_PIN) + #define LED_SET_PIN_L GPIO.out_w1tc |= (1 << WS2812B_PIN) + + #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() + #define SLEEP_US(us) ets_delay_us(us) + + //#define NS_PER_TICK ( (1000ul*1000ul*1000ul) / (80ul*1000ul*1000ul) ) + + +// Color* colors; + + /** enable/disable each led */ + bool enabled[numLEDs] = {true}; + + /** color-value for each attached LED */ + Color colors[numLEDs]; + + public: + + /** ctor */ + WS2812B() { + init(); + } + + void init() { + LED_SET_PIN_TO_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]; + } + + /** flush configured changes */ + IRAM_ATTR void flush() { + + SLEEP_US(50); + + DISABLE_INTERRUPTS(); + + LED_SET_PIN_TO_OUTPUT; + + // process each LED + for (int i = 0; i < 8; ++i) { + + //if (i == 0) {LED_SET_PIN_TO_INPUT;} + //if (i > 0) {LED_SET_PIN_TO_OUTPUT;} + + const Color rgb = colors[i]; + + // send each LEDs 24-bit GRB data + //if (enabled[i]) { + // sendByte(rgb.g); + // sendByte(rgb.r); + // sendByte(rgb.b); + //} else { + sendByte(1); + sendByte(2); + sendByte(3); + //} + + } + + ENABLE_INTERRUPTS(); + + reset(); + + } + + private: + + IRAM_ATTR __attribute__((always_inline)) 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();} + } + + IRAM_ATTR __attribute__((always_inline)) inline void send1() { // 800ns high, 450ns low + LED_SET_PIN_H; + delay800(); + LED_SET_PIN_L; + delay100(); + } + + IRAM_ATTR __attribute__((always_inline)) inline void send0() { // 400ns high, 850ns low + LED_SET_PIN_H; + delay100(); + //delay100(); + LED_SET_PIN_L; + delay800(); + } + + IRAM_ATTR __attribute__((always_inline)) inline void reset() { + LED_SET_PIN_L; + SLEEP_US(50); + } + + + //#pragma GCC optimize 0 + + + // one NOP is: + // 80 MHz 12.5 ns + // 240 MHz 4.166 ns + IRAM_ATTR __attribute__((always_inline)) inline void delay100() { + + // 24 * 4.166ns = 100ns + asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); + asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); + asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); + + asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); + +// for (register int i = 0; i < 24/6; ++i) { +// asm volatile("nop"); +// } + + } + + IRAM_ATTR __attribute__((always_inline)) inline void delay800() { + + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + delay100(); + +// for (register int i = 0; i < 24*8/6-8; ++i) { +// asm volatile("nop"); +// } + + } + +}; + +#endif // WS2812B_H