Sun, 24 Nov 2019 16:44:00 +0100
Version 0.3.7. The WiFi task uses the new event handlers. Cooling temperature top is now 45 instead of 30 degreees for pitching Kveik. One extra cosmetic message during OTA update.
0 | 1 | /* |
2 | * Author: LoBo (loboris@gmail.com, loboris.github) | |
3 | * | |
4 | * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers | |
5 | * | |
6 | * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS | |
7 | * USING DIRECT or DMA SPI TRANSFER MODEs | |
8 | * | |
9 | */ | |
10 | ||
11 | #include <string.h> | |
12 | #include "tftspi.h" | |
13 | #include "esp_system.h" | |
34
5c92103c5e72
Version 0.2.10. Fixed spelling error in websocket component. Updated esp-idf to v4.0-dev-459-gba1ff1692 and adjusted several sources for changed headers. Finalized keeping the AP running at all times. Increased websocket server stack depth from 6000 to 7000.
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
14 | #include "driver/gpio.h" |
0 | 15 | #include "freertos/task.h" |
16 | #include "esp_heap_caps.h" | |
17 | #include "soc/spi_reg.h" | |
18 | ||
19 | // ==================================================== | |
20 | // ==== Global variables, default values ============== | |
21 | ||
22 | // Converts colors to grayscale if set to 1 | |
23 | uint8_t gray_scale = 0; | |
24 | // Spi clock for reading data from display memory in Hz | |
25 | uint32_t max_rdclock = 8000000; | |
26 | ||
27 | // Default display dimensions | |
28 | int _width = DEFAULT_TFT_DISPLAY_WIDTH; | |
29 | int _height = DEFAULT_TFT_DISPLAY_HEIGHT; | |
30 | ||
31 | // Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 | |
32 | uint8_t tft_disp_type = DEFAULT_DISP_TYPE; | |
33 | ||
34 | // Spi device handles for display and touch screen | |
35 | spi_lobo_device_handle_t disp_spi = NULL; | |
36 | spi_lobo_device_handle_t ts_spi = NULL; | |
37 | ||
38 | // ==================================================== | |
39 | ||
40 | ||
41 | static color_t *trans_cline = NULL; | |
42 | static uint8_t _dma_sending = 0; | |
43 | ||
44 | // RGB to GRAYSCALE constants | |
45 | // 0.2989 0.5870 0.1140 | |
46 | #define GS_FACT_R 0.2989 | |
47 | #define GS_FACT_G 0.4870 | |
48 | #define GS_FACT_B 0.2140 | |
49 | ||
50 | ||
51 | ||
52 | // ==== Functions ===================== | |
53 | ||
54 | //------------------------------------------------------ | |
55 | esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line) | |
56 | { | |
57 | // Wait for SPI bus ready | |
58 | while (disp_spi->host->hw->cmd.usr); | |
59 | if ((free_line) && (trans_cline)) { | |
60 | free(trans_cline); | |
61 | trans_cline = NULL; | |
62 | } | |
63 | if (_dma_sending) { | |
64 | //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset. | |
65 | if (disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(disp_spi->host->dma_chan); | |
66 | ||
67 | // Reset DMA | |
68 | disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; | |
69 | disp_spi->host->hw->dma_out_link.start=0; | |
70 | disp_spi->host->hw->dma_in_link.start=0; | |
71 | disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); | |
72 | disp_spi->host->hw->dma_conf.out_data_burst_en=1; | |
73 | _dma_sending = 0; | |
74 | } | |
75 | return ESP_OK; | |
76 | } | |
77 | ||
78 | //------------------------------- | |
79 | esp_err_t IRAM_ATTR disp_select() | |
80 | { | |
81 | wait_trans_finish(1); | |
82 | return spi_lobo_device_select(disp_spi, 0); | |
83 | } | |
84 | ||
85 | //--------------------------------- | |
86 | esp_err_t IRAM_ATTR disp_deselect() | |
87 | { | |
88 | wait_trans_finish(1); | |
89 | return spi_lobo_device_deselect(disp_spi); | |
90 | } | |
91 | ||
92 | //--------------------------------------------------------------------------------------------------- | |
93 | static void IRAM_ATTR _spi_transfer_start(spi_lobo_device_handle_t spi_dev, int wrbits, int rdbits) { | |
94 | // Load send buffer | |
95 | spi_dev->host->hw->user.usr_mosi_highpart = 0; | |
96 | spi_dev->host->hw->mosi_dlen.usr_mosi_dbitlen = wrbits-1; | |
97 | spi_dev->host->hw->user.usr_mosi = 1; | |
98 | if (rdbits) { | |
99 | spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = rdbits; | |
100 | spi_dev->host->hw->user.usr_miso = 1; | |
101 | } | |
102 | else { | |
103 | spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = 0; | |
104 | spi_dev->host->hw->user.usr_miso = 0; | |
105 | } | |
106 | // Start transfer | |
107 | spi_dev->host->hw->cmd.usr = 1; | |
108 | // Wait for SPI bus ready | |
109 | while (spi_dev->host->hw->cmd.usr); | |
110 | } | |
111 | ||
112 | // Send 1 byte display command, display must be selected | |
113 | //------------------------------------------------ | |
114 | void IRAM_ATTR disp_spi_transfer_cmd(int8_t cmd) { | |
115 | // Wait for SPI bus ready | |
116 | while (disp_spi->host->hw->cmd.usr); | |
117 | ||
118 | // Set DC to 0 (command mode); | |
119 | gpio_set_level(PIN_NUM_DC, 0); | |
120 | ||
121 | disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; | |
122 | _spi_transfer_start(disp_spi, 8, 0); | |
123 | } | |
124 | ||
125 | // Send command with data to display, display must be selected | |
126 | //---------------------------------------------------------------------------------- | |
127 | void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t len) { | |
128 | // Wait for SPI bus ready | |
129 | while (disp_spi->host->hw->cmd.usr); | |
130 | ||
131 | // Set DC to 0 (command mode); | |
132 | gpio_set_level(PIN_NUM_DC, 0); | |
133 | ||
134 | disp_spi->host->hw->data_buf[0] = (uint32_t)cmd; | |
135 | _spi_transfer_start(disp_spi, 8, 0); | |
136 | ||
137 | if ((len == 0) || (data == NULL)) return; | |
138 | ||
139 | // Set DC to 1 (data mode); | |
140 | gpio_set_level(PIN_NUM_DC, 1); | |
141 | ||
142 | uint8_t idx=0, bidx=0; | |
143 | uint32_t bits=0; | |
144 | uint32_t count=0; | |
145 | uint32_t wd = 0; | |
146 | while (count < len) { | |
147 | // get data byte from buffer | |
148 | wd |= (uint32_t)data[count] << bidx; | |
149 | count++; | |
150 | bits += 8; | |
151 | bidx += 8; | |
152 | if (count == len) { | |
153 | disp_spi->host->hw->data_buf[idx] = wd; | |
154 | break; | |
155 | } | |
156 | if (bidx == 32) { | |
157 | disp_spi->host->hw->data_buf[idx] = wd; | |
158 | idx++; | |
159 | bidx = 0; | |
160 | wd = 0; | |
161 | } | |
162 | if (idx == 16) { | |
163 | // SPI buffer full, send data | |
164 | _spi_transfer_start(disp_spi, bits, 0); | |
165 | ||
166 | bits = 0; | |
167 | idx = 0; | |
168 | bidx = 0; | |
169 | } | |
170 | } | |
171 | if (bits > 0) _spi_transfer_start(disp_spi, bits, 0); | |
172 | } | |
173 | ||
174 | // Set the address window for display write & read commands, display must be selected | |
175 | //--------------------------------------------------------------------------------------------------- | |
176 | static void IRAM_ATTR disp_spi_transfer_addrwin(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2) { | |
177 | uint32_t wd; | |
178 | ||
179 | taskDISABLE_INTERRUPTS(); | |
180 | // Wait for SPI bus ready | |
181 | while (disp_spi->host->hw->cmd.usr); | |
182 | gpio_set_level(PIN_NUM_DC, 0); | |
183 | ||
184 | disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET; | |
185 | disp_spi->host->hw->user.usr_mosi_highpart = 0; | |
186 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; | |
187 | disp_spi->host->hw->user.usr_mosi = 1; | |
188 | disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0; | |
189 | disp_spi->host->hw->user.usr_miso = 0; | |
190 | ||
191 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
192 | ||
193 | wd = (uint32_t)(x1>>8); | |
194 | wd |= (uint32_t)(x1&0xff) << 8; | |
195 | wd |= (uint32_t)(x2>>8) << 16; | |
196 | wd |= (uint32_t)(x2&0xff) << 24; | |
197 | ||
198 | while (disp_spi->host->hw->cmd.usr); // wait transfer end | |
199 | gpio_set_level(PIN_NUM_DC, 1); | |
200 | disp_spi->host->hw->data_buf[0] = wd; | |
201 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; | |
202 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
203 | ||
204 | while (disp_spi->host->hw->cmd.usr); | |
205 | gpio_set_level(PIN_NUM_DC, 0); | |
206 | disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET; | |
207 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; | |
208 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
209 | ||
210 | wd = (uint32_t)(y1>>8); | |
211 | wd |= (uint32_t)(y1&0xff) << 8; | |
212 | wd |= (uint32_t)(y2>>8) << 16; | |
213 | wd |= (uint32_t)(y2&0xff) << 24; | |
214 | ||
215 | while (disp_spi->host->hw->cmd.usr); | |
216 | gpio_set_level(PIN_NUM_DC, 1); | |
217 | ||
218 | disp_spi->host->hw->data_buf[0] = wd; | |
219 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31; | |
220 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
221 | while (disp_spi->host->hw->cmd.usr); | |
222 | taskENABLE_INTERRUPTS(); | |
223 | } | |
224 | ||
225 | // Convert color to gray scale | |
226 | //---------------------------------------------- | |
227 | static color_t IRAM_ATTR color2gs(color_t color) | |
228 | { | |
229 | color_t _color; | |
230 | float gs_clr = GS_FACT_R * color.r + GS_FACT_G * color.g + GS_FACT_B * color.b; | |
231 | if (gs_clr > 255) gs_clr = 255; | |
232 | ||
233 | _color.r = (uint8_t)gs_clr; | |
234 | _color.g = (uint8_t)gs_clr; | |
235 | _color.b = (uint8_t)gs_clr; | |
236 | ||
237 | return _color; | |
238 | } | |
239 | ||
240 | // Set display pixel at given coordinates to given color | |
241 | //------------------------------------------------------------------------ | |
242 | void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) | |
243 | { | |
244 | if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; | |
245 | ||
246 | if (sel) { | |
247 | if (disp_select()) return; | |
248 | } | |
249 | else wait_trans_finish(1); | |
250 | ||
251 | uint32_t wd = 0; | |
252 | color_t _color = color; | |
253 | if (gray_scale) _color = color2gs(color); | |
254 | ||
255 | taskDISABLE_INTERRUPTS(); | |
256 | disp_spi_transfer_addrwin(x, x+1, y, y+1); | |
257 | ||
258 | // Send RAM WRITE command | |
259 | gpio_set_level(PIN_NUM_DC, 0); | |
260 | disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; | |
261 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; | |
262 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
263 | while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready | |
264 | ||
265 | wd = (uint32_t)_color.r; | |
266 | wd |= (uint32_t)_color.g << 8; | |
267 | wd |= (uint32_t)_color.b << 16; | |
268 | ||
269 | // Set DC to 1 (data mode); | |
270 | gpio_set_level(PIN_NUM_DC, 1); | |
271 | ||
272 | disp_spi->host->hw->data_buf[0] = wd; | |
273 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23; | |
274 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
275 | while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready | |
276 | ||
277 | taskENABLE_INTERRUPTS(); | |
278 | if (sel) disp_deselect(); | |
279 | } | |
280 | ||
281 | //----------------------------------------------------------- | |
282 | static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size) | |
283 | { | |
284 | //Fill DMA descriptors | |
285 | spi_lobo_dmaworkaround_transfer_active(disp_spi->host->dma_chan); //mark channel as active | |
286 | spi_lobo_setup_dma_desc_links(disp_spi->host->dmadesc_tx, size, data, false); | |
287 | disp_spi->host->hw->user.usr_mosi_highpart=0; | |
288 | disp_spi->host->hw->dma_out_link.addr=(int)(&disp_spi->host->dmadesc_tx[0]) & 0xFFFFF; | |
289 | disp_spi->host->hw->dma_out_link.start=1; | |
290 | disp_spi->host->hw->user.usr_mosi_highpart=0; | |
291 | ||
292 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1; | |
293 | ||
294 | _dma_sending = 1; | |
295 | // Start transfer | |
296 | disp_spi->host->hw->cmd.usr = 1; | |
297 | } | |
298 | ||
299 | //--------------------------------------------------------------------------- | |
300 | static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep) | |
301 | { | |
302 | uint32_t cidx = 0; // color buffer index | |
303 | uint32_t wd = 0; | |
304 | int idx = 0; | |
305 | int bits = 0; | |
306 | int wbits = 0; | |
307 | ||
308 | taskDISABLE_INTERRUPTS(); | |
309 | color_t _color = color[0]; | |
310 | if ((rep) && (gray_scale)) _color = color2gs(color[0]); | |
311 | ||
312 | while (len) { | |
313 | // ** Get color data from color buffer ** | |
314 | if (rep == 0) { | |
315 | if (gray_scale) _color = color2gs(color[cidx]); | |
316 | else _color = color[cidx]; | |
317 | } | |
318 | ||
319 | wd |= (uint32_t)_color.r << wbits; | |
320 | wbits += 8; | |
321 | if (wbits == 32) { | |
322 | bits += wbits; | |
323 | wbits = 0; | |
324 | disp_spi->host->hw->data_buf[idx++] = wd; | |
325 | wd = 0; | |
326 | } | |
327 | wd |= (uint32_t)_color.g << wbits; | |
328 | wbits += 8; | |
329 | if (wbits == 32) { | |
330 | bits += wbits; | |
331 | wbits = 0; | |
332 | disp_spi->host->hw->data_buf[idx++] = wd; | |
333 | wd = 0; | |
334 | } | |
335 | wd |= (uint32_t)_color.b << wbits; | |
336 | wbits += 8; | |
337 | if (wbits == 32) { | |
338 | bits += wbits; | |
339 | wbits = 0; | |
340 | disp_spi->host->hw->data_buf[idx++] = wd; | |
341 | wd = 0; | |
342 | } | |
343 | len--; // Decrement colors counter | |
344 | if (rep == 0) cidx++; // if not repeating color, increment color buffer index | |
345 | } | |
346 | if (bits) { | |
347 | while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready | |
348 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent | |
349 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
350 | } | |
351 | taskENABLE_INTERRUPTS(); | |
352 | } | |
353 | ||
354 | // ================================================================ | |
355 | // === Main function to send data to display ====================== | |
356 | // If rep==true: repeat sending color data to display 'len' times | |
357 | // If rep==false: send 'len' color data from color buffer to display | |
358 | // ** Device must already be selected and address window set ** | |
359 | // ================================================================ | |
360 | //---------------------------------------------------------------------------------------------- | |
361 | static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t rep, uint8_t wait) | |
362 | { | |
363 | if (len == 0) return; | |
364 | if (!(disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return; | |
365 | ||
366 | // Send RAM WRITE command | |
367 | gpio_set_level(PIN_NUM_DC, 0); | |
368 | disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR; | |
369 | disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7; | |
370 | disp_spi->host->hw->cmd.usr = 1; // Start transfer | |
371 | while (disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready | |
372 | ||
373 | gpio_set_level(PIN_NUM_DC, 1); // Set DC to 1 (data mode); | |
374 | ||
375 | if ((len*24) <= 512) { | |
376 | ||
377 | _direct_send(color, len, rep); | |
378 | ||
379 | } | |
380 | else if (rep == 0) { | |
381 | // ==== use DMA transfer ==== | |
382 | // ** Prepare data | |
383 | if (gray_scale) { | |
384 | for (int n=0; n<len; n++) { | |
385 | color[n] = color2gs(color[n]); | |
386 | } | |
387 | } | |
388 | ||
389 | _dma_send((uint8_t *)color, len*3); | |
390 | } | |
391 | else { | |
392 | // ==== Repeat color, more than 512 bits total ==== | |
393 | ||
394 | color_t _color; | |
395 | uint32_t buf_colors; | |
396 | int buf_bytes, to_send; | |
397 | ||
398 | /* | |
399 | to_send = len; | |
400 | while (to_send > 0) { | |
401 | wait_trans_finish(0); | |
402 | _direct_send(color, ((to_send > 21) ? 21 : to_send), rep); | |
403 | to_send -= 21; | |
404 | } | |
405 | */ | |
406 | ||
407 | buf_colors = ((len > (_width*2)) ? (_width*2) : len); | |
408 | buf_bytes = buf_colors * 3; | |
409 | ||
410 | // Prepare color buffer of maximum 2 color lines | |
411 | trans_cline = heap_caps_malloc(buf_bytes, MALLOC_CAP_DMA); | |
412 | if (trans_cline == NULL) return; | |
413 | ||
414 | // Prepare fill color | |
415 | if (gray_scale) _color = color2gs(color[0]); | |
416 | else _color = color[0]; | |
417 | ||
418 | // Fill color buffer with fill color | |
419 | for (uint32_t i=0; i<buf_colors; i++) { | |
420 | trans_cline[i] = _color; | |
421 | } | |
422 | ||
423 | // Send 'len' colors | |
424 | to_send = len; | |
425 | while (to_send > 0) { | |
426 | wait_trans_finish(0); | |
427 | _dma_send((uint8_t *)trans_cline, ((to_send > buf_colors) ? buf_bytes : (to_send*3))); | |
428 | to_send -= buf_colors; | |
429 | } | |
430 | } | |
431 | ||
432 | if (wait) wait_trans_finish(1); | |
433 | } | |
434 | ||
435 | // Write 'len' color data to TFT 'window' (x1,y2),(x2,y2) | |
436 | //------------------------------------------------------------------------------------------- | |
437 | void IRAM_ATTR TFT_pushColorRep(int x1, int y1, int x2, int y2, color_t color, uint32_t len) | |
438 | { | |
439 | if (disp_select() != ESP_OK) return; | |
440 | ||
441 | // ** Send address window ** | |
442 | disp_spi_transfer_addrwin(x1, x2, y1, y2); | |
443 | ||
444 | _TFT_pushColorRep(&color, len, 1, 1); | |
445 | ||
446 | disp_deselect(); | |
447 | } | |
448 | ||
449 | // Write 'len' color data to TFT 'window' (x1,y2),(x2,y2) from given buffer | |
450 | // ** Device must already be selected ** | |
451 | //----------------------------------------------------------------------------------- | |
452 | void IRAM_ATTR send_data(int x1, int y1, int x2, int y2, uint32_t len, color_t *buf) | |
453 | { | |
454 | // ** Send address window ** | |
455 | disp_spi_transfer_addrwin(x1, x2, y1, y2); | |
456 | _TFT_pushColorRep(buf, len, 0, 0); | |
457 | } | |
458 | ||
459 | // Reads 'len' pixels/colors from the TFT's GRAM 'window' | |
460 | // 'buf' is an array of bytes with 1st byte reserved for reading 1 dummy byte | |
461 | // and the rest is actually an array of color_t values | |
462 | //-------------------------------------------------------------------------------------------- | |
463 | int IRAM_ATTR read_data(int x1, int y1, int x2, int y2, int len, uint8_t *buf, uint8_t set_sp) | |
464 | { | |
465 | spi_lobo_transaction_t t; | |
466 | uint32_t current_clock = 0; | |
467 | ||
468 | memset(&t, 0, sizeof(t)); //Zero out the transaction | |
469 | memset(buf, 0, len*sizeof(color_t)); | |
470 | ||
471 | if (set_sp) { | |
472 | if (disp_deselect() != ESP_OK) return -1; | |
473 | // Change spi clock if needed | |
474 | current_clock = spi_lobo_get_speed(disp_spi); | |
475 | if (max_rdclock < current_clock) spi_lobo_set_speed(disp_spi, max_rdclock); | |
476 | } | |
477 | ||
478 | if (disp_select() != ESP_OK) return -2; | |
479 | ||
480 | // ** Send address window ** | |
481 | disp_spi_transfer_addrwin(x1, x2, y1, y2); | |
482 | ||
483 | // ** GET pixels/colors ** | |
484 | disp_spi_transfer_cmd(TFT_RAMRD); | |
485 | ||
486 | t.length=0; //Send nothing | |
487 | t.tx_buffer=NULL; | |
488 | t.rxlength=8*((len*3)+1); //Receive size in bits | |
489 | t.rx_buffer=buf; | |
490 | //t.user = (void*)1; | |
491 | ||
492 | esp_err_t res = spi_lobo_transfer_data(disp_spi, &t); // Receive using direct mode | |
493 | ||
494 | disp_deselect(); | |
495 | ||
496 | if (set_sp) { | |
497 | // Restore spi clock if needed | |
498 | if (max_rdclock < current_clock) spi_lobo_set_speed(disp_spi, current_clock); | |
499 | } | |
500 | ||
501 | return res; | |
502 | } | |
503 | ||
504 | // Reads one pixel/color from the TFT's GRAM at position (x,y) | |
505 | //----------------------------------------------- | |
506 | color_t IRAM_ATTR readPixel(int16_t x, int16_t y) | |
507 | { | |
508 | uint8_t color_buf[sizeof(color_t)+1] = {0}; | |
509 | ||
510 | read_data(x, y, x+1, y+1, 1, color_buf, 1); | |
511 | ||
512 | color_t color; | |
513 | color.r = color_buf[1]; | |
514 | color.g = color_buf[2]; | |
515 | color.b = color_buf[3]; | |
516 | return color; | |
517 | } | |
518 | ||
519 | // get 16-bit data from touch controller for specified type | |
520 | // ** Touch device must already be selected ** | |
521 | //---------------------------------------- | |
522 | int IRAM_ATTR touch_get_data(uint8_t type) | |
523 | { | |
524 | /* | |
525 | esp_err_t ret; | |
526 | spi_lobo_transaction_t t; | |
527 | memset(&t, 0, sizeof(t)); //Zero out the transaction | |
528 | uint8_t rxdata[2] = {0}; | |
529 | ||
530 | // send command byte & receive 2 byte response | |
531 | t.rxlength=8*2; | |
532 | t.rx_buffer=&rxdata; | |
533 | t.command = type; | |
534 | ||
535 | ret = spi_lobo_transfer_data(ts_spi, &t); // Transmit using direct mode | |
536 | ||
537 | if (ret != ESP_OK) res = -1; | |
538 | res = (((int)(rxdata[0] << 8) | (int)(rxdata[1])) >> 4); | |
539 | */ | |
540 | // spi_lobo_device_select(ts_spi, 0); | |
541 | ||
542 | ts_spi->host->hw->data_buf[0] = type; | |
543 | _spi_transfer_start(ts_spi, 24, 24); | |
544 | ||
545 | // printf("touch_get_data(%02X) %06x %02x %02x %02x ", type, ts_spi->host->hw->data_buf[0], | |
546 | // (ts_spi->host->hw->data_buf[0] & 0x0ff), | |
547 | // (ts_spi->host->hw->data_buf[0] >> 11 & 0x0ff), | |
548 | // (ts_spi->host->hw->data_buf[0] >> 19 & 0x0ff) ); | |
549 | uint16_t res = (uint16_t)(((ts_spi->host->hw->data_buf[0] >> 11 & 0x0ff) << 8) | (ts_spi->host->hw->data_buf[0] >> 19 & 0x0ff)); | |
550 | // printf("res=%d %04x\n", res, res); | |
551 | // spi_lobo_device_deselect(ts_spi); | |
552 | return res; | |
553 | } | |
554 | ||
555 | // ==== STMPE610 =============================================================== | |
556 | ||
557 | ||
558 | // ----- STMPE610 -------------------------------------------------------------------------- | |
559 | ||
560 | // Send 1 byte display command, display must be selected | |
561 | //--------------------------------------------------------- | |
562 | static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val) { | |
563 | ||
564 | spi_lobo_device_select(ts_spi, 0); | |
565 | ||
566 | ts_spi->host->hw->data_buf[0] = (val << 8) | reg; | |
567 | _spi_transfer_start(ts_spi, 16, 0); | |
568 | ||
569 | spi_lobo_device_deselect(ts_spi); | |
570 | } | |
571 | ||
572 | //----------------------------------------------- | |
573 | static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) { | |
574 | spi_lobo_device_select(ts_spi, 0); | |
575 | ||
576 | ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80); | |
577 | _spi_transfer_start(ts_spi, 16, 16); | |
578 | uint8_t res = ts_spi->host->hw->data_buf[0] >> 8; | |
579 | ||
580 | spi_lobo_device_deselect(ts_spi); | |
581 | return res; | |
582 | } | |
583 | ||
584 | //----------------------------------------- | |
585 | static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) { | |
586 | spi_lobo_device_select(ts_spi, 0); | |
587 | ||
588 | ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80); | |
589 | _spi_transfer_start(ts_spi, 32, 32); | |
590 | uint16_t res = (uint16_t)(ts_spi->host->hw->data_buf[0] & 0xFF00); | |
591 | res |= (uint16_t)(ts_spi->host->hw->data_buf[0] >> 24); | |
592 | ||
593 | spi_lobo_device_deselect(ts_spi); | |
594 | return res; | |
595 | } | |
596 | ||
597 | //----------------------- | |
598 | uint32_t stmpe610_getID() | |
599 | { | |
600 | uint16_t tid = stmpe610_read_word(0); | |
601 | uint8_t tver = stmpe610_read_byte(2); | |
602 | return (tid << 8) | tver; | |
603 | } | |
604 | ||
605 | //================== | |
606 | void stmpe610_Init() | |
607 | { | |
608 | stmpe610_write_reg(STMPE610_REG_SYS_CTRL1, 0x02); // Software chip reset | |
609 | vTaskDelay(10 / portTICK_RATE_MS); | |
610 | ||
611 | stmpe610_write_reg(STMPE610_REG_SYS_CTRL2, 0x04); // Temperature sensor clock off, GPIO clock off, touch clock on, ADC clock on | |
612 | ||
613 | stmpe610_write_reg(STMPE610_REG_INT_EN, 0x00); // Don't Interrupt on INT pin | |
614 | ||
615 | stmpe610_write_reg(STMPE610_REG_ADC_CTRL1, 0x48); // ADC conversion time = 80 clock ticks, 12-bit ADC, internal voltage refernce | |
616 | vTaskDelay(2 / portTICK_RATE_MS); | |
617 | stmpe610_write_reg(STMPE610_REG_ADC_CTRL2, 0x01); // ADC speed 3.25MHz | |
618 | stmpe610_write_reg(STMPE610_REG_GPIO_AF, 0x00); // GPIO alternate function - OFF | |
619 | stmpe610_write_reg(STMPE610_REG_TSC_CFG, 0xE3); // Averaging 8, touch detect delay 1ms, panel driver settling time 1ms | |
620 | stmpe610_write_reg(STMPE610_REG_FIFO_TH, 0x01); // FIFO threshold = 1 | |
621 | stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable | |
622 | stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable | |
623 | stmpe610_write_reg(STMPE610_REG_TSC_FRACT_XYZ, 0x07); // Z axis data format | |
624 | stmpe610_write_reg(STMPE610_REG_TSC_I_DRIVE, 0x01); // max 50mA touchscreen line current | |
625 | stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x30); // X&Y&Z, 16 reading window | |
626 | stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x31); // X&Y&Z, 16 reading window, TSC enable | |
627 | stmpe610_write_reg(STMPE610_REG_INT_STA, 0xFF); // Clear all interrupts | |
628 | stmpe610_write_reg(STMPE610_REG_INT_CTRL, 0x00); // Level interrupt, disable interrupts | |
629 | } | |
630 | ||
631 | //=========================================================== | |
632 | int stmpe610_get_touch(uint16_t *x, uint16_t *y, uint16_t *z) | |
633 | { | |
634 | if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; | |
635 | ||
636 | // Get touch data | |
637 | uint8_t fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); | |
638 | while (fifo_size < 2) { | |
639 | if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; | |
640 | fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); | |
641 | } | |
642 | while (fifo_size > 120) { | |
643 | if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0; | |
644 | *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X); | |
645 | *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y); | |
646 | *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z); | |
647 | fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE); | |
648 | } | |
649 | for (uint8_t i=0; i < (fifo_size-1); i++) { | |
650 | *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X); | |
651 | *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y); | |
652 | *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z); | |
653 | } | |
654 | ||
655 | *x = 4096 - *x; | |
656 | /* | |
657 | // Clear the rest of the fifo | |
658 | { | |
659 | stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable | |
660 | stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable | |
661 | } | |
662 | */ | |
663 | return 1; | |
664 | } | |
665 | ||
666 | // ==== STMPE610 =========================================================================== | |
667 | ||
668 | ||
669 | // Find maximum spi clock for successful read from display RAM | |
670 | // ** Must be used AFTER the display is initialized ** | |
671 | //====================== | |
672 | uint32_t find_rd_speed() | |
673 | { | |
674 | esp_err_t ret; | |
675 | color_t color; | |
676 | uint32_t max_speed = 1000000; | |
677 | uint32_t change_speed, cur_speed; | |
678 | int line_check; | |
679 | color_t *color_line = NULL; | |
680 | uint8_t *line_rdbuf = NULL; | |
681 | uint8_t gs = gray_scale; | |
682 | ||
683 | gray_scale = 0; | |
684 | cur_speed = spi_lobo_get_speed(disp_spi); | |
685 | ||
686 | color_line = malloc(_width*3); | |
687 | if (color_line == NULL) goto exit; | |
688 | ||
689 | line_rdbuf = malloc((_width*3)+1); | |
690 | if (line_rdbuf == NULL) goto exit; | |
691 | ||
692 | color_t *rdline = (color_t *)(line_rdbuf+1); | |
693 | ||
694 | // Fill test line with colors | |
695 | color = (color_t){0xEC,0xA8,0x74}; | |
696 | for (int x=0; x<_width; x++) { | |
697 | color_line[x] = color; | |
698 | } | |
699 | ||
700 | // Find maximum read spi clock | |
701 | for (uint32_t speed=2000000; speed<=cur_speed; speed += 1000000) { | |
702 | change_speed = spi_lobo_set_speed(disp_spi, speed); | |
703 | if (change_speed == 0) goto exit; | |
704 | ||
705 | memset(line_rdbuf, 0, _width*sizeof(color_t)+1); | |
706 | ||
707 | if (disp_select()) goto exit; | |
708 | // Write color line | |
709 | send_data(0, _height/2, _width-1, _height/2, _width, color_line); | |
710 | if (disp_deselect()) goto exit; | |
711 | ||
712 | // Read color line | |
713 | ret = read_data(0, _height/2, _width-1, _height/2, _width, line_rdbuf, 0); | |
714 | ||
715 | // Compare | |
716 | line_check = 0; | |
717 | if (ret == ESP_OK) { | |
718 | for (int y=0; y<_width; y++) { | |
719 | if ((color_line[y].r & 0xFC) != (rdline[y].r & 0xFC)) line_check = 1; | |
720 | else if ((color_line[y].g & 0xFC) != (rdline[y].g & 0xFC)) line_check = 1; | |
721 | else if ((color_line[y].b & 0xFC) != (rdline[y].b & 0xFC)) line_check = 1; | |
722 | if (line_check) break; | |
723 | } | |
724 | } | |
725 | else line_check = ret; | |
726 | ||
727 | if (line_check) break; | |
728 | max_speed = speed; | |
729 | } | |
730 | ||
731 | exit: | |
732 | gray_scale = gs; | |
733 | if (line_rdbuf) free(line_rdbuf); | |
734 | if (color_line) free(color_line); | |
735 | ||
736 | // restore spi clk | |
737 | change_speed = spi_lobo_set_speed(disp_spi, cur_speed); | |
738 | ||
739 | return max_speed; | |
740 | } | |
741 | ||
742 | //--------------------------------------------------------------------------- | |
743 | // Companion code to the initialization table. | |
744 | // Reads and issues a series of LCD commands stored in byte array | |
745 | //--------------------------------------------------------------------------- | |
746 | static void commandList(spi_lobo_device_handle_t spi, const uint8_t *addr) { | |
747 | uint8_t numCommands, numArgs, cmd; | |
748 | uint16_t ms; | |
749 | ||
750 | numCommands = *addr++; // Number of commands to follow | |
751 | while(numCommands--) { // For each command... | |
752 | cmd = *addr++; // save command | |
753 | numArgs = *addr++; // Number of args to follow | |
754 | ms = numArgs & TFT_CMD_DELAY; // If high bit set, delay follows args | |
755 | numArgs &= ~TFT_CMD_DELAY; // Mask out delay bit | |
756 | ||
757 | disp_spi_transfer_cmd_data(cmd, (uint8_t *)addr, numArgs); | |
758 | ||
759 | addr += numArgs; | |
760 | ||
761 | if(ms) { | |
762 | ms = *addr++; // Read post-command delay time (ms) | |
763 | if(ms == 255) ms = 500; // If 255, delay for 500 ms | |
764 | vTaskDelay(ms / portTICK_RATE_MS); | |
765 | } | |
766 | } | |
767 | } | |
768 | ||
769 | //================================== | |
770 | void _tft_setRotation(uint8_t rot) { | |
771 | uint8_t rotation = rot & 3; // can't be higher than 3 | |
772 | uint8_t send = 1; | |
773 | uint8_t madctl = 0; | |
774 | uint16_t tmp; | |
775 | ||
776 | if ((rotation & 1)) { | |
777 | // in landscape modes must be width > height | |
778 | if (_width < _height) { | |
779 | tmp = _width; | |
780 | _width = _height; | |
781 | _height = tmp; | |
782 | } | |
783 | } | |
784 | else { | |
785 | // in portrait modes must be width < height | |
786 | if (_width > _height) { | |
787 | tmp = _width; | |
788 | _width = _height; | |
789 | _height = tmp; | |
790 | } | |
791 | } | |
792 | #if TFT_INVERT_ROTATION | |
793 | switch (rotation) { | |
794 | case PORTRAIT: | |
795 | madctl = (MADCTL_MV | TFT_RGB_BGR); | |
796 | break; | |
797 | case LANDSCAPE: | |
798 | madctl = (MADCTL_MX | TFT_RGB_BGR); | |
799 | break; | |
800 | case PORTRAIT_FLIP: | |
801 | madctl = (MADCTL_MV | TFT_RGB_BGR); | |
802 | break; | |
803 | case LANDSCAPE_FLIP: | |
804 | madctl = (MADCTL_MY | TFT_RGB_BGR); | |
805 | break; | |
806 | } | |
807 | #elif TFT_INVERT_ROTATION1 | |
808 | switch (rotation) { | |
809 | case PORTRAIT: | |
810 | madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR); | |
811 | break; | |
812 | case LANDSCAPE: | |
813 | madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); | |
814 | break; | |
815 | case PORTRAIT_FLIP: | |
816 | madctl = (TFT_RGB_BGR); | |
817 | break; | |
818 | case LANDSCAPE_FLIP: | |
819 | madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR); | |
820 | break; | |
821 | } | |
822 | #elif TFT_INVERT_ROTATION2 | |
823 | switch (rotation) { | |
824 | case PORTRAIT: | |
825 | madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR); | |
826 | break; | |
827 | case LANDSCAPE: | |
828 | madctl = (TFT_RGB_BGR); | |
829 | break; | |
830 | case PORTRAIT_FLIP: | |
831 | madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); | |
832 | break; | |
833 | case LANDSCAPE_FLIP: | |
834 | madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR); | |
835 | break; | |
836 | } | |
837 | #else | |
838 | switch (rotation) { | |
839 | case PORTRAIT: | |
840 | madctl = (MADCTL_MX | TFT_RGB_BGR); | |
841 | break; | |
842 | case LANDSCAPE: | |
843 | madctl = (MADCTL_MV | TFT_RGB_BGR); | |
844 | break; | |
845 | case PORTRAIT_FLIP: | |
846 | madctl = (MADCTL_MY | TFT_RGB_BGR); | |
847 | break; | |
848 | case LANDSCAPE_FLIP: | |
849 | madctl = (MADCTL_MX | MADCTL_MY | MADCTL_MV | TFT_RGB_BGR); | |
850 | break; | |
851 | } | |
852 | #endif | |
853 | if (send) { | |
854 | if (disp_select() == ESP_OK) { | |
855 | disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1); | |
856 | disp_deselect(); | |
857 | } | |
858 | } | |
859 | ||
860 | } | |
861 | ||
862 | //================= | |
863 | void TFT_PinsInit() | |
864 | { | |
865 | // Route all used pins to GPIO control | |
866 | gpio_pad_select_gpio(PIN_NUM_CS); | |
867 | gpio_pad_select_gpio(PIN_NUM_MISO); | |
868 | gpio_pad_select_gpio(PIN_NUM_MOSI); | |
869 | gpio_pad_select_gpio(PIN_NUM_CLK); | |
870 | gpio_pad_select_gpio(PIN_NUM_DC); | |
871 | ||
872 | gpio_set_direction(PIN_NUM_MISO, GPIO_MODE_INPUT); | |
873 | gpio_set_pull_mode(PIN_NUM_MISO, GPIO_PULLUP_ONLY); | |
874 | gpio_set_direction(PIN_NUM_CS, GPIO_MODE_OUTPUT); | |
875 | gpio_set_direction(PIN_NUM_MOSI, GPIO_MODE_OUTPUT); | |
876 | gpio_set_direction(PIN_NUM_CLK, GPIO_MODE_OUTPUT); | |
877 | gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT); | |
878 | gpio_set_level(PIN_NUM_DC, 0); | |
879 | #if USE_TOUCH | |
880 | gpio_pad_select_gpio(PIN_NUM_TCS); | |
881 | gpio_set_direction(PIN_NUM_TCS, GPIO_MODE_OUTPUT); | |
882 | #endif | |
883 | #if PIN_NUM_BCKL | |
884 | gpio_pad_select_gpio(PIN_NUM_BCKL); | |
885 | gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT); | |
886 | gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_OFF); | |
887 | #endif | |
888 | ||
889 | #if PIN_NUM_RST | |
890 | gpio_pad_select_gpio(PIN_NUM_RST); | |
891 | gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT); | |
892 | gpio_set_level(PIN_NUM_RST, 0); | |
893 | #endif | |
894 | } | |
895 | ||
896 | // Initialize the display | |
897 | // ==================== | |
898 | void TFT_display_init() | |
899 | { | |
900 | esp_err_t ret; | |
901 | ||
902 | #if PIN_NUM_RST | |
903 | //Reset the display | |
904 | gpio_set_level(PIN_NUM_RST, 0); | |
905 | vTaskDelay(20 / portTICK_RATE_MS); | |
906 | gpio_set_level(PIN_NUM_RST, 1); | |
907 | vTaskDelay(150 / portTICK_RATE_MS); | |
908 | #endif | |
909 | ||
910 | ret = disp_select(); | |
911 | assert(ret==ESP_OK); | |
912 | //Send all the initialization commands | |
913 | if (tft_disp_type == DISP_TYPE_ILI9341) { | |
914 | commandList(disp_spi, ILI9341_init); | |
915 | } | |
916 | else if (tft_disp_type == DISP_TYPE_ILI9488) { | |
917 | commandList(disp_spi, ILI9488_init); | |
918 | } | |
919 | else if (tft_disp_type == DISP_TYPE_ST7789V) { | |
920 | commandList(disp_spi, ST7789V_init); | |
921 | } | |
922 | else if (tft_disp_type == DISP_TYPE_ST7735) { | |
923 | commandList(disp_spi, STP7735_init); | |
924 | } | |
925 | else if (tft_disp_type == DISP_TYPE_ST7735R) { | |
926 | commandList(disp_spi, STP7735R_init); | |
927 | commandList(disp_spi, Rcmd2green); | |
928 | commandList(disp_spi, Rcmd3); | |
929 | } | |
930 | else if (tft_disp_type == DISP_TYPE_ST7735B) { | |
931 | commandList(disp_spi, STP7735R_init); | |
932 | commandList(disp_spi, Rcmd2red); | |
933 | commandList(disp_spi, Rcmd3); | |
934 | uint8_t dt = 0xC0; | |
935 | disp_spi_transfer_cmd_data(TFT_MADCTL, &dt, 1); | |
936 | } | |
937 | else assert(0); | |
938 | ||
939 | ret = disp_deselect(); | |
940 | assert(ret==ESP_OK); | |
941 | ||
942 | // Clear screen | |
943 | _tft_setRotation(PORTRAIT); | |
944 | TFT_pushColorRep(0, 0, _width-1, _height-1, (color_t){0,0,0}, (uint32_t)(_height*_width)); | |
945 | ||
946 | ///Enable backlight | |
947 | #if PIN_NUM_BCKL | |
948 | gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_ON); | |
949 | #endif | |
950 | } | |
951 | ||
952 |