upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYour Name <you@example.com>2026-05-18 18:47:54 +0530
committerYour Name <you@example.com>2026-05-18 18:47:54 +0530
commita02a0dbbf4e945ad00433e3fc9fd2b40550d3e82 (patch)
treed36e349ac6030dd70548e7b91b4c54cc513e844a
parent067a5700e26518d9a3f8bd92966c8957d17f0817 (diff)
Fix display colors with internal DMA byte-swap buffer
- Allocate 4KB internal DMA buffer (MALLOC_CAP_DMA) for pixel byte-swap - Byte-swap from PSRAM framebuffer into DMA buffer in 2048-pixel chunks - Matches ArduinoGFX approach: separate DMA buffer avoids PSRAM cache issues - Restore render-on-change logic (only redraw on state/QR mode change) - Use saturated colors: cyan 0x07FF for title, yellow 0xFFE0 for subtitle - Remove debug flush log message
-rw-r--r--components/axs15231b/axs15231b.c38
-rw-r--r--main/display.c48
2 files changed, 54 insertions, 32 deletions
diff --git a/components/axs15231b/axs15231b.c b/components/axs15231b/axs15231b.c
index d64c9bc..49122a1 100644
--- a/components/axs15231b/axs15231b.c
+++ b/components/axs15231b/axs15231b.c
@@ -37,6 +37,8 @@ static spi_device_handle_t s_spi = NULL;
37static uint16_t *s_fb = NULL; 37static uint16_t *s_fb = NULL;
38static int s_width = AXS15231B_WIDTH; 38static int s_width = AXS15231B_WIDTH;
39static int s_height = AXS15231B_HEIGHT; 39static int s_height = AXS15231B_HEIGHT;
40static uint8_t *s_swap_buf = NULL;
41#define SWAP_BUF_PIXELS 2048
40 42
41typedef struct { 43typedef struct {
42 uint8_t cmd; 44 uint8_t cmd;
@@ -248,6 +250,13 @@ esp_err_t axs15231b_init(void) {
248 memset(s_fb, 0, fb_size); 250 memset(s_fb, 0, fb_size);
249 ESP_LOGI(TAG, "Framebuffer allocated: %zu bytes in PSRAM", fb_size); 251 ESP_LOGI(TAG, "Framebuffer allocated: %zu bytes in PSRAM", fb_size);
250 252
253 s_swap_buf = heap_caps_aligned_alloc(16, SWAP_BUF_PIXELS * 2, MALLOC_CAP_DMA);
254 if (!s_swap_buf) {
255 ESP_LOGE(TAG, "Failed to allocate DMA swap buffer (%d bytes)", SWAP_BUF_PIXELS * 2);
256 return ESP_ERR_NO_MEM;
257 }
258 ESP_LOGI(TAG, "DMA swap buffer: %d bytes in internal RAM", SWAP_BUF_PIXELS * 2);
259
251 gpio_config_t bl_cfg = { 260 gpio_config_t bl_cfg = {
252 .pin_bit_mask = (1ULL << AXS15231B_PIN_BL), 261 .pin_bit_mask = (1ULL << AXS15231B_PIN_BL),
253 .mode = GPIO_MODE_OUTPUT, 262 .mode = GPIO_MODE_OUTPUT,
@@ -303,23 +312,26 @@ void axs15231b_fill_rect(int x, int y, int w, int h, uint16_t color) {
303} 312}
304 313
305void axs15231b_flush(void) { 314void axs15231b_flush(void) {
306 if (!s_spi || !s_fb) return; 315 if (!s_spi || !s_fb || !s_swap_buf) return;
307
308 ESP_LOGI(TAG, "Flush %dx%d", s_width, s_height);
309 316
310 qspi_write_cmd_d16d16(CASET, 0, s_width - 1); 317 qspi_write_cmd_d16d16(CASET, 0, s_width - 1);
311 qspi_write_cmd_d16d16(RASET, 0, s_height - 1); 318 qspi_write_cmd_d16d16(RASET, 0, s_height - 1);
312 319
313 int total_bytes = s_width * s_height * 2; 320 int total_pixels = s_width * s_height;
314 int chunk_size = 32768; 321 int pixel_offset = 0;
315 int offset = 0;
316 uint8_t *fb_bytes = (uint8_t *)s_fb;
317 bool first = true; 322 bool first = true;
318 323
319 cs_low(); 324 cs_low();
320 while (offset < total_bytes) { 325 while (pixel_offset < total_pixels) {
321 int remaining = total_bytes - offset; 326 int remaining = total_pixels - pixel_offset;
322 int this_chunk = remaining < chunk_size ? remaining : chunk_size; 327 int chunk_pixels = remaining < SWAP_BUF_PIXELS ? remaining : SWAP_BUF_PIXELS;
328 int chunk_bytes = chunk_pixels * 2;
329
330 uint8_t *src = (uint8_t *)(s_fb + pixel_offset);
331 for (int i = 0; i < chunk_bytes; i += 2) {
332 s_swap_buf[i] = src[i + 1];
333 s_swap_buf[i + 1] = src[i];
334 }
323 335
324 spi_transaction_ext_t t = {0}; 336 spi_transaction_ext_t t = {0};
325 if (first) { 337 if (first) {
@@ -331,10 +343,10 @@ void axs15231b_flush(void) {
331 t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD | 343 t.base.flags = SPI_TRANS_MODE_QIO | SPI_TRANS_VARIABLE_CMD |
332 SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY; 344 SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY;
333 } 345 }
334 t.base.tx_buffer = fb_bytes + offset; 346 t.base.tx_buffer = s_swap_buf;
335 t.base.length = this_chunk * 8; 347 t.base.length = chunk_pixels * 16;
336 spi_device_polling_transmit(s_spi, (spi_transaction_t *)&t); 348 spi_device_polling_transmit(s_spi, (spi_transaction_t *)&t);
337 offset += this_chunk; 349 pixel_offset += chunk_pixels;
338 } 350 }
339 cs_high(); 351 cs_high();
340} 352}
diff --git a/main/display.c b/main/display.c
index 1085d4a..26d0c5f 100644
--- a/main/display.c
+++ b/main/display.c
@@ -148,8 +148,8 @@ void display_render_qr(const char *text) {
148 148
149static void render_boot_screen(void) { 149static void render_boot_screen(void) {
150 axs15231b_fill_screen(0x0000); 150 axs15231b_fill_screen(0x0000);
151 display_render_text(64, 210, "TollGate", 0xF79F, 0x0000, 3); 151 display_render_text(64, 210, "TollGate", 0x07FF, 0x0000, 3);
152 display_render_text(72, 250, "starting...", 0xB5B6, 0x0000, 2); 152 display_render_text(72, 250, "starting...", 0xFFE0, 0x0000, 2);
153 axs15231b_flush(); 153 axs15231b_flush();
154} 154}
155 155
@@ -199,37 +199,47 @@ static void render_error_screen(void) {
199 199
200static void display_task(void *pvParameters) { 200static void display_task(void *pvParameters) {
201 ESP_LOGI(TAG, "Display task started"); 201 ESP_LOGI(TAG, "Display task started");
202 vTaskDelay(pdMS_TO_TICKS(500));
203 202
204 while (1) { 203 while (1) {
205 display_state_t state = s_state; 204 display_state_t state = s_state;
206 205
206 bool qr_changed = false;
207 if (state == DISPLAY_READY) { 207 if (state == DISPLAY_READY) {
208 int64_t now = (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS; 208 int64_t now = (int64_t)xTaskGetTickCount() * portTICK_PERIOD_MS;
209 if ((now - s_last_qr_switch) >= QR_CYCLE_MS) { 209 if ((now - s_last_qr_switch) >= QR_CYCLE_MS) {
210 s_qr_mode = (s_qr_mode == DISPLAY_QR_WIFI) ? DISPLAY_QR_PORTAL : DISPLAY_QR_WIFI; 210 s_qr_mode = (s_qr_mode == DISPLAY_QR_WIFI) ? DISPLAY_QR_PORTAL : DISPLAY_QR_WIFI;
211 s_last_qr_switch = now; 211 s_last_qr_switch = now;
212 qr_changed = true;
212 } 213 }
213 } 214 }
214 215
215 switch (state) { 216 if (s_force_render || state != s_rendered_state ||
216 case DISPLAY_BOOT: 217 (state == DISPLAY_READY && qr_changed)) {
217 render_boot_screen(); 218 s_force_render = false;
218 break; 219
219 case DISPLAY_READY: 220 switch (state) {
220 render_ready_screen(); 221 case DISPLAY_BOOT:
221 break; 222 render_boot_screen();
222 case DISPLAY_PAYMENT_RECEIVED: 223 break;
223 render_payment_screen(); 224 case DISPLAY_READY:
224 vTaskDelay(pdMS_TO_TICKS(2000)); 225 render_ready_screen();
225 s_state = DISPLAY_READY; 226 break;
226 break; 227 case DISPLAY_PAYMENT_RECEIVED:
227 case DISPLAY_ERROR: 228 render_payment_screen();
228 render_error_screen(); 229 vTaskDelay(pdMS_TO_TICKS(2000));
229 break; 230 s_state = DISPLAY_READY;
231 s_force_render = true;
232 break;
233 case DISPLAY_ERROR:
234 render_error_screen();
235 break;
236 }
237
238 s_rendered_state = state;
239 s_rendered_qr_mode = s_qr_mode;
230 } 240 }
231 241
232 vTaskDelay(pdMS_TO_TICKS(1000)); 242 vTaskDelay(pdMS_TO_TICKS(500));
233 } 243 }
234} 244}
235 245