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