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:
Michael Miller
2019-01-14 17:00:00 -08:00
committed by GitHub
parent 037d9be562
commit 57bf7e83cb

View File

@@ -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
@@ -93,6 +94,7 @@ class NeoEsp8266DmaSpeed400Kbps
public:
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;
@@ -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,7 +290,7 @@ public:
void ICACHE_RAM_ATTR Update()
{
// wait for not actively sending data
while (_dmaState != NeoDmaState_Idle)
while (!IsReadyToUpdate())
{
yield();
}
@@ -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>