Lumitronix_Iflex_Pro_Workshop
Library to interact with the iFlexPro
NeoEsp8266DmaMethod.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2 LumitronixIFlex library helper functions for Esp8266.
3 
4 
5 Written by Michael C. Miller.
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).
9 
10 I invest time and resources providing this open source code,
11 please support me by dontating (see https://github.com/Makuna)
12 
13 -------------------------------------------------------------------------
14 This file is part of the LUMITRONIX_iFlex_Workshop library.
15 
16 LumitronixIFlexBus is free software: you can redistribute it and/or modify
17 it under the terms of the GNU Lesser General Public License as
18 published by the Free Software Foundation, either version 3 of
19 the License, or (at your option) any later version.
20 
21 LumitronixIFlexBus is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU Lesser General Public License for more details.
25 
26 You should have received a copy of the GNU Lesser General Public
27 License along with LumitronixIFlex. If not, see
28 <http://www.gnu.org/licenses/>.
29 -------------------------------------------------------------------------*/
30 
31 #pragma once
32 
33 #ifdef ARDUINO_ARCH_ESP8266
35 
36 class NeoEsp8266DmaSpeedBase
37 {
38 public:
39  static const uint8_t IdleLevel = 0;
40  static uint16_t Convert(uint8_t value)
41  {
42  const uint16_t bitpatterns[16] =
43  {
44  0b1000100010001000, 0b1000100010001110, 0b1000100011101000, 0b1000100011101110,
45  0b1000111010001000, 0b1000111010001110, 0b1000111011101000, 0b1000111011101110,
46  0b1110100010001000, 0b1110100010001110, 0b1110100011101000, 0b1110100011101110,
47  0b1110111010001000, 0b1110111010001110, 0b1110111011101000, 0b1110111011101110,
48  };
49 
50  return bitpatterns[value];
51  }
52 };
53 
54 class NeoEsp8266DmaInvertedSpeedBase
55 {
56 public:
57  static const uint8_t IdleLevel = 1;
58  static uint16_t Convert(uint8_t value)
59  {
60  const uint16_t bitpatterns[16] =
61  {
62  0b0111011101110111, 0b0111011101110001, 0b0111011100010111, 0b0111011100010001,
63  0b0111000101110111, 0b0111000101110001, 0b0111000100010111, 0b0111000100010001,
64  0b0001011101110111, 0b0001011101110001, 0b0001011100010111, 0b0001011100010001,
65  0b0001000101110111, 0b0001000101110001, 0b0001000100010111, 0b0001000100010001,
66  };
67 
68  return bitpatterns[value];
69  }
70 };
71 
72 class NeoEsp8266DmaSpeed800KbpsBase : public NeoEsp8266DmaSpeedBase
73 {
74 public:
75  const static uint32_t I2sClockDivisor = 5; // 0-63
76  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
77  const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
78 };
79 
80 class NeoEsp8266DmaSpeedWs2812x : public NeoEsp8266DmaSpeed800KbpsBase
81 {
82 public:
83  const static uint32_t ResetTimeUs = 300;
84 };
85 
86 class NeoEsp8266DmaSpeedSk6812 : public NeoEsp8266DmaSpeed800KbpsBase
87 {
88 public:
89  const static uint32_t ResetTimeUs = 80;
90 };
91 
92 class NeoEsp8266DmaInvertedSpeedTm1814 : public NeoEsp8266DmaSpeed800KbpsBase
93 {
94 public:
95  const static uint32_t ResetTimeUs = 200;
96 };
97 
98 class NeoEsp8266DmaInvertedSpeedTm1829 : public NeoEsp8266DmaSpeed800KbpsBase
99 {
100 public:
101  const static uint32_t ResetTimeUs = 200;
102 };
103 
104 class NeoEsp8266DmaSpeed800Kbps : public NeoEsp8266DmaSpeed800KbpsBase
105 {
106 public:
107  const static uint32_t ResetTimeUs = 50;
108 };
109 
110 class NeoEsp8266DmaSpeed400Kbps : public NeoEsp8266DmaSpeedBase
111 {
112 public:
113  const static uint32_t I2sClockDivisor = 10; // 0-63
114  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
115  const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
116  const static uint32_t ResetTimeUs = 50;
117 };
118 
119 class NeoEsp8266DmaSpeedApa106 : public NeoEsp8266DmaSpeedBase
120 {
121 public:
122  const static uint32_t I2sClockDivisor = 4; // 0-63
123  const static uint32_t I2sBaseClockDivisor = 17; // 0-63
124  const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element
125  const static uint32_t ResetTimeUs = 50;
126 };
127 
128 class NeoEsp8266DmaSpeedIntertek : public NeoEsp8266DmaSpeedBase
129 {
130 public:
131  const static uint32_t I2sClockDivisor = 5; // 0-63
132  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
133  const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element
134  const static uint32_t ResetTimeUs = 12470;
135  const static uint32_t InterPixelTimeUs = 20;
136 };
137 
138 class NeoEsp8266DmaInvertedSpeed800KbpsBase : public NeoEsp8266DmaInvertedSpeedBase
139 {
140 public:
141  const static uint32_t I2sClockDivisor = 5; // 0-63
142  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
143  const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
144 };
145 
146 class NeoEsp8266DmaInvertedSpeedWs2812x : public NeoEsp8266DmaInvertedSpeed800KbpsBase
147 {
148 public:
149  const static uint32_t ResetTimeUs = 300;
150 };
151 
152 class NeoEsp8266DmaInvertedSpeedSk6812 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
153 {
154 public:
155  const static uint32_t ResetTimeUs = 80;
156 };
157 
158 class NeoEsp8266DmaSpeedTm1814 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
159 {
160 public:
161  const static uint32_t ResetTimeUs = 200;
162 };
163 
164 class NeoEsp8266DmaSpeedTm1829 : public NeoEsp8266DmaInvertedSpeed800KbpsBase
165 {
166 public:
167  const static uint32_t ResetTimeUs = 200;
168 };
169 
170 class NeoEsp8266DmaInvertedSpeed800Kbps : public NeoEsp8266DmaInvertedSpeed800KbpsBase
171 {
172 public:
173  const static uint32_t ResetTimeUs = 50;
174 };
175 
176 class NeoEsp8266DmaInvertedSpeed400Kbps : public NeoEsp8266DmaInvertedSpeedBase
177 {
178 public:
179  const static uint32_t I2sClockDivisor = 10; // 0-63
180  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
181  const static uint32_t ByteSendTimeUs = 20; // us it takes to send a single pixel element at 400khz speed
182  const static uint32_t ResetTimeUs = 50;
183 };
184 
185 class NeoEsp8266DmaInvertedSpeedApa106 : public NeoEsp8266DmaInvertedSpeedBase
186 {
187 public:
188  const static uint32_t I2sClockDivisor = 4; // 0-63
189  const static uint32_t I2sBaseClockDivisor = 17; // 0-63
190  const static uint32_t ByteSendTimeUs = 14; // us it takes to send a single pixel element
191  const static uint32_t ResetTimeUs = 50;
192 };
193 
194 class NeoEsp8266DmaInvertedSpeedIntertek : public NeoEsp8266DmaInvertedSpeedBase
195 {
196 public:
197  const static uint32_t I2sClockDivisor = 5; // 0-63
198  const static uint32_t I2sBaseClockDivisor = 10; // 0-63
199  const static uint32_t ByteSendTimeUs = 10; // us it takes to send a single pixel element at 800khz speed
200  const static uint32_t ResetTimeUs = 12470;
201  const static uint32_t InterPixelTimeUs = 20;
202 };
203 
204 template<typename T_SPEED> class NeoEsp8266DmaEncode : public T_SPEED
205 {
206 public:
207  static size_t SpacingPixelSize(size_t sizePixel)
208  {
209  return sizePixel;
210  }
211 
212  static void FillBuffers(uint8_t* i2sBuffer,
213  const uint8_t* data,
214  size_t sizeData,
215  [[maybe_unused]] size_t sizePixel)
216  {
217  uint16_t* pDma = (uint16_t*)i2sBuffer;
218  const uint8_t* pEnd = data + sizeData;
219  for (const uint8_t* pData = data; pData < pEnd; pData++)
220  {
221  *(pDma++) = T_SPEED::Convert(((*pData) & 0x0f));
222  *(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f);
223  }
224  }
225 };
226 
227 template<typename T_SPEED> class NeoEsp8266DmaPixelSpacingEncode : public T_SPEED
228 {
229 public:
230  static size_t SpacingPixelSize(size_t sizePixel)
231  {
232  return sizePixel + T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs;
233  }
234 
235  static void FillBuffers(uint8_t* i2sBuffer,
236  const uint8_t* data,
237  size_t sizeData,
238  size_t sizePixel)
239  {
240  uint16_t* pDma = (uint16_t*)i2sBuffer;
241  const uint8_t* pEnd = data + sizeData;
242  uint8_t element = 0;
243  for (const uint8_t* pData = data; pData < pEnd; pData++)
244  {
245  *(pDma++) = T_SPEED::Convert(((*pData) & 0x0f));
246  *(pDma++) = T_SPEED::Convert(((*pData) >> 4) & 0x0f);
247 
248  element++;
249  if (element == sizePixel)
250  {
251  element = 0;
252 
253  for (uint8_t padding = 0;
254  padding < (T_SPEED::InterPixelTimeUs / T_SPEED::ByteSendTimeUs);
255  padding++)
256  {
257  *(pDma++) = T_SPEED::IdleLevel * 0xffff;
258  *(pDma++) = T_SPEED::IdleLevel * 0xffff;
259  }
260  }
261  }
262  }
263 };
264 
265 template<typename T_ENCODER> class NeoEsp8266DmaMethodBase : NeoEsp8266I2sMethodCore
266 {
267 public:
268  typedef NeoNoSettings SettingsObject;
269 
270  NeoEsp8266DmaMethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
271  _sizePixel(elementSize),
272  _sizeData(pixelCount * elementSize + settingsSize)
273  {
274  size_t dmaPixelSize = DmaBytesPerPixelBytes * T_ENCODER::SpacingPixelSize(_sizePixel);
275  size_t dmaSettingsSize = DmaBytesPerPixelBytes * settingsSize;
276 
277  size_t i2sBufferSize = pixelCount * dmaPixelSize + dmaSettingsSize;
278  // size is rounded up to nearest c_I2sByteBoundarySize
279  i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize);
280 
281  // calculate a buffer size that takes reset amount of time
282  size_t i2sResetSize = T_ENCODER::ResetTimeUs * DmaBytesPerPixelBytes / T_ENCODER::ByteSendTimeUs;
283  // size is rounded up to nearest c_I2sByteBoundarySize
284  i2sResetSize = NeoUtil::RoundUp(i2sResetSize, c_I2sByteBoundarySize);
285  size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / dmaPixelSize) * dmaPixelSize;
286 
287  _data = static_cast<uint8_t*>(malloc(_sizeData));
288  // data cleared later in Begin()
289 
290  AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_ENCODER::IdleLevel);
291  }
292 
293  NeoEsp8266DmaMethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
294  NeoEsp8266DmaMethodBase(pixelCount, elementSize, settingsSize)
295  {
296  }
297 
298  ~NeoEsp8266DmaMethodBase()
299  {
300  uint8_t waits = 1;
301  while (!IsReadyToUpdate())
302  {
303  waits = 2;
304  yield();
305  }
306 
307  // wait for any pending sends to complete
308  // due to internal i2s caching/send delays, this can more that once the data size
309  uint32_t time = micros();
310  while ((micros() - time) < ((getPixelTime() + T_ENCODER::ResetTimeUs) * waits))
311  {
312  yield();
313  }
314 
315  FreeI2s();
316 
317  free(_data);
318  }
319 
320  bool IsReadyToUpdate() const
321  {
322  return IsIdle();
323  }
324 
325  void Initialize()
326  {
327  InitializeI2s(T_ENCODER::I2sClockDivisor, T_ENCODER::I2sBaseClockDivisor);
328  }
329 
330  void IRAM_ATTR Update(bool)
331  {
332  // wait for not actively sending data
333  while (!IsReadyToUpdate())
334  {
335  yield();
336  }
337  T_ENCODER::FillBuffers(_i2sBuffer, _data, _sizeData, _sizePixel);
338 
339  WriteI2s();
340  }
341 
342  bool AlwaysUpdate()
343  {
344  // this method requires update to be called only if changes to buffer
345  return false;
346  }
347 
348  uint8_t* getData() const
349  {
350  return _data;
351  };
352 
353  size_t getDataSize() const
354  {
355  return _sizeData;
356  }
357 
358  void applySettings([[maybe_unused]] const SettingsObject& settings)
359  {
360  }
361 
362 private:
363  // due to encoding required for i2s, we need 4 bytes to encode the pulses
364  static const uint16_t DmaBytesPerPixelBytes = 4;
365 
366  const size_t _sizePixel; // size of a pixel in _data
367  const size_t _sizeData; // Size of '_data' buffer
368  uint8_t* _data; // Holds LED color values
369 
370  uint32_t getPixelTime() const
371  {
372  return (T_ENCODER::ByteSendTimeUs * GetSendSize() / DmaBytesPerPixelBytes);
373  };
374 
375 };
376 
377 
378 
379 // normal
380 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedWs2812x>> NeoEsp8266DmaWs2812xMethod;
381 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedSk6812>> NeoEsp8266DmaSk6812Method;
382 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedTm1814>> NeoEsp8266DmaTm1814Method;
383 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedTm1829>> NeoEsp8266DmaTm1829Method;
384 typedef NeoEsp8266DmaTm1814Method NeoEsp8266DmaTm1914Method;
385 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeed800Kbps>> NeoEsp8266Dma800KbpsMethod;
386 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeed400Kbps>> NeoEsp8266Dma400KbpsMethod;
387 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaSpeedApa106>> NeoEsp8266DmaApa106Method;
388 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaPixelSpacingEncode<NeoEsp8266DmaSpeedIntertek>> NeoEsp8266DmaIntertekMethod;
389 
390 
391 // inverted
392 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedWs2812x>> NeoEsp8266DmaInvertedWs2812xMethod;
393 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedSk6812>> NeoEsp8266DmaInvertedSk6812Method;
394 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedTm1814>> NeoEsp8266DmaInvertedTm1814Method;
395 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedTm1829>> NeoEsp8266DmaInvertedTm1829Method;
396 typedef NeoEsp8266DmaInvertedTm1814Method NeoEsp8266DmaInvertedTm1914Method;
397 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeed800Kbps>> NeoEsp8266DmaInverted800KbpsMethod;
398 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeed400Kbps>> NeoEsp8266DmaInverted400KbpsMethod;
399 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaEncode<NeoEsp8266DmaInvertedSpeedApa106>> NeoEsp8266DmaInvertedApa106Method;
400 typedef NeoEsp8266DmaMethodBase<NeoEsp8266DmaPixelSpacingEncode<NeoEsp8266DmaInvertedSpeedIntertek>> NeoEsp8266DmaInvertedIntertekMethod;
401 
402 // Dma method is the default method for Esp8266
403 typedef NeoEsp8266DmaWs2812xMethod NeoWs2813Method;
404 typedef NeoEsp8266DmaWs2812xMethod NeoWs2812xMethod;
405 typedef NeoEsp8266Dma800KbpsMethod NeoWs2812Method;
406 typedef NeoEsp8266DmaWs2812xMethod NeoWs2811Method;
407 typedef NeoEsp8266DmaWs2812xMethod NeoWs2816Method;
408 typedef NeoEsp8266DmaSk6812Method NeoSk6812Method;
409 typedef NeoEsp8266DmaTm1814Method NeoTm1814Method;
410 typedef NeoEsp8266DmaTm1829Method NeoTm1829Method;
411 typedef NeoEsp8266DmaTm1914Method NeoTm1914Method;
412 typedef NeoEsp8266DmaSk6812Method NeoLc8812Method;
413 typedef NeoEsp8266DmaApa106Method NeoApa106Method;
414 typedef NeoEsp8266DmaIntertekMethod NeoIntertekMethod;
415 
416 typedef NeoEsp8266DmaWs2812xMethod Neo800KbpsMethod;
417 typedef NeoEsp8266Dma400KbpsMethod Neo400KbpsMethod;
418 
419 // inverted
420 typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2813InvertedMethod;
421 typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2812xInvertedMethod;
422 typedef NeoEsp8266DmaInverted800KbpsMethod NeoWs2812InvertedMethod;
423 typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2811InvertedMethod;
424 typedef NeoEsp8266DmaInvertedWs2812xMethod NeoWs2816InvertedMethod;
425 typedef NeoEsp8266DmaInvertedSk6812Method NeoSk6812InvertedMethod;
426 typedef NeoEsp8266DmaInvertedTm1814Method NeoTm1814InvertedMethod;
427 typedef NeoEsp8266DmaInvertedTm1829Method NeoTm1829InvertedMethod;
428 typedef NeoEsp8266DmaInvertedTm1914Method NeoTm1914InvertedMethod;
429 typedef NeoEsp8266DmaInvertedSk6812Method NeoLc8812InvertedMethod;
430 typedef NeoEsp8266DmaInvertedApa106Method NeoApa106InvertedMethod;
431 typedef NeoEsp8266DmaInvertedIntertekMethod NeoInvertedIntertekMethod;
432 
433 typedef NeoEsp8266DmaInvertedWs2812xMethod Neo800KbpsInvertedMethod;
434 typedef NeoEsp8266DmaInverted400KbpsMethod Neo400KbpsInvertedMethod;
435 #endif
Definition: NeoSettings.h:29
static size_t RoundUp(size_t numToRound, size_t multiple)
Definition: NeoUtil.h:62