This commit is contained in:
2020-06-11 11:25:19 +02:00
parent 5cb02880b3
commit 5177cd5cbd
15 changed files with 1090 additions and 411 deletions

View File

@@ -1,11 +1,10 @@
#ifndef WS2812B_H
#define WS2812B_H
#include "../../data/Color.h"
#include "../../Platforms.h"
#include "../../Debug.h"
#include "../../io/GPIO.h"
@@ -17,9 +16,9 @@
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 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) )
@@ -453,36 +452,21 @@
};
#elif false //ESP32
#elif ESP32
#include <driver/gpio.h>
#include <rom/ets_sys.h>
#include <xtensa/core-macros.h>
#include "driver/timer.h"
#include "../../io/I2S.h"
static constexpr timer_group_t grp = TIMER_GROUP_0;
static constexpr timer_idx_t idx = TIMER_0;
template <int PIN_DATA, int NUM_LEDS> class WS2812B {
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};
static constexpr const char* TAG = "WS2818B";
/** color-value for each attached LED */
Color colors[numLEDs];
Color colors[NUM_LEDS];
public:
/** enable/disable each led */
bool enabled[NUM_LEDS] = {true};
public:
/** ctor */
WS2812B() {
@@ -490,13 +474,11 @@
}
void init() {
ESP_LOGI(TAG, "init()");
gpio_set_direction(outPin, GPIO_MODE_OUTPUT);
debugMod1(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;
@@ -504,7 +486,7 @@
/** set the color for all LEDs */
void setColor(const Color rgb) {
for (int idx = 0; idx < numLEDs; ++idx) {
for (int idx = 0; idx < NUM_LEDS; ++idx) {
colors[idx] = rgb;
}
}
@@ -516,7 +498,7 @@
/** enable/disable all LEDs */
void setEnabled(const bool en) {
for (int idx = 0; idx < numLEDs; ++idx) {
for (int idx = 0; idx < NUM_LEDS; ++idx) {
enabled[idx] = en;
}
}
@@ -527,78 +509,49 @@
}
Color& getColor(const uint8_t idx) {
return colors[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));
void flush() {
flushBrightness(255);
}
void flushBrightness(const uint8_t brightness) {
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&mux);
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);
//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 < numLEDs; ++i) {
for (int i = 0; i < NUM_LEDS; ++i) {
portDISABLE_INTERRUPTS();
const volatile Color c = colors[i];
_x = colors[i+1].r;
if (enabled[i]) {
sendByte(c.g);
sendByte(c.r);
sendByte(c.b);
} else {
sendByte(0);
sendByte(0);
sendByte(0);
}
// 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(&myMutex);
portENABLE_INTERRUPTS();
portEXIT_CRITICAL(&mux);
delay5000();
}
private:
static inline uint32_t _getCycleCount(void) {
uint32_t ccount;
__asm__ __volatile__("rsr %0,ccount":"=a" (ccount));
return ccount;
}
/** send one whole byte */
inline void sendByte(const uint8_t b) {
if (b & 0b10000000) {send1();} else {send0();}
if (b & 0b01000000) {send1();} else {send0();}
@@ -610,300 +563,111 @@
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();
MyGPIO::set(PIN_DATA);
delay800();
MyGPIO::clear(PIN_DATA);
delay100();
}
inline void delayShort() {
volatile int i;
i = 0; i = 1; i = 2; i = 3;
(void) i;
inline void send0() {
MyGPIO::set(PIN_DATA);
delay100();
MyGPIO::clear(PIN_DATA);
delay800();
}
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");
// }
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
#elif false //othertest
/** comm via I2S */
using MyI2S = I2S<PIN_DATA,0,0>;
MyI2S i2s;
//#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");
void cfg() {
const uint32_t sRate = 1000*1000 / (1.0) / 2 / 2;
i2s.configure(sRate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
}
//waitForTX();
/** flush configured changes, including global brightness */
void flushBrightness(const uint8_t brightness) {
const size_t toWrite = 8+8+8+1;// 8-bit R, 8-bit G, 8-bit B, null-terminator
// seems important for i2s ?
reset();
uint8_t tmp[8*3];
// add null terminator
items[toWrite-1].val = 0;
// process each LED
for (int i = 0; i < NUM_LEDS; ++i) {
// process each LED
for (int i = 0; i < numLEDs; i+=2) {
// 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]);
}
enqueueNext();
i2s.add(tmp, sizeof(tmp));
// 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;
}
reset();
}
//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);
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));
}
}
ESP_ERROR_CHECK(rmt_fill_tx_items(this->chan, items, toWrite, 0));
static constexpr const uint8_t DATA0 = 0b11100000;
static constexpr const uint8_t DATA1 = 0b11111000;
++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
// 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
// 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