touch: P4 — move the flush upscale scratch out of PSRAM (#167 teal flash on RX)
Both P4 SKUs render at half resolution and nearest-neighbour-upscale each LVGL band into a scratch buffer before handing it to the panel. That scratch was allocated with MALLOC_CAP_SPIRAM, which put it on the worst possible bus. These are DPI/DSI panels with num_fbs = 1: the ~1.4 MB framebuffer can only live in PSRAM, and the DSI DMA streams it to the glass CONTINUOUSLY. Every draw_bitmap is a copy INTO that framebuffer, so with the scratch also in PSRAM a single flush put three streams on one bus at once — the scratch read, the framebuffer write, and the DSI's own read. Anything else wanting PSRAM in that window can starve the DSI read, and a DPI underrun presents as a whole-screen colour flash for one frame. That is the reported "screen flashes teal on every RX" (#167 flicker): it happens on ANY screen and was there from the start of the port, because it is a display-bus problem and has nothing to do with what is being drawn. The RX correlation is the priority-10 "lora_rx" drain task, which wakes on every received packet and is ON by default (rx_queue defaults to 1). Allocating the scratch in internal DMA RAM removes the scratch read from the PSRAM bus entirely. The cost is bounded and small: one LVGL band is LV_DRAW_BUF_LINES = 24 rows, so AMOLED 284x24 -> 568*48*2 = ~53 KB and LCD 270x24 -> 540*48*2 = ~51 KB, against a 348 KiB internal region. PSRAM stays as the fallback so a fragmented heap degrades to the previous behaviour rather than dropping to the unscaled draw path. Not done here: lowering the drain task's priority. It is created in the vendored core (RadioLibWrappers.cpp), which is not tracked in this repo, so a change there cannot ship from wadamesh — it needs a monorepo core change plus a core-* tag bump. It is also the weaker lever: the core pins that task to core 0 while LVGL and loop run on core 1, so it is not preempting the UI thread, and the contention this fixes is the memory bus. Builds clean and boot-verified on the device (T-Display P4, MAC ...e1:c2:a7); no allocation failures, only rst:0x1 (POWERON). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
471a522d8f
commit
7aaff80742
2 files changed
+27
-2
No files matched your search
@@ -185,11 +185,21 @@ void HI8561Display::writePixelsRGB565(int x, int y, int w, int h, const uint16_t
|
||||
// Nearest-neighbour upscale the (half-res) LVGL band to the native panel: each source pixel becomes
|
||||
// an HI_UI_SCALE x HI_UI_SCALE block. One expanded band -> one draw_bitmap (exclusive end coords).
|
||||
const int S = HI_UI_SCALE, dw = w * S, dh = h * S;
|
||||
// INTERNAL DMA RAM, not PSRAM — same reasoning as the AMOLED SKU (long note in
|
||||
// RM69A10Display::writePixelsRGB565). This is a DPI/DSI panel whose single framebuffer must
|
||||
// live in PSRAM and is streamed to the glass continuously, so keeping the upscale scratch in
|
||||
// PSRAM too put three streams on one bus: the scratch read, the framebuffer write, and the
|
||||
// DSI's own read. Any other PSRAM consumer — the priority-10 "lora_rx" drain task fires on
|
||||
// every received packet — could then starve the DSI, and a DPI underrun shows as a
|
||||
// whole-screen colour flash for one frame (#167). One band is 270x24 upscaled 2x =
|
||||
// 540*48*2 = ~51 KB out of 768 KB SRAM. PSRAM stays the fallback.
|
||||
static uint16_t* s_up = nullptr; static size_t s_up_px = 0;
|
||||
size_t need = (size_t)dw * dh;
|
||||
if (need > s_up_px) {
|
||||
if (s_up) heap_caps_free(s_up);
|
||||
s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
|
||||
s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t),
|
||||
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
if (!s_up) s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
|
||||
s_up_px = s_up ? need : 0;
|
||||
}
|
||||
if (s_up) {
|
||||
|
||||
@@ -149,11 +149,26 @@ void RM69A10Display::writePixelsRGB565(int x, int y, int w, int h, const uint16_
|
||||
// Nearest-neighbour upscale the (half-res) LVGL band to the native panel: each source pixel becomes
|
||||
// an RM_UI_SCALE x RM_UI_SCALE block. One expanded band -> one draw_bitmap (exclusive end coords).
|
||||
const int S = RM_UI_SCALE, dw = w * S, dh = h * S;
|
||||
// The scratch band lives in INTERNAL DMA RAM, not PSRAM. This panel is a DPI/DSI one with a
|
||||
// single ~1.4 MB framebuffer that can only live in PSRAM, and the DSI DMA streams that
|
||||
// framebuffer out to the glass CONTINUOUSLY. Every draw_bitmap below is a copy INTO that
|
||||
// framebuffer, so with the scratch also in PSRAM a flush put three streams on the same bus at
|
||||
// once: the scratch read, the framebuffer write, and the DSI's own read. Anything else that
|
||||
// wants PSRAM at that moment — notably the priority-10 "lora_rx" drain task, which wakes on
|
||||
// every received packet — can then starve the DSI read, and a DPI underrun shows up as a
|
||||
// whole-screen colour flash for one frame. That is the P4 "flicker on every RX" report
|
||||
// (issue #167): it appears on any screen, because it is a display-bus problem and nothing to
|
||||
// do with what is being drawn. Internal RAM removes the scratch read from the PSRAM bus.
|
||||
// Cost is bounded and small: one LVGL band is 284x24 (LV_DRAW_BUF_LINES), so upscaled 2x this
|
||||
// is 568*48*2 = ~53 KB out of the P4's 768 KB SRAM. PSRAM stays as the fallback so a
|
||||
// fragmented heap degrades to the old behaviour instead of dropping to the unscaled path.
|
||||
static uint16_t* s_up = nullptr; static size_t s_up_px = 0;
|
||||
size_t need = (size_t)dw * dh;
|
||||
if (need > s_up_px) {
|
||||
if (s_up) heap_caps_free(s_up);
|
||||
s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
|
||||
s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t),
|
||||
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
if (!s_up) s_up = (uint16_t*)heap_caps_malloc(need * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
|
||||
s_up_px = s_up ? need : 0;
|
||||
}
|
||||
if (s_up) {
|
||||
|
||||
Reference in new issue
Block a user