forked from Makuna/NeoPixelBus
Update NeoEsp8266DmaMethod.h (#245)
Fix Esp8266 DMA to not truncate the send when the bus is destructed Fix Esp8266 DMA to send first show, due to state change to idle to quickly
This commit is contained in:
@@ -68,6 +68,7 @@ class NeoEsp8266DmaSpeed800KbpsBase
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 3;
|
||||
const static uint32_t I2sBaseClockDivisor = 16;
|
||||
const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
|
||||
};
|
||||
|
||||
class NeoEsp8266DmaSpeedWs2812x : public NeoEsp8266DmaSpeed800KbpsBase
|
||||
@@ -91,8 +92,9 @@ public:
|
||||
class NeoEsp8266DmaSpeed400Kbps
|
||||
{
|
||||
public:
|
||||
const static uint32_t I2sClockDivisor = 6;
|
||||
const static uint32_t I2sClockDivisor = 6;
|
||||
const static uint32_t I2sBaseClockDivisor = 16;
|
||||
const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
|
||||
const static uint32_t ResetTimeUs = 50;
|
||||
};
|
||||
|
||||
@@ -101,6 +103,7 @@ enum NeoDmaState
|
||||
NeoDmaState_Idle,
|
||||
NeoDmaState_Pending,
|
||||
NeoDmaState_Sending,
|
||||
NeoDmaState_Zeroing,
|
||||
};
|
||||
const uint16_t c_maxDmaBlockSize = 4095;
|
||||
const uint16_t c_dmaBytesPerPixelBytes = 4;
|
||||
@@ -109,7 +112,7 @@ const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted t
|
||||
template<typename T_SPEED> class NeoEsp8266DmaMethodBase
|
||||
{
|
||||
public:
|
||||
NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize)
|
||||
NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize)
|
||||
{
|
||||
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
|
||||
|
||||
@@ -122,6 +125,8 @@ public:
|
||||
_i2sBuffer = (uint8_t*)malloc(_i2sBufferSize);
|
||||
memset(_i2sBuffer, 0x00, _i2sBufferSize);
|
||||
|
||||
// _i2sBuffer[0] = 0b11101000; // debug, 1 bit then 0 bit
|
||||
|
||||
memset(_i2sZeroes, 0x00, sizeof(_i2sZeroes));
|
||||
|
||||
_is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize;
|
||||
@@ -138,14 +143,36 @@ public:
|
||||
|
||||
~NeoEsp8266DmaMethodBase()
|
||||
{
|
||||
digitalWrite(4, HIGH);
|
||||
|
||||
uint8_t waits = 1;
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
waits = 2;
|
||||
yield();
|
||||
}
|
||||
|
||||
// wait for any pending sends to complete
|
||||
// due to internal i2s caching/send delays, this can more that once the data size
|
||||
uint32_t time = micros();
|
||||
while ((micros() - time) < ((getPixelTime() + T_SPEED::ResetTimeUs) * waits))
|
||||
{
|
||||
yield();
|
||||
}
|
||||
|
||||
digitalWrite(4, LOW);
|
||||
|
||||
StopDma();
|
||||
|
||||
s_this = nullptr;
|
||||
pinMode(c_I2sPin, INPUT);
|
||||
|
||||
free(_pixels);
|
||||
free(_i2sBuffer);
|
||||
free(_i2sBufDesc);
|
||||
}
|
||||
|
||||
bool IsReadyToUpdate() const
|
||||
bool IsReadyToUpdate()
|
||||
{
|
||||
return (_dmaState == NeoDmaState_Idle);
|
||||
}
|
||||
@@ -153,7 +180,8 @@ public:
|
||||
void Initialize()
|
||||
{
|
||||
StopDma();
|
||||
_dmaState = NeoDmaState_Sending; // start off sending empty buffer
|
||||
|
||||
pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA
|
||||
|
||||
uint8_t* is2Buffer = _i2sBuffer;
|
||||
uint32_t is2BufferSize = _i2sBufferSize;
|
||||
@@ -198,6 +226,11 @@ public:
|
||||
// setup the rest of i2s DMA
|
||||
//
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
// start off in sending state as that is what it will be all setup to be
|
||||
// for the interrupt
|
||||
_dmaState = NeoDmaState_Sending;
|
||||
|
||||
SLCC0 |= SLCRXLR | SLCTXLR;
|
||||
SLCC0 &= ~(SLCRXLR | SLCTXLR);
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
@@ -213,9 +246,11 @@ public:
|
||||
// expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
|
||||
// an error at us otherwise. Just feed it any random descriptor.
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
SLCTXL |= (uint32)&(_i2sBufDesc[_i2sBufDescCount-1]) << SLCTXLA; // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
|
||||
// set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
|
||||
SLCTXL |= (uint32)&(_i2sBufDesc[_i2sBufDescCount-1]) << SLCTXLA;
|
||||
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
|
||||
SLCRXL |= (uint32)_i2sBufDesc << SLCRXLA; // set RX descriptor address
|
||||
// set RX descriptor address. use first of the data addresses
|
||||
SLCRXL |= (uint32)&(_i2sBufDesc[0]) << SLCRXLA;
|
||||
|
||||
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
|
||||
SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt
|
||||
@@ -226,8 +261,6 @@ public:
|
||||
SLCTXL |= SLCTXLS;
|
||||
SLCRXL |= SLCRXLS;
|
||||
|
||||
pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA
|
||||
|
||||
I2S_CLK_ENABLE();
|
||||
I2SIC = 0x3F;
|
||||
I2SIE = 0;
|
||||
@@ -237,16 +270,18 @@ public:
|
||||
I2SC |= I2SRST;
|
||||
I2SC &= ~(I2SRST);
|
||||
|
||||
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
|
||||
// Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
|
||||
I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM));
|
||||
I2SFC |= I2SDE; //Enable DMA
|
||||
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM)); // Set RX/TX CHAN_MOD=0
|
||||
// Set RX/TX CHAN_MOD=0
|
||||
I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM));
|
||||
|
||||
// set the rate
|
||||
uint32_t i2s_clock_div = T_SPEED::I2sClockDivisor & I2SCDM;
|
||||
uint8_t i2s_bck_div = T_SPEED::I2sBaseClockDivisor & I2SBDM;
|
||||
|
||||
//!trans master, !bits mod, rece slave mod, rece msb shift, right first, msb right
|
||||
I2SC &= ~(I2STSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
||||
I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
|
||||
I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD);
|
||||
|
||||
I2SC |= I2STXS; // Start transmission
|
||||
@@ -255,12 +290,12 @@ public:
|
||||
void ICACHE_RAM_ATTR Update()
|
||||
{
|
||||
// wait for not actively sending data
|
||||
while (_dmaState != NeoDmaState_Idle)
|
||||
while (!IsReadyToUpdate())
|
||||
{
|
||||
yield();
|
||||
}
|
||||
FillBuffers();
|
||||
|
||||
|
||||
// toggle state so the ISR reacts
|
||||
_dmaState = NeoDmaState_Pending;
|
||||
}
|
||||
@@ -285,9 +320,9 @@ private:
|
||||
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
|
||||
|
||||
// normally 24 bytes creates the minimum 50us latch per spec, but
|
||||
// with the new logic, this latch is used to space between three states
|
||||
// buffer size = (24 * (speed / 50)) / 3
|
||||
uint8_t _i2sZeroes[(24L * (T_SPEED::ResetTimeUs / 50L)) / 3L];
|
||||
// with the new logic, this latch is used to space between mulitple states
|
||||
// buffer size = (24 * (reset time / 50)) / 6
|
||||
uint8_t _i2sZeroes[(24L * (T_SPEED::ResetTimeUs / 50L)) / 6L];
|
||||
|
||||
slc_queue_item* _i2sBufDesc; // dma block descriptors
|
||||
uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc
|
||||
@@ -301,15 +336,15 @@ private:
|
||||
// in the case of this code, the second to last state descriptor
|
||||
volatile static void ICACHE_RAM_ATTR i2s_slc_isr(void)
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
uint32_t slc_intr_status = SLCIS;
|
||||
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
|
||||
if (slc_intr_status & SLCIRXEOF)
|
||||
if ((slc_intr_status & SLCIRXEOF) && s_this)
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
switch (s_this->_dmaState)
|
||||
switch (s_this->_dmaState)
|
||||
{
|
||||
case NeoDmaState_Idle:
|
||||
break;
|
||||
@@ -335,14 +370,17 @@ private:
|
||||
// just looping and not sending the data blocks
|
||||
(finished_item + 1)->next_link_ptr = (uint32_t)(finished_item);
|
||||
|
||||
s_this->_dmaState = NeoDmaState_Idle;
|
||||
s_this->_dmaState = NeoDmaState_Zeroing;
|
||||
}
|
||||
break;
|
||||
|
||||
case NeoDmaState_Zeroing:
|
||||
s_this->_dmaState = NeoDmaState_Idle;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
}
|
||||
|
||||
ETS_SLC_INTR_ENABLE();
|
||||
}
|
||||
|
||||
void FillBuffers()
|
||||
@@ -367,6 +405,16 @@ private:
|
||||
void StopDma()
|
||||
{
|
||||
ETS_SLC_INTR_DISABLE();
|
||||
|
||||
// Disable any I2S send or receive
|
||||
I2SC &= ~(I2STXS | I2SRXS);
|
||||
|
||||
// Reset I2S
|
||||
I2SC &= ~(I2SRST);
|
||||
I2SC |= I2SRST;
|
||||
I2SC &= ~(I2SRST);
|
||||
|
||||
|
||||
SLCIC = 0xFFFFFFFF;
|
||||
SLCIE = 0;
|
||||
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
|
||||
@@ -374,6 +422,12 @@ private:
|
||||
|
||||
pinMode(c_I2sPin, INPUT);
|
||||
}
|
||||
|
||||
uint32_t getPixelTime() const
|
||||
{
|
||||
return (T_SPEED::ByteSendTimeUs * this->_pixelsSize);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template<typename T_SPEED>
|
||||
|
Reference in New Issue
Block a user