️ Optimize Graphical TFT (#28103)
Some checks are pending
CI - Build Tests / Build Test (push) Waiting to run
CI - Unit Tests / Unit Test (push) Waiting to run
CI - Validate Source Files / Validate Source Files (push) Waiting to run

This commit is contained in:
Scott Lahteine 2025-10-16 13:42:39 -05:00 committed by GitHub
parent 5e1a1fffcf
commit 23e7905b20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 80 additions and 74 deletions

View file

@ -132,6 +132,9 @@ void TFT_FSMC::init() {
DMAtx.Init.Priority = DMA_PRIORITY_HIGH;
LCD = (LCD_CONTROLLER_TypeDef *)controllerAddress;
DMAtx.Init.PeriphInc = DMA_PINC_DISABLE;
HAL_DMA_Init(&DMAtx);
}
uint32_t TFT_FSMC::getID() {
@ -179,15 +182,19 @@ void TFT_FSMC::abort() {
}
void TFT_FSMC::transmitDMA(uint32_t memoryIncrease, uint16_t *data, uint16_t count) {
DMAtx.Init.PeriphInc = memoryIncrease;
HAL_DMA_Init(&DMAtx);
if (DMAtx.Init.PeriphInc != memoryIncrease) {
DMAtx.Init.PeriphInc = memoryIncrease;
HAL_DMA_Init(&DMAtx);
}
HAL_DMA_Start(&DMAtx, (uint32_t)data, (uint32_t)&(LCD->RAM), count);
TERN_(TFT_SHARED_IO, while (isBusy()));
}
void TFT_FSMC::transmit(uint32_t memoryIncrease, uint16_t *data, uint16_t count) {
DMAtx.Init.PeriphInc = memoryIncrease;
HAL_DMA_Init(&DMAtx);
if (DMAtx.Init.PeriphInc != memoryIncrease) {
DMAtx.Init.PeriphInc = memoryIncrease;
HAL_DMA_Init(&DMAtx);
}
dataTransferBegin();
HAL_DMA_Start(&DMAtx, (uint32_t)data, (uint32_t)&(LCD->RAM), count);
HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY);

View file

@ -86,7 +86,6 @@ __attribute__((always_inline)) __STATIC_INLINE void __DSB() {
#define FSMC_ADDRESS_SETUP_TIME 15 // AddressSetupTime
#define FSMC_DATA_SETUP_TIME 15 // DataSetupTime
static uint8_t fsmcInit = 0;
void TFT_FSMC::init() {
uint8_t cs = FSMC_CS_PIN, rs = FSMC_RS_PIN;
uint32_t controllerAddress;
@ -99,8 +98,9 @@ void TFT_FSMC::init() {
struct fsmc_nor_psram_reg_map* fsmcPsramRegion;
static bool fsmcInit = false;
if (fsmcInit) return;
fsmcInit = 1;
fsmcInit = true;
switch (cs) {
case FSMC_CS_NE1: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION1; fsmcPsramRegion = FSMC_NOR_PSRAM1_BASE; break;

View file

@ -107,19 +107,17 @@ void Canvas::addImage(int16_t x, int16_t y, MarlinImage image, uint16_t *colors)
if (color_mode == HIGHCOLOR) {
// HIGHCOLOR - 16 bits per pixel
int16_t line = y;
for (int16_t i = 0; i < image_height; i++, line++) {
if (WITHIN(line, startLine, endLine - 1)) {
uint16_t *pixel = buffer + x + (line - startLine) * width;
uint16_t cx = x;
for (int16_t j = 0; j < image_width; j++, cx++) {
if (WITHIN(cx, 0, width - 1)) {
uint16_t color = ENDIAN_COLOR(*data);
if (color == 0x0001) color = COLOR_BACKGROUND;
*pixel = color;
}
pixel++;
data++;
uint16_t yc = y <= startLine ? 0 : (y - startLine) * width;
for (int16_t i = 0; i < image_height && y < endLine; i++, y++) {
if (y >= startLine) {
uint16_t *pixel = buffer + x + yc;
yc += width;
int16_t cx = x;
for (int16_t j = 0; j < image_width && cx < width; j++, cx++, pixel++, data++) {
if (cx < 0) continue;
uint16_t color = ENDIAN_COLOR(*data);
if (color == 0x0001) color = COLOR_BACKGROUND;
*pixel = color;
}
}
else
@ -214,63 +212,60 @@ void Canvas::addImage(int16_t x, int16_t y, uint8_t image_width, uint8_t image_h
case GREYSCALE4: bitsPerPixel = 4; break;
default: return;
}
const uint8_t obase = 8 - bitsPerPixel, mask = 0xFF >> obase, pixelsPerByte = 8 / bitsPerPixel;
const uintptr_t span = (image_width + pixelsPerByte - 1) / pixelsPerByte;
uint8_t mask = 0xFF >> (8 - bitsPerPixel),
pixelsPerByte = 8 / bitsPerPixel;
colors--; // Color 1 is at index 0
colors--;
for (int16_t i = 0; i < image_height; i++) {
const int16_t line = y + i;
if (WITHIN(line, startLine, endLine - 1)) {
uint16_t *pixel = buffer + x + (line - startLine) * width;
uint8_t offset = 8 - bitsPerPixel;
for (int16_t j = 0; j < image_width; j++) {
if (offset > 8) {
data++;
offset = 8 - bitsPerPixel;
uint16_t yc = y <= startLine ? 0 : (y - startLine) * width; // Multiple of width as y offset
for (int16_t i = 0; i < image_height && y < endLine; i++, y++) { // Loop through image lines
if (y >= startLine) { // Within the canvas?
uint16_t *pixel = buffer + x + yc; // Pixel address of line at x pos
yc += width; // + is faster than *
int8_t offset = obase; // Bit offset of incoming pixel
for (int16_t j = 0; j < image_width; j++, pixel++) { // Loop through image pixels
if (offset < 0) { data++; offset = obase; } // Got all pixels in the byte? next byte.
if (WITHIN(x + j, 0, width - 1)) { // Within the canvas?
const uint8_t ci = ((*data) >> offset) & mask; // Shift the color index to low bits
if (ci) *pixel = colors[ci]; // Draw a solid pixel with the indexed color
}
if (WITHIN(x + j, 0, width - 1)) {
const uint8_t color = ((*data) >> offset) & mask;
if (color) *pixel = *(colors + color);
}
pixel++;
offset -= bitsPerPixel;
offset -= bitsPerPixel; // Subtract bits used for the pixel
}
data++;
data++; // New line, so new set of pixels
}
else
data += (image_width + pixelsPerByte - 1) / pixelsPerByte;
data += span; // Skip line outside the canvas
}
}
void Canvas::addRect(uint16_t x, uint16_t y, uint16_t rectangleWidth, uint16_t rectangleHeight, uint16_t color) {
if (endLine < y || startLine > y + rectangleHeight) return;
if (endLine < y || startLine > y + rectangleHeight) return; // Nothing to draw?
for (uint16_t i = 0; i < rectangleHeight; i++) {
const uint16_t line = y + i;
if (WITHIN(line, startLine, endLine - 1)) {
uint16_t *pixel = buffer + x + (line - startLine) * width;
if (i == 0 || i == rectangleHeight - 1) {
for (uint16_t j = 0; j < rectangleWidth; j++) *pixel++ = color;
uint16_t yc = y <= startLine ? 0 : (y - startLine) * width; // Multiple of width as y offset
for (uint16_t i = 0; i < rectangleHeight && y < endLine; i++, y++) { // Loop over the rect height
if (y >= startLine) { // Within the canvas?
uint16_t *pixel = buffer + x + yc; // Pixel address of line at x pos
yc += width; // + is faster than *
if (i == 0 || i == rectangleHeight - 1) { // Top or bottom line?
for (uint16_t j = 0; j < rectangleWidth; j++) *pixel++ = color; // Fill the width. (No transparency)
}
else {
*pixel = color;
pixel += rectangleWidth - 1;
*pixel = color;
pixel[0] = color; // Left line
pixel[rectangleWidth - 1] = color; // Right line
}
}
}
}
void Canvas::addBar(uint16_t x, uint16_t y, uint16_t barWidth, uint16_t barHeight, uint16_t color) {
if (endLine < y || startLine > y + barHeight) return;
if (endLine < y || startLine > y + barHeight) return; // Nothing to draw?
for (uint16_t i = 0; i < barHeight; i++) {
const uint16_t line = y + i;
if (WITHIN(line, startLine, endLine - 1)) {
uint16_t *pixel = buffer + x + (line - startLine) * width;
for (uint16_t j = 0; j < barWidth; j++) *pixel++ = color;
uint16_t yc = y <= startLine ? 0 : (y - startLine) * width; // Multiple of width as y offset
for (uint16_t i = 0; i < barHeight && y < endLine; i++, y++) { // Loop over the bar height
if (y >= startLine) { // Within the canvas?
uint16_t *pixel = buffer + x + yc; // Pixel address of line at x pos
yc += width; // + is faster than *
for (uint16_t j = 0; j < barWidth; j++) *pixel++ = color; // Fill the width. (No transparency)
}
}
}

View file

@ -70,7 +70,7 @@ class TFT {
static bool is_busy() { return io.isBusy(); }
static void abort() { io.abort(); }
static void write_multiple(uint16_t data, uint16_t count) { io.WriteMultipleDMA(data, count); }
static void write_multiple(uint16_t data, uint16_t count) { io.writeMultipleDMA(data, count); }
static void write_sequence(uint16_t *data, uint16_t count) { io.writeSequenceDMA(data, count); }
static void set_window(uint16_t xMin, uint16_t yMin, uint16_t xMax, uint16_t yMax) { io.set_window(xMin, yMin, xMax, yMax); }

View file

@ -116,25 +116,29 @@ void TFT_Queue::canvas(queueTask_t *task) {
case CANVAS_SET_BACKGROUND:
tftCanvas.setBackground(((parametersCanvasBackground_t *)item)->color);
break;
case CANVAS_ADD_TEXT:
tftCanvas.addText(((parametersCanvasText_t *)item)->x, ((parametersCanvasText_t *)item)->y, ((parametersCanvasText_t *)item)->color, (uint16_t*)(item + sizeof(parametersCanvasText_t)), ((parametersCanvasText_t *)item)->maxWidth);
break;
case CANVAS_ADD_IMAGE:
MarlinImage image;
uint16_t *colors;
case CANVAS_ADD_TEXT: {
parametersCanvasText_t *p_text = (parametersCanvasText_t *)item;
uint16_t *str = (uint16_t*)(item + sizeof(parametersCanvasText_t));
tftCanvas.addText(p_text->x, p_text->y, p_text->color, str, p_text->maxWidth);
} break;
image = ((parametersCanvasImage_t *)item)->image;
colors = (uint16_t *)(item + sizeof(parametersCanvasImage_t));
tftCanvas.addImage(((parametersCanvasImage_t *)item)->x, ((parametersCanvasImage_t *)item)->y, image, colors);
break;
case CANVAS_ADD_IMAGE: {
parametersCanvasImage_t *p_img = (parametersCanvasImage_t *)item;
MarlinImage image = p_img->image;
uint16_t *colors = (uint16_t *)(item + sizeof(parametersCanvasImage_t));
tftCanvas.addImage(p_img->x, p_img->y, image, colors);
} break;
case CANVAS_ADD_BAR:
tftCanvas.addBar(((parametersCanvasBar_t *)item)->x, ((parametersCanvasBar_t *)item)->y, ((parametersCanvasBar_t *)item)->width, ((parametersCanvasBar_t *)item)->height, ((parametersCanvasBar_t *)item)->color);
break;
case CANVAS_ADD_RECT:
tftCanvas.addRect(((parametersCanvasRectangle_t *)item)->x, ((parametersCanvasRectangle_t *)item)->y, ((parametersCanvasRectangle_t *)item)->width, ((parametersCanvasRectangle_t *)item)->height, ((parametersCanvasRectangle_t *)item)->color);
break;
case CANVAS_ADD_BAR: {
parametersCanvasBar_t *p_bar = (parametersCanvasBar_t *)item;
tftCanvas.addBar(p_bar->x, p_bar->y, p_bar->width, p_bar->height, p_bar->color);
} break;
case CANVAS_ADD_RECT: {
parametersCanvasRectangle_t *p_rect = (parametersCanvasRectangle_t *)item;
tftCanvas.addRect(p_rect->x, p_rect->y, p_rect->width, p_rect->height, p_rect->color);
} break;
}
item = ((parametersCanvasBackground_t *)item)->nextParameter;
}

View file

@ -74,7 +74,7 @@ public:
// Non-blocking DMA-based IO used by TFT_COLOR_UI only
// These functions start data transfer using DMA and do NOT wait for data transfer completion
inline static void writeSequenceDMA(uint16_t *data, uint16_t count) { io.writeSequence_DMA(data, count); }
inline static void WriteMultipleDMA(uint16_t color, uint16_t count) { io.writeMultiple_DMA(color, count); }
inline static void writeMultipleDMA(uint16_t color, uint16_t count) { io.writeMultiple_DMA(color, count); }
// Non-blocking DMA-based IO with IRQ callback used by TFT_LVGL_UI only
// This function starts data transfer using DMA and does NOT wait for data transfer completion