Quantum Painter QoL enhancements -- auto-poweroff, auto-flush, buffer sizing (#20013)

This commit is contained in:
Nick Brassel 2023-03-20 14:13:53 +11:00 committed by GitHub
parent dfec6ac471
commit cd94ba031c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 199 additions and 32 deletions

View file

@ -32,16 +32,18 @@ Supported devices:
## Quantum Painter Configuration :id=quantum-painter-config ## Quantum Painter Configuration :id=quantum-painter-config
| Option | Default | Purpose | | Option | Default | Purpose |
|------------------------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `QUANTUM_PAINTER_NUM_IMAGES` | `8` | The maximum number of images/animations that can be loaded at any one time. | | `QUANTUM_PAINTER_DISPLAY_TIMEOUT` | `30000` | This controls the amount of time (in milliseconds) that all displays will remain on after the last user input. If set to `0`, the display will remain on indefinitely. |
| `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. | | `QUANTUM_PAINTER_TASK_THROTTLE` | `1` | This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between each execution. Affects animations, display timeout, and LVGL timing if enabled. |
| `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same time. | | `QUANTUM_PAINTER_NUM_IMAGES` | `8` | The maximum number of images/animations that can be loaded at any one time. |
| `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM` | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash. | | `QUANTUM_PAINTER_NUM_FONTS` | `4` | The maximum number of fonts that can be loaded at any one time. |
| `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE` | `32` | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. | | `QUANTUM_PAINTER_CONCURRENT_ANIMATIONS` | `4` | The maximum number of animations that can be executed at the same time. |
| `QUANTUM_PAINTER_SUPPORTS_256_PALETTE` | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU. | | `QUANTUM_PAINTER_LOAD_FONTS_TO_RAM` | `FALSE` | Whether or not fonts should be loaded to RAM. Relevant for fonts stored in off-chip persistent storage, such as external flash. |
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU. | | `QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE` | `32` | The limit of the amount of pixel data that can be transmitted in one transaction to the display. Higher values require more RAM on the MCU. |
| `QUANTUM_PAINTER_DEBUG` | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging. | | `QUANTUM_PAINTER_SUPPORTS_256_PALETTE` | `FALSE` | If 256-color palettes are supported. Requires significantly more RAM on the MCU. |
| `QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS` | `FALSE` | If native color range is supported. Requires significantly more RAM on the MCU. |
| `QUANTUM_PAINTER_DEBUG` | _unset_ | Prints out significant amounts of debugging information to CONSOLE output. Significant performance degradation, use only for debugging. |
Drivers have their own set of configurable options, and are described in their respective sections. Drivers have their own set of configurable options, and are described in their respective sections.

View file

@ -1,4 +1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver) // Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <wait.h> #include <wait.h>
@ -141,6 +142,12 @@ painter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -110,6 +110,12 @@ painter_device_t qp_ili9163_make_spi_device(uint16_t panel_width, uint16_t panel
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -117,6 +117,12 @@ painter_device_t qp_ili9341_make_spi_device(uint16_t panel_width, uint16_t panel
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -110,6 +110,12 @@ painter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -114,6 +114,12 @@ painter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,5 +1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver) // Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// Copyright 2022 David Hoelscher (@customMK) // Copyright 2022 David Hoelscher (@customMK)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -134,6 +134,12 @@ painter_device_t qp_st7735_make_spi_device(uint16_t panel_width, uint16_t panel_
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,5 +1,5 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver) // Copyright 2021 Paul Cotter (@gr1mr3aver)
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -133,6 +133,12 @@ painter_device_t qp_st7789_make_spi_device(uint16_t panel_width, uint16_t panel_
driver->spi_dc_reset_config.spi_config.mode = spi_mode; driver->spi_dc_reset_config.spi_config.mode = spi_mode;
driver->spi_dc_reset_config.dc_pin = dc_pin; driver->spi_dc_reset_config.dc_pin = dc_pin;
driver->spi_dc_reset_config.reset_pin = reset_pin; driver->spi_dc_reset_config.reset_pin = reset_pin;
if (!qp_internal_register_device((painter_device_t)driver)) {
memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));
return NULL;
}
return (painter_device_t)driver; return (painter_device_t)driver;
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@ -11,6 +11,22 @@
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter global configurables (add to your keyboard's config.h) // Quantum Painter global configurables (add to your keyboard's config.h)
#ifndef QUANTUM_PAINTER_DISPLAY_TIMEOUT
/**
* @def This controls the amount of time (in milliseconds) that all displays will remain on after the last user input.
* If set to 0, the display will remain on indefinitely.
*/
# define QUANTUM_PAINTER_DISPLAY_TIMEOUT 30000
#endif // QUANTUM_PAINTER_DISPLAY_TIMEOUT
#ifndef QUANTUM_PAINTER_TASK_THROTTLE
/**
* @def This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between
* each execution.
*/
# define QUANTUM_PAINTER_TASK_THROTTLE 1
#endif // QUANTUM_PAINTER_TASK_THROTTLE
#ifndef QUANTUM_PAINTER_NUM_IMAGES #ifndef QUANTUM_PAINTER_NUM_IMAGES
/** /**
* @def This controls the maximum number of images that Quantum Painter can load at any one time. Images can be loaded * @def This controls the maximum number of images that Quantum Painter can load at any one time. Images can be loaded
@ -53,7 +69,7 @@
* @def This controls the maximum size of the pixel data buffer used for single blocks of transmission. Larger buffers * @def This controls the maximum size of the pixel data buffer used for single blocks of transmission. Larger buffers
* means more data is processed at one time, with less frequent transmissions, at the cost of RAM. * means more data is processed at one time, with less frequent transmissions, at the cost of RAM.
*/ */
# define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 32 # define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 1024
#endif #endif
#ifndef QUANTUM_PAINTER_SUPPORTS_256_PALETTE #ifndef QUANTUM_PAINTER_SUPPORTS_256_PALETTE
@ -442,34 +458,50 @@ int16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, pai
#ifdef QUANTUM_PAINTER_RGB565_SURFACE_ENABLE #ifdef QUANTUM_PAINTER_RGB565_SURFACE_ENABLE
# include "qp_rgb565_surface.h" # include "qp_rgb565_surface.h"
#else // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE
# define RGB565_SURFACE_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE #endif // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE
#ifdef QUANTUM_PAINTER_ILI9163_ENABLE #ifdef QUANTUM_PAINTER_ILI9163_ENABLE
# include "qp_ili9163.h" # include "qp_ili9163.h"
#else // QUANTUM_PAINTER_ILI9163_ENABLE
# define ILI9163_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9163_ENABLE #endif // QUANTUM_PAINTER_ILI9163_ENABLE
#ifdef QUANTUM_PAINTER_ILI9341_ENABLE #ifdef QUANTUM_PAINTER_ILI9341_ENABLE
# include "qp_ili9341.h" # include "qp_ili9341.h"
#else // QUANTUM_PAINTER_ILI9341_ENABLE
# define ILI9341_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9341_ENABLE #endif // QUANTUM_PAINTER_ILI9341_ENABLE
#ifdef QUANTUM_PAINTER_ILI9488_ENABLE #ifdef QUANTUM_PAINTER_ILI9488_ENABLE
# include "qp_ili9488.h" # include "qp_ili9488.h"
#else // QUANTUM_PAINTER_ILI9488_ENABLE
# define ILI9488_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ILI9488_ENABLE #endif // QUANTUM_PAINTER_ILI9488_ENABLE
#ifdef QUANTUM_PAINTER_ST7789_ENABLE #ifdef QUANTUM_PAINTER_ST7789_ENABLE
# include "qp_st7789.h" # include "qp_st7789.h"
#else // QUANTUM_PAINTER_ST7789_ENABLE
# define ST7789_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ST7789_ENABLE #endif // QUANTUM_PAINTER_ST7789_ENABLE
#ifdef QUANTUM_PAINTER_ST7735_ENABLE #ifdef QUANTUM_PAINTER_ST7735_ENABLE
# include "qp_st7735.h" # include "qp_st7735.h"
#else // QUANTUM_PAINTER_ST7735_ENABLE
# define ST7735_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_ST7735_ENABLE #endif // QUANTUM_PAINTER_ST7735_ENABLE
#ifdef QUANTUM_PAINTER_GC9A01_ENABLE #ifdef QUANTUM_PAINTER_GC9A01_ENABLE
# include "qp_gc9a01.h" # include "qp_gc9a01.h"
#else // QUANTUM_PAINTER_GC9A01_ENABLE
# define GC9A01_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_GC9A01_ENABLE #endif // QUANTUM_PAINTER_GC9A01_ENABLE
#ifdef QUANTUM_PAINTER_SSD1351_ENABLE #ifdef QUANTUM_PAINTER_SSD1351_ENABLE
# include "qp_ssd1351.h" # include "qp_ssd1351.h"
#else // QUANTUM_PAINTER_SSD1351_ENABLE
# define SSD1351_NUM_DEVICES 0
#endif // QUANTUM_PAINTER_SSD1351_ENABLE #endif // QUANTUM_PAINTER_SSD1351_ENABLE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h" #include "qp_internal.h"
@ -414,15 +414,3 @@ void qp_internal_animation_tick(void) {
static uint32_t last_anim_exec = 0; static uint32_t last_anim_exec = 0;
deferred_exec_advanced_task(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, &last_anim_exec); deferred_exec_advanced_task(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, &last_anim_exec);
} }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: qp_internal_task
void qp_internal_task(void) {
qp_internal_animation_tick();
#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
// Run LVGL ticks
void qp_lvgl_internal_tick(void);
qp_lvgl_internal_tick();
#endif
}

