f860922d67
I have added a pre-configured version of the ws2812b i2s library to the Arduino code. This removes the need to download and install the ws2812b i2s library manually. The ws2812b code has been preconfigured to reduce the temporal compared to the default value. The default value was found to cause excessive flickering. The readme has been updated to reflect this change. Also added a note about the maximum number of LEDs (255)
170 lines
4.7 KiB
C++
170 lines
4.7 KiB
C++
// ws2812_lib.cpp
|
|
//
|
|
// main library file / contains class implementation
|
|
//
|
|
// Need to give credits to Charles Lohr (https://github.com/cnlohr due
|
|
// to his work on his software for driving ws2812 led-strips using
|
|
// the i2s interface of the ESP8266.
|
|
//
|
|
// This inspired me to create an ESP8266 library for the Arduino IDE
|
|
// I've added temporal dithering & gamma-correction
|
|
// for a 'more natural' light intensity profile.
|
|
//
|
|
// No pin-definitions/mapings are required/possible as the library used the I2S
|
|
// output-pin. This pin in it's turn is shared with GPIO3 & RXD0
|
|
//
|
|
|
|
#include <Arduino.h>
|
|
#include <stdint.h>
|
|
#include "ws2812_i2s.h"
|
|
#include "ws2812_defs.h"
|
|
#include "ws2812_gamma.h"
|
|
|
|
// include C-style header
|
|
extern "C"
|
|
{
|
|
#include "ws2812_dma.h"
|
|
};
|
|
|
|
// class constructor
|
|
WS2812::WS2812(void)
|
|
{
|
|
// empty for now
|
|
}
|
|
|
|
// class de-constructor
|
|
WS2812::~WS2812(void)
|
|
{
|
|
// empty for now
|
|
// TODO : should implement switching of DMA
|
|
}
|
|
|
|
// Init led-string / memory buffers etc.
|
|
void WS2812::init(uint16_t _num_leds)
|
|
{
|
|
uint8_t i;
|
|
uint16_t j;
|
|
|
|
num_leds = _num_leds;
|
|
|
|
// clear zero buffer
|
|
for(j=0; j<NUM_I2S_ZERO_WORDS; j++)
|
|
{
|
|
i2s_zeros_buffer[j] = 0;
|
|
}
|
|
|
|
// do memory allocation for i2s buffer(s)
|
|
for(i=0; i<WS2812_DITHER_NUM; i++)
|
|
{
|
|
i2s_pixels_buffer[i] = new uint32_t[NUM_I2S_PIXEL_WORDS];
|
|
|
|
// TODO : handle memory allocation error better
|
|
if (i2s_pixels_buffer[i] == 0)
|
|
{
|
|
Serial.println("WS2812_I2S : ERROR ALLOCATING MEMORY");
|
|
return;
|
|
}
|
|
|
|
for(j=0; j<NUM_I2S_PIXEL_WORDS; j++)
|
|
{
|
|
i2s_pixels_buffer[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
// set-up DMA descriptors / 1 pair per dither-factor
|
|
for(i=0; i<WS2812_DITHER_NUM; i++)
|
|
{
|
|
i2s_pixels_queue[i].owner = 1;
|
|
i2s_pixels_queue[i].eof = 1;
|
|
i2s_pixels_queue[i].sub_sof = 0;
|
|
i2s_pixels_queue[i].datalen = NUM_I2S_PIXEL_BYTES; // Size in bytes
|
|
i2s_pixels_queue[i].blocksize = NUM_I2S_PIXEL_BYTES; // Size in bytes
|
|
i2s_pixels_queue[i].buf_ptr = (uint32_t)i2s_pixels_buffer[i];
|
|
i2s_pixels_queue[i].unused = 0;
|
|
i2s_pixels_queue[i].next_link_ptr = (uint32_t)&i2s_zeros_queue[i]; // always link to zeros-buffer
|
|
|
|
i2s_zeros_queue[i].owner = 1;
|
|
i2s_zeros_queue[i].eof = 1;
|
|
i2s_zeros_queue[i].sub_sof = 0;
|
|
i2s_zeros_queue[i].datalen = NUM_I2S_ZERO_BYTES; // Size in bytes)
|
|
i2s_zeros_queue[i].blocksize = NUM_I2S_ZERO_BYTES; // Size in bytes
|
|
i2s_zeros_queue[i].buf_ptr = (uint32_t)i2s_zeros_buffer;
|
|
i2s_zeros_queue[i].unused = 0;
|
|
|
|
if (i == (WS2812_DITHER_NUM-1)) // last DMA descriptor in linked list ?
|
|
{
|
|
// yes : link to first DMA descriptor
|
|
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[0];
|
|
}
|
|
else
|
|
{
|
|
// no : link to next DMA descriptor
|
|
i2s_zeros_queue[i].next_link_ptr = (uint32_t)&i2s_pixels_queue[i+1];
|
|
}
|
|
}
|
|
|
|
// call C based helper function
|
|
// I did not really succeed in putting the code from the helper
|
|
// funtion directly into this constructor...has something to do
|
|
// with C vs. C++
|
|
// the i2c_writeReg_Mask macro (+ others) failed.. see ws2812_defs.h
|
|
// may be I solve this later
|
|
|
|
// parameter = first entry in DMA descriptor list, i.e the descriptor list
|
|
ws2812_dma(i2s_pixels_queue);
|
|
|
|
}
|
|
|
|
// left this comment in tact....credit to Charles
|
|
|
|
// All functions below this line are Public Domain 2015 Charles Lohr.
|
|
// this code may be used by anyone in any way without restriction or limitation.
|
|
|
|
// Send out WS2812 bits with coded pulses, one nibble, then the other.
|
|
static const uint16_t bitpatterns[16] =
|
|
{
|
|
0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
|
|
0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
|
|
0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
|
|
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
|
|
};
|
|
|
|
// display the pixels
|
|
void WS2812::show(Pixel_t *pixels)
|
|
{
|
|
uint8_t *buffer;
|
|
uint8_t pixelbyte;
|
|
uint8_t gammabyte;
|
|
uint16_t i,b;
|
|
uint16_t *i2s_ptr[WS2812_DITHER_NUM];
|
|
|
|
buffer = (uint8_t *)pixels;
|
|
|
|
// set-up pointers into the i2s-pixel-buffers
|
|
for(i=0; i<WS2812_DITHER_NUM; i++)
|
|
{
|
|
i2s_ptr[i] = (uint16_t *)i2s_pixels_buffer[i];
|
|
}
|
|
|
|
// for every pixel in the input-array
|
|
// - get the pixel value (either R,G, or B)
|
|
// - for every dithered buffer
|
|
// get the gamma-corrected output value
|
|
// - and transform into i2s nibble
|
|
|
|
for(b=0; b<NUM_RGB_BYTES; b++)
|
|
{
|
|
pixelbyte = *buffer++;
|
|
|
|
for(i=0; i<WS2812_DITHER_NUM; i++)
|
|
{
|
|
gammabyte = gamma_dither[i][pixelbyte];
|
|
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte & 0x0f) ];
|
|
*(i2s_ptr[i]++) = bitpatterns[ (gammabyte>>4) & 0x0f ];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// end of file
|