TinyGPU
Loading...
Searching...
No Matches
DSIBusSTM32.h
Go to the documentation of this file.
1#pragma once
2#include <stdint.h>
3#include <string.h>
4
5#include "TinyGPU/Abstractions/IDSIBus.h"
6
7// Only STM32 parts with a DSI host (STM32F469/F479, STM32F769/F779,
8// STM32H747/H757, STM32U5x9, ...) ship stm32yyxx_hal_dsi.h in STM32duino's
9// core - __has_include lets this file compile as an empty no-op on every
10// other STM32 target (and on non-STM32 platforms) instead of hard-erroring,
11// the same role DSIBusESP32.h's __has_include("esp_lcd_mipi_dsi.h") plays
12// for the ESP32-P4-only DSI peripheral.
13#if defined(STM32) || defined(ARDUINO_ARCH_STM32)
14#if __has_include("stm32yyxx_hal_dsi.h") && __has_include("stm32yyxx_hal_ltdc.h")
15#include "stm32yyxx_hal_dsi.h"
16#include "stm32yyxx_hal_ltdc.h"
17#define TINYGPU_HAS_STM32_DSI 1
18#endif
19#endif
20
21namespace tinygpu {
22
23#ifdef TINYGPU_HAS_STM32_DSI
24
25/**
26 * @brief IDSIBus backend for STM32 parts with a DSI host + LTDC (F469/
27 * F479, F769/F779, H747/H757, U5x9, ...), built on STM32Cube HAL's
28 * HAL_DSI_* (command channel, adapted/video-mode DSI wrapper) and
29 * HAL_LTDC_* (the pixel-timing generator/framebuffer scanner DSI sits in
30 * front of on these parts - conceptually the same role ESP32-P4's DPI
31 * peripheral plays for DSIBusESP32).
32 *
33 * UNVERIFIED ON REAL HARDWARE - unlike DSIBusESP32 (whose ESP32-P4 DPI
34 * path is exercised by boards this library ships examples for), no
35 * STM32-DSI board has been used to test this class. The DSI-video-timing
36 * math (HSA/HBP/HLINE expressed in DSI lane-byte-clock cycles, derived
37 * from the pixel-domain porches via the standard
38 * `<pixels> * laneByteClk_kHz / dpiClock_kHz` conversion) and the LTDC
39 * accumulated-timing fields follow the pattern used throughout ST's own
40 * DSI panel BSPs (e.g. the OTM8009A driver shipped with the
41 * STM32F769I-Discovery/STM32H747I-Discovery BSPs) - treat it as a
42 * documented starting point for board bring-up, not a guarantee.
43 *
44 * Deliberately NOT in scope here: the DSI PHY PLL (dsiPllInit - NDIV/IDF/
45 * ODF) and the LTDC pixel clock (normally routed off RCC PLL3 on these
46 * parts). Both depend on the board's external oscillator and are
47 * ordinarily produced by STM32CubeMX's generated SystemClock_Config() -
48 * this class takes the already-computed DSI_PLLInitTypeDef and LTDC
49 * pixel clock (in kHz) as constructor inputs rather than guessing at
50 * divisors that would silently be wrong for any board other than the one
51 * they were tuned for.
52 *
53 * Only a single framebuffer is used (no double-buffering), same tradeoff
54 * DSIBusESP32 accepts - see that class's doc comment.
55 */
56class DSIBusSTM32 : public IDSIBus {
57 public:
58 /**
59 * @param rst panel reset GPIO, -1 if unused
60 * @param width/height panel resolution in pixels
61 * @param laneNum number of DSI data lanes (1 or 2)
62 * @param laneMbps per-lane bit rate in Mbps (used only for the DSI
63 * video-timing conversion below - does NOT configure the DSI PHY PLL,
64 * see pllInit)
65 * @param dpiClockKHz pixel clock in kHz that the caller has already
66 * arranged to feed the LTDC (typically via RCC PLL3), matching what
67 * `pllInit` was computed for
68 * @param pllInit pre-computed DSI PHY PLL config (NDIV/IDF/ODF) for
69 * this board's external oscillator - normally CubeMX-generated
70 * @param framebuffer caller-owned RGB565 framebuffer,
71 * width*height*2 bytes, placed by the caller in whatever RAM region
72 * the board's LTDC/DMA2D can actually read from (not every STM32 RAM
73 * region is - AXI SRAM/SDRAM are typical, not all of DTCM/ITCM)
74 */
78 : rst_(rst),
86
87 /// Sets the panel's video timing (porches/sync widths, in pixels/
88 /// lines). Must be called before begin(); every panel documents its
89 /// own values, there is no cross-panel default worth assuming.
99 }
100
102 resetPanel();
103
110 if (HAL_DSI_Init(&hdsi_, &pllInit) != HAL_OK) return false;
111
119 if (HAL_DSI_ConfigPhyTimer(&hdsi_, &phyTimings) != HAL_OK) return false;
120
132 return false;
133 }
134
136 return false;
137 }
138 if (HAL_DSI_SetGenericVCID(&hdsi_, 0) != HAL_OK) return false;
139
140 // LP command mode only for now (writeCommand()) - video mode/LTDC are
141 // brought up afterwards in finishInit(), once the panel's init
142 // sequence has been sent, mirroring DisplayDriverDSI's beginBus()-
143 // then-init-sequence-then-finishInit() split for DSIBusESP32.
147 cmdCfg.CommandSize = static_cast<uint16_t>(width_);
154 return false;
155 }
156
157 if (HAL_DSI_Start(&hdsi_) != HAL_OK) return false;
158 return true;
159 }
160
161 void end() override {
165 }
166
168 if (len == 0) {
170 0) == HAL_OK;
171 }
172 if (len == 1) {
174 param[0]) == HAL_OK;
175 }
176 // HAL_DSI_LongWrite's payload buffer is {cmd, param[0], param[1], ...}
177 // - build it in scratch_ since `param` is caller-owned and const.
178 if (scratchCapacity_ < len + 1) {
179 delete[] scratch_;
180 scratch_ = new uint8_t[len + 1];
181 scratchCapacity_ = len + 1;
182 }
183 scratch_[0] = cmd;
184 memcpy(scratch_ + 1, param, len);
186 static_cast<uint32_t>(len + 1), cmd,
187 scratch_) == HAL_OK;
188 }
189
190 /// Configures and starts the DSI video wrapper + LTDC framebuffer scan-
191 /// out - the panel is expected to be fully initialized (sleep-out,
192 /// display-on, ...) by this point via writeCommand().
194 // DSI video timing, converted from the pixel-clock domain into the
195 // DSI lane-byte-clock domain - see class doc comment.
196 const uint32_t laneByteClkKHz = (laneMbps_ * 1000) / 8;
201 vidCfg.PacketSize = static_cast<uint32_t>(width_);
203 vidCfg.NullPacketSize = 0xFFF;
214 vidCfg.VerticalActive = static_cast<uint32_t>(height_);
225 if (HAL_DSI_ConfigVideoMode(&hdsi_, &vidCfg) != HAL_OK) return false;
226
232 hltdc_.Init.HorizontalSync = static_cast<uint32_t>(hsa_ - 1);
233 hltdc_.Init.VerticalSync = static_cast<uint32_t>(vsa_ - 1);
234 hltdc_.Init.AccumulatedHBP = static_cast<uint32_t>(hsa_ + hbp_ - 1);
235 hltdc_.Init.AccumulatedVBP = static_cast<uint32_t>(vsa_ + vbp_ - 1);
237 static_cast<uint32_t>(hsa_ + hbp_ + width_ - 1);
239 static_cast<uint32_t>(vsa_ + vbp_ + height_ - 1);
241 static_cast<uint32_t>(hsa_ + hbp_ + width_ + hfp_ - 1);
243 static_cast<uint32_t>(vsa_ + vbp_ + height_ + vfp_ - 1);
247 if (HAL_LTDC_Init(&hltdc_) != HAL_OK) return false;
248
250 layerCfg.WindowX0 = 0;
251 layerCfg.WindowX1 = static_cast<uint32_t>(width_);
252 layerCfg.WindowY0 = 0;
253 layerCfg.WindowY1 = static_cast<uint32_t>(height_);
255 layerCfg.Alpha = 255;
256 layerCfg.Alpha0 = 0;
259 layerCfg.FBStartAdress = reinterpret_cast<uint32_t>(framebuffer_);
260 layerCfg.ImageWidth = static_cast<uint32_t>(width_);
261 layerCfg.ImageHeight = static_cast<uint32_t>(height_);
265 if (HAL_LTDC_ConfigLayer(&hltdc_, &layerCfg, 0) != HAL_OK) return false;
266
267 return HAL_DSI_Refresh(&hdsi_) == HAL_OK;
268 }
269
271 const uint8_t* rgb565) override {
272 // Adapted-command-mode panel: the framebuffer IS the LTDC layer
273 // buffer, so a "draw" is a plain memory copy into it, followed by a
274 // refresh request telling the DSI wrapper to push the frame out.
275 const size_t rowBytes = (x1 - x0) * sizeof(uint16_t);
276 const uint8_t* src = rgb565;
277 uint8_t* dstBase = reinterpret_cast<uint8_t*>(framebuffer_);
278 for (size_t y = y0; y < y1; ++y) {
279 uint8_t* dst = dstBase + (y * width_ + x0) * sizeof(uint16_t);
281 src += rowBytes;
282 }
283 return HAL_DSI_Refresh(&hdsi_) == HAL_OK;
284 }
285
286 private:
287 void resetPanel() {
288 if (rst_ < 0) return;
291 delay(20);
293 delay(120);
294 }
295
296 int8_t rst_;
303 uint16_t hsa_ = 10, hbp_ = 10, hfp_ = 10;
304 uint16_t vsa_ = 2, vbp_ = 10, vfp_ = 10;
307 uint8_t* scratch_ = nullptr;
309};
310
311#endif // TINYGPU_HAS_STM32_DSI
312
313} // namespace tinygpu
Definition: DSIBusESP32.h:19