View file

@ -0,0 +1,96 @@
// Copyright 2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "qp_internal.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: device registration
enum {
// Work out how many devices we're actually going to be instantiating
// NOTE: We intentionally do not include surfaces here, despite them conforming to the same API.
QP_NUM_DEVICES = (ILI9163_NUM_DEVICES) // ILI9163
+ (ILI9341_NUM_DEVICES) // ILI9341
+ (ILI9488_NUM_DEVICES) // ILI9488
+ (ST7789_NUM_DEVICES) // ST7789
+ (ST7735_NUM_DEVICES) // ST7735
+ (GC9A01_NUM_DEVICES) // GC9A01
+ (SSD1351_NUM_DEVICES) // SSD1351
};
static painter_device_t qp_devices[QP_NUM_DEVICES] = {NULL};
bool qp_internal_register_device(painter_device_t driver) {
for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
if (qp_devices[i] == NULL) {
qp_devices[i] = driver;
return true;
}
}
// We should never get here -- someone has screwed up their device counts during config
qp_dprintf("qp_internal_register_device: no more space for devices!\n");
return false;
}
#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
static void qp_internal_display_timeout_task(void) {
// Handle power on/off state
static bool display_on = true;
bool should_change_display_state = false;
bool target_display_state = false;
if (last_input_activity_elapsed() < (QUANTUM_PAINTER_DISPLAY_TIMEOUT)) {
should_change_display_state = display_on == false;
target_display_state = true;
} else {
should_change_display_state = display_on == true;
target_display_state = false;
}
if (should_change_display_state) {
for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
if (qp_devices[i] != NULL) {
qp_power(qp_devices[i], target_display_state);
}
}
display_on = target_display_state;
}
}
#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: qp_internal_task
_Static_assert((QUANTUM_PAINTER_TASK_THROTTLE) > 0 && (QUANTUM_PAINTER_TASK_THROTTLE) < 1000, "QUANTUM_PAINTER_TASK_THROTTLE must be between 1 and 999");
void qp_internal_task(void) {
// Perform throttling of the internal processing of Quantum Painter
static uint32_t last_tick = 0;
uint32_t now = timer_read32();
if (TIMER_DIFF_32(now, last_tick) < (QUANTUM_PAINTER_TASK_THROTTLE)) {
return;
}
last_tick = now;
#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
qp_internal_display_timeout_task();
#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0
// Handle animations
void qp_internal_animation_tick(void);
qp_internal_animation_tick();
#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE
// Run LVGL ticks
void qp_lvgl_internal_tick(void);
qp_lvgl_internal_tick();
#endif
// Flush (render) dirty regions to corresponding displays
for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {
if (qp_devices[i] != NULL) {
qp_flush(qp_devices[i]);
}
}
}

