audio-reactive-led-strip/arduino/ws2812_controller/ws2812_i2s.cpp

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