FabGL
ESP32 Display Controller and Graphics Library
swgenerator.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2020 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6  This file is part of FabGL Library.
7 
8  FabGL is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  FabGL is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 
24 #include <alloca.h>
25 
26 #include "freertos/FreeRTOS.h"
27 #include "freertos/task.h"
28 
29 #include "soc/i2s_struct.h"
30 #include "soc/i2s_reg.h"
31 #include "driver/periph_ctrl.h"
32 #include "rom/lldesc.h"
33 #include "soc/rtc.h"
34 
35 #include "fabutils.h"
36 #include "swgenerator.h"
37 
38 
39 
40 
41 
42 
43 namespace fabgl {
44 
45 
46 
47 /*************************************************************************************/
48 /* GPIOStream definitions */
49 
50 void GPIOStream::begin()
51 {
52  m_DMAStarted = false;
53  m_DMABuffer = nullptr;
54  m_DMAData = nullptr;
55 }
56 
57 
58 void GPIOStream::begin(bool div1_onGPIO0, gpio_num_t div2, gpio_num_t div4, gpio_num_t div8, gpio_num_t div16, gpio_num_t div32, gpio_num_t div64, gpio_num_t div128, gpio_num_t div256)
59 {
60  m_DMAStarted = false;
61 
62  if (div1_onGPIO0)
63  setupGPIO(GPIO_NUM_0, -1, GPIO_MODE_OUTPUT); // note: GPIO_NUM_0 cannot be changed!
64  setupGPIO(div2, 0, GPIO_MODE_OUTPUT);
65  setupGPIO(div4, 1, GPIO_MODE_OUTPUT);
66  setupGPIO(div8, 2, GPIO_MODE_OUTPUT);
67  setupGPIO(div16, 3, GPIO_MODE_OUTPUT);
68  setupGPIO(div32, 4, GPIO_MODE_OUTPUT);
69  setupGPIO(div64, 5, GPIO_MODE_OUTPUT);
70  setupGPIO(div128, 6, GPIO_MODE_OUTPUT);
71  setupGPIO(div256, 7, GPIO_MODE_OUTPUT);
72 
73  m_DMAData = (volatile uint8_t *) heap_caps_malloc(256, MALLOC_CAP_DMA);
74  for (int i = 0; i < 256; ++i)
75  m_DMAData[i] = i;
76 
77  m_DMABuffer = (volatile lldesc_t *) heap_caps_malloc(sizeof(lldesc_t), MALLOC_CAP_DMA);
78  m_DMABuffer->eof = 0;
79  m_DMABuffer->sosf = 0;
80  m_DMABuffer->owner = 1;
81  m_DMABuffer->qe.stqe_next = (lldesc_t *) m_DMABuffer;
82  m_DMABuffer->offset = 0;
83  m_DMABuffer->size = 256;
84  m_DMABuffer->length = 256;
85  m_DMABuffer->buf = (uint8_t*) m_DMAData;
86 }
87 
88 
89 void GPIOStream::end()
90 {
91  stop();
92 }
93 
94 
95 
96 // if bit is -1 = clock signal
97 // gpio = GPIO_UNUSED means not set
98 void GPIOStream::setupGPIO(gpio_num_t gpio, int bit, gpio_mode_t mode)
99 {
100  if (gpio != GPIO_UNUSED) {
101 
102  if (bit == -1) {
103  // I2S1 clock out to CLK_OUT1 (fixed to GPIO0)
104  WRITE_PERI_REG(PIN_CTRL, 0xF);
105  PIN_FUNC_SELECT(GPIO_PIN_REG_0, FUNC_GPIO0_CLK_OUT1);
106  } else {
107  configureGPIO(gpio, mode);
108  gpio_matrix_out(gpio, I2S1O_DATA_OUT0_IDX + bit, false, false);
109  }
110 
111  }
112 }
113 
114 
115 void GPIOStream::play(int freq, lldesc_t volatile * dmaBuffers)
116 {
117  if (!m_DMAStarted) {
118 
119  // Power on device
120  periph_module_enable(PERIPH_I2S1_MODULE);
121 
122  // Initialize I2S device
123  I2S1.conf.tx_reset = 1;
124  I2S1.conf.tx_reset = 0;
125 
126  // Reset DMA
127  I2S1.lc_conf.out_rst = 1;
128  I2S1.lc_conf.out_rst = 0;
129 
130  // Reset FIFO
131  I2S1.conf.tx_fifo_reset = 1;
132  I2S1.conf.tx_fifo_reset = 0;
133 
134  // LCD mode
135  I2S1.conf2.val = 0;
136  I2S1.conf2.lcd_en = 1;
137  I2S1.conf2.lcd_tx_wrx2_en = 1;
138  I2S1.conf2.lcd_tx_sdx2_en = 0;
139 
140  I2S1.sample_rate_conf.val = 0;
141  I2S1.sample_rate_conf.tx_bits_mod = 8;
142 
143  setupClock(freq);
144 
145  I2S1.fifo_conf.val = 0;
146  I2S1.fifo_conf.tx_fifo_mod_force_en = 1;
147  I2S1.fifo_conf.tx_fifo_mod = 1;
148  I2S1.fifo_conf.tx_fifo_mod = 1;
149  I2S1.fifo_conf.tx_data_num = 32;
150  I2S1.fifo_conf.dscr_en = 1;
151 
152  I2S1.conf1.val = 0;
153  I2S1.conf1.tx_stop_en = 0;
154  I2S1.conf1.tx_pcm_bypass = 1;
155 
156  I2S1.conf_chan.val = 0;
157  I2S1.conf_chan.tx_chan_mod = 1;
158 
159  I2S1.conf.tx_right_first = 1;
160 
161  I2S1.timing.val = 0;
162 
163  // Reset AHB interface of DMA
164  I2S1.lc_conf.ahbm_rst = 1;
165  I2S1.lc_conf.ahbm_fifo_rst = 1;
166  I2S1.lc_conf.ahbm_rst = 0;
167  I2S1.lc_conf.ahbm_fifo_rst = 0;
168 
169  // Start DMA
170  I2S1.lc_conf.val = I2S_OUT_DATA_BURST_EN | I2S_OUTDSCR_BURST_EN;
171  I2S1.out_link.addr = (uint32_t) (dmaBuffers ? &dmaBuffers[0] : m_DMABuffer);
172  I2S1.out_link.start = 1;
173  I2S1.conf.tx_start = 1;
174 
175  m_DMAStarted = true;
176 
177  }
178 }
179 
180 
181 void GPIOStream::stop()
182 {
183  if (m_DMAStarted) {
184  rtc_clk_apll_enable(false, 0, 0, 0, 0);
185  periph_module_disable(PERIPH_I2S1_MODULE);
186 
187  m_DMAStarted = false;
188  }
189 }
190 
191 
192 struct APLLParams {
193  uint8_t sdm0;
194  uint8_t sdm1;
195  uint8_t sdm2;
196  uint8_t o_div;
197 };
198 
199 
200 // Must be:
201 // maxDen > 1
202 // value >= 0
203 #if FABGLIB_USE_APLL_AB_COEF
204 void floatToFraction(double value, int maxDen, int * num, int * den)
205 {
206  int64_t a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
207  int64_t x, d, n = 1;
208  while (value != floor(value)) {
209  n <<= 1;
210  value *= 2;
211  }
212  d = value;
213  for (int i = 0; i < 64; ++i) {
214  a = n ? d / n : 0;
215  if (i && !a)
216  break;
217  x = d;
218  d = n;
219  n = x % n;
220  x = a;
221  if (k[1] * a + k[0] >= maxDen) {
222  x = (maxDen - k[0]) / k[1];
223  if (x * 2 >= a || k[1] >= maxDen)
224  i = 65;
225  else
226  break;
227  }
228  h[2] = x * h[1] + h[0];
229  h[0] = h[1];
230  h[1] = h[2];
231  k[2] = x * k[1] + k[0];
232  k[0] = k[1];
233  k[1] = k[2];
234  }
235  *den = k[1];
236  *num = h[1];
237 }
238 #endif
239 
240 
241 
242 // definitions:
243 // apll_clk = XTAL * (4 + sdm2 + sdm1 / 256 + sdm0 / 65536) / (2 * o_div + 4)
244 // dividend = XTAL * (4 + sdm2 + sdm1 / 256 + sdm0 / 65536)
245 // divisor = (2 * o_div + 4)
246 // freq = apll_clk / (2 + b / a) => assumes tx_bck_div_num = 1 and clkm_div_num = 2
247 // Other values range:
248 // sdm0 0..255
249 // sdm1 0..255
250 // sdm2 0..63
251 // o_div 0..31
252 // Assume xtal = FABGLIB_XTAL (40MHz)
253 // The dividend should be in the range of 350 - 500 MHz (350000000-500000000), so these are the
254 // actual parameters ranges (so the minimum apll_clk is 5303030 Hz and maximum is 125000000Hz):
255 // MIN 87500000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 0
256 // MAX 125000000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 0
257 //
258 // MIN 58333333Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 1
259 // MAX 83333333Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 1
260 //
261 // MIN 43750000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 2
262 // MAX 62500000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 2
263 //
264 // MIN 35000000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 3
265 // MAX 50000000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 3
266 //
267 // MIN 29166666Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 4
268 // MAX 41666666Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 4
269 //
270 // MIN 25000000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 5
271 // MAX 35714285Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 5
272 //
273 // MIN 21875000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 6
274 // MAX 31250000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 6
275 //
276 // MIN 19444444Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 7
277 // MAX 27777777Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 7
278 //
279 // MIN 17500000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 8
280 // MAX 25000000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 8
281 //
282 // MIN 15909090Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 9
283 // MAX 22727272Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 9
284 //
285 // MIN 14583333Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 10
286 // MAX 20833333Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 10
287 //
288 // MIN 13461538Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 11
289 // MAX 19230769Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 11
290 //
291 // MIN 12500000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 12
292 // MAX 17857142Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 12
293 //
294 // MIN 11666666Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 13
295 // MAX 16666666Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 13
296 //
297 // MIN 10937500Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 14
298 // MAX 15625000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 14
299 //
300 // MIN 10294117Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 15
301 // MAX 14705882Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 15
302 //
303 // MIN 9722222Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 16
304 // MAX 13888888Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 16
305 //
306 // MIN 9210526Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 17
307 // MAX 13157894Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 17
308 //
309 // MIN 8750000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 18
310 // MAX 12500000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 18
311 //
312 // MIN 8333333Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 19
313 // MAX 11904761Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 19
314 //
315 // MIN 7954545Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 20
316 // MAX 11363636Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 20
317 //
318 // MIN 7608695Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 21
319 // MAX 10869565Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 21
320 //
321 // MIN 7291666Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 22
322 // MAX 10416666Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 22
323 //
324 // MIN 7000000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 23
325 // MAX 10000000Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 23
326 //
327 // MIN 6730769Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 24
328 // MAX 9615384Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 24
329 //
330 // MIN 6481481Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 25
331 // MAX 9259259Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 25
332 //
333 // MIN 6250000Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 26
334 // MAX 8928571Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 26
335 //
336 // MIN 6034482Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 27
337 // MAX 8620689Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 27
338 //
339 // MIN 5833333Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 28
340 // MAX 8333333Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 28
341 //
342 // MIN 5645161Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 29
343 // MAX 8064516Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 29
344 //
345 // MIN 5468750Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 30
346 // MAX 7812500Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 30
347 //
348 // MIN 5303030Hz - sdm0 = 0 sdm1 = 192 sdm2 = 4 o_div = 31
349 // MAX 7575757Hz - sdm0 = 0 sdm1 = 128 sdm2 = 8 o_div = 31
350 static void APLLCalcParams(double freq, APLLParams * params, uint8_t * a, uint8_t * b, double * out_freq, double * error)
351 {
352  double FXTAL = FABGLIB_XTAL;
353 
354  *error = 999999999;
355 
356  double apll_freq = freq * 2;
357 
358  for (int o_div = 0; o_div <= 31; ++o_div) {
359 
360  int idivisor = (2 * o_div + 4);
361 
362  for (int sdm2 = 4; sdm2 <= 8; ++sdm2) {
363 
364  // from tables above
365  int minSDM1 = (sdm2 == 4 ? 192 : 0);
366  int maxSDM1 = (sdm2 == 8 ? 128 : 255);
367  // apll_freq = XTAL * (4 + sdm2 + sdm1 / 256) / divisor -> sdm1 = (apll_freq * divisor - XTAL * 4 - XTAL * sdm2) * 256 / XTAL
368  int startSDM1 = ((apll_freq * idivisor - FXTAL * 4.0 - FXTAL * sdm2) * 256.0 / FXTAL);
369 #if FABGLIB_USE_APLL_AB_COEF
370  for (int isdm1 = tmax(minSDM1, startSDM1); isdm1 <= maxSDM1; ++isdm1) {
371 #else
372  int isdm1 = startSDM1; {
373 #endif
374 
375  int sdm1 = isdm1;
376  sdm1 = tmax(minSDM1, sdm1);
377  sdm1 = tmin(maxSDM1, sdm1);
378 
379  // apll_freq = XTAL * (4 + sdm2 + sdm1 / 256 + sdm0 / 65536) / divisor -> sdm0 = (apll_freq * divisor - XTAL * 4 - XTAL * sdm2 - XTAL * sdm1 / 256) * 65536 / XTAL
380  int sdm0 = ((apll_freq * idivisor - FXTAL * 4.0 - FXTAL * sdm2 - FXTAL * sdm1 / 256.0) * 65536.0 / FXTAL);
381  // from tables above
382  sdm0 = (sdm2 == 8 && sdm1 == 128 ? 0 : tmin(255, sdm0));
383  sdm0 = tmax(0, sdm0);
384 
385  // dividend inside 350-500Mhz?
386  double dividend = FXTAL * (4.0 + sdm2 + sdm1 / 256.0 + sdm0 / 65536.0);
387  if (dividend >= 350000000 && dividend <= 500000000) {
388  // adjust output frequency using "b/a"
389  double oapll_freq = dividend / idivisor;
390 
391  // Calculates "b/a", assuming tx_bck_div_num = 1 and clkm_div_num = 2:
392  // freq = apll_clk / (2 + clkm_div_b / clkm_div_a)
393  // abr = clkm_div_b / clkm_div_a
394  // freq = apll_clk / (2 + abr) => abr = apll_clk / freq - 2
395  uint8_t oa = 1, ob = 0;
396 #if FABGLIB_USE_APLL_AB_COEF
397  double abr = oapll_freq / freq - 2.0;
398  if (abr > 0 && abr < 1) {
399  int num, den;
400  floatToFraction(abr, 63, &num, &den);
401  ob = tclamp(num, 0, 63);
402  oa = tclamp(den, 0, 63);
403  }
404 #endif
405 
406  // is this the best?
407  double ofreq = oapll_freq / (2.0 + (double)ob / oa);
408  double err = freq - ofreq;
409  if (abs(err) < abs(*error)) {
410  *params = (APLLParams){(uint8_t)sdm0, (uint8_t)sdm1, (uint8_t)sdm2, (uint8_t)o_div};
411  *a = oa;
412  *b = ob;
413  *out_freq = ofreq;
414  *error = err;
415  if (err == 0.0)
416  return;
417  }
418  }
419  }
420 
421  }
422  }
423 }
424 
425 
426 void GPIOStream::setupClock(int freq)
427 {
428  APLLParams p = {0, 0, 0, 0};
429  double error, out_freq;
430  uint8_t a = 1, b = 0;
431  APLLCalcParams(freq, &p, &a, &b, &out_freq, &error);
432 
433  I2S1.clkm_conf.val = 0;
434  I2S1.clkm_conf.clkm_div_b = b;
435  I2S1.clkm_conf.clkm_div_a = a;
436  I2S1.clkm_conf.clkm_div_num = 2; // not less than 2
437 
438  I2S1.sample_rate_conf.tx_bck_div_num = 1; // this makes I2S1O_BCK = I2S1_CLK
439 
440  rtc_clk_apll_enable(true, p.sdm0, p.sdm1, p.sdm2, p.o_div);
441 
442  I2S1.clkm_conf.clka_en = 1;
443 }
444 
445 
446 
447 
448 
449 
450 } // end of namespace
451 
452 
This file contains fabgl::GPIOStream definition.
This file contains some utility classes and functions.
Definition: canvas.cpp:31