View file

@ -1,4 +1,4 @@
// Copyright 2021 Nick Brassel (@tzarc) // Copyright 2021-2023 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@ -82,3 +82,8 @@ struct painter_driver_t {
// Comms config pointer -- needs to point to an appropriate comms config if the comms driver requires it. // Comms config pointer -- needs to point to an appropriate comms config if the comms driver requires it.
void *comms_config; void *comms_config;
}; };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Device internals
bool qp_internal_register_device(painter_device_t driver);

View file

@ -24,6 +24,7 @@ SRC += \
$(QUANTUM_DIR)/unicode/utf8.c \ $(QUANTUM_DIR)/unicode/utf8.c \
$(QUANTUM_DIR)/color.c \ $(QUANTUM_DIR)/color.c \
$(QUANTUM_DIR)/painter/qp.c \ $(QUANTUM_DIR)/painter/qp.c \
$(QUANTUM_DIR)/painter/qp_internal.c \
$(QUANTUM_DIR)/painter/qp_stream.c \ $(QUANTUM_DIR)/painter/qp_stream.c \
$(QUANTUM_DIR)/painter/qgf.c \ $(QUANTUM_DIR)/painter/qgf.c \
$(QUANTUM_DIR)/painter/qff.c \ $(QUANTUM_DIR)/painter/qff.c \