TinyGPU
Loading...
Searching...
No Matches
DisplayDriverDSI.h
Go to the documentation of this file.
1#pragma once
2#include <stdint.h>
3#include <string.h>
4
5#include "TinyGPU/Drivers/DisplayDriver.h"
6#include "TinyGPU/Color/RGB565.h"
7#include "TinyGPU/Emulation.h"
8#include "TinyGPU/Abstractions/IDSIBus.h"
9#include "TinyGPU/Abstractions/DSIBusESP32.h"
10#include "TinyGPU/Abstractions/DSIBusSTM32.h"
11
12namespace tinygpu {
13
14/**
15 * @brief Base driver for MIPI-DSI panels. All of the DSI PHY/lane bring-up,
16 * command-channel writes and video-timing/framebuffer scan-out are handled
17 * by an IDSIBus (dependency-injected, see the constructor below), not by
18 * this class - this class only turns ISurface writes into drawBitmap()
19 * calls and byte-swaps into the bus's expected native RGB565 order (see
20 * writeData()). That split is what makes this family portable: adding a
21 * platform means implementing IDSIBus once (see DSIBusESP32.h, the
22 * ESP32-P4 DBI+DPI path; DSIBusSTM32.h, STM32's HAL_DSI+LTDC path), while
23 * every panel subclass (ST7701Driver, ...) keeps working unchanged on
24 * every platform that has a bus.
25 *
26 * RP2040 has no MIPI-DSI peripheral on any variant, so there is no
27 * RP2040 IDSIBus backend - QSPI/SPI panels (DisplayDriverQSPI.h/
28 * DisplayDriverSPI.h) are the only display option on that platform.
29 */
30template <typename RGB_T = RGB565>
31class DisplayDriverDSI : public DisplayDriver<RGB_T> {
32 public:
33 DisplayDriverDSI(IDSIBus& bus, size_t width, size_t height)
34 : bus_(&bus), width_(width), height_(height) {}
35
36 size_t width() const override { return width_; }
37 size_t height() const override { return height_; }
38
39 void end() override {
40 if (bus_ != nullptr) bus_->end();
41 }
42
43 ~DisplayDriverDSI() override { delete[] scratch_; }
44
45 bool writeData(ISurface<RGB_T>& surface) override {
46 return writeData(surface, 0, 0);
47 }
48
49 bool writeData(ISurface<RGB_T>& surface, size_t x, size_t y) override {
50 static_assert(sizeof(RGB_T) == 2,
51 "DisplayDriverDSI assumes a 16bpp RGB_T (RGB565)");
52 if (bus_ == nullptr) return false;
53
54 // RGB_T (RGB565) values are stored byte-swapped to SPI/QSPI wire order
55 // (see RGB565.h) - a DSI framebuffer instead wants the conventional
56 // (native) RGB565 bit layout, so swap back into a scratch buffer
57 // before handing it to the bus, same as DisplayDriverSDL does and for
58 // the same reason (this is a plain memory framebuffer, not a
59 // command-driven SPI panel).
60 const size_t n = surface.size();
61 if (scratchCapacity_ < n) {
62 delete[] scratch_;
63 scratch_ = new uint8_t[n];
64 scratchCapacity_ = n;
65 }
66 const uint16_t* src = reinterpret_cast<const uint16_t*>(surface.data());
67 uint16_t* dst = reinterpret_cast<uint16_t*>(scratch_);
68 const size_t pixelCount = n / sizeof(RGB_T);
69 for (size_t i = 0; i < pixelCount; ++i) {
70 dst[i] = RGB565::swapBytes(src[i]);
71 }
72
73 return bus_->drawBitmap(x, y, x + surface.width(), y + surface.height(),
74 scratch_);
75 }
76
77 protected:
78 /// Sets up the bus (PHY/lanes/command channel). Subclasses call this
79 /// first thing in their begin(), then send their panel's init register
80 /// sequence via writeCommand(), then finishInit() - the same
81 /// beginBus()-then-init-sequence-then-finishInit() division of labor
82 /// DisplayDriverQSPI's subclasses use for beginBus()/writeCommand().
83 bool beginBus() { return bus_ != nullptr && bus_->begin(); }
84
85 /// Sends one command + parameter bytes over the DSI command channel.
86 bool writeCommand(uint8_t cmd, const uint8_t* param = nullptr,
87 size_t len = 0) {
88 return bus_ != nullptr && bus_->writeCommand(cmd, param, len);
89 }
90
91 /// Finalizes the panel after the init register sequence has been sent -
92 /// active video scan-out starts once this returns true.
93 bool finishInit() { return bus_ != nullptr && bus_->finishInit(); }
94
95 void setBacklight(bool on) {
96 if (backlight_ < 0) return;
97 pinMode(backlight_, OUTPUT);
98 digitalWrite(backlight_, on ? HIGH : LOW);
99 }
100
101 void setBacklightPin(int8_t backlight) { backlight_ = backlight; }
102
103 bool setAddressWindow(size_t /*x*/, size_t /*y*/, size_t /*w*/,
104 size_t /*h*/) override {
105 return true; // no-op - see class comment
106 }
107
110 int8_t backlight_ = -1;
111
112 private:
113 uint8_t* scratch_ = nullptr;
114 size_t scratchCapacity_ = 0;
115};
116
117#ifdef TINYGPU_HAS_ESP_LCD_DSI
118
119/**
120 * @brief Driver for the Sitronix ST7701S MIPI-DSI TFT controller, as wired
121 * on the Guition JC4880P443C_I_W (ESP32-P4, 480x800 portrait, 4.3").
122 *
123 * Init register sequence, DSI lane config (2 lanes @ 500Mbps), DPI clock
124 * (34MHz) and video timing all transcribed verbatim from
125 * guition-jc4880p4-bsp's board_p4_st7701_init.h / board_p4.c
126 * (https://github.com/ultramcu/guition-jc4880p4-bsp), which documents them
127 * as VERIFIED-ON-HARDWARE on this exact board - this class only re-expresses
128 * that same recipe as a TinyGPU DisplayDriver instead of a bespoke BSP
129 * function, it does not add or change any of the underlying values. As with
130 * every other example in this repo's esp32-p4-480x800-guition-mipi-dsi-touch-display
131 * board directory, this file itself has not been run on real hardware.
132 *
133 * Register meanings beyond 0x11 (SLPOUT) / 0x29 (DISPON) are vendor-specific
134 * (ST7701 command-bank-switched extension registers) and undocumented
135 * beyond that source.
136 */
137template <typename RGB_T = RGB565>
139 public:
145
147 size_t height = 800, int8_t phyLdoChan = 3,
148 uint16_t phyLdoMv = 2500)
150 ownedBus_(rst, width, height, /*laneNum=*/2, /*laneMbps=*/500,
151 /*dpiClockMhz=*/34, phyLdoChan, phyLdoMv) {
153 ownedBus_.setVideoTiming(/*hsyncPulseWidth=*/12, /*hsyncBackPorch=*/42,
154 /*hsyncFrontPorch=*/42, /*vsyncPulseWidth=*/2,
155 /*vsyncBackPorch=*/8, /*vsyncFrontPorch=*/166);
156 }
157
159 static_assert(sizeof(RGB_T) == 2, "ST7701Driver assumes a 16bpp RGB_T (RGB565)");
160
161 if (!beginBus()) return false;
162
163 for (size_t i = 0; i < sizeof(kInitOps) / sizeof(kInitOps[0]); ++i) {
164 const InitOp& op = kInitOps[i];
166 if (op.delayMs) delay(op.delayMs);
167 }
168
169 if (!finishInit()) return false;
170 setBacklight(true);
171 return true;
172 }
173
174 private:
176
177 struct InitOp {
178 uint8_t cmd;
179 const uint8_t* data;
180 size_t len;
182 };
183
184 // {cmd, &params, param byte count, post-delay ms}, verbatim from
185 // guition-jc4880p4-bsp's board_p4_panel_init_cmds[] - see class comment.
186 static constexpr uint8_t kFf13[] = {0x77, 0x01, 0x00, 0x00, 0x13};
187 static constexpr uint8_t kEf08[] = {0x08};
188 static constexpr uint8_t kFf10[] = {0x77, 0x01, 0x00, 0x00, 0x10};
189 static constexpr uint8_t kC0[] = {0x63, 0x00};
190 static constexpr uint8_t kC1[] = {0x0D, 0x02};
191 static constexpr uint8_t kC2[] = {0x10, 0x08};
192 static constexpr uint8_t kCc[] = {0x10};
193 static constexpr uint8_t kB0Gamma[] = {0x80, 0x09, 0x53, 0x0C, 0xD0, 0x07,
194 0x0C, 0x09, 0x09, 0x28, 0x06, 0xD4,
195 0x13, 0x69, 0x2B, 0x71};
196 static constexpr uint8_t kB1Gamma[] = {0x80, 0x94, 0x5A, 0x10, 0xD3, 0x06,
197 0x0A, 0x08, 0x08, 0x25, 0x03, 0xD3,
198 0x12, 0x66, 0x6A, 0x0D};
199 static constexpr uint8_t kFf11[] = {0x77, 0x01, 0x00, 0x00, 0x11};
200 static constexpr uint8_t kB0[] = {0x5D};
201 static constexpr uint8_t kB1[] = {0x58};
202 static constexpr uint8_t kB2[] = {0x87};
203 static constexpr uint8_t kB3[] = {0x80};
204 static constexpr uint8_t kB5[] = {0x4E};
205 static constexpr uint8_t kB7[] = {0x85};
206 static constexpr uint8_t kB8[] = {0x21};
207 static constexpr uint8_t kB9[] = {0x10, 0x1F};
208 static constexpr uint8_t kBb[] = {0x03};
209 static constexpr uint8_t kBc[] = {0x00};
210 static constexpr uint8_t kC178[] = {0x78};
211 static constexpr uint8_t kC278[] = {0x78};
212 static constexpr uint8_t kD0[] = {0x88};
213 static constexpr uint8_t kE0[] = {0x00, 0x3A, 0x02};
214 static constexpr uint8_t kE1[] = {0x04, 0xA0, 0x00, 0xA0, 0x05, 0xA0,
215 0x00, 0xA0, 0x00, 0x40, 0x40};
216 static constexpr uint8_t kE2[] = {0x30, 0x00, 0x40, 0x40, 0x32, 0xA0,
217 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00};
218 static constexpr uint8_t kE3[] = {0x00, 0x00, 0x33, 0x33};
219 static constexpr uint8_t kE4[] = {0x44, 0x44};
220 static constexpr uint8_t kE5[] = {0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30,
221 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0,
222 0x07, 0x2C, 0xA0, 0xA0};
223 static constexpr uint8_t kE6[] = {0x00, 0x00, 0x33, 0x33};
224 static constexpr uint8_t kE7[] = {0x44, 0x44};
225 static constexpr uint8_t kE8[] = {0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F,
226 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0,
227 0x06, 0x2B, 0xA0, 0xA0};
228 static constexpr uint8_t kEb[] = {0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00};
229 static constexpr uint8_t kEc[] = {0x08, 0x01};
230 static constexpr uint8_t kEd[] = {0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F,
231 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65,
232 0x4A, 0x89, 0xB2, 0x0B};
233 static constexpr uint8_t kEf[] = {0x08, 0x08, 0x08, 0x45, 0x3F, 0x54};
234 static constexpr uint8_t kFf00[] = {0x77, 0x01, 0x00, 0x00, 0x00};
235 static constexpr uint8_t kNop[] = {0x00};
236
237 static constexpr InitOp kInitOps[] = {
238 {0xFF, kFf13, sizeof(kFf13), 0},
239 {0xEF, kEf08, sizeof(kEf08), 0},
240 {0xFF, kFf10, sizeof(kFf10), 0},
241 {0xC0, kC0, sizeof(kC0), 0},
242 {0xC1, kC1, sizeof(kC1), 0},
243 {0xC2, kC2, sizeof(kC2), 0},
244 {0xCC, kCc, sizeof(kCc), 0},
245 {0xB0, kB0Gamma, sizeof(kB0Gamma), 0},
246 {0xB1, kB1Gamma, sizeof(kB1Gamma), 0},
247 {0xFF, kFf11, sizeof(kFf11), 0},
248 {0xB0, kB0, sizeof(kB0), 0},
249 {0xB1, kB1, sizeof(kB1), 0},
250 {0xB2, kB2, sizeof(kB2), 0},
251 {0xB3, kB3, sizeof(kB3), 0},
252 {0xB5, kB5, sizeof(kB5), 0},
253 {0xB7, kB7, sizeof(kB7), 0},
254 {0xB8, kB8, sizeof(kB8), 0},
255 {0xB9, kB9, sizeof(kB9), 0},
256 {0xBB, kBb, sizeof(kBb), 0},
257 {0xBC, kBc, sizeof(kBc), 0},
258 {0xC1, kC178, sizeof(kC178), 0},
259 {0xC2, kC278, sizeof(kC278), 0},
260 {0xD0, kD0, sizeof(kD0), 0},
261 {0xE0, kE0, sizeof(kE0), 0},
262 {0xE1, kE1, sizeof(kE1), 0},
263 {0xE2, kE2, sizeof(kE2), 0},
264 {0xE3, kE3, sizeof(kE3), 0},
265 {0xE4, kE4, sizeof(kE4), 0},
266 {0xE5, kE5, sizeof(kE5), 0},
267 {0xE6, kE6, sizeof(kE6), 0},
268 {0xE7, kE7, sizeof(kE7), 0},
269 {0xE8, kE8, sizeof(kE8), 0},
270 {0xEB, kEb, sizeof(kEb), 0},
271 {0xEC, kEc, sizeof(kEc), 0},
272 {0xED, kEd, sizeof(kEd), 0},
273 {0xEF, kEf, sizeof(kEf), 0},
274 {0xFF, kFf00, sizeof(kFf00), 0},
275 {0x11, kNop, sizeof(kNop), 120}, // SLPOUT
276 {0x29, kNop, sizeof(kNop), 20}, // DISPON
277 };
278};
279
280#endif // TINYGPU_HAS_ESP_LCD_DSI
281
282} // namespace tinygpu
Base driver for MIPI-DSI panels. All of the DSI PHY/lane bring-up, command-channel writes and video-t...
Definition: DisplayDriverDSI.h:31
bool writeCommand(uint8_t cmd, const uint8_t *param=nullptr, size_t len=0)
Sends one command + parameter bytes over the DSI command channel.
Definition: DisplayDriverDSI.h:86
IDSIBus * bus_
Definition: DisplayDriverDSI.h:108
bool setAddressWindow(size_t, size_t, size_t, size_t) override
Definition: DisplayDriverDSI.h:103
bool beginBus()
Definition: DisplayDriverDSI.h:83
size_t width_
Definition: DisplayDriverDSI.h:109
void setBacklightPin(int8_t backlight)
Definition: DisplayDriverDSI.h:101
void end() override
Definition: DisplayDriverDSI.h:39
size_t height_
Definition: DisplayDriverDSI.h:109
size_t height() const override
Definition: DisplayDriverDSI.h:37
~DisplayDriverDSI() override
Definition: DisplayDriverDSI.h:43
bool writeData(ISurface< RGB_T > &surface, size_t x, size_t y) override
Definition: DisplayDriverDSI.h:49
bool finishInit()
Definition: DisplayDriverDSI.h:93
size_t width() const override
Definition: DisplayDriverDSI.h:36
DisplayDriverDSI(IDSIBus &bus, size_t width, size_t height)
Definition: DisplayDriverDSI.h:33
void setBacklight(bool on)
Definition: DisplayDriverDSI.h:95
int8_t backlight_
Definition: DisplayDriverDSI.h:110
bool writeData(ISurface< RGB_T > &surface) override
Definition: DisplayDriverDSI.h:45
Definition: DisplayDriver.h:49
Abstraction over the transport used by DisplayDriverDSI (see TinyGPU/Drivers/DisplayDriverDSI....
Definition: IDSIBus.h:26
virtual bool finishInit()=0
virtual void end()=0
virtual bool begin()=0
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Definition: DSIBusESP32.h:19