473 lines
14 KiB
C++
473 lines
14 KiB
C++
#include "LEDStrip.h"
|
|
#include "driver/i2s.h"
|
|
#include <Arduino.h>
|
|
#include "HSVTable.h"
|
|
|
|
static const char* tag = "LEDStrip";
|
|
|
|
LEDSTRIP::LEDSTRIP(int port, int size, int pin, LED_ORDER ledOrder, int shift, int offset)
|
|
{
|
|
this->port = port;
|
|
this->size = size;
|
|
this->shift = shift;
|
|
this->offset = offset;
|
|
this->effSize = this->size - this->offset;
|
|
this->ledOrder = ledOrder;
|
|
|
|
// create pixel array and buffer array
|
|
pixels = new rgbpixel_t[size];
|
|
out_buffer_size = size * PIXEL_SIZE;
|
|
out_buffer = new uint8_t[out_buffer_size + RESET_BUFFER_SIZE];
|
|
reset_buffer = &out_buffer[out_buffer_size + 1]; // pointer to reset buffer section
|
|
active_out_buffer = &out_buffer[0] + (offset * PIXEL_SIZE); // start of the first active
|
|
|
|
// initialize buffer
|
|
memset(pixels, 0, this->size * sizeof(rgbpixel_t));
|
|
memset(out_buffer, 0, out_buffer_size + RESET_BUFFER_SIZE);
|
|
|
|
dma_frame_size = I2S_TX_DMA_BUFF_SIZE;
|
|
if((size * PIXEL_SIZE) < I2S_TX_DMA_BUFF_SIZE) {
|
|
dma_frame_size = size * PIXEL_SIZE;
|
|
}
|
|
|
|
// Although the buffer can be smaller than total buffer size it will require more cpu interrupts
|
|
// So this provides for the entire size rounded up to the closes buffer chunk (I2S_TX_DMA_BUFF_SIZE)
|
|
int dma_buff_count = 1 + ((out_buffer_size + RESET_BUFFER_SIZE + 2) / I2S_TX_DMA_BUFF_SIZE);
|
|
|
|
// install driver
|
|
i2s_driver_config_t i2s_config = {
|
|
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_TX),
|
|
.sample_rate = SAMPLE_RATE,
|
|
.bits_per_sample = I2S_BITS_PER_SAMPLE_8BIT,
|
|
.channel_format = i2s_channel_fmt_t(I2S_CHANNEL_FMT_RIGHT_LEFT),
|
|
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
|
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2, // high interrupt priority,
|
|
//.intr_alloc_flags = ESP_INTR_FLAG_LOWMED, // high interrupt priority,
|
|
.dma_buf_count = dma_buff_count, //
|
|
.dma_buf_len = I2S_TX_DMA_BUFF_SIZE,
|
|
.use_apll = true
|
|
}; // possibly needed for accuracy
|
|
|
|
//i2sEventQueue = xQueueCreate(8, sizeof(i2s_event_t));
|
|
esp_err_t err = i2s_driver_install((i2s_port_t)port, &i2s_config, 8, &i2sEventQueue);
|
|
|
|
if(err){
|
|
if(err == ESP_ERR_INVALID_ARG) {ESP_LOGE(tag, "I2S driver error: Parameter error");}
|
|
else if(err == ESP_ERR_NO_MEM) {ESP_LOGE(tag, "I2S driver error: Out of Memory");}
|
|
else if(err == ESP_ERR_INVALID_STATE) {ESP_LOGE(tag, "I2S driver error: Port is in use");}
|
|
}
|
|
else{
|
|
i2s_pin_config_t pin_config = {.bck_io_num = I2S_PIN_NO_CHANGE,
|
|
.ws_io_num = I2S_PIN_NO_CHANGE,
|
|
.data_out_num = pin,
|
|
.data_in_num = I2S_PIN_NO_CHANGE };
|
|
|
|
ESP_LOGV(tag, "I2S port pin configured....");
|
|
err = i2s_set_pin((i2s_port_t)port, &pin_config);
|
|
if(err){
|
|
if(err == ESP_ERR_INVALID_ARG) {ESP_LOGE(tag, "I2S driver error: Parameter error");}
|
|
else if(err == ESP_FAIL) {ESP_LOGE(tag, "I2S driver error: IO error");}
|
|
}
|
|
else{
|
|
ESP_LOGV(tag, "I2S pin config success!");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#if(HIGH_RES_BIT_RATE == 1)
|
|
static const uint8_t bitpatterns[2] = {0b11000000, 0b11111100};
|
|
void LEDSTRIP::updateBuff(void) {
|
|
uint8_t* buff = active_out_buffer;
|
|
for (uint16_t i = 0; i < this->effSize; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[red >> 7 & 0x01];
|
|
*buff++ = bitpatterns[red >> 6 & 0x01];
|
|
*buff++ = bitpatterns[red >> 5 & 0x01];
|
|
*buff++ = bitpatterns[red >> 4 & 0x01];
|
|
*buff++ = bitpatterns[red >> 3 & 0x01];
|
|
*buff++ = bitpatterns[red >> 2 & 0x01];
|
|
*buff++ = bitpatterns[red >> 1 & 0x01];
|
|
*buff++ = bitpatterns[red & 0x01];
|
|
|
|
*buff++ = bitpatterns[grn >> 7 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 6 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 5 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 3 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x01];
|
|
*buff++ = bitpatterns[grn >> 1 & 0x01];
|
|
*buff++ = bitpatterns[grn & 0x01];
|
|
|
|
*buff++ = bitpatterns[blu >> 7 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 6 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 5 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 3 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x01];
|
|
*buff++ = bitpatterns[blu >> 1 & 0x01];
|
|
*buff++ = bitpatterns[blu & 0x01];
|
|
}
|
|
|
|
*buff++ = 0;
|
|
}
|
|
#else
|
|
static const uint16_t bitpatterns[4] = {0x88, 0x8e, 0xe8, 0xee};
|
|
void LEDSTRIP::updateBuff(void)
|
|
{
|
|
uint8_t* buff = active_out_buffer;
|
|
|
|
switch(this->ledOrder){
|
|
case ORDER_RGB:
|
|
for (uint16_t i = 0; i < this->effSize; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
}
|
|
break;
|
|
case ORDER_RBG:
|
|
for (uint16_t i = 0; i < this->size; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
}
|
|
break;
|
|
case ORDER_GRB:
|
|
for (uint16_t i = 0; i < this->size; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
}
|
|
break;
|
|
case ORDER_GBR:
|
|
for (uint16_t i = 0; i < this->size; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
}
|
|
break;
|
|
case ORDER_BRG:
|
|
for (uint16_t i = 0; i < this->size; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
}
|
|
break;
|
|
default: //ORDER_BGR:
|
|
for (uint16_t i = 0; i < this->size; i++)
|
|
{
|
|
uint8_t red = pixels[i].red >> powerDiv;
|
|
uint8_t grn = pixels[i].grn >> powerDiv;
|
|
uint8_t blu = pixels[i].blu >> powerDiv;
|
|
|
|
*buff++ = bitpatterns[blu >> 6 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 4 & 0x03];
|
|
*buff++ = bitpatterns[blu >> 2 & 0x03];
|
|
*buff++ = bitpatterns[blu & 0x03];
|
|
|
|
*buff++ = bitpatterns[grn >> 6 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 4 & 0x03];
|
|
*buff++ = bitpatterns[grn >> 2 & 0x03];
|
|
*buff++ = bitpatterns[grn & 0x03];
|
|
|
|
*buff++ = bitpatterns[red >> 6 & 0x03];
|
|
*buff++ = bitpatterns[red >> 4 & 0x03];
|
|
*buff++ = bitpatterns[red >> 2 & 0x03];
|
|
*buff++ = bitpatterns[red & 0x03];
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void LEDSTRIP::show(bool update)
|
|
{
|
|
if(update){ updateBuff(); };
|
|
|
|
//i2s_stop((i2s_port_t)this->port);
|
|
i2s_start((i2s_port_t)this->port);
|
|
i2s_zero_dma_buffer((i2s_port_t)this->port);
|
|
i2s_write_data(out_buffer, out_buffer_size + RESET_BUFFER_SIZE);
|
|
|
|
/*
|
|
i2s_event_t i2s_event;
|
|
while(1){
|
|
if(xQueueReceive(i2sEventQueue, &i2s_event, 100)){
|
|
if(i2s_event.type == I2S_EVENT_TX_DONE) { break;}
|
|
}else{
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
void LEDSTRIP::setPowerDiv(uint8_t div){
|
|
if(div < 0){div = 0;}
|
|
if(div > 4){div = 4;}
|
|
powerDiv = div;
|
|
}
|
|
|
|
// Function to write data to the I2S buffer
|
|
void LEDSTRIP::i2s_write_data(const void *data, size_t len)
|
|
{
|
|
size_t bytesWritten = 0;
|
|
|
|
while (bytesWritten < len) {
|
|
size_t bytesToWrite = len - bytesWritten;
|
|
|
|
size_t written = 0;
|
|
esp_err_t err = i2s_write(I2S_NUM_0, (const char*)data + bytesWritten, bytesToWrite, &written, portMAX_DELAY);
|
|
if (err != ESP_OK) {
|
|
// Handle error
|
|
ESP_LOGE(tag, "i2s send error: %d", err);
|
|
break;
|
|
}
|
|
|
|
bytesWritten += written;
|
|
}
|
|
}
|
|
|
|
inline int LEDSTRIP::calcIndex(int index)
|
|
{
|
|
//int x = (index + shift) % effSize;
|
|
//if(x < 0) { // convert nex index to positive range
|
|
// x += effSize;
|
|
//}
|
|
//return x + offset;
|
|
int x = (index + shift) % effSize;
|
|
x = (x < 0) ? (x + effSize) : x;
|
|
return (x + offset) % effSize;
|
|
}
|
|
|
|
void LEDSTRIP::setPixel(int index, rgbpixel_t& col)
|
|
{
|
|
pixels[calcIndex(index)] = col;
|
|
}
|
|
|
|
void LEDSTRIP::setPixel(int index, const rgbpixel_t col)
|
|
{
|
|
pixels[calcIndex(index)] = col;
|
|
}
|
|
|
|
/*
|
|
void LEDSTRIP::setPixel(int index, rgbpixel_t col, uint8_t scale)
|
|
{
|
|
uint16_t n = scale + 1;
|
|
uint8_t r = (col.red * n) >> 8;
|
|
uint8_t g = (col.grn * n) >> 8;
|
|
uint8_t b = (col.blu * n) >> 8;
|
|
pixels[calcIndex(index)] = {r, g, b};
|
|
}
|
|
*/
|
|
|
|
void LEDSTRIP::setPixelRaw(int index, rgbpixel_t& col){
|
|
pixels[index] = col;
|
|
}
|
|
|
|
void LEDSTRIP::setPixelMirrored(int index, rgbpixel_t col)
|
|
{
|
|
pixels[calcIndex(index)] = col;
|
|
pixels[calcIndex(-index-1)] = col;
|
|
}
|
|
|
|
rgbpixel_t LEDSTRIP::getPixel(int index)
|
|
{
|
|
return pixels[calcIndex(index)];
|
|
}
|
|
|
|
void LEDSTRIP::rotatePixels(LED_DIR dir)
|
|
{
|
|
// store the shifted out pixels
|
|
rgbpixel_t tPix;
|
|
if(dir == DIR_FWD){
|
|
tPix = pixels[size -1];
|
|
for(int i=size-1; i > offset; i--){
|
|
pixels[i] = pixels[i-1];
|
|
}
|
|
pixels[offset] = tPix;
|
|
}else{
|
|
tPix = pixels[offset];
|
|
for(int i=offset; i < size-1; i++){
|
|
pixels[i] = pixels[i+1];
|
|
}
|
|
pixels[size-1] = tPix;
|
|
}
|
|
}
|
|
|
|
void LEDSTRIP::shiftPixels(int numPixels, LED_DIR dir)
|
|
{
|
|
// Calculate the shift direction and absolute shift count
|
|
int shiftDir = (dir == DIR_FWD) ? 1 : -1;
|
|
int absShiftCount = (numPixels % this->effSize) * shiftDir;
|
|
|
|
// Shift the pixels by swapping them in place using circular buffering
|
|
for (int i = 0; i < this->effSize; i++) {
|
|
rgbpixel_t temp = pixels[i];
|
|
int j = i;
|
|
|
|
while (true) {
|
|
int k = j + absShiftCount;
|
|
|
|
if (k < 0) {
|
|
k += this->effSize;
|
|
} else if (k >= this->effSize) {
|
|
k -= this->effSize;
|
|
}
|
|
|
|
if (k == i) {
|
|
break;
|
|
}
|
|
|
|
pixels[j] = pixels[k];
|
|
j = k;
|
|
}
|
|
|
|
pixels[j] = temp;
|
|
|
|
// Check if we have completed a cycle
|
|
if (j == i + absShiftCount) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void scalePixel(rgbpixel_t& pix, uint8_t _scale)
|
|
{
|
|
uint16_t n = _scale + 1;
|
|
pix.red = (pix.red * n) >> 8;
|
|
pix.grn = (pix.grn * n) >> 8;
|
|
pix.blu = (pix.blu * n) >> 8;
|
|
}
|
|
|
|
void linearizePixel(rgbpixel_t& pix){
|
|
pix.red = ((int)pix.red * (int)pix.red ) >> 8;
|
|
pix.grn = ((int)pix.grn * (int)pix.grn ) >> 8;
|
|
pix.blu = ((int)pix.blu * (int)pix.blu ) >> 8;
|
|
}
|
|
|
|
|
|
void PixelFadeToBlack(rgbpixel_t& pix, uint8_t fadeValue) {
|
|
pix.red = (pix.red <= 8) ? 0 : pix.red - ((pix.red * fadeValue)>>8);
|
|
pix.grn = (pix.grn <= 8) ? 0 : pix.grn - ((pix.grn * fadeValue)>>8);
|
|
pix.blu = (pix.blu <= 8) ? 0 : pix.blu - ((pix.blu * fadeValue)>>8);
|
|
}
|
|
|
|
void LEDSTRIP::zeroPixels(void)
|
|
{
|
|
for(int i=0; i < this->size; i++){
|
|
pixels[i] = {0,0,0};
|
|
}
|
|
}
|
|
|
|
void LEDSTRIP::fill(rgbpixel_t color, int startIndex, int count)
|
|
{
|
|
int endIndex = startIndex + count;
|
|
for (int i = startIndex; i < endIndex; i++) {
|
|
pixels[calcIndex(i)] = color;
|
|
}
|
|
}
|
|
|
|
// a factor of 0=no changer, factor=255 newCol replaces 100%
|
|
void LEDSTRIP::transitionPixel(int index, rgbpixel_t& newCol, uint8_t factor)
|
|
{
|
|
int i = calcIndex(index);
|
|
int f = factor + 1;
|
|
pixels[i].red = (pixels[i].red * (256 - f) + newCol.red * f) >> 8;
|
|
pixels[i].grn = (pixels[i].grn * (256 - f) + newCol.grn * f) >> 8;
|
|
pixels[i].blu = (pixels[i].blu * (256 - f) + newCol.blu * f) >> 8;
|
|
}
|
|
|
|
LED_ORDER getRGBOrder(const char* order){
|
|
if(strcmp(order, "rgb")==0){ return ORDER_RGB; }
|
|
if(strcmp(order, "rbg")==0){ return ORDER_RBG; }
|
|
if(strcmp(order, "grb")==0){ return ORDER_GRB; }
|
|
if(strcmp(order, "gbr")==0){ return ORDER_GBR; }
|
|
if(strcmp(order, "brg")==0){ return ORDER_BRG; }
|
|
if(strcmp(order, "bgr")==0){ return ORDER_BGR; }
|
|
else{ return ORDER_GRB; }
|
|
} |