diff options
| author | Your Name <you@example.com> | 2026-05-18 18:47:54 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-18 18:47:54 +0530 |
| commit | a02a0dbbf4e945ad00433e3fc9fd2b40550d3e82 (patch) | |
| tree | d36e349ac6030dd70548e7b91b4c54cc513e844a | |
| parent | 067a5700e26518d9a3f8bd92966c8957d17f0817 (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.c | 38 | ||||
| -rw-r--r-- | main/display.c | 48 |
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; | |||
| 37 | static uint16_t *s_fb = NULL; | 37 | static uint16_t *s_fb = NULL; |
| 38 | static int s_width = AXS15231B_WIDTH; | 38 | static int s_width = AXS15231B_WIDTH; |
| 39 | static int s_height = AXS15231B_HEIGHT; | 39 | static int s_height = AXS15231B_HEIGHT; |
| 40 | static uint8_t *s_swap_buf = NULL; | ||
| 41 | #define SWAP_BUF_PIXELS 2048 | ||
| 40 | 42 | ||
| 41 | typedef struct { | 43 | typedef 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 | ||
| 305 | void axs15231b_flush(void) { | 314 | void 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 | ||
| 149 | static void render_boot_screen(void) { | 149 | static 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 | ||
| 200 | static void display_task(void *pvParameters) { | 200 | static 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 | ||