#ifndef WS2812B_H #define WS2812B_H #include "../../data/Color.h" #include "../../Platforms.h" #include "../../Debug.h" #include "../../io/GPIO.h" #include #if IS_ESP8266 // https://github.com/FastLED/FastLED/tree/master/src/platforms/esp/8266 #include "../../io/GPIO.h" template class WS2812B { static constexpr const char* NAME = "WS2812B"; #define LED_SET_PIN_TO_OUTPUT MyGPIO::setOutput(5) //GPIO5_OUTPUT_SET #define LED_SET_PIN_H MyGPIO::set(5) //GPIO5_H #define LED_SET_PIN_L MyGPIO::clear(5) //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; Log::addInfo(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 */ IRAM_ATTR void flush() { //LED_SET_PIN_TO_OUTPUT; //ets_intr_lock(); //taskENTER_CRITICAL(); //taskDISABLE_INTERRUPTS(); //const uint32_t saved = XTOS_DISABLE_ALL_INTERRUPTS; vPortETSIntrLock(); // 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(); //taskEXIT_CRITICAL(); //XTOS_RESTORE_INTLEVEL(saved); vPortETSIntrUnlock(); //taskENABLE_INTERRUPTS(); reset(); } /** flush configured changes, including global brightness */ IRAM_ATTR void flushBrightness(const uint8_t brightness) { //LED_SET_PIN_TO_OUTPUT; //ets_intr_lock(); //taskENTER_CRITICAL(); taskDISABLE_INTERRUPTS(); //const uint32_t saved = XTOS_DISABLE_ALL_INTERRUPTS; //vPortETSIntrLock(); // 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(); taskENABLE_INTERRUPTS(); //taskEXIT_CRITICAL(); //XTOS_RESTORE_INTLEVEL(saved); //vPortETSIntrUnlock(); reset(); } private: IRAM_ATTR void sendByte(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();} for (uint8_t i = 0; i < 8; ++i) { if (b & 0b10000000) {send1();} else {send0();} b <<= 1; } } // @80 MHz one instruction = 12.5ns // @160 MHz one instruction = 6.25ns __attribute__((always_inline)) void send1() { // 800ns high, 450ns low LED_SET_PIN_H; delayL(); LED_SET_PIN_L; delayS(); //const uint32_t tmp = soc_get_ccount(); //LED_SET_PIN_H; //while(soc_get_ccount() - tmp < 144) {} //LED_SET_PIN_L; //while(soc_get_ccount() - tmp < 192-24) {} } __attribute__((always_inline)) void send0() { // 400ns high, 850ns low LED_SET_PIN_H; delayS(); LED_SET_PIN_L; delayL(); //const uint32_t tmp = soc_get_ccount(); //LED_SET_PIN_H; //while(soc_get_ccount() - tmp < 48) {} //LED_SET_PIN_L; //while(soc_get_ccount() - tmp < 192-24) {} } __attribute__((always_inline)) void reset() { LED_SET_PIN_L; // low for more than 50 us //const uint32_t tmp = soc_get_ccount(); //while(soc_get_ccount() - tmp < 9999) {} //os_delay_us(100); //LED_SET_PIN_H; //asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop");asm("nop"); //LED_SET_PIN_L; //delayMicroseconds(50); } __attribute__((always_inline)) void delayS() { for(uint8_t i = 0; i < (9*1); ++i) {asm("nop");} } __attribute__((always_inline)) void delayL() { for(uint8_t i = 0; i < (9*2); ++i) {asm("nop");} } }; #elif false// ESP32aaa //#include #include #include #include #include template 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 IS_ESP32 #include "../../io/I2S.h" template class WS2812B { static constexpr const char* TAG = "WS2818B"; /** color-value for each attached LED */ Color colors[NUM_LEDS]; /** enable/disable each led */ bool enabled[NUM_LEDS] = {true}; public: /** ctor */ WS2812B() { init(); } void init() { Log::addInfo(TAG, "init with %d leds", NUM_LEDS); cfg(); } /** 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 < NUM_LEDS; ++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 < NUM_LEDS; ++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() { flushBrightness(255); } void flushBrightness(const uint8_t brightness) { portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; portENTER_CRITICAL(&mux); //for (volatile int i = 0; i < 256; ++i) {;} //while (_getCycleCount() > 10000000) {;} volatile uint8_t _x; for (int i = 4; i >= 0; --i) {_x = colors[i].r;} // process each LED for (int i = 0; i < NUM_LEDS; ++i) { _x = colors[i+1].r; // send each LEDs 24-bit GRB data const Color rgb = colors[i].brightness(brightness); sendByte(rgb.g); sendByte(rgb.r); sendByte(rgb.b); } portEXIT_CRITICAL(&mux); delay5000(); } static inline uint32_t _getCycleCount(void) { uint32_t ccount; __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); return ccount; } 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();} } inline void send1() { MyGPIO::set(PIN_DATA); delay800(); MyGPIO::clear(PIN_DATA); delay100(); } inline void send0() { MyGPIO::set(PIN_DATA); delay100(); MyGPIO::clear(PIN_DATA); delay800(); } void delay100() { //for (volatile uint8_t i = 0; i < 1; ++i) {;} const uint32_t start = _getCycleCount(); while (_getCycleCount() < start + 10) {;} } void delay800() { //for (volatile uint8_t i = 0; i < 10; ++i) {;} const uint32_t start = _getCycleCount(); while (_getCycleCount() < start + 100) {;} } void delay5000() { //for (volatile uint8_t i = 0; i < 10; ++i) {;} const uint32_t start = _getCycleCount(); while (_getCycleCount() < start + 1000) {;} } void cfg() { MyGPIO::setOutput(PIN_DATA); } #ifdef xxx /** comm via I2S */ using MyI2S = I2S; MyI2S i2s; void cfg() { const uint32_t sRate = 1000*1000 / (1.0) / 2 / 2; i2s.configure(sRate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); } /** flush configured changes, including global brightness */ void flushBrightness(const uint8_t brightness) { // seems important for i2s ? reset(); uint8_t tmp[8*3]; // process each LED for (int i = 0; i < NUM_LEDS; ++i) { // send each LEDs 24-bit GRB data if (enabled[i]) { const Color rgb = colors[i].brightness(brightness); sendByte(rgb.g, &tmp[0]); sendByte(rgb.r, &tmp[8]); sendByte(rgb.b, &tmp[16]); } else { sendByte(0, &tmp[0]); sendByte(0, &tmp[8]); sendByte(0, &tmp[16]); } i2s.add(tmp, sizeof(tmp)); } reset(); } void reset() { const uint8_t lo[64] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, }; for (int i = 0; i < 8 ; ++i) { i2s.add(lo, sizeof(lo)); } } static constexpr const uint8_t DATA0 = 0b11100000; static constexpr const uint8_t DATA1 = 0b11111000; // send 1 LED byte. 1 bit to send = 8 byte i2s. also converts to little endian inline void sendByte(const uint8_t b, uint8_t* dst) { if (b & 0b10000000) {dst[1] = DATA1;} else {dst[1] = DATA0;} if (b & 0b01000000) {dst[0] = DATA1;} else {dst[0] = DATA0;} if (b & 0b00100000) {dst[3] = DATA1;} else {dst[3] = DATA0;} if (b & 0b00010000) {dst[2] = DATA1;} else {dst[2] = DATA0;} if (b & 0b00001000) {dst[5] = DATA1;} else {dst[5] = DATA0;} if (b & 0b00000100) {dst[4] = DATA1;} else {dst[4] = DATA0;} if (b & 0b00000010) {dst[7] = DATA1;} else {dst[7] = DATA0;} if (b & 0b00000001) {dst[6] = DATA1;} else {dst[6] = DATA0;} } #endif }; #endif #endif // WS2812B_H