Lumitronix_Iflex_Pro_Workshop
Library to interact with the iFlexPro
NeoEsp8266I2sMethodCore.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 #ifdef ARDUINO_ARCH_ESP8266
29 
30 #include "Arduino.h"
31 
32 extern "C"
33 {
34 #include "osapi.h"
35 #include "ets_sys.h"
36 
37 #include "i2s_reg.h"
38 
39 #ifdef ARDUINO_ESP8266_MAJOR //this define was added in ESP8266 Arduino Core version v3.0.1
40 #include "core_esp8266_i2s.h" //for Arduino core >= 3.0.1
41 #else
42 #include "i2s.h" //for Arduino core <= 3.0.0
43 #endif
44 
45 #include "eagle_soc.h"
46 #include "esp8266_peri.h"
47 #include "slc_register.h"
48 
49 #include "osapi.h"
50 #include "ets_sys.h"
51 #include "user_interface.h"
52 
53 #if !defined(__CORE_ESP8266_VERSION_H) || defined(ARDUINO_ESP8266_RELEASE_2_5_0)
54  void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id, uint32_t reg_add, uint32_t Msb, uint32_t Lsb, uint32_t indata);
55 #endif
56 }
57 
58 struct slc_queue_item
59 {
60  uint32 blocksize : 12;
61  uint32 datalen : 12;
62  uint32 unused : 5;
63  uint32 sub_sof : 1;
64  uint32 eof : 1;
65  uint32 owner : 1;
66  uint8* buf_ptr;
67  struct slc_queue_item* next_link_ptr;
68 };
69 
70 enum NeoDmaState
71 {
72  NeoDmaState_Idle,
73  NeoDmaState_Pending,
74  NeoDmaState_Sending
75 };
76 
77 const uint16_t c_maxDmaBlockSize = 4095;
78 
79 const uint8_t c_I2sPin = 3; // due to I2S hardware, the pin used is restricted to this
80 
81 class NeoEsp8266I2sMethodCore
82 {
83 private:
84  static const uint8_t c_StateBlockCount = 2;
85  static const size_t c_StateDataSize = 4; // mulitples of c_I2sByteBoundarySize
86 
87  // i2s sends 4 byte elements,
88  static const uint16_t c_I2sByteBoundarySize = 4;
89 
90 protected:
91  static NeoEsp8266I2sMethodCore* s_this; // for the ISR
92 
93  volatile NeoDmaState _dmaState;
94 
95  slc_queue_item* _i2sBufDesc; // dma block descriptors
96  uint16_t _i2sBufDescCount; // count of block descriptors in _i2sBufDesc
97 
98  size_t _i2sBufferSize; // total size of _i2sBuffer
99  uint8_t* _i2sBuffer; // holds the DMA buffer that is referenced by _i2sBufDesc
100 
101  size_t _i2sIdleDataTotalSize; // total size of represented zeroes, mulitple uses of _i2sIdleData
102  size_t _i2sIdleDataSize; // size of _i2sIdleData
103  uint8_t* _i2sIdleData;
104 
105  uint16_t _is2BufMaxBlockSize; // max size based on size of a pixel of a single block
106 
107  size_t GetSendSize() const
108  {
109  return _i2sBufferSize + _i2sIdleDataTotalSize;
110  }
111 
112  // This routine is called as soon as the DMA routine has something to tell us. All we
113  // handle here is the RX_EOF_INT status, which indicate the DMA has sent a buffer whose
114  // descriptor has the 'EOF' field set to 1.
115  // in the case of this code, the second to last state descriptor
116  static void IRAM_ATTR i2s_slc_isr(void)
117  {
118  ETS_SLC_INTR_DISABLE();
119 
120  uint32_t slc_intr_status = SLCIS;
121 
122  SLCIC = 0xFFFFFFFF;
123 
124  if ((slc_intr_status & SLCIRXEOF) && s_this)
125  {
126  if (s_this->_dmaState != NeoDmaState_Idle)
127  {
128  // first two items are the state blocks
129  slc_queue_item* itemLoop = s_this->_i2sBufDesc;
130  slc_queue_item* itemLoopBreaker = itemLoop + 1;
131  // set to loop on idle items
132  itemLoopBreaker->next_link_ptr = itemLoop;
133 
134  s_this->_dmaState = NeoDmaState_Idle;
135  }
136  }
137 
138  ETS_SLC_INTR_ENABLE();
139  }
140 
141  NeoEsp8266I2sMethodCore()
142  { };
143 
144  void AllocateI2s(const size_t i2sBufferSize, // expected multiples of c_I2sByteBoundarySize
145  const size_t i2sZeroesSize, // expected multiples of c_I2sByteBoundarySize
146  const size_t is2BufMaxBlockSize,
147  const uint8_t idleLevel)
148  {
149  _i2sBufferSize = i2sBufferSize;
150  _i2sIdleDataTotalSize = i2sZeroesSize;
151  _i2sIdleDataSize = _i2sIdleDataTotalSize;
152 
153  size_t countIdleQueueItems = 1;
154  if (_i2sIdleDataSize > 256)
155  {
156  // reuse a single idle data buffer of 256 with multiple dma slc_queue_items
157  countIdleQueueItems = _i2sIdleDataSize / 256 + 1;
158  _i2sIdleDataSize = 256;
159  }
160  else
161  {
162  _i2sIdleDataSize = NeoUtil::RoundUp(_i2sIdleDataSize, c_I2sByteBoundarySize);
163  }
164  _is2BufMaxBlockSize = is2BufMaxBlockSize;
165 
166  _i2sBuffer = static_cast<uint8_t*>(malloc(_i2sBufferSize));
167  // no need to initialize it, it gets overwritten on every send
168  _i2sIdleData = static_cast<uint8_t*>(malloc(_i2sIdleDataSize));
169  memset(_i2sIdleData, idleLevel * 0xff, _i2sIdleDataSize);
170 
171  _i2sBufDescCount = (_i2sBufferSize / _is2BufMaxBlockSize) + 1 +
172  countIdleQueueItems +
173  c_StateBlockCount; // need more for state/latch blocks
174 
175  _i2sBufDesc = (slc_queue_item*)malloc(_i2sBufDescCount * sizeof(slc_queue_item));
176 
177  s_this = this; // store this for the ISR
178  }
179 
180  void FreeI2s()
181  {
182  StopI2s();
183 
184  s_this = nullptr;
185  pinMode(c_I2sPin, INPUT);
186 
187  free(_i2sBuffer);
188  free(_i2sBufDesc);
189  free(_i2sIdleData);
190  }
191 
192  bool IsIdle() const
193  {
194  return (_dmaState == NeoDmaState_Idle);
195  }
196 
197 
198  void DmaItemInit(slc_queue_item* item, uint8_t* data, size_t sizeData, slc_queue_item* itemNext)
199  {
200  item->owner = 1;
201  item->eof = 0; // no need to trigger interrupt generally
202  item->sub_sof = 0;
203  item->datalen = sizeData;
204  item->blocksize = sizeData;
205  item->buf_ptr = data;
206  item->unused = 0;
207  item->next_link_ptr = itemNext;
208  }
209 
210  void InitializeI2s(const uint32_t i2sClockDivisor, const uint32_t i2sBaseClockDivisor)
211  {
212  StopI2s();
213 
214  pinMode(c_I2sPin, FUNCTION_1); // I2S0_DATA
215 
216  uint8_t* is2Buffer = _i2sBuffer;
217  uint8_t* is2BufferEnd = _i2sBuffer + _i2sBufferSize;
218  uint32_t is2BufferSize;
219  uint16_t indexDesc = 0;
220 
221  // prepare the two state/latch descriptors
222  uint16_t stateDataSize = min(c_StateDataSize, _i2sIdleDataSize);
223  while (indexDesc < c_StateBlockCount)
224  {
225  DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, stateDataSize, &(_i2sBufDesc[indexDesc + 1]));
226 
227  indexDesc++;
228  }
229 
230  // prepare main data block decriptors that point into our one static dma buffer
231  is2BufferSize = _i2sBufferSize;
232  while (is2Buffer < is2BufferEnd)
233  {
234  uint32_t blockSize = (is2BufferSize > _is2BufMaxBlockSize) ? _is2BufMaxBlockSize : is2BufferSize;
235 
236  DmaItemInit(&_i2sBufDesc[indexDesc], is2Buffer, blockSize, &(_i2sBufDesc[indexDesc + 1]));
237 
238  is2Buffer += blockSize;
239  is2BufferSize -= blockSize;
240  indexDesc++;
241  }
242 
243  // last data item triggers EOF ISR
244  _i2sBufDesc[indexDesc - 1].eof = 1;
245 
246  // prepare idle block decriptors that point into our one idle dma buffer
247  is2BufferSize = _i2sIdleDataTotalSize;
248  while (indexDesc < _i2sBufDescCount)
249  {
250  uint32_t blockSize = (is2BufferSize > _i2sIdleDataSize) ? _i2sIdleDataSize : is2BufferSize;
251 
252  DmaItemInit(&_i2sBufDesc[indexDesc], _i2sIdleData, blockSize, &(_i2sBufDesc[indexDesc + 1]));
253 
254  is2Buffer += blockSize;
255  is2BufferSize -= blockSize;
256  indexDesc++;
257  }
258 
259  // the last item will loop to the first item
260  _i2sBufDesc[indexDesc - 1].next_link_ptr = reinterpret_cast<struct slc_queue_item*>(&(_i2sBufDesc[0]));
261 
262  // the last state block will loop to the first state block by defualt
263  _i2sBufDesc[c_StateBlockCount - 1].next_link_ptr = reinterpret_cast<struct slc_queue_item*>(&(_i2sBufDesc[0]));
264 
265  // setup the rest of i2s DMA
266  //
267  ETS_SLC_INTR_DISABLE();
268 
269  // start off in idel state as that is what it will be all setup to be
270  // for the interrupt
271  _dmaState = NeoDmaState_Idle;
272 
273  SLCC0 |= SLCRXLR | SLCTXLR;
274  SLCC0 &= ~(SLCRXLR | SLCTXLR);
275  SLCIC = 0xFFFFFFFF;
276 
277  // Configure DMA
278  SLCC0 &= ~(SLCMM << SLCM); // clear DMA MODE
279  SLCC0 |= (1 << SLCM); // set DMA MODE to 1
280  SLCRXDC |= SLCBINR | SLCBTNR; // enable INFOR_NO_REPLACE and TOKEN_NO_REPLACE
281  SLCRXDC &= ~(SLCBRXFE | SLCBRXEM | SLCBRXFM); // disable RX_FILL, RX_EOF_MODE and RX_FILL_MODE
282 
283  // Feed DMA the 1st buffer desc addr
284  // To send data to the I2S subsystem, counter-intuitively we use the RXLINK part, not the TXLINK as you might
285  // expect. The TXLINK part still needs a valid DMA descriptor, even if it's unused: the DMA engine will throw
286  // an error at us otherwise. Just feed it any random descriptor.
287  SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
288  // set TX descriptor address. any random desc is OK, we don't use TX but it needs to be valid
289  SLCTXL |= (uint32) & (_i2sBufDesc[_i2sBufDescCount - 1]) << SLCTXLA;
290  SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
291  // set RX descriptor address. use first of the data addresses
292  SLCRXL |= (uint32) & (_i2sBufDesc[0]) << SLCRXLA;
293 
294  ETS_SLC_INTR_ATTACH(i2s_slc_isr, NULL);
295  SLCIE = SLCIRXEOF; // Enable only for RX EOF interrupt
296 
297  ETS_SLC_INTR_ENABLE();
298 
299  //Start transmission
300  SLCTXL |= SLCTXLS;
301  SLCRXL |= SLCRXLS;
302 
303  I2S_CLK_ENABLE();
304  I2SIC = 0x3F;
305  I2SIE = 0;
306 
307  //Reset I2S
308  I2SC &= ~(I2SRST);
309  I2SC |= I2SRST;
310  I2SC &= ~(I2SRST);
311 
312  // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only)
313  I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM));
314  I2SFC |= I2SDE; //Enable DMA
315  // Set RX/TX CHAN_MOD=0
316  I2SCC &= ~((I2STXCMM << I2STXCM) | (I2SRXCMM << I2SRXCM));
317 
318  // set the rate
319  uint32_t i2s_clock_div = i2sClockDivisor & I2SCDM;
320  uint8_t i2s_bck_div = i2sBaseClockDivisor & I2SBDM;
321 
323  I2SC &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD));
324  I2SC |= I2SRF | I2SMR | I2SRSM | I2SRMS | (i2s_bck_div << I2SBD) | (i2s_clock_div << I2SCD);
325 
326  I2SC |= I2STXS; // Start transmission
327  }
328 
329  void WriteI2s()
330  {
331  // first two items are the state blocks
332  slc_queue_item* itemLoopBreaker = &(_i2sBufDesc[1]);
333  slc_queue_item* itemData = itemLoopBreaker + 1;
334 
335  // set to NOT loop on idle items
336  itemLoopBreaker->next_link_ptr = itemData;
337 
338  _dmaState = NeoDmaState_Sending;
339  }
340 
341  void StopI2s()
342  {
343  ETS_SLC_INTR_DISABLE();
344 
345  // Disable any I2S send or receive
346  I2SC &= ~(I2STXS | I2SRXS);
347 
348  // Reset I2S
349  I2SC &= ~(I2SRST);
350  I2SC |= I2SRST;
351  I2SC &= ~(I2SRST);
352 
353  SLCIC = 0xFFFFFFFF;
354  SLCIE = 0;
355  SLCTXL &= ~(SLCTXLAM << SLCTXLA); // clear TX descriptor address
356  SLCRXL &= ~(SLCRXLAM << SLCRXLA); // clear RX descriptor address
357 
358  pinMode(c_I2sPin, INPUT);
359  }
360 };
361 
362 #endif // ARDUINO_ARCH_ESP8266
static size_t RoundUp(size_t numToRound, size_t multiple)
Definition: NeoUtil.h:62