Lumitronix_Iflex_Pro_Workshop
Library to interact with the iFlexPro
NeoEsp8266I2sDmx512Method.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2 LumitronixIFlex library helper functions for Esp8266.
3 
4 Written by Michael C. Miller.
5 
6 I invest time and resources providing this open source code,
7 please support me by dontating (see https://github.com/Makuna)
8 
9 -------------------------------------------------------------------------
10 This file is part of the LUMITRONIX_iFlex_Workshop library.
11 
12 LumitronixIFlexBus is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Lesser General Public License as
14 published by the Free Software Foundation, either version 3 of
15 the License, or (at your option) any later version.
16 
17 LumitronixIFlexBus is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Lesser General Public License for more details.
21 
22 You should have received a copy of the GNU Lesser General Public
23 License along with LumitronixIFlex. If not, see
24 <http://www.gnu.org/licenses/>.
25 -------------------------------------------------------------------------*/
26 
27 #pragma once
28 
29 #ifdef ARDUINO_ARCH_ESP8266
31 
32 
33 class NeoEsp8266I2sDmx512SpeedBase
34 {
35 public:
36  // 4 us bit send, 250Kbps
37  static const uint32_t I2sClockDivisor = 20; // 0-63
38  static const uint32_t I2sBaseClockDivisor = 32; // 0-63
39  static const uint32_t ByteSendTimeUs = 44; // us it takes to send a single pixel element of 11 bits
40  static const uint32_t BreakMabUs = 96; // Break min 92, Mab min 12
41  static const size_t BreakMabSize = 4; // roundupby((BreakMabUs/4us)/8,4) count of bytes needed for the Break+Mab timing
42  static const uint32_t MtbpUs = 11; // Mtbp, min 0, buy we use at least one byte of space (8*1.35)
43  static const size_t MtbpSize = 1; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing
44  // DMX requires the first slot to be zero
45  static const size_t HeaderSize = 1;
46 };
47 
48 class NeoEsp8266I2sDmx512Speed : public NeoEsp8266I2sDmx512SpeedBase
49 {
50 public:
51  static const uint8_t MtbpLevel = 0x1; // high
52  static const uint8_t StartBit = 0b00000000;
53  static const uint8_t StopBits = 0b00000011;
54  static const uint32_t Break = 0x00000000; // Break
55  static const uint32_t BreakMab = 0x00000007; // Break + Mab
56 
57  static uint8_t Convert(uint8_t value)
58  {
59  // DMX requires LSB order
60  return NeoUtil::Reverse8Bits( value );
61  }
62 };
63 
64 class NeoEsp8266I2sDmx512InvertedSpeed : public NeoEsp8266I2sDmx512SpeedBase
65 {
66 public:
67  static const uint8_t MtbpLevel = 0x00; // low
68  static const uint8_t StartBit = 0b00000001;
69  static const uint8_t StopBits = 0b00000000;
70  static const uint32_t Break = 0xffffffff; // Break
71  static const uint32_t BreakMab = 0xfffffff8; // Break + Mab
72 
73  static uint8_t Convert(uint8_t value)
74  {
75  // DMX requires LSB order
76  return NeoUtil::Reverse8Bits( ~value );
77  }
78 };
79 
80 
81 class NeoEsp8266I2sWs2821SpeedBase
82 {
83 public:
84  // 1.35 us bit send, 750Kbps
85  static const uint32_t I2sClockDivisor = 27; // 0-63
86  static const uint32_t I2sBaseClockDivisor = 8; // 0-63
87  static const uint32_t ByteSendTimeUs = 15; // us it takes to send a single pixel element of 11 bits
88  static const uint32_t BreakMabUs = 92; // Break min 88, Mab min 4
89  static const size_t BreakMabSize = 12; // roundupby((BreakMabUs/1.35)/8,4) count of bytes needed for the Break+Mab timing
90  static const uint32_t MtbpUs = 88; // Mtbp, min 88
91  static const size_t MtbpSize = 9; // (MtbpUs/1.35)/8 count of bytes needed for the Mtbp timing
92 
93  // DMX/WS2821 requires the first slot to be zero
94  static const size_t HeaderSize = 1;
95 };
96 
97 class NeoEsp8266I2sWs2821Speed : public NeoEsp8266I2sWs2821SpeedBase
98 {
99 public:
100  static const uint8_t MtbpLevel = 0x1; // high
101  static const uint8_t StartBit = 0b00000000;
102  static const uint8_t StopBits = 0b00000011;
103  static const uint32_t Break = 0x00000000; // Break
104  static const uint32_t BreakMab = 0x00000007; // Break + Mab (4~12us/1.35us)
105 
106  static uint8_t Convert(uint8_t value)
107  {
108  // DMX requires LSB order
109  return NeoUtil::Reverse8Bits(value);
110  }
111 };
112 
113 class NeoEsp8266I2sWs2821InvertedSpeed : public NeoEsp8266I2sWs2821SpeedBase
114 {
115 public:
116  static const uint8_t MtbpLevel = 0x00; // low
117  static const uint8_t StartBit = 0b00000001;
118  static const uint8_t StopBits = 0b00000000;
119  static const uint32_t Break = 0xffffffff; // Break
120  static const uint32_t BreakMab = 0xfffffff8; // Break + Mab
121 
122  static uint8_t Convert(uint8_t value)
123  {
124  // DMX requires LSB order
125  return NeoUtil::Reverse8Bits(~value);
126  }
127 };
128 
129 template<typename T_SPEED> class NeoEsp8266I2sDmx512MethodBase : NeoEsp8266I2sMethodCore
130 {
131 public:
132  typedef NeoNoSettings SettingsObject;
133 
134  NeoEsp8266I2sDmx512MethodBase(uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
135  _sizeData(pixelCount * elementSize + settingsSize + T_SPEED::HeaderSize)
136  {
137  size_t dmaPixelBits = I2sBitsPerPixelBytes * elementSize;
138  size_t dmaSettingsBits = I2sBitsPerPixelBytes * (settingsSize + T_SPEED::HeaderSize);
139 
140  // bits + half rounding byte of bits / bits per byte
141  size_t i2sBufferSize = (pixelCount * dmaPixelBits + dmaSettingsBits + 4) / 8;
142 
143  i2sBufferSize = i2sBufferSize + T_SPEED::BreakMabSize;
144 
145  // size is rounded up to nearest c_I2sByteBoundarySize
146  i2sBufferSize = NeoUtil::RoundUp(i2sBufferSize, c_I2sByteBoundarySize);
147 
148  // size of a looping silent space rounded up to nearest c_I2sByteBoundarySize
149  size_t i2sResetSize = NeoUtil::RoundUp(T_SPEED::MtbpSize, c_I2sByteBoundarySize);
150 
151  // protocol limits use of full block size to c_I2sByteBoundarySize
152  size_t is2BufMaxBlockSize = (c_maxDmaBlockSize / c_I2sByteBoundarySize) * c_I2sByteBoundarySize;
153 
154  _data = static_cast<uint8_t*>(malloc(_sizeData));
155  // first "slot" cleared due to protocol requiring it to be zero
156  memset(_data, 0x00, 1);
157 
158  AllocateI2s(i2sBufferSize, i2sResetSize, is2BufMaxBlockSize, T_SPEED::MtbpLevel);
159  }
160 
161  NeoEsp8266I2sDmx512MethodBase([[maybe_unused]] uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
162  NeoEsp8266I2sDmx512MethodBase(pixelCount, elementSize, settingsSize)
163  {
164  }
165 
166  ~NeoEsp8266I2sDmx512MethodBase()
167  {
168  uint8_t waits = 1;
169  while (!IsReadyToUpdate())
170  {
171  waits = 2;
172  yield();
173  }
174 
175  // wait for any pending sends to complete
176  // due to internal i2s caching/send delays, this can more that once the data size
177  uint32_t time = micros();
178  while ((micros() - time) < ((getPixelTime() + T_SPEED::MtbpUs) * waits))
179  {
180  yield();
181  }
182 
183  FreeI2s();
184 
185  free(_data);
186  }
187 
188  bool IsReadyToUpdate() const
189  {
190  return IsIdle();
191  }
192 
193  void Initialize()
194  {
195  InitializeI2s(T_SPEED::I2sClockDivisor, T_SPEED::I2sBaseClockDivisor);
196  }
197 
198  void IRAM_ATTR Update(bool)
199  {
200  // wait for not actively sending data
201  while (!IsReadyToUpdate())
202  {
203  yield();
204  }
205  FillBuffers();
206 
207  WriteI2s();
208  }
209 
210  bool AlwaysUpdate()
211  {
212  // this method requires update to be called only if changes to buffer
213  return false;
214  }
215 
216  uint8_t* getData() const
217  {
218  return _data + T_SPEED::HeaderSize;
219  };
220 
221  size_t getDataSize() const
222  {
223  return _sizeData - T_SPEED::HeaderSize;
224  }
225 
226  void applySettings([[maybe_unused]] const SettingsObject& settings)
227  {
228  }
229 
230 private:
231  // given 11 sending bits per pixel byte,
232  static const uint16_t I2sBitsPerPixelBytes = 11;
233 
234  const size_t _sizeData; // Size of '_data' buffer
235  uint8_t* _data; // Holds LED color values
236 
237  // encodes the data with start and stop bits
238  // input buffer is bytes
239  // output stream is uint31_t
240  static void Encoder(const uint8_t* pSrc, const uint8_t* pSrcEnd,
241  uint32_t* pOutput, const uint32_t* pOutputEnd)
242  {
243  static const uint32_t Mtbp = 0xffffffff * T_SPEED::MtbpLevel;
244  const uint8_t* pData = pSrc;
245 
246  int8_t outputBit = 32;
247  uint32_t output = 0;
248 
249  // DATA stream, one start, two stop
250  while (pData < pSrcEnd)
251  {
252  uint8_t data = T_SPEED::Convert( *(pData++) );
253 
254  if (outputBit > 10)
255  {
256  // simple
257  outputBit -= 1;
258  output |= T_SPEED::StartBit << outputBit;
259 
260  outputBit -= 8;
261  output |= data << outputBit;
262 
263  outputBit -= 2;
264  output |= T_SPEED::StopBits << outputBit;
265  }
266  else
267  {
268  // split across an output uint32_t
269  // handle start bit
270  if (outputBit < 1)
271  {
272  *(pOutput++) = output;
273  output = 0;
274  outputBit += 32;
275  }
276  outputBit -= 1;
277  output |= (T_SPEED::StartBit << outputBit);
278 
279  // handle data bits
280  if (outputBit < 8)
281  {
282  output |= data >> (8 - outputBit);
283 
284  *(pOutput++) = output;
285  output = 0;
286  outputBit += 32;
287  }
288  outputBit -= 8;
289  output |= data << outputBit;
290 
291  // handle stop bits
292  if (outputBit < 2)
293  {
294  output |= T_SPEED::StopBits >> (2 - outputBit);
295 
296  *(pOutput++) = output;
297  output = 0;
298  outputBit += 32;
299  }
300  outputBit -= 2;
301  output |= T_SPEED::StopBits << outputBit;
302  }
303  }
304  if (outputBit > 0)
305  {
306  // padd last output uint32_t with Mtbp
307  output |= Mtbp >> (32 - outputBit);
308  *(pOutput++) = output;
309  }
310  // fill the rest of the output with Mtbp
311  while (pOutput < pOutputEnd)
312  {
313  *(pOutput++) = Mtbp;
314  }
315  }
316 
317 
318  void FillBuffers()
319  {
320  uint32_t* pDma32 = reinterpret_cast<uint32_t*>(_i2sBuffer);
321  const uint32_t* pDma32End = reinterpret_cast<uint32_t*>(_i2sBuffer + _i2sBufferSize);
322 
323  // first insert Break space as needed
324  for (size_t count = 1;
325  count < (T_SPEED::BreakMabSize/sizeof(T_SPEED::Break));
326  count++)
327  {
328  *(pDma32++) = T_SPEED::Break;
329  }
330  // then tail of break with mab
331  *(pDma32++) = T_SPEED::BreakMab;
332 
333  Encoder(_data, _data + _sizeData, pDma32, pDma32End);
334  }
335 
336  uint32_t getPixelTime() const
337  {
338  return (T_SPEED::ByteSendTimeUs * this->_sizeData);
339  };
340 
341 };
342 
343 
344 // normal
345 typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sDmx512Speed> NeoEsp8266Dmx512Method;
346 typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sWs2821Speed> NeoEsp8266Ws2821Method;
347 
348 // inverted
349 typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sDmx512InvertedSpeed> NeoEsp8266Dmx512InvertedMethod;
350 typedef NeoEsp8266I2sDmx512MethodBase<NeoEsp8266I2sWs2821InvertedSpeed> NeoEsp8266Ws2821InvertedMethod;
351 
352 #endif
Definition: NeoSettings.h:29
static size_t RoundUp(size_t numToRound, size_t multiple)
Definition: NeoUtil.h:62
static uint8_t Reverse8Bits(uint8_t n)
Definition: NeoUtil.h:57