Skip to content

Instantly share code, notes, and snippets.

@hpwit
Last active April 28, 2019 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hpwit/5c6c796bba99b0840d6dd6c806a1eca3 to your computer and use it in GitHub Desktop.
Save hpwit/5c6c796bba99b0840d6dd6c806a1eca3 to your computer and use it in GitHub Desktop.
new verison
/*
*
*
*/
/*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once
FASTLED_NAMESPACE_BEGIN
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_heap_caps.h"
#include "soc/soc.h"
#include "soc/gpio_sig_map.h"
#include "soc/i2s_reg.h"
#include "soc/i2s_struct.h"
#include "soc/io_mux_reg.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "rom/lldesc.h"
#include "esp_intr.h"
#include "esp_log.h"
#ifdef __cplusplus
}
#endif
__attribute__ ((always_inline)) inline static uint32_t __clock_cycles() {
uint32_t cyc;
__asm__ __volatile__ ("rsr %0,ccount":"=a" (cyc));
return cyc;
}
#define FASTLED_HAS_CLOCKLESS 1
#define NUM_COLOR_CHANNELS 3
// -- Choose which I2S device to use
#ifndef I2S_DEVICE
#define I2S_DEVICE 0
#endif
// -- Max number of controllers we can support
#ifndef FASTLED_I2S_MAX_CONTROLLERS
#define FASTLED_I2S_MAX_CONTROLLERS 24
#endif
// -- I2S clock
#define I2S_BASE_CLK (80000000L)
#define I2S_MAX_CLK (20000000L) //more tha a certain speed and the I2s looses some bits
#define I2S_MAX_PULSE_PER_BIT 20 //put it higher to get more accuracy but it could decrease the refresh rate without real improvement
// -- Convert ESP32 cycles back into nanoseconds
#define ESPCLKS_TO_NS(_CLKS) (((long)(_CLKS) * 1000L) / F_CPU_MHZ)
// -- I2S bit encoding
// For now, this stuff is hard-coded
#define FASTLED_I2S_CLOCK_DIVIDER 25 // 10 // 80MHz --> 8MHz
#define FASTLED_I2S_NS_PER_PULSE 312.5 // 125 // == 125ns per cycle
// -- Array of all controllers
static CLEDController * gControllers[FASTLED_I2S_MAX_CONTROLLERS];
static int gNumControllers = 0;
static int gNumStarted = 0;
// -- Global semaphore for the whole show process
// Semaphore is not given until all data has been sent
static xSemaphoreHandle gTX_sem = NULL;
// -- I2S global configuration stuff
static bool gInitialized = false;
static intr_handle_t gI2S_intr_handle = NULL;
static i2s_dev_t * i2s; // A pointer to the memory-mapped structure: I2S0 or I2S1
static int i2s_base_pin_index; // I2S goes to these pins until we remap them using the GPIO matrix
// --- I2S DMA buffers
struct DMABuffer {
lldesc_t descriptor;
uint8_t * buffer;
};
#define NUM_DMA_BUFFERS 2
static DMABuffer * dmaBuffers[NUM_DMA_BUFFERS];
// -- Bit patterns
// We configure the I2S data clock so that each pulse is
// 125ns. Depending on the kind of LED we compute a pattern of
// pulses that match the timing. For example, a "1" bit for the
// WS2812 consists of 700-900ns high, followed by 300-500ns
// low. Using 125ns per pulse, we can send a "1" bit using this
// pattern: 1111111000 (a total of 10 bits, or 1250ns)
//
// For now, we require all strips to be the same chipset, so these
// are global variables.
static int gPulsesPerBit = 0;
static uint32_t gOneBit[40] = {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};
static uint32_t gZeroBit[40] = {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};
// -- Counters to track progress
static int gCurBuffer = 0;
static bool gDoneFilling = false;
static int ones_for_one;
static int ones_for_zero;
// -- Temp buffers for pixels and bits being formatted for DMA
static uint8_t gPixelRow[NUM_COLOR_CHANNELS][32];
static uint8_t gPixelBits[NUM_COLOR_CHANNELS][8][4];
static int CLOCK_DIVIDER_N;
static int CLOCK_DIVIDER_A;
static int CLOCK_DIVIDER_B;
template <int DATA_PIN, int T1, int T2, int T3, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false, int WAIT_TIME = 5>
class ClocklessController : public CPixelLEDController<RGB_ORDER>
{
// -- The index of this controller in the global gControllers array
int m_index;
// -- Store the GPIO pin
gpio_num_t mPin;
// -- This instantiation forces a check on the pin choice
FastPin<DATA_PIN> mFastPin;
// -- Save the pixel controller
PixelController<RGB_ORDER> * mPixels;
public:
void init()
{
i2sInit();
// -- Allocate space to save the pixel controller
// during parallel output
mPixels = (PixelController<RGB_ORDER> *) malloc(sizeof(PixelController<RGB_ORDER>));
gControllers[gNumControllers] = this;
m_index = gNumControllers;
gNumControllers++;
// -- Set up the pin We have to do two things: configure the
// actual GPIO pin, and route the output from the default
// pin (determined by the I2S device) to the pin we
// want. We compute the default pin using the index of this
// controller in the array. This order is crucial because
// the bits must go into the DMA buffer in the same order.
mPin = gpio_num_t(DATA_PIN);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DATA_PIN], PIN_FUNC_GPIO);
gpio_set_direction(mPin, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT);
pinMode(mPin,OUTPUT);
gpio_matrix_out(mPin, i2s_base_pin_index + m_index, false, false);
}
virtual uint16_t getMaxRefreshRate() const { return 400; }
protected:
static int pgcd(int smallest,int precision,int a,int b,int c)
{
int pgc_=1;
for( int i=smallest;i>0;i--)
{
if( a%i<=precision && b%i<=precision && c%i<=precision)
{
pgc_=i;
break;
}
}
return pgc_;
}
static void initBitPatterns()
{
// Precompute the bit patterns based on the I2S sample rate
uint32_t T1ns = ESPCLKS_TO_NS(T1);
uint32_t T2ns = ESPCLKS_TO_NS(T2);
uint32_t T3ns = ESPCLKS_TO_NS(T3);
Serial.print("T1 = "); Serial.print(T1); Serial.print(" ns "); Serial.println(T1ns);
Serial.print("T2 = "); Serial.print(T2); Serial.print(" ns "); Serial.println(T2ns);
Serial.print("T3 = "); Serial.print(T3); Serial.print(" ns "); Serial.println(T3ns);
/*
We calculate the best pcgd to the timing
ie
WS2811 77 77 154 => 1 1 2 => nb pulses= 4
WS2812 60 150 90 => 2 5 3 => nb pulses=10
*/
int smallest=0;
if (T1>T2)
smallest=T2;
else
smallest=T1;
if(smallest>T3)
smallest=T3;
double freq=(double)1/(double)(T1ns + T2ns + T3ns);
Serial.printf("chipset frequency:%f Khz\n", 1000000L*freq);
// Serial.printf("smallest %d\n",smallest);
int pgc_=1;
int precision=0;
pgc_=pgcd(smallest,precision,T1,T2,T3);
//Serial.printf("%f\n",I2S_MAX_CLK/(1000000000L*freq));
while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_PULSE_PER_BIT) //while(pgc_==1 || (T1/pgc_ +T2/pgc_ +T3/pgc_)>I2S_MAX_CLK/(1000000000L*freq))
{
precision++;
pgc_=pgcd(smallest,precision,T1,T2,T3);
//Serial.printf("%d %d\n",pgc_,(a+b+c)/pgc_);
}
pgc_=pgcd(smallest,precision,T1,T2,T3);
Serial.printf("pgcd %d precision:%d\n",pgc_,precision);
Serial.printf("nb pulse per bit:%d\n",T1/pgc_ +T2/pgc_ +T3/pgc_);
gPulsesPerBit=(int)T1/pgc_ +(int)T2/pgc_ +(int)T3/pgc_;
/*
we calculate the duration of one pulse nd htre base frequency of the led
ie WS2812B F=1/(250+625+375)=800kHz or 1250ns
as we need 10 pulses each pulse is 125ns => frequency 800Khz*10=8MHz
WS2811 T=320+320+641=1281ns qnd we need 4 pulses => pulse duration 320.25ns =>frequency 3.1225605Mhz
*/
freq=1000000000L*freq*gPulsesPerBit;
Serial.printf("needed frequency (nbpiulse per bit)*(chispset frequency):%f Mhz\n",freq/1000000);
/*
we do calculate the needed N a and b
as f=basefred/(N+b/a);
as a is max 63 the precision for the decimal is 1/63
*/
CLOCK_DIVIDER_N=(int)((double)I2S_BASE_CLK/freq);
double v=I2S_BASE_CLK/freq-CLOCK_DIVIDER_N;
double prec=(double)1/63;
int a=1;
int b=0;
CLOCK_DIVIDER_A=1;
CLOCK_DIVIDER_B=0;
for(a=1;a<64;a++)
{
for(b=0;b<a;b++)
{
//printf("%d %d %f %f %f\n",b,a,v,(double)v*(double)a,fabsf(v-(double)b/a));
if(fabsf(v-(double)b/a) <= prec/2)
break;
}
if(fabsf(v-(double)b/a) ==0)
{
CLOCK_DIVIDER_A=a;
CLOCK_DIVIDER_B=b;
break;
}
if(fabsf(v-(double)b/a) < prec/2)
{
if (fabsf(v-(double)b/a) <fabsf(v-(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A))
{
CLOCK_DIVIDER_A=a;
CLOCK_DIVIDER_B=b;
}
}
}
//top take care of an issue with double 0.9999999999
if(CLOCK_DIVIDER_A==CLOCK_DIVIDER_B)
{
CLOCK_DIVIDER_A=1;
CLOCK_DIVIDER_B=0;
CLOCK_DIVIDER_N++;
}
//printf("%d %d %f %f %d\n",CLOCK_DIVIDER_B,CLOCK_DIVIDER_A,(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A,v,CLOCK_DIVIDER_N);
//Serial.printf("freq %f %f\n",freq,I2S_BASE_CLK/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A));
freq=1/(CLOCK_DIVIDER_N+(double)CLOCK_DIVIDER_B/CLOCK_DIVIDER_A);
freq=freq*I2S_BASE_CLK;
Serial.printf("calculted for i2s frequency:%f Mhz N:%d B:%d A:%d\n",freq/1000000,CLOCK_DIVIDER_N,CLOCK_DIVIDER_B,CLOCK_DIVIDER_A);
double pulseduration=1000000000/freq;
Serial.printf("Pulse duration: %f ns\n",pulseduration);
// gPulsesPerBit = (T1ns + T2ns + T3ns)/FASTLED_I2S_NS_PER_PULSE;
//Serial.print("Pulses per bit: "); Serial.println(gPulsesPerBit);
//int ones_for_one = ((T1ns + T2ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
ones_for_one = T1/pgc_ +T2/pgc_;
//Serial.print("One bit: target ");
//Serial.print(T1ns+T2ns); Serial.print("ns --- ");
//Serial.print(ones_for_one); Serial.print(" 1 bits");
//Serial.print(" = "); Serial.print(ones_for_one * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
Serial.printf("one bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns+T2ns,ones_for_one ,ones_for_one*pulseduration);
int i = 0;
while ( i < ones_for_one ) {
gOneBit[i] = 0xFFFFFF00;
i++;
}
while ( i < gPulsesPerBit ) {
gOneBit[i] = 0x00000000;
i++;
}
//int ones_for_zero = ((T1ns - 1)/FASTLED_I2S_NS_PER_PULSE) + 1;
ones_for_zero =T1/pgc_ ;
// Serial.print("Zero bit: target ");
// Serial.print(T1ns); Serial.print("ns --- ");
//Serial.print(ones_for_zero); Serial.print(" 1 bits");
//Serial.print(" = "); Serial.print(ones_for_zero * FASTLED_I2S_NS_PER_PULSE); Serial.println("ns");
Serial.printf("Zero bit : target %d ns --- %d pulses 1 bit = %f ns\n",T1ns,ones_for_zero ,ones_for_zero*pulseduration);
i = 0;
while ( i < ones_for_zero ) {
gZeroBit[i] = 0xFFFFFF00;
i++;
}
while ( i < gPulsesPerBit ) {
gZeroBit[i] = 0x00000000;
i++;
}
memset(gPixelRow, 0, NUM_COLOR_CHANNELS * 32);
memset(gPixelBits, 0, NUM_COLOR_CHANNELS * 32);
}
static DMABuffer * allocateDMABuffer(int bytes)
{
DMABuffer * b = (DMABuffer *)heap_caps_malloc(sizeof(DMABuffer), MALLOC_CAP_DMA);
b->buffer = (uint8_t *)heap_caps_malloc(bytes, MALLOC_CAP_DMA);
memset(b->buffer, 0, bytes);
b->descriptor.length = bytes;
b->descriptor.size = bytes;
b->descriptor.owner = 1;
b->descriptor.sosf = 1;
b->descriptor.buf = b->buffer;
b->descriptor.offset = 0;
b->descriptor.empty = 0;
b->descriptor.eof = 1;
b->descriptor.qe.stqe_next = 0;
return b;
}
static void i2sInit()
{
// -- Only need to do this once
if (gInitialized) return;
// -- Construct the bit patterns for ones and zeros
initBitPatterns();
// -- Choose whether to use I2S device 0 or device 1
// Set up the various device-specific parameters
int interruptSource;
if (I2S_DEVICE == 0) {
i2s = &I2S0;
periph_module_enable(PERIPH_I2S0_MODULE);
interruptSource = ETS_I2S0_INTR_SOURCE;
i2s_base_pin_index = I2S0O_DATA_OUT0_IDX;
} else {
i2s = &I2S1;
periph_module_enable(PERIPH_I2S1_MODULE);
interruptSource = ETS_I2S1_INTR_SOURCE;
i2s_base_pin_index = I2S1O_DATA_OUT0_IDX;
}
// -- Reset everything
i2sReset();
i2sReset_DMA();
i2sReset_FIFO();
// -- Main configuration
i2s->conf.tx_msb_right = 1;
i2s->conf.tx_mono = 0;
i2s->conf.tx_short_sync = 0;
i2s->conf.tx_msb_shift = 0;
i2s->conf.tx_right_first = 1; // 0;//1;
i2s->conf.tx_slave_mod = 0;
// -- Set parallel mode
i2s->conf2.val = 0;
i2s->conf2.lcd_en = 1;
i2s->conf2.lcd_tx_wrx2_en = 0; // 0 for 16 or 32 parallel output
i2s->conf2.lcd_tx_sdx2_en = 0; // HN
// -- Set up the clock rate and sampling
i2s->sample_rate_conf.val = 0;
i2s->sample_rate_conf.tx_bits_mod = 32; // Number of parallel bits/pins
i2s->sample_rate_conf.tx_bck_div_num = 1;
i2s->clkm_conf.val = 0;
i2s->clkm_conf.clka_en = 0;
// -- Data clock is computed as Base/(div_num + (div_b/div_a))
// Base is 80Mhz, so 80/(10 + 0/1) = 8Mhz
// One cycle is 125ns
i2s->clkm_conf.clkm_div_a = CLOCK_DIVIDER_A;
i2s->clkm_conf.clkm_div_b = CLOCK_DIVIDER_B;
i2s->clkm_conf.clkm_div_num = CLOCK_DIVIDER_N;
i2s->fifo_conf.val = 0;
i2s->fifo_conf.tx_fifo_mod_force_en = 1;
i2s->fifo_conf.tx_fifo_mod = 3; // 32-bit single channel data
i2s->fifo_conf.tx_data_num = 32; // fifo length
i2s->fifo_conf.dscr_en = 1; // fifo will use dma
i2s->conf1.val = 0;
i2s->conf1.tx_stop_en = 0;
i2s->conf1.tx_pcm_bypass = 1;
i2s->conf_chan.val = 0;
i2s->conf_chan.tx_chan_mod = 1; // Mono mode, with tx_msb_right = 1, everything goes to right-channel
i2s->timing.val = 0;
// -- Allocate two DMA buffers
dmaBuffers[0] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit);
dmaBuffers[1] = allocateDMABuffer(32 * NUM_COLOR_CHANNELS * gPulsesPerBit);
// -- Arrange them as a circularly linked list
dmaBuffers[0]->descriptor.qe.stqe_next = &(dmaBuffers[1]->descriptor);
dmaBuffers[1]->descriptor.qe.stqe_next = &(dmaBuffers[0]->descriptor);
// -- Allocate i2s interrupt
SET_PERI_REG_BITS(I2S_INT_ENA_REG(I2S_DEVICE), I2S_OUT_EOF_INT_ENA_V, 1, I2S_OUT_EOF_INT_ENA_S);
esp_err_t e = esp_intr_alloc(interruptSource, 0, // ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3,
&interruptHandler, 0, &gI2S_intr_handle);
// -- Create a semaphore to block execution until all the controllers are done
if (gTX_sem == NULL) {
gTX_sem = xSemaphoreCreateBinary();
xSemaphoreGive(gTX_sem);
}
// Serial.println("Init I2S");
gInitialized = true;
}
static void empty( uint32_t *buf)
{
for(int i=0;i<8*NUM_COLOR_CHANNELS;i++)
{
int offset=gPulsesPerBit*i;
for(int j=0;j<ones_for_zero;j++)
buf[offset+j]=0xffffffff;
for(int j=ones_for_one;j<gPulsesPerBit;j++)
buf[offset+j]=0;
}
}
// -- Show pixels
// This is the main entry point for the controller.
virtual void showPixels(PixelController<RGB_ORDER> & pixels)
{
if (gNumStarted == 0) {
// -- First controller: make sure everything is set up
xSemaphoreTake(gTX_sem, portMAX_DELAY);
}
// -- Initialize the local state, save a pointer to the pixel
// data. We need to make a copy because pixels is a local
// variable in the calling function, and this data structure
// needs to outlive this call to showPixels.
(*mPixels) = pixels;
// -- Keep track of the number of strips we've seen
gNumStarted++;
// Serial.print("Show pixels ");
// Serial.println(gNumStarted);
// -- The last call to showPixels is the one responsible for doing
// all of the actual work
if (gNumStarted == gNumControllers) {
empty((uint32_t*)dmaBuffers[0]->buffer);
empty((uint32_t*)dmaBuffers[1]->buffer);
gCurBuffer = 0;
gDoneFilling = false;
// -- Prefill both buffers
fillBuffer();
fillBuffer();
i2sStart();
// -- Wait here while the rest of the data is sent. The interrupt handler
// will keep refilling the RMT buffers until it is all sent; then it
// gives the semaphore back.
xSemaphoreTake(gTX_sem, portMAX_DELAY);
xSemaphoreGive(gTX_sem);
i2sStop();
// -- Reset the counters
gNumStarted = 0;
}
}
// -- Custom interrupt handler
static IRAM_ATTR void interruptHandler(void *arg)
{
if (i2s->int_st.out_eof) {
i2s->int_clr.val = i2s->int_raw.val;
if ( ! gDoneFilling) {
fillBuffer();
} else {
portBASE_TYPE HPTaskAwoken = 0;
xSemaphoreGiveFromISR(gTX_sem, &HPTaskAwoken);
if(HPTaskAwoken == pdTRUE) portYIELD_FROM_ISR();
}
}
}
static void fillBuffer()
{
volatile uint32_t * buf = (uint32_t *) dmaBuffers[gCurBuffer]->buffer;
gCurBuffer = (gCurBuffer + 1) % NUM_DMA_BUFFERS;
// -- Get the requested pixel from each controller. Store the
// data for each color channel in a separate array.
uint32_t has_data_mask = 0;
for (int i = 0; i < gNumControllers; i++) {
// -- Store the pixels in reverse controller order starting at index 23
// This causes the bits to come out in the right position after we
// transpose them.
int bit_index = 23-i;
ClocklessController * pController = static_cast<ClocklessController*>(gControllers[i]);
if (pController->mPixels->has(1)) {
gPixelRow[0][bit_index] = pController->mPixels->loadAndScale0();
gPixelRow[1][bit_index] = pController->mPixels->loadAndScale1();
gPixelRow[2][bit_index] = pController->mPixels->loadAndScale2();
pController->mPixels->advanceData();
pController->mPixels->stepDithering();
// -- Record that this controller still has data to send
has_data_mask |= (1 << (i+8));
}
}
if (has_data_mask == 0) {
gDoneFilling = true;
return;
}
// -- Transpose and encode the pixel data for the DMA buffer
int buf_index = 0;
for (int channel = 0; channel < NUM_COLOR_CHANNELS; channel++) {
// -- Tranpose each array: all the bit 7's, then all the bit 6's, ...
transpose32(gPixelRow[channel], gPixelBits[channel][0] );
//Serial.print("Channel: "); Serial.print(channel); Serial.print(" ");
for (int bitnum = 0; bitnum < 8; bitnum++) {
uint8_t * row = (uint8_t *) (gPixelBits[channel][bitnum]);
uint32_t bit = (row[0] << 24) | (row[1] << 16) | (row[2] << 8) | row[3];
/*
Serial.print(bitnum); Serial.print(": ");
uint32_t bt = bit;
for (int k = 0; k < 32; k++) {
if (bt & 0x80000000) Serial.print("1");
else Serial.print("0");
bt = bt << 1;
}
Serial.println();
*/
/* for (int pulse_num = 0; pulse_num < gPulsesPerBit; pulse_num++) {
buf[buf_index++] = has_data_mask & ( (bit & gOneBit[pulse_num]) | (~bit & gZeroBit[pulse_num]) );*/
//when the loop is too big => issues in timing hence i only fill the the 1
for(int pulse_num=ones_for_zero;pulse_num<ones_for_one;pulse_num++) {
buf[bitnum*gPulsesPerBit+channel*8*gPulsesPerBit+pulse_num] = has_data_mask & bit;
//if (buf[buf_index-1] & 0x100) Serial.print("1");
//else Serial.print("0");
}
}
}
}
static void transpose32(uint8_t * pixels, uint8_t * bits)
{
transpose8rS32(& pixels[0], 1, 4, & bits[0]);
transpose8rS32(& pixels[8], 1, 4, & bits[1]);
transpose8rS32(& pixels[16], 1, 4, & bits[2]);
//transpose8rS32(& pixels[24], 1, 4, & bits[3]);
}
static void transpose8rS32(uint8_t * A, int m, int n, uint8_t * B)
{
uint32_t x, y, t;
// Load the array and pack it into x and y.
x = (A[0]<<24) | (A[m]<<16) | (A[2*m]<<8) | A[3*m];
y = (A[4*m]<<24) | (A[5*m]<<16) | (A[6*m]<<8) | A[7*m];
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
t = (x ^ (x >>14)) & 0x0000CCCC; x = x ^ t ^ (t <<14);
t = (y ^ (y >>14)) & 0x0000CCCC; y = y ^ t ^ (t <<14);
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
x = t;
B[0]=x>>24; B[n]=x>>16; B[2*n]=x>>8; B[3*n]=x;
B[4*n]=y>>24; B[5*n]=y>>16; B[6*n]=y>>8; B[7*n]=y;
}
/** Start I2S transmission
*/
static void i2sStart()
{
// esp_intr_disable(gI2S_intr_handle);
// Serial.println("I2S start");
i2sReset();
//Serial.println(dmaBuffers[0]->sampleCount());
i2s->lc_conf.val=I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN | I2S_OUT_DATA_BURST_EN;
i2s->out_link.addr = (uint32_t) & (dmaBuffers[0]->descriptor);
i2s->out_link.start = 1;
////vTaskDelay(5);
i2s->int_clr.val = i2s->int_raw.val;
// //vTaskDelay(5);
i2s->int_ena.out_dscr_err = 1;
//enable interrupt
////vTaskDelay(5);
esp_intr_enable(gI2S_intr_handle);
// //vTaskDelay(5);
i2s->int_ena.val = 0;
i2s->int_ena.out_eof = 1;
//start transmission
i2s->conf.tx_start = 1;
}
static void i2sReset()
{
// Serial.println("I2S reset");
const unsigned long lc_conf_reset_flags = I2S_IN_RST_M | I2S_OUT_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M;
i2s->lc_conf.val |= lc_conf_reset_flags;
i2s->lc_conf.val &= ~lc_conf_reset_flags;
const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M;
i2s->conf.val |= conf_reset_flags;
i2s->conf.val &= ~conf_reset_flags;
}
static void i2sReset_DMA()
{
i2s->lc_conf.in_rst=1; i2s->lc_conf.in_rst=0;
i2s->lc_conf.out_rst=1; i2s->lc_conf.out_rst=0;
}
static void i2sReset_FIFO()
{
i2s->conf.rx_fifo_reset=1; i2s->conf.rx_fifo_reset=0;
i2s->conf.tx_fifo_reset=1; i2s->conf.tx_fifo_reset=0;
}
static void i2sStop()
{
// Serial.println("I2S stop");
esp_intr_disable(gI2S_intr_handle);
i2sReset();
i2s->conf.rx_start = 0;
i2s->conf.tx_start = 0;
}
};
FASTLED_NAMESPACE_END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment