Skip to content

Commit 89dcbb7

Browse files
committed
Merge pull request #91 from Makuna/Esp8266DmaLargerCountSupport
Esp8266 Dma Method now supports dynamic DMA duffer descriptors
2 parents 302c974 + 9244cbb commit 89dcbb7

2 files changed

Lines changed: 110 additions & 72 deletions

File tree

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=NeoPixelBus by Makuna
2-
version=2.0.6
2+
version=2.0.7
33
author=Michael C. Miller (makuna@live.com)
44
maintainer=Michael C. Miller (makuna@live.com)
55
sentence=A library that makes controlling NeoPixels (WS2811, WS2812 & SK6812) easy.

src/internal/NeoEsp8266DmaMethod.h

Lines changed: 109 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ NeoPixel library helper functions for Esp8266.
33
44
55
Written by Michael C. Miller.
6-
Thanks to g3gg0.de for porting the initial DMA support.
7-
Thanks to github/cnlohr for the original work on DMA support, which is
8-
located at https://github.com/cnlohr/esp8266ws2812i2s.
6+
Thanks to g3gg0.de for porting the initial DMA support which lead to this.
7+
Thanks to github/cnlohr for the original work on DMA support, which opend
8+
all our minds to a better way (located at https://github.com/cnlohr/esp8266ws2812i2s).
99
1010
I invest time and resources providing this open source code,
1111
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)
@@ -84,32 +84,43 @@ enum NeoDmaState
8484
NeoDmaState_Pending,
8585
NeoDmaState_Sending,
8686
};
87+
const uint16_t c_maxDmaBlockSize = 4095;
88+
const uint16_t c_dmaBytesPerPixelBytes = 4;
89+
const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted to this
8790

8891
template<typename T_SPEED> class NeoEsp8266DmaMethodBase
8992
{
9093
public:
9194
NeoEsp8266DmaMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize)
9295
{
93-
_sizePixels = pixelCount * elementSize;
94-
_bitBufferSize = CalculateI2sBufferSize(pixelCount, elementSize);
96+
uint16_t dmaPixelSize = c_dmaBytesPerPixelBytes * elementSize;
9597

96-
_pixels = (uint8_t*)malloc(_sizePixels);
97-
memset(_pixels, 0x00, _sizePixels);
98+
_pixelsSize = pixelCount * elementSize;
99+
_i2sBufferSize = pixelCount * dmaPixelSize;
98100

99-
_i2sBlock = (uint8_t*)malloc(_bitBufferSize);
100-
memset(_i2sBlock, 0x00, _bitBufferSize);
101+
_pixels = (uint8_t*)malloc(_pixelsSize);
102+
memset(_pixels, 0x00, _pixelsSize);
103+
104+
_i2sBuffer = (uint8_t*)malloc(_i2sBufferSize);
105+
memset(_i2sBuffer, 0x00, _i2sBufferSize);
101106

102107
memset(_i2sZeroes, 0x00, sizeof(_i2sZeroes));
103108

104-
s_this = this;
109+
_is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize;
110+
111+
_i2sBufDescCount = (_i2sBufferSize / _is2BufMaxBlockSize) + 1 + 2; // need two more for state/latch blocks
112+
_i2sBufDesc = (slc_queue_item*)malloc(_i2sBufDescCount * sizeof(slc_queue_item));
113+
114+
s_this = this; // store this for the ISR
105115
}
106116

107117
~NeoEsp8266DmaMethodBase()
108118
{
109119
StopDma();
110120

111121
free(_pixels);
112-
free(_i2sBlock);
122+
free(_i2sBuffer);
123+
free(_i2sBufDesc);
113124
}
114125

115126
bool IsReadyToUpdate() const
@@ -119,31 +130,58 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
119130

120131
void Initialize()
121132
{
122-
_dmaState = NeoDmaState_Idle;
123-
124-
// prepare linked DMA descriptors, having EOF set only for the data
125-
// primary data item
126-
_i2sBufDescData.owner = 1;
127-
_i2sBufDescData.eof = 1;
128-
_i2sBufDescData.sub_sof = 0;
129-
_i2sBufDescData.datalen = sizeof(_i2sZeroes); // will get modified in ISR
130-
_i2sBufDescData.blocksize = sizeof(_i2sZeroes); // will get modified in ISR
131-
_i2sBufDescData.buf_ptr = (uint32_t)_i2sZeroes; // will get modified in ISR
132-
_i2sBufDescData.unused = 0;
133-
_i2sBufDescData.next_link_ptr = (uint32_t)&_i2sBufDescLatch;
134-
135-
// this zero-buffer block helps implements the latch/reset signal
136-
// and gives the ISR time to modify buf_ptr in _i2sBufDescData
137-
_i2sBufDescLatch.owner = 1;
138-
_i2sBufDescLatch.eof = 0; // no need to trigger interrupt
139-
_i2sBufDescLatch.sub_sof = 0;
140-
_i2sBufDescLatch.datalen = sizeof(_i2sZeroes);
141-
_i2sBufDescLatch.blocksize = sizeof(_i2sZeroes);
142-
_i2sBufDescLatch.buf_ptr = (uint32_t)_i2sZeroes;
143-
_i2sBufDescLatch.unused = 0;
144-
_i2sBufDescLatch.next_link_ptr = (uint32_t)&_i2sBufDescData;
145-
133+
_dmaState = NeoDmaState_Sending; // start off sending empty buffer
134+
135+
uint8_t* is2Buffer = _i2sBuffer;
136+
uint32_t is2BufferSize = _i2sBufferSize;
137+
uint16_t indexDesc;
138+
139+
// prepare main data block decriptors that point into our one static dma buffer
140+
for (indexDesc = 0; indexDesc < (_i2sBufDescCount - 2); indexDesc++)
141+
{
142+
uint32_t blockSize = (is2BufferSize > _is2BufMaxBlockSize) ? _is2BufMaxBlockSize : is2BufferSize;
143+
144+
_i2sBufDesc[indexDesc].owner = 1;
145+
_i2sBufDesc[indexDesc].eof = 0; // no need to trigger interrupt generally
146+
_i2sBufDesc[indexDesc].sub_sof = 0;
147+
_i2sBufDesc[indexDesc].datalen = blockSize;
148+
_i2sBufDesc[indexDesc].blocksize = blockSize;
149+
_i2sBufDesc[indexDesc].buf_ptr = (uint32_t)is2Buffer;
150+
_i2sBufDesc[indexDesc].unused = 0;
151+
_i2sBufDesc[indexDesc].next_link_ptr = (uint32_t)&(_i2sBufDesc[indexDesc + 1]);
152+
153+
Serial.print("block #");
154+
Serial.print(indexDesc);
155+
Serial.print(" 0x");
156+
Serial.print(_i2sBufDesc[indexDesc].buf_ptr, HEX);
157+
Serial.print(" (");
158+
Serial.print(_i2sBufDesc[indexDesc].blocksize);
159+
Serial.println(")");
160+
161+
is2Buffer += blockSize;
162+
is2BufferSize -= blockSize;
163+
}
164+
165+
// prepare the two state/latch descriptors
166+
for (; indexDesc < _i2sBufDescCount; indexDesc++)
167+
{
168+
_i2sBufDesc[indexDesc].owner = 1;
169+
_i2sBufDesc[indexDesc].eof = 0; // no need to trigger interrupt generally
170+
_i2sBufDesc[indexDesc].sub_sof = 0;
171+
_i2sBufDesc[indexDesc].datalen = sizeof(_i2sZeroes);
172+
_i2sBufDesc[indexDesc].blocksize = sizeof(_i2sZeroes);
173+
_i2sBufDesc[indexDesc].buf_ptr = (uint32_t)_i2sZeroes;
174+
_i2sBufDesc[indexDesc].unused = 0;
175+
_i2sBufDesc[indexDesc].next_link_ptr = (uint32_t)&(_i2sBufDesc[indexDesc + 1]);
176+
}
177+
178+
// the first state block will trigger the interrupt
179+
_i2sBufDesc[indexDesc - 2].eof = 1;
180+
// the last state block will loop to the first state block by defualt
181+
_i2sBufDesc[indexDesc - 1].next_link_ptr = (uint32_t)&(_i2sBufDesc[indexDesc - 2]);
146182

183+
// setup the rest of i2s DMA
184+
//
147185
ETS_SLC_INTR_DISABLE();
148186
SLCC0 |= SLCRXLR | SLCTXLR;
149187
SLCC0 &= ~(SLCRXLR | SLCTXLR);
@@ -160,9 +198,9 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
160198
// expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
161199
// an error at us otherwise. Just feed it any random descriptor.
162200
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
163-
SLCTXL |= (uint32)&_i2sBufDescLatch << SLCTXLA; // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
201+
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
164202
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
165-
SLCRXL |= (uint32)&_i2sBufDescData << SLCRXLA; // set RX descriptor address
203+
SLCRXL |= (uint32)_i2sBufDesc << SLCRXLA; // set RX descriptor address
166204

167205
ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
168206
SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt
@@ -173,7 +211,7 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
173211
SLCTXL |= SLCTXLS;
174212
SLCRXL |= SLCRXLS;
175213

176-
pinMode(3, FUNCTION_1); // I2S0_DATA
214+
pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA
177215

178216
I2S_CLK_ENABLE();
179217
I2SIC = 0x3F;
@@ -219,29 +257,32 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
219257

220258
size_t getPixelsSize() const
221259
{
222-
return _sizePixels;
260+
return _pixelsSize;
223261
}
224262

225263
private:
226264
static NeoEsp8266DmaMethodBase* s_this; // for the ISR
227265

228-
size_t _sizePixels; // Size of '_pixels' buffer below
266+
size_t _pixelsSize; // Size of '_pixels' buffer
229267
uint8_t* _pixels; // Holds LED color values
230268

231-
struct slc_queue_item _i2sBufDescData;
232-
struct slc_queue_item _i2sBufDescLatch;
269+
uint32_t _i2sBufferSize; // total size of _i2sBuffer
270+
uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
233271

234-
uint32_t _bitBufferSize;
235-
uint8_t* _i2sBlock;
236-
// normally 24 bytes creates the minimum 50us latch per spec but
272+
// normally 24 bytes creates the minimum 50us latch per spec, but
237273
// with the new logic, this latch is used to space between three states
238274
uint8_t _i2sZeroes[8];
239275

276+
slc_queue_item* _i2sBufDesc; // dma block descriptors
277+
uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc
278+
uint16_t _is2BufMaxBlockSize; // max size based on size of a pixel of a single block
279+
240280
volatile NeoDmaState _dmaState;
241281

242282
// This routine is called as soon as the DMA routine has something to tell us. All we
243283
// handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose
244284
// descriptor has the 'EOF' field set to 1.
285+
// in the case of this code, the second to last state descriptor
245286
volatile static void ICACHE_RAM_ATTR i2s_slc_isr(void)
246287
{
247288
uint32_t slc_intr_status = SLCIS;
@@ -252,45 +293,42 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
252293
{
253294
ETS_SLC_INTR_DISABLE();
254295

255-
slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA;
256-
257-
if (finished_item == &(s_this->_i2sBufDescData))
296+
switch (s_this->_dmaState)
258297
{
259-
switch (s_this->_dmaState)
298+
case NeoDmaState_Idle:
299+
break;
300+
301+
case NeoDmaState_Pending:
260302
{
261-
case NeoDmaState_Idle:
262-
break;
303+
slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA;
263304

264-
case NeoDmaState_Pending:
265305
// data block has pending data waiting to send, prepare it
266-
finished_item->datalen = s_this->_bitBufferSize;
267-
finished_item->blocksize = s_this->_bitBufferSize;
268-
finished_item->buf_ptr = (uint32_t)(s_this->_i2sBlock);
306+
// point last state block to top
307+
(finished_item + 1)->next_link_ptr = (uint32_t)(s_this->_i2sBufDesc);
269308

270309
s_this->_dmaState = NeoDmaState_Sending;
271-
break;
310+
}
311+
break;
312+
313+
case NeoDmaState_Sending:
314+
{
315+
slc_queue_item* finished_item = (slc_queue_item*)SLCRXEDA;
272316

273-
case NeoDmaState_Sending:
274-
// the data block had actual data, clear it
275-
finished_item->datalen = sizeof(s_this->_i2sZeroes);
276-
finished_item->blocksize = sizeof(s_this->_i2sZeroes);
277-
finished_item->buf_ptr = (uint32_t)(s_this->_i2sZeroes);
317+
// the data block had actual data sent
318+
// point last state block to first state block thus
319+
// just looping and not sending the data blocks
320+
(finished_item + 1)->next_link_ptr = (uint32_t)(finished_item);
278321

279322
s_this->_dmaState = NeoDmaState_Idle;
280-
break;
281323
}
324+
break;
282325
}
283326

327+
284328
ETS_SLC_INTR_ENABLE();
285329
}
286330
}
287331

288-
static uint32_t CalculateI2sBufferSize(uint16_t pixelCount, size_t elementSize)
289-
{
290-
// 4 I2S bytes per pixels byte
291-
return ((uint32_t)pixelCount * elementSize * 4);
292-
}
293-
294332
void FillBuffers()
295333
{
296334
const uint16_t bitpatterns[16] =
@@ -301,8 +339,8 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
301339
0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
302340
};
303341

304-
uint16_t* pDma = (uint16_t*)_i2sBlock;
305-
uint8_t* pPixelsEnd = _pixels + _sizePixels;
342+
uint16_t* pDma = (uint16_t*)_i2sBuffer;
343+
uint8_t* pPixelsEnd = _pixels + _pixelsSize;
306344
for (uint8_t* pPixel = _pixels; pPixel < pPixelsEnd; pPixel++)
307345
{
308346
*(pDma++) = bitpatterns[((*pPixel) & 0x0f)];
@@ -318,7 +356,7 @@ template<typename T_SPEED> class NeoEsp8266DmaMethodBase
318356
SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
319357
SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
320358

321-
pinMode(3, INPUT);
359+
pinMode(c_I2sPin, INPUT);
322360
}
323361
};
324362

0 commit comments

Comments
 (0)