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