@@ -3,9 +3,9 @@ NeoPixel library helper functions for Esp8266.
33
44
55Written 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
1010I invest time and resources providing this open source code,
1111please 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
8891template <typename T_SPEED > class NeoEsp8266DmaMethodBase
8992{
9093public:
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
225263private:
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