Add descriptor-driven ST7305 uDisplay packing (#24738)

This commit is contained in:
Frederick Blais
2026-05-19 12:24:18 +02:00
committed by GitHub
parent 01d408ae6d
commit e6d471f25f
5 changed files with 166 additions and 8 deletions
@@ -460,8 +460,8 @@ void Renderer::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16
}
// this display doesn't need ints for coordinates, use local byte registers for faster juggling
register uint8_t y = __y;
register uint8_t h = __h;
register uint16_t y = __y;
register uint16_t h = __h;
// set up the pointer for fast movement through the buffer
@@ -8,6 +8,11 @@
#include "uDisplay_panel.h"
#include "uDisplay_SPI_controller.h"
enum UDisplayMonoPackFlags : uint8_t {
UDISP_MONO_PACK_INVERT = 1 << 0,
UDISP_MONO_PACK_REVERSE_Y = 1 << 1,
};
typedef struct LVGL_PARAMS_t {
uint16_t flushlines;
union {
@@ -40,6 +45,13 @@ struct SPIPanelConfig {
uint8_t cmd_set_addr_x; // Command to set X address range
uint8_t cmd_set_addr_y; // Command to set Y address range
uint8_t cmd_write_ram; // Command to write pixel data
uint8_t ram_x_start; // Optional full-frame RAM window for packed mono modes
uint8_t ram_x_end;
uint8_t ram_y_start;
uint8_t ram_y_end;
uint8_t mono_pack_width; // Optional descriptor-selected 1bpp transfer packing
uint8_t mono_pack_height;
uint8_t mono_pack_flags;
// ===== Display Control Commands =====
uint8_t cmd_display_on;
@@ -107,6 +119,9 @@ private:
bool use_hw_spi = false;
// ===== Internal Helpers =====
bool hasPackedMono() const;
bool updateFramePackedMono();
uint8_t getMonoPixel(int16_t x, int16_t y) const;
void setAddrWindow_internal(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void sendAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void sendCommand(uint8_t cmd);
@@ -115,4 +130,4 @@ private:
void writeColor(uint16_t color);
void resetDisplay();
void waitBusy();
};
};
+32 -4
View File
@@ -614,7 +614,18 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) {
rot_t[3] = next_hex(&lp1);
break;
case 'A':
if (interface == _UDSP_I2C || bpp == 1) {
if (interface == _UDSP_SPI && panel_config->spi.mono_pack_width && panel_config->spi.mono_pack_height) {
// Some 1bpp SPI panels use TFT-style RAMWR instead of OLED pages.
// A descriptor-selected packed mono mode provides the RAM window.
panel_config->spi.cmd_set_addr_x = next_hex(&lp1);
panel_config->spi.cmd_set_addr_y = next_hex(&lp1);
panel_config->spi.cmd_write_ram = next_hex(&lp1);
panel_config->spi.address_mode = next_val(&lp1);
panel_config->spi.ram_x_start = next_hex(&lp1);
panel_config->spi.ram_x_end = next_hex(&lp1);
panel_config->spi.ram_y_start = next_hex(&lp1);
panel_config->spi.ram_y_end = next_hex(&lp1);
} else if (interface == _UDSP_I2C || bpp == 1) {
// Parse directly into I2C config
panel_config->i2c.cmd_set_addr_x = next_hex(&lp1);
panel_config->i2c.page_start = next_hex(&lp1);
@@ -677,6 +688,13 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) {
}
#endif
break;
case 'F':
if (interface == _UDSP_SPI && bpp == 1) {
panel_config->spi.mono_pack_width = next_val(&lp1);
panel_config->spi.mono_pack_height = next_val(&lp1);
panel_config->spi.mono_pack_flags = next_val(&lp1);
}
break;
case 'i':
inv_off = next_hex(&lp1);
inv_on = next_hex(&lp1);
@@ -1141,13 +1159,23 @@ Renderer *uDisplay::Init(void) {
// for any bpp below native 16 bits, we allocate a local framebuffer to copy into
if (ep_mode || bpp < 16) {
if (framebuffer) free(framebuffer);
uint32_t framebuffer_size = (gxs * gys * bpp) / 8;
if (bpp == 1) {
// Renderer packs monochrome pixels in vertical 8-pixel pages:
// framebuffer[x + (y >> 3) * width]. Non-8-multiple heights need
// storage for the partial final page.
framebuffer_size = gxs * ((gys + 7) / 8);
}
#ifdef ESP8266
framebuffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1);
framebuffer = (uint8_t*)calloc(framebuffer_size, 1);
#else
if (UsePSRAM()) {
framebuffer = (uint8_t*)heap_caps_malloc((gxs * gys * bpp) / 8, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
framebuffer = (uint8_t*)heap_caps_malloc(framebuffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (framebuffer) {
memset(framebuffer, 0, framebuffer_size);
}
} else {
framebuffer = (uint8_t*)calloc((gxs * gys * bpp) / 8, 1);
framebuffer = (uint8_t*)calloc(framebuffer_size, 1);
}
#endif // ESP8266
}
@@ -203,6 +203,14 @@ bool SPIPanel::pushColors(uint16_t *data, uint32_t len, bool not_swapped) {
}
bool SPIPanel::setAddrWindow(int16_t x0, int16_t y0, int16_t x1, int16_t y1) {
if (hasPackedMono()) {
window_x0 = x0;
window_y0 = y0;
window_x1 = x1;
window_y1 = y1;
return false;
}
// From original uDisplay::setAddrWindow
window_x0 = x0;
window_y0 = y0;
@@ -294,6 +302,10 @@ bool SPIPanel::displayOnff(int8_t on) {
}
spi->csHigh();
spi->endTransaction();
if (display_on && fb_buffer && hasPackedMono()) {
return updateFramePackedMono();
}
return false; //true;
}
@@ -352,6 +364,10 @@ bool SPIPanel::updateFrame() {
// From original uDisplay::Updateframe - only for monochrome SPI OLEDs
// Only handle framebuffer updates for monochrome displays
if (!fb_buffer || cfg.bpp != 1) return false;
if (hasPackedMono()) {
return updateFramePackedMono();
}
// OLED page-based framebuffer update (from original code)
uint8_t ys = height >> 3;
@@ -378,4 +394,61 @@ bool SPIPanel::updateFrame() {
spi->csHigh();
spi->endTransaction();
return true;
}
}
uint8_t SPIPanel::getMonoPixel(int16_t x, int16_t y) const {
if (x < 0 || x >= width || y < 0 || y >= height) {
return 0;
}
return (fb_buffer[x + (y >> 3) * width] & (1 << (y & 7))) ? 1 : 0;
}
bool SPIPanel::hasPackedMono() const {
return fb_buffer && cfg.bpp == 1 && cfg.mono_pack_width && cfg.mono_pack_height &&
(cfg.mono_pack_width * cfg.mono_pack_height <= 8);
}
bool SPIPanel::updateFramePackedMono() {
if (!hasPackedMono()) {
return false;
}
spi->beginTransaction();
spi->csLow();
// Packed monochrome RAMWR displays may not restart at the full-screen RAM
// window on a bare RAMWR, so set the descriptor-provided range each flush.
spi->writeCommand(cfg.cmd_set_addr_x);
spi->writeData8(cfg.ram_x_start);
spi->writeData8(cfg.ram_x_end);
spi->writeCommand(cfg.cmd_set_addr_y);
spi->writeData8(cfg.ram_y_start);
spi->writeData8(cfg.ram_y_end);
spi->writeCommand(cfg.cmd_write_ram);
for (uint16_t x = 0; x < width; x += cfg.mono_pack_width) {
for (uint16_t y = 0; y < height; y += cfg.mono_pack_height) {
// Descriptor-defined packed mono format, row-major from bit7 down.
uint8_t packed = 0;
uint8_t bit = 0x80;
for (uint8_t row = 0; row < cfg.mono_pack_height; row++) {
int16_t sy = (cfg.mono_pack_flags & UDISP_MONO_PACK_REVERSE_Y)
? height - 1 - y - row : y + row;
for (uint8_t col = 0; col < cfg.mono_pack_width; col++) {
if (getMonoPixel(x + col, sy)) {
packed |= bit;
}
bit >>= 1;
}
}
if (cfg.mono_pack_flags & UDISP_MONO_PACK_INVERT) {
packed = ~packed;
}
spi->writeData8(packed);
}
}
spi->csHigh();
spi->endTransaction();
return true;
}
@@ -0,0 +1,42 @@
:H,ST7305_RLCD_400x300,400,300,1,SPI,1,40,11,12,5,-1,41,-1,1
:F,2,4,3
:S,0,4,0,1,116,130
:B,40,0
:I
D6,02,17,02
D1,01,01
C0,02,11,04
C1,04,69,69,69,69
C2,04,19,19,19,19
C4,04,4B,4B,4B,4B
C5,04,19,19,19,19
D8,02,80,E9
B2,01,02
B3,0A,E5,F6,05,46,77,77,77,77,76,45
B4,08,05,46,77,77,77,77,76,45
62,03,32,03,1F
B7,01,13
B0,01,64
11,80
C9,01,00
36,01,48
3A,01,11
B9,01,20
B8,01,29
21,00
2A,02,12,2A
2B,02,00,C7
35,01,00
D0,01,FF
38,00
29,80
:o,28
:O,29
:A,2A,2B,2C,0,12,2A,00,C7
:R,36
:0,48,00,00,00
:1,28,00,00,01
:2,88,00,00,02
:3,E8,00,00,03
:i,20,21
#