TinyGPU
Loading...
Searching...
No Matches
DisplayDriverSPI.h
Go to the documentation of this file.
1#pragma once
2#include <initializer_list>
3#include <stdint.h>
4#include <string.h>
5
6#include "TinyGPU/Drivers/DisplayDriver.h"
7#include "TinyGPU/Emulation.h"
8
9namespace tinygpu {
10
11/**
12 * @brief Common base class for SPI-based display drivers.
13 */
14template <typename RGB_T = RGB565>
15class DisplayDriverSPI : public DisplayDriver<RGB_T> {
16 public:
17 /// @param width/height the panel's addressable resolution, as reported
18 /// by width()/height() - subclasses with a rotation concept (e.g.
19 /// ILI9341Driver) override width()/height() themselves instead and can
20 /// ignore these.
21 DisplayDriverSPI(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1,
22 size_t xOffset = 0, size_t yOffset = 0,
23 uint32_t frequencyHz = 40000000,
24 uint32_t readFrequencyHz = 15000000, size_t width = 240,
25 size_t height = 320)
26 : spi_(spi),
27 cs_(cs),
28 dc_(dc),
29 rst_(rst),
32 frequencyHz_(frequencyHz),
33 readFrequencyHz_(readFrequencyHz),
35 height_(height) {}
36
37 size_t width() const override { return width_; }
38 size_t height() const override { return height_; }
39
40 bool writeData(ISurface<RGB_T>& surface) override {
41 return writeData(surface, 0, 0);
42 }
43
44 /// Swaps each pixel's two bytes right before they go out over SPI (see
45 /// writeData()) - off by default, since RGB_T (RGB565) already stores
46 /// values pre-swapped to the wire order most panels expect (see
47 /// RGB565.h). Some panels/wiring combinations expect the opposite of
48 /// that default (symptom: colors come out scrambled/inverted-looking -
49 /// e.g. a purple app bar rendering green, a light background rendering
50 /// black - across *everything* drawn, not just images); this is the one
51 /// place all pixel data funnels through before hitting the wire, so
52 /// flipping it here fixes fills/text/images uniformly instead of needing
53 /// a per-source fix (contrast JPEGParser::setSwapBytes(), which only
54 /// affects decoded images).
55 void setSwapOutputBytes(bool swap) { swapOutputBytes_ = swap; }
56
57 bool writeData(ISurface<RGB_T>& surface, size_t x, size_t y) override {
58 static_assert(sizeof(RGB_T) == 2,
59 "writeData assumes a 16bpp RGB_T (RGB565) stored in "
60 "wire byte order");
61 setAddressWindow(x, y, surface.width(), surface.height());
62 spi_.beginTransaction(SPISettings(frequencyHz_, MSBFIRST, SPI_MODE0));
63 digitalWrite(dc_, HIGH);
64 digitalWrite(cs_, LOW);
65 // RGB_T (RGB565) values are stored in the buffer already byte-swapped
66 // to the panel's wire order (see RGB565.h), so the buffer's raw memory
67 // order can normally be sent as-is - no per-pixel swap needed here.
68 // swapOutputBytes_ (see setSwapOutputBytes()) undoes that for panels
69 // that need the opposite order.
70 // Still copied through a small scratch buffer and sent with the
71 // portable bulk SPIClass::transfer(void*, size_t) - available on every
72 // core, not just ESP32 - rather than one SPIClass::transfer16() call
73 // per pixel, since transfer() lets the underlying implementation batch
74 // many bytes into fewer hardware transactions. Can't bulk-transfer the
75 // surface's own buffer directly: transfer(buf, count) is full-duplex
76 // and overwrites buf with received data, which would corrupt the
77 // framebuffer itself.
78 constexpr size_t kChunkBytes = 256;
79 uint8_t chunk[kChunkBytes];
80 const uint8_t* src = surface.data();
81 size_t remaining = surface.size();
82 while (remaining > 0) {
83 const size_t n = remaining < kChunkBytes ? remaining : kChunkBytes;
84 memcpy(chunk, src, n);
85 if (swapOutputBytes_) {
86 // n is always even here: surface.size() is width*height*sizeof(RGB_T)
87 // with sizeof(RGB_T) == 2 (see the static_assert above), and
88 // kChunkBytes is even, so every chunk - including the last,
89 // possibly-partial one - covers whole pixels.
90 for (size_t i = 0; i + 1 < n; i += 2) {
91 const uint8_t tmp = chunk[i];
92 chunk[i] = chunk[i + 1];
93 chunk[i + 1] = tmp;
94 }
95 }
96 spi_.transfer(chunk, n);
97 src += n;
98 remaining -= n;
99 }
100 digitalWrite(cs_, HIGH);
101 spi_.endTransaction();
102 return true;
103 }
104
105 bool readData(ISurface<RGB_T>& surface, size_t x, size_t y) {
106 size_t w = surface.width();
107 size_t h = surface.height();
108 size_t x0 = x + xOffset_;
109 size_t y0 = y + yOffset_;
110
111 spi_.beginTransaction(SPISettings(readFrequencyHz_, MSBFIRST, SPI_MODE0));
112 digitalWrite(cs_, LOW);
113
114 digitalWrite(dc_, LOW);
115 spi_.transfer(0x2A);
116 digitalWrite(dc_, HIGH);
117 spi_.transfer((x0) >> 8);
118 spi_.transfer((x0) & 0xFF);
119 spi_.transfer((x0 + w - 1) >> 8);
120 spi_.transfer((x0 + w - 1) & 0xFF);
121
122 digitalWrite(dc_, LOW);
123 spi_.transfer(0x2B);
124 digitalWrite(dc_, HIGH);
125 spi_.transfer((y0) >> 8);
126 spi_.transfer((y0) & 0xFF);
127 spi_.transfer((y0 + h - 1) >> 8);
128 spi_.transfer((y0 + h - 1) & 0xFF);
129
130 digitalWrite(dc_, LOW);
131 spi_.transfer(0x2E);
132 digitalWrite(dc_, HIGH);
133
134 spi_.transfer(0); // leading dummy byte before real pixel data
135 size_t pixelCount = w * h;
136 for (size_t i = 0; i < pixelCount; ++i) {
137 uint8_t r = spi_.transfer(0);
138 uint8_t g = spi_.transfer(0);
139 uint8_t b = spi_.transfer(0);
140 surface.setPixel(i % w, i / w, RGB_T(r, g, b));
141 }
142
143 digitalWrite(cs_, HIGH);
144 spi_.endTransaction();
145 return true;
146 }
147
148 protected:
150 int8_t cs_, dc_, rst_;
152 uint32_t frequencyHz_;
155 bool swapOutputBytes_ = false;
156
157 void setColumnRowAddress(size_t x, size_t y, size_t w, size_t h) {
158 writeCommand(0x2A);
159 writeData16(x + xOffset_, x + xOffset_ + w - 1);
160 writeCommand(0x2B);
161 writeData16(y + yOffset_, y + yOffset_ + h - 1);
162 }
163
164 bool setAddressWindow(size_t x, size_t y, size_t w, size_t h) override {
165 setColumnRowAddress(x, y, w, h);
166 writeCommand(0x2C); // RAMWR
167 return true;
168 }
169
171 pinMode(cs_, OUTPUT);
172 pinMode(dc_, OUTPUT);
173 if (rst_ >= 0) {
174 pinMode(rst_, OUTPUT);
175 digitalWrite(rst_, LOW);
176 delay(20);
177 digitalWrite(rst_, HIGH);
178 delay(150);
179 }
180 digitalWrite(cs_, HIGH);
181 }
182
183 void writeCommand(uint8_t cmd) {
184 spi_.beginTransaction(SPISettings(frequencyHz_, MSBFIRST, SPI_MODE0));
185 digitalWrite(dc_, LOW);
186 digitalWrite(cs_, LOW);
187 spi_.transfer(cmd);
188 digitalWrite(cs_, HIGH);
189 spi_.endTransaction();
190 }
191
192 void writeData16(uint16_t d1, uint16_t d2) {
193 spi_.beginTransaction(SPISettings(frequencyHz_, MSBFIRST, SPI_MODE0));
194 digitalWrite(dc_, HIGH);
195 digitalWrite(cs_, LOW);
196 spi_.transfer(d1 >> 8);
197 spi_.transfer(d1 & 0xFF);
198 spi_.transfer(d2 >> 8);
199 spi_.transfer(d2 & 0xFF);
200 digitalWrite(cs_, HIGH);
201 spi_.endTransaction();
202 }
203
204 void writeData8(uint8_t data) {
205 spi_.beginTransaction(SPISettings(frequencyHz_, MSBFIRST, SPI_MODE0));
206 digitalWrite(dc_, HIGH);
207 digitalWrite(cs_, LOW);
208 spi_.transfer(data);
209 digitalWrite(cs_, HIGH);
210 spi_.endTransaction();
211 }
212
213 /// Writes each byte as a separate writeData8() call - a convenience for
214 /// sending a command's multi-byte parameter list during init sequences.
215 void writeDataN(std::initializer_list<uint8_t> bytes) {
216 for (uint8_t b : bytes) {
217 writeData8(b);
218 }
219 }
220};
221
222/**
223 * @brief Driver for ST7735 SPI display controller.
224 */
225template <typename RGB_T = RGB565>
226class ST7735Driver : public DisplayDriverSPI<RGB_T> {
227 public:
228 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
229 using DisplayDriverSPI<RGB_T>::writeCommand;
230 using DisplayDriverSPI<RGB_T>::writeData8;
231
232 /// @param xOffset/yOffset column/row start offset baked into this
233 /// panel's glass vs. its 132x162 driver RAM - 2,1 (the default) is the
234 /// common 128x160 portrait module's offset; pass your panel's real
235 /// values for other sizes/orientations (e.g. 1,26 for the common
236 /// 160x80 "0.96 inch" module mounted landscape).
237 /// @param width/height the panel's addressable resolution, as reported
238 /// by width()/height().
239 /// @param madctl MADCTL (0x36) byte to program during begin(), or -1
240 /// (default) to leave the panel at its post-reset MADCTL (this
241 /// driver's original behavior) - pass an explicit value (see your
242 /// panel's datasheet) for panels/orientations that need BGR/MX/MY/MV
243 /// bits set, e.g. a landscape-mounted module.
244 ST7735Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1,
245 size_t xOffset = 2, size_t yOffset = 1, size_t width = 128,
246 size_t height = 160, int madctl = -1)
247 : DisplayDriverSPI<RGB_T>(spi, cs, dc, rst, xOffset, yOffset, 40000000,
248 15000000, width, height),
249 madctl_(madctl) {}
250
251 bool begin() override {
252 setupPinsAndReset();
253 writeCommand(0x01);
254 delay(150);
255 writeCommand(0x11);
256 delay(120);
257 writeCommand(0x3A);
258 writeData8(0x05);
259 if (madctl_ >= 0) {
260 writeCommand(0x36);
261 writeData8(static_cast<uint8_t>(madctl_));
262 }
263 writeCommand(0x29);
264 return true;
265 }
266
267 private:
268 int madctl_;
269};
270
271/**
272 * @brief Driver for ST7789 SPI display controller.
273 */
274template <typename RGB_T = RGB565>
275class ST7789Driver : public DisplayDriverSPI<RGB_T> {
276 public:
277 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
278 using DisplayDriverSPI<RGB_T>::writeCommand;
279 using DisplayDriverSPI<RGB_T>::writeData8;
280
281 ST7789Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1)
282 : DisplayDriverSPI<RGB_T>(spi, cs, dc, rst, 0, 0) {}
283
284 bool begin() override {
285 setupPinsAndReset();
286 writeCommand(0x01);
287 delay(150);
288 writeCommand(0x11);
289 delay(120);
290 writeCommand(0x3A);
291 writeData8(0x55);
292 writeCommand(0x29);
293 return true;
294 }
295};
296
297/**
298 * @brief Driver for ILI9341 SPI display controller.
299 *
300 * This init sequence (power control, VCOM, gamma correction, and the
301 * leading 0xEF unlock command some clone panels need) and the MADCTL/BGR
302 * rotation table are ported from the widely-used, independently
303 * maintained Bodmer/TFT_eSPI library's ILI9341 driver - confirmed on
304 * real hardware to render standard RGB565 colors correctly (no field
305 * compensation needed) on a panel where this library's earlier minimal
306 * init (reset/sleep-out/pixel-format/MADCTL/display-on only, with a
307 * guessed non-BGR MADCTL) did not, no matter which MADCTL value was
308 * tried - the missing piece was the full power/gamma programming, not
309 * just the MADCTL bit.
310 */
311template <typename RGB_T = RGB565>
312class ILI9341Driver : public DisplayDriverSPI<RGB_T> {
313 public:
314 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
315 using DisplayDriverSPI<RGB_T>::writeCommand;
316 using DisplayDriverSPI<RGB_T>::writeData8;
317 using DisplayDriverSPI<RGB_T>::writeDataN;
318
319 /// Alias for the shared tinygpu::DisplayRotation (see DisplayDriver.h) - kept
320 /// so existing code written against ILI9341Driver<RGB_T>::Rotation
321 /// still compiles unchanged now that the enum lives on the base class.
322 using Rotation = tinygpu::DisplayRotation;
323
324 /// @param nativeWidth/nativeHeight the panel's physical resolution in
325 /// its native (portrait, MV bit clear) orientation - 240x320 is the
326 /// common ILI9341 module size and the default; pass your panel's real
327 /// values if it differs. width()/height() report these swapped or not
328 /// depending on `rotation` (and any later setRotation() call) - see
329 /// isLandscapeFamily().
330 ILI9341Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1,
331 Rotation rotation = Rotation::kNone,
332 uint32_t frequencyHz = 40000000,
333 size_t nativeWidth = 240, size_t nativeHeight = 320)
335 rotation_(rotation),
338
339 size_t width() const override {
340 return isLandscapeFamily(rotation_) ? nativeHeight_ : nativeWidth_;
341 }
342 size_t height() const override {
343 return isLandscapeFamily(rotation_) ? nativeWidth_ : nativeHeight_;
344 }
345
346 bool begin() override {
347 setupPinsAndReset();
348
349 writeCommand(0x01); // SWRESET
350 delay(150);
351
352 writeCommand(0xEF); // Undocumented "unlock" command some clone
353 writeDataN({0x03, 0x80, 0x02}); // panels need for correct init
354
355 writeCommand(0xCF); // Power control B
356 writeDataN({0x00, 0xC1, 0x30});
357
358 writeCommand(0xED); // Power on sequence control
359 writeDataN({0x64, 0x03, 0x12, 0x81});
360
361 writeCommand(0xE8); // Driver timing control A
362 writeDataN({0x85, 0x00, 0x78});
363
364 writeCommand(0xCB); // Power control A
365 writeDataN({0x39, 0x2C, 0x00, 0x34, 0x02});
366
367 writeCommand(0xF7); // Pump ratio control
368 writeDataN({0x20});
369
370 writeCommand(0xEA); // Driver timing control B
371 writeDataN({0x00, 0x00});
372
373 writeCommand(0xC0); // Power control 1
374 writeDataN({0x23});
375
376 writeCommand(0xC1); // Power control 2
377 writeDataN({0x10});
378
379 writeCommand(0xC5); // VCOM control 1
380 writeDataN({0x3E, 0x28});
381
382 writeCommand(0xC7); // VCOM control 2
383 writeDataN({0x86});
384
385 writeCommand(0x36); // MADCTL
386 writeDataN({madctlForRotation(rotation_)});
387
388 writeCommand(0x3A); // Pixel format: 16bpp
389 writeDataN({0x55});
390
391 writeCommand(0xB1); // Frame rate control
392 writeDataN({0x00, 0x13});
393
394 writeCommand(0xB6); // Display function control
395 writeDataN({0x08, 0x82, 0x27});
396
397 writeCommand(0xF2); // Disable 3-gamma
398 writeDataN({0x00});
399
400 writeCommand(0x26); // Gamma set
401 writeDataN({0x01});
402
403 writeCommand(0xE0); // Positive gamma correction
404 writeDataN({0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07,
405 0x10, 0x03, 0x0E, 0x09, 0x00});
406
407 writeCommand(0xE1); // Negative gamma correction
408 writeDataN({0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08,
409 0x0F, 0x0C, 0x31, 0x36, 0x0F});
410
411 writeCommand(0x11); // Sleep out
412 delay(120);
413 writeCommand(0x29); // Display on
414
415 // Re-assert whatever setInvertColor() last requested - SWRESET above
416 // reset the panel's own inversion register to its power-on default
417 // (off) regardless, so a call to setInvertColor() made before this
418 // begin() would otherwise be silently lost. See that method's doc
419 // comment.
420 writeCommand(invertColor_ ? 0x21 : 0x20);
421
422 return true;
423 }
424
425 void setRotation(Rotation rotation) override {
426 if (rotation == Rotation::kNone) return;
427 rotation_ = rotation;
428 writeCommand(0x36);
429 writeDataN({madctlForRotation(rotation_)});
430 }
431
432 Rotation rotation() const { return rotation_; }
433
434 /**
435 * @brief Toggles the ILI9341's display inversion (DINVON/DINVOFF, 0x21/
436 * 0x20). Some cheap ILI9341-compatible clone panels' color filter needs
437 * this set, or every color renders as its photographic negative (RED
438 * shows as cyan, GREEN as magenta, BLUE as yellow) - confirmed on real
439 * hardware. Safe to call any time after begin() - and safe to call
440 * *before* a later begin() too: begin() sends SWRESET (0x01), a real
441 * hardware reset that puts the panel's own inversion register back to
442 * its power-on default regardless of what this method last told it (no
443 * explicit invert command appears anywhere in begin() itself, so this
444 * is easy to miss), so begin() re-sends whatever this last requested
445 * (via invertColor_) as its final step - a call to this method always
446 * sticks no matter how many further begin() calls follow (e.g. a
447 * caller's own redundant DeviceOutput::begin()).
448 */
449 void setInvertColor(bool invert) {
450 invertColor_ = invert;
451 writeCommand(invert ? 0x21 : 0x20);
452 }
453
454 protected:
455 Rotation rotation_;
457 // Re-sent at the end of begin() - see setInvertColor()'s doc comment.
458 bool invertColor_ = false;
459
460 // MADCTL values with the BGR bit set (0x08), matching the MX/MY/MV
461 // (row/column/exchange) + BGR combinations Bodmer/TFT_eSPI's ILI9341
462 // rotation table uses for its 4 primary rotations - not just "BGR set
463 // for every rotation", the MX/MY/MV bits differ per rotation too.
464 static uint8_t madctlForRotation(Rotation rotation) {
465 switch (rotation) {
466 case Rotation::kLandscape:
467 return 0x28; // MV | BGR
468 case Rotation::kPortraitFlipped:
469 return 0x88; // MY | BGR
470 case Rotation::kLandscapeFlipped:
471 return 0xE8; // MX | MY | MV | BGR
472 case Rotation::kNone:
473 case Rotation::kPortrait:
474 default:
475 return 0x48; // MX | BGR
476 }
477 }
478};
479
480/**
481 * @brief Driver for ILI9342 SPI display controller.
482 *
483 * ILI9342 shares ILI9341's command set (this class is structurally
484 * identical to ILI9341Driver), but this init sequence additionally
485 * programs power control, VCOM and gamma correction registers, which
486 * ILI9341Driver's minimal init (reset/sleep-out/pixel-format/MADCTL/
487 * display-on only) leaves at the chip's power-on defaults. On some cheap
488 * clone panels sold as "ILI9341-compatible" - including ones that may
489 * actually be ILI9342 silicon - unprogrammed gamma/power registers are a
490 * plausible cause of color/near-black-tint issues that ILI9341Driver's
491 * minimal init doesn't fix. If your panel has that kind of issue, try
492 * this driver in place of ILI9341Driver; the register values below are a
493 * commonly used, well-established init sequence for this chip family,
494 * not something tuned to a specific board.
495 */
496template <typename RGB_T = RGB565>
497class ILI9342Driver : public DisplayDriverSPI<RGB_T> {
498 public:
499 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
500 using DisplayDriverSPI<RGB_T>::writeCommand;
501 using DisplayDriverSPI<RGB_T>::writeData8;
502 using DisplayDriverSPI<RGB_T>::writeDataN;
503
504 /// Alias for the shared tinygpu::DisplayRotation (see DisplayDriver.h) - kept
505 /// so existing code written against ILI9342Driver<RGB_T>::Rotation
506 /// still compiles unchanged now that the enum lives on the base class.
507 using Rotation = tinygpu::DisplayRotation;
508
509 /// @param nativeWidth/nativeHeight the panel's physical resolution in
510 /// its native (portrait, MV bit clear) orientation - 240x320 is the
511 /// common size for this chip family and the default; pass your panel's
512 /// real values if it differs. width()/height() report these swapped or
513 /// not depending on `rotation` (and any later setRotation() call) -
514 /// see isLandscapeFamily().
515 ILI9342Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1,
516 Rotation rotation = Rotation::kNone,
517 size_t nativeWidth = 240, size_t nativeHeight = 320)
518 : DisplayDriverSPI<RGB_T>(spi, cs, dc, rst, 0, 0),
519 rotation_(rotation),
522
523 size_t width() const override {
524 return isLandscapeFamily(rotation_) ? nativeHeight_ : nativeWidth_;
525 }
526 size_t height() const override {
527 return isLandscapeFamily(rotation_) ? nativeWidth_ : nativeHeight_;
528 }
529
530 bool begin() override {
531 setupPinsAndReset();
532
533 writeCommand(0x01); // SWRESET
534 delay(150);
535
536 writeCommand(0xCB); // Power control B
537 writeDataN({0x39, 0x2C, 0x00, 0x34, 0x02});
538
539 writeCommand(0xCF); // Power on sequence control
540 writeDataN({0x00, 0xC1, 0x30});
541
542 writeCommand(0xE8); // Driver timing control A
543 writeDataN({0x85, 0x00, 0x78});
544
545 writeCommand(0xEA); // Driver timing control B
546 writeDataN({0x00, 0x00});
547
548 writeCommand(0xED); // Power on sequence control
549 writeDataN({0x64, 0x03, 0x12, 0x81});
550
551 writeCommand(0xF7); // Pump ratio control
552 writeDataN({0x20});
553
554 writeCommand(0xC0); // Power control 1
555 writeDataN({0x23});
556
557 writeCommand(0xC1); // Power control 2
558 writeDataN({0x10});
559
560 writeCommand(0xC5); // VCOM control 1
561 writeDataN({0x3E, 0x28});
562
563 writeCommand(0xC7); // VCOM control 2
564 writeDataN({0x86});
565
566 writeCommand(0x36); // MADCTL
567 writeDataN({madctlForRotation(rotation_)});
568
569 writeCommand(0x3A); // Pixel format: 16bpp
570 writeDataN({0x55});
571
572 writeCommand(0xB1); // Frame rate control
573 writeDataN({0x00, 0x18});
574
575 writeCommand(0xB6); // Display function control
576 writeDataN({0x08, 0x82, 0x27});
577
578 writeCommand(0xF2); // Disable 3-gamma
579 writeDataN({0x00});
580
581 writeCommand(0x26); // Gamma set
582 writeDataN({0x01});
583
584 writeCommand(0xE0); // Positive gamma correction
585 writeDataN({0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07,
586 0x10, 0x03, 0x0E, 0x09, 0x00});
587
588 writeCommand(0xE1); // Negative gamma correction
589 writeDataN({0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08,
590 0x0F, 0x0C, 0x31, 0x36, 0x0F});
591
592 writeCommand(0x11); // Sleep out
593 delay(120);
594 writeCommand(0x29); // Display on
595
596 return true;
597 }
598
599 void setRotation(Rotation rotation) override {
600 if (rotation == Rotation::kNone) return;
601 rotation_ = rotation;
602 writeCommand(0x36);
603 writeDataN({madctlForRotation(rotation_)});
604 }
605
606 Rotation rotation() const { return rotation_; }
607
608 protected:
609 Rotation rotation_;
611
612 static uint8_t madctlForRotation(Rotation rotation) {
613 switch (rotation) {
614 case Rotation::kLandscape:
615 return 0x20;
616 case Rotation::kPortraitFlipped:
617 return 0x80;
618 case Rotation::kLandscapeFlipped:
619 return 0xE0;
620 case Rotation::kNone:
621 case Rotation::kPortrait:
622 default:
623 return 0x40;
624 }
625 }
626};
627
628/**
629 * @brief Driver for HX8357 SPI display controller.
630 */
631template <typename RGB_T = RGB565>
632class HX8357Driver : public DisplayDriverSPI<RGB_T> {
633 public:
634 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
635 using DisplayDriverSPI<RGB_T>::writeCommand;
636 using DisplayDriverSPI<RGB_T>::writeData8;
637
638 HX8357Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1)
639 : DisplayDriverSPI<RGB_T>(spi, cs, dc, rst, 0, 0) {}
640
641 bool begin() override {
642 setupPinsAndReset();
643 writeCommand(0x01);
644 delay(150);
645 writeCommand(0x11);
646 delay(120);
647 writeCommand(0x3A);
648 writeData8(0x55);
649 writeCommand(0x29);
650 return true;
651 }
652};
653
654/**
655 * @brief Driver for ST7796 SPI display controller.
656 */
657template <typename RGB_T = RGB565>
658class ST7796Driver : public DisplayDriverSPI<RGB_T> {
659 public:
660 using DisplayDriverSPI<RGB_T>::setupPinsAndReset;
661 using DisplayDriverSPI<RGB_T>::writeCommand;
662 using DisplayDriverSPI<RGB_T>::writeData8;
663
664 ST7796Driver(SPIClass& spi, int8_t cs, int8_t dc, int8_t rst = -1)
665 : DisplayDriverSPI<RGB_T>(spi, cs, dc, rst, 0, 0) {}
666
667 bool begin() override {
668 setupPinsAndReset();
669 writeCommand(0x01);
670 delay(150);
671 writeCommand(0x11);
672 delay(120);
673 writeCommand(0x3A);
674 writeData8(0x55);
675 writeCommand(0x36);
676 writeData8(0x48);
677 writeCommand(0x29);
678 return true;
679 }
680};
681
682} // namespace tinygpu
Common base class for SPI-based display drivers.
Definition: DisplayDriverSPI.h:15
bool readData(ISurface< RGB_T > &surface, size_t x, size_t y)
Definition: DisplayDriverSPI.h:105
void setSwapOutputBytes(bool swap)
Definition: DisplayDriverSPI.h:55
SPIClass & spi_
Definition: DisplayDriverSPI.h:149
DisplayDriverSPI(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1, size_t xOffset=0, size_t yOffset=0, uint32_t frequencyHz=40000000, uint32_t readFrequencyHz=15000000, size_t width=240, size_t height=320)
Definition: DisplayDriverSPI.h:21
int8_t dc_
Definition: DisplayDriverSPI.h:150
bool swapOutputBytes_
Definition: DisplayDriverSPI.h:155
size_t xOffset_
Definition: DisplayDriverSPI.h:151
uint32_t frequencyHz_
Definition: DisplayDriverSPI.h:152
size_t yOffset_
Definition: DisplayDriverSPI.h:151
size_t width_
Definition: DisplayDriverSPI.h:154
void writeDataN(std::initializer_list< uint8_t > bytes)
Definition: DisplayDriverSPI.h:215
size_t height_
Definition: DisplayDriverSPI.h:154
size_t height() const override
Definition: DisplayDriverSPI.h:38
void setColumnRowAddress(size_t x, size_t y, size_t w, size_t h)
Definition: DisplayDriverSPI.h:157
int8_t cs_
Definition: DisplayDriverSPI.h:150
int8_t rst_
Definition: DisplayDriverSPI.h:150
void setupPinsAndReset()
Definition: DisplayDriverSPI.h:170
void writeCommand(uint8_t cmd)
Definition: DisplayDriverSPI.h:183
bool writeData(ISurface< RGB_T > &surface, size_t x, size_t y) override
Definition: DisplayDriverSPI.h:57
size_t width() const override
Definition: DisplayDriverSPI.h:37
bool setAddressWindow(size_t x, size_t y, size_t w, size_t h) override
Definition: DisplayDriverSPI.h:164
void writeData8(uint8_t data)
Definition: DisplayDriverSPI.h:204
uint32_t readFrequencyHz_
Definition: DisplayDriverSPI.h:153
bool writeData(ISurface< RGB_T > &surface) override
Definition: DisplayDriverSPI.h:40
void writeData16(uint16_t d1, uint16_t d2)
Definition: DisplayDriverSPI.h:192
Definition: DisplayDriver.h:49
Driver for HX8357 SPI display controller.
Definition: DisplayDriverSPI.h:632
HX8357Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1)
Definition: DisplayDriverSPI.h:638
bool begin() override
Definition: DisplayDriverSPI.h:641
Driver for ILI9341 SPI display controller.
Definition: DisplayDriverSPI.h:312
static uint8_t madctlForRotation(Rotation rotation)
Definition: DisplayDriverSPI.h:464
size_t nativeHeight_
Definition: DisplayDriverSPI.h:456
size_t nativeWidth_
Definition: DisplayDriverSPI.h:456
void setInvertColor(bool invert)
Toggles the ILI9341's display inversion (DINVON/DINVOFF, 0x21/ 0x20). Some cheap ILI9341-compatible c...
Definition: DisplayDriverSPI.h:449
ILI9341Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1, Rotation rotation=Rotation::kNone, uint32_t frequencyHz=40000000, size_t nativeWidth=240, size_t nativeHeight=320)
Definition: DisplayDriverSPI.h:330
size_t height() const override
Definition: DisplayDriverSPI.h:342
size_t width() const override
Definition: DisplayDriverSPI.h:339
Rotation rotation() const
Definition: DisplayDriverSPI.h:432
bool invertColor_
Definition: DisplayDriverSPI.h:458
bool begin() override
Definition: DisplayDriverSPI.h:346
void setRotation(Rotation rotation) override
Definition: DisplayDriverSPI.h:425
Rotation rotation_
Definition: DisplayDriverSPI.h:455
Driver for ILI9342 SPI display controller.
Definition: DisplayDriverSPI.h:497
static uint8_t madctlForRotation(Rotation rotation)
Definition: DisplayDriverSPI.h:612
size_t nativeHeight_
Definition: DisplayDriverSPI.h:610
size_t nativeWidth_
Definition: DisplayDriverSPI.h:610
size_t height() const override
Definition: DisplayDriverSPI.h:526
ILI9342Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1, Rotation rotation=Rotation::kNone, size_t nativeWidth=240, size_t nativeHeight=320)
Definition: DisplayDriverSPI.h:515
size_t width() const override
Definition: DisplayDriverSPI.h:523
Rotation rotation() const
Definition: DisplayDriverSPI.h:606
bool begin() override
Definition: DisplayDriverSPI.h:530
void setRotation(Rotation rotation) override
Definition: DisplayDriverSPI.h:599
Rotation rotation_
Definition: DisplayDriverSPI.h:609
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Driver for ST7735 SPI display controller.
Definition: DisplayDriverSPI.h:226
ST7735Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1, size_t xOffset=2, size_t yOffset=1, size_t width=128, size_t height=160, int madctl=-1)
Definition: DisplayDriverSPI.h:244
bool begin() override
Definition: DisplayDriverSPI.h:251
Driver for ST7789 SPI display controller.
Definition: DisplayDriverSPI.h:275
ST7789Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1)
Definition: DisplayDriverSPI.h:281
bool begin() override
Definition: DisplayDriverSPI.h:284
Driver for ST7796 SPI display controller.
Definition: DisplayDriverSPI.h:658
ST7796Driver(SPIClass &spi, int8_t cs, int8_t dc, int8_t rst=-1)
Definition: DisplayDriverSPI.h:664
bool begin() override
Definition: DisplayDriverSPI.h:667
Definition: DSIBusESP32.h:19
void delay(unsigned long ms)
Definition: EmulationDesktop.h:52
DisplayRotation
Abstract base class for display drivers.
Definition: DisplayDriver.h:28