From 4bf05b10af1e74a07dc2d2f59670c8328e506204 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 08:31:38 +0000 Subject: [PATCH 01/52] ili948x: Remove duplicate UNUSED macro libAtomVM/utils.h already provides UNUSED. Signed-off-by: Davide Bettio --- ili948x_display_driver.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index ee15d76..c3cfa0f 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -102,8 +102,6 @@ #define ILI948X_TFTWIDTH 320 #define ILI948X_TFTHEIGHT 480 -#define UNUSED(x) ((void) (x)) - #include "font.c" static const char *TAG = "ili948x_display_driver"; From 2efb4104ba242007f8137236657820bbacde247a Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 08:53:45 +0000 Subject: [PATCH 02/52] sdl: Fix register_font for libAtomVM 0.7 API globalcontext_atomstring_from_term() was removed in libAtomVM 0.7. Signed-off-by: Davide Bettio --- sdl_display/display.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdl_display/display.c b/sdl_display/display.c index 2ab01d5..b13e0c3 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -601,10 +601,11 @@ static void process_message(Context *ctx) term font_bin = term_get_tuple_element(req, 2); EpdFont *loaded_font = ufont_parse(term_binary_data(font_bin), term_binary_size(font_bin)); - AtomString handle_atom = globalcontext_atomstring_from_term(ctx->global, term_get_tuple_element(req, 1)); - char handle[255]; - atom_string_to_c(handle_atom, handle, sizeof(handle)); - ufont_manager_register(ufont_manager, handle, loaded_font); + char *handle = interop_atom_to_string(ctx, term_get_tuple_element(req, 1)); + if (handle != NULL) { + ufont_manager_register(ufont_manager, handle, loaded_font); + free(handle); + } } else { fprintf(stderr, "unexpected command: "); From 26781c1ac12908564165d166b3ce9a9895f90cd5 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 08:48:20 +0000 Subject: [PATCH 03/52] font_data: Add shared font data module font.c was previously included verbatim by every driver via array in the ESP32 build (and one more in the SDL build). Refactor into a proper compiled module so the data is linked once. font.c is renamed to font_data.c with the static qualifier removed on the array. A new font_data.h declares the array as extern and exposes FONTDATAMAX. All previous consumers (6 ESP32 drivers + the SDL display.c) now include font_data.h instead of font.c, and both CMakeLists.txt entries are updated to compile font_data.c. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 2 +- CMakeLists.txt | 1 + font.c => font_data.c | 4 ++-- font_data.h | 12 ++++++++++++ ili934x_display_driver.c | 2 +- ili948x_display_driver.c | 2 +- memory_display_driver.c | 2 +- sdl_display/CMakeLists.txt | 2 +- sdl_display/display.c | 2 +- ssd1306_display_driver.c | 2 +- st7789_display_driver.c | 2 +- 11 files changed, 23 insertions(+), 10 deletions(-) rename font.c => font_data.c (99%) create mode 100644 font_data.h diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 91483a1..f621c95 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -43,7 +43,7 @@ #include "display_items.h" #include "display_common.h" #include "draw_common.h" -#include "font.c" +#include "font_data.h" #include "image_helpers.h" #include "spi_display.h" diff --git a/CMakeLists.txt b/CMakeLists.txt index 64bf005..04260d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ idf_component_register(SRCS "display_common.c" "display_driver.c" "5in65_acep_7c_display_driver.c" + "font_data.c" "ili934x_display_driver.c" "ili948x_display_driver.c" "memory_display_driver.c" diff --git a/font.c b/font_data.c similarity index 99% rename from font.c rename to font_data.c index 4e6d9f9..b72a491 100644 --- a/font.c +++ b/font_data.c @@ -2,9 +2,9 @@ // SPDX-FileCopyrightText: Linux kernel developers et al. // See also lib/fonts/font_8x16.c -#define FONTDATAMAX 4096 +#include "font_data.h" -static const unsigned char fontdata[FONTDATAMAX] = { +const unsigned char fontdata[FONTDATAMAX] = { /* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, /* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, diff --git a/font_data.h b/font_data.h new file mode 100644 index 0000000..b3a7e46 --- /dev/null +++ b/font_data.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Linux kernel developers et al. +// See also lib/fonts/font_8x16.c + +#ifndef _FONT_DATA_H_ +#define _FONT_DATA_H_ + +#define FONTDATAMAX 4096 + +extern const unsigned char fontdata[FONTDATAMAX]; + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index f2e1d52..3c6fd74 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -107,7 +107,7 @@ #define TFT_INVOFF 0x20 #define TFT_INVON 0x21 -#include "font.c" +#include "font_data.h" static const char *TAG = "ili934x_display_driver"; diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index c3cfa0f..696315b 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -102,7 +102,7 @@ #define ILI948X_TFTWIDTH 320 #define ILI948X_TFTHEIGHT 480 -#include "font.c" +#include "font_data.h" static const char *TAG = "ili948x_display_driver"; diff --git a/memory_display_driver.c b/memory_display_driver.c index 3b77279..cb0a70c 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -60,7 +60,7 @@ #define CHECK_OVERFLOW 1 #define REPORT_UNEXPECTED_MSGS 0 -#include "font.c" +#include "font_data.h" struct SPI { diff --git a/sdl_display/CMakeLists.txt b/sdl_display/CMakeLists.txt index 6df9ac7..19c24d6 100644 --- a/sdl_display/CMakeLists.txt +++ b/sdl_display/CMakeLists.txt @@ -43,7 +43,7 @@ endif() set(CMAKE_SHARED_LIBRARY_PREFIX "") -add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c) +add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c) if (AVM_DISABLE_SMP) target_compile_definitions(avm_display_port_driver PUBLIC AVM_NO_SMP) diff --git a/sdl_display/display.c b/sdl_display/display.c index b13e0c3..6358b04 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -40,7 +40,7 @@ #define CHAR_WIDTH 8 #include "../display_items.h" -#include "../font.c" +#include "../font_data.h" #include "../image_helpers.h" struct DisplayOpts diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index 785734e..527b805 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -70,7 +70,7 @@ struct SPI static void do_update(Context *ctx, term display_list); -#include "font.c" +#include "font_data.h" #include "display_items.h" #include "draw_common.h" #include "monochrome.h" diff --git a/st7789_display_driver.c b/st7789_display_driver.c index c20a85e..2aa7d9b 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -96,7 +96,7 @@ #define TFT_MAD_BGR 0x08 #define TFT_MAD_COLOR_ORDER TFT_MAD_RGB -#include "font.c" +#include "font_data.h" static const char *TAG = "st7789_display_driver"; From b163c3644f362d02489f7985cef7faaddcc4f9ac Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 09:15:34 +0000 Subject: [PATCH 04/52] display_items: Refactor into compiled module display_items.h was previously included by every driver via init_item() and destroy_items() function bodies in the ESP32 build (and one more in the SDL build). Refactor into a proper compiled module so the bodies are linked once. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + display_items.c | 224 +++++++++++++++++++++++++++++++++++++ display_items.h | 198 +------------------------------- sdl_display/CMakeLists.txt | 2 +- 4 files changed, 231 insertions(+), 194 deletions(-) create mode 100644 display_items.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 04260d8..a67fc32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ endif() idf_component_register(SRCS "display_common.c" "display_driver.c" + "display_items.c" "5in65_acep_7c_display_driver.c" "font_data.c" "ili934x_display_driver.c" diff --git a/display_items.c b/display_items.c new file mode 100644 index 0000000..69dbcb6 --- /dev/null +++ b/display_items.c @@ -0,0 +1,224 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2022 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_items.h" + +#include +#include +#include + +#include + +void init_item(BaseDisplayItem *item, term req, Context *ctx) +{ + term cmd = term_get_tuple_element(req, 0); + + if (cmd == context_make_atom(ctx, "\x5" + "image")) { + item->primitive = Image; + item->x = term_to_int(term_get_tuple_element(req, 1)); + item->y = term_to_int(term_get_tuple_element(req, 2)); + + term bgcolor = term_get_tuple_element(req, 3); + if (bgcolor == context_make_atom(ctx, "\xB" + "transparent")) { + item->brcolor = 0; + } else { + item->brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; + } + + term img = term_get_tuple_element(req, 4); + + term format = term_get_tuple_element(img, 0); + if (format != context_make_atom(ctx, "\x8" + "rgba8888")) { + fprintf(stderr, "unsupported image format: "); + term_display(stderr, format, ctx); + fprintf(stderr, "\n"); + return; + } + item->width = term_to_int(term_get_tuple_element(img, 1)); + item->height = term_to_int(term_get_tuple_element(img, 2)); + item->data.image_data.pix = term_binary_data(term_get_tuple_element(img, 3)); + + } else if (cmd == globalcontext_make_atom(ctx->global, ATOM_STR("\x14", "scaled_cropped_image"))) { + item->primitive = ScaledCroppedImage; + item->x = term_to_int(term_get_tuple_element(req, 1)); + item->y = term_to_int(term_get_tuple_element(req, 2)); + item->width = term_to_int(term_get_tuple_element(req, 3)); + item->height = term_to_int(term_get_tuple_element(req, 4)); + + term bgcolor = term_get_tuple_element(req, 5); + if (bgcolor == globalcontext_make_atom(ctx->global, "\xB" + "transparent")) { + item->brcolor = 0; + } else { + item->brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; + } + + item->source_x = term_to_int(term_get_tuple_element(req, 6)); + item->source_y = term_to_int(term_get_tuple_element(req, 7)); + item->x_scale = term_to_int(term_get_tuple_element(req, 8)); + item->y_scale = term_to_int(term_get_tuple_element(req, 9)); + + // 10th element is for opts, but right now no opts are supported + + term img = term_get_tuple_element(req, 11); + + term format = term_get_tuple_element(img, 0); + if (format != globalcontext_make_atom(ctx->global, "\x8" + "rgba8888")) { + fprintf(stderr, "unsupported image format: "); + term_display(stderr, format, ctx); + fprintf(stderr, "\n"); + return; + } + item->data.image_data_with_size.width = term_to_int(term_get_tuple_element(img, 1)); + item->data.image_data_with_size.height = term_to_int(term_get_tuple_element(img, 2)); + item->data.image_data_with_size.pix = term_binary_data(term_get_tuple_element(img, 3)); + + } else if (cmd == context_make_atom(ctx, "\x4" + "rect")) { + item->primitive = Rect; + item->x = term_to_int(term_get_tuple_element(req, 1)); + item->y = term_to_int(term_get_tuple_element(req, 2)); + item->width = term_to_int(term_get_tuple_element(req, 3)); + item->height = term_to_int(term_get_tuple_element(req, 4)); + item->brcolor = term_to_int(term_get_tuple_element(req, 5)) << 8 | 0xFF; + + } else if (cmd == context_make_atom(ctx, "\x4" + "text")) { + item->x = term_to_int(term_get_tuple_element(req, 1)); + item->y = term_to_int(term_get_tuple_element(req, 2)); + uint32_t fgcolor = term_to_int(term_get_tuple_element(req, 4)) << 8 | 0xFF; + uint32_t brcolor; + term bgcolor = term_get_tuple_element(req, 5); + if (bgcolor == globalcontext_make_atom(ctx->global, "\xB" + "transparent")) { + brcolor = 0; + } else { + brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; + } + term text_term = term_get_tuple_element(req, 6); + int ok; + char *text = interop_term_to_string(text_term, &ok); + if (!ok) { + fprintf(stderr, "invalid text.\n"); + return; + } + + term font = term_get_tuple_element(req, 3); + + if (font == globalcontext_make_atom(ctx->global, "\xB" "default16px")) { + item->primitive = Text; + item->height = 16; + item->width = strlen(text) * 8; + item->brcolor = brcolor; + item->data.text_data.fgcolor = fgcolor; + item->data.text_data.text = text; + + } else { +#ifdef ENABLE_UFONT + AtomString handle_atom = globalcontext_atomstring_from_term(ctx->global, font); + char handle[255]; + atom_string_to_c(handle_atom, handle, sizeof(handle)); + EpdFont *loaded_font = ufont_manager_find_by_handle(ufont_manager, handle); + + if (!loaded_font) { + fprintf(stderr, "unsupported font: "); + term_display(stderr, font, ctx); + fprintf(stderr, "\n"); + return; + } + + EpdFontProperties props = epd_font_properties_default(); + EpdRect rect = epd_get_string_rect(loaded_font, text, 0, 0, 0, &props); + + struct Surface surface; + surface.width = rect.width; + surface.height = rect.height; + surface.buffer = malloc(rect.width * rect.height * BPP); + memset(surface.buffer, 0, rect.width * rect.height * BPP); + int text_x = 0; + int text_y = loaded_font->ascender; + enum EpdDrawError res = epd_write_default(loaded_font, text, &text_x, &text_y, &surface); + free(text); + if (res != EPD_DRAW_SUCCESS) { + fprintf(stderr, "Failed to draw text. Error code: %i\n", res); + return; + } + + item->primitive = Image; + item->width = surface.width; + item->height = surface.height; + item->brcolor = 0; + //FIXME: surface buffer leak + item->data.image_data.pix = surface.buffer; +#else + fprintf(stderr, "unsupported font: "); + term_display(stderr, font, ctx); + fprintf(stderr, "\n"); + item->primitive = Text; + item->height = 16; + item->width = strlen(text) * 8; + item->brcolor = brcolor; + item->data.text_data.fgcolor = fgcolor; + item->data.text_data.text = text; + +#endif + } + + } else { + fprintf(stderr, "unexpected display list command: "); + term_display(stderr, req, ctx); + fprintf(stderr, "\n"); + + item->primitive = Invalid; + item->x = -1; + item->y = -1; + item->width = 1; + item->height = 1; + } +} + +void destroy_items(BaseDisplayItem *items, int items_count) +{ + for (int i = 0; i < items_count; i++) { + BaseDisplayItem *item = &items[i]; + + switch (item->primitive) { + case Image: + break; + + case Rect: + break; + + case Text: + free((char *) item->data.text_data.text); + break; + + default: { + break; + } + } + } + + free(items); +} diff --git a/display_items.h b/display_items.h index 8d6c1f6..268099b 100644 --- a/display_items.h +++ b/display_items.h @@ -18,6 +18,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +#ifndef _DISPLAY_ITEMS_H_ +#define _DISPLAY_ITEMS_H_ + #include #include @@ -78,199 +81,8 @@ struct BaseDisplayItem typedef struct BaseDisplayItem BaseDisplayItem; -static void init_item(BaseDisplayItem *item, term req, Context *ctx) -{ - term cmd = term_get_tuple_element(req, 0); - - if (cmd == context_make_atom(ctx, "\x5" - "image")) { - item->primitive = Image; - item->x = term_to_int(term_get_tuple_element(req, 1)); - item->y = term_to_int(term_get_tuple_element(req, 2)); - - term bgcolor = term_get_tuple_element(req, 3); - if (bgcolor == context_make_atom(ctx, "\xB" - "transparent")) { - item->brcolor = 0; - } else { - item->brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; - } - - term img = term_get_tuple_element(req, 4); - - term format = term_get_tuple_element(img, 0); - if (format != context_make_atom(ctx, "\x8" - "rgba8888")) { - fprintf(stderr, "unsupported image format: "); - term_display(stderr, format, ctx); - fprintf(stderr, "\n"); - return; - } - item->width = term_to_int(term_get_tuple_element(img, 1)); - item->height = term_to_int(term_get_tuple_element(img, 2)); - item->data.image_data.pix = term_binary_data(term_get_tuple_element(img, 3)); - - } else if (cmd == globalcontext_make_atom(ctx->global, ATOM_STR("\x14", "scaled_cropped_image"))) { - item->primitive = ScaledCroppedImage; - item->x = term_to_int(term_get_tuple_element(req, 1)); - item->y = term_to_int(term_get_tuple_element(req, 2)); - item->width = term_to_int(term_get_tuple_element(req, 3)); - item->height = term_to_int(term_get_tuple_element(req, 4)); - - term bgcolor = term_get_tuple_element(req, 5); - if (bgcolor == globalcontext_make_atom(ctx->global, "\xB" - "transparent")) { - item->brcolor = 0; - } else { - item->brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; - } - - item->source_x = term_to_int(term_get_tuple_element(req, 6)); - item->source_y = term_to_int(term_get_tuple_element(req, 7)); - item->x_scale = term_to_int(term_get_tuple_element(req, 8)); - item->y_scale = term_to_int(term_get_tuple_element(req, 9)); - - // 10th element is for opts, but right now no opts are supported - - term img = term_get_tuple_element(req, 11); - - term format = term_get_tuple_element(img, 0); - if (format != globalcontext_make_atom(ctx->global, "\x8" - "rgba8888")) { - fprintf(stderr, "unsupported image format: "); - term_display(stderr, format, ctx); - fprintf(stderr, "\n"); - return; - } - item->data.image_data_with_size.width = term_to_int(term_get_tuple_element(img, 1)); - item->data.image_data_with_size.height = term_to_int(term_get_tuple_element(img, 2)); - item->data.image_data_with_size.pix = term_binary_data(term_get_tuple_element(img, 3)); - - } else if (cmd == context_make_atom(ctx, "\x4" - "rect")) { - item->primitive = Rect; - item->x = term_to_int(term_get_tuple_element(req, 1)); - item->y = term_to_int(term_get_tuple_element(req, 2)); - item->width = term_to_int(term_get_tuple_element(req, 3)); - item->height = term_to_int(term_get_tuple_element(req, 4)); - item->brcolor = term_to_int(term_get_tuple_element(req, 5)) << 8 | 0xFF; - - } else if (cmd == context_make_atom(ctx, "\x4" - "text")) { - item->x = term_to_int(term_get_tuple_element(req, 1)); - item->y = term_to_int(term_get_tuple_element(req, 2)); - uint32_t fgcolor = term_to_int(term_get_tuple_element(req, 4)) << 8 | 0xFF; - uint32_t brcolor; - term bgcolor = term_get_tuple_element(req, 5); - if (bgcolor == globalcontext_make_atom(ctx->global, "\xB" - "transparent")) { - brcolor = 0; - } else { - brcolor = ((uint32_t) term_to_int(bgcolor)) << 8 | 0xFF; - } - term text_term = term_get_tuple_element(req, 6); - int ok; - char *text = interop_term_to_string(text_term, &ok); - if (!ok) { - fprintf(stderr, "invalid text.\n"); - return; - } - - term font = term_get_tuple_element(req, 3); - - if (font == globalcontext_make_atom(ctx->global, "\xB" "default16px")) { - item->primitive = Text; - item->height = 16; - item->width = strlen(text) * 8; - item->brcolor = brcolor; - item->data.text_data.fgcolor = fgcolor; - item->data.text_data.text = text; - - } else { -#ifdef ENABLE_UFONT - AtomString handle_atom = globalcontext_atomstring_from_term(ctx->global, font); - char handle[255]; - atom_string_to_c(handle_atom, handle, sizeof(handle)); - EpdFont *loaded_font = ufont_manager_find_by_handle(ufont_manager, handle); - - if (!loaded_font) { - fprintf(stderr, "unsupported font: "); - term_display(stderr, font, ctx); - fprintf(stderr, "\n"); - return; - } - - EpdFontProperties props = epd_font_properties_default(); - EpdRect rect = epd_get_string_rect(loaded_font, text, 0, 0, 0, &props); - - struct Surface surface; - surface.width = rect.width; - surface.height = rect.height; - surface.buffer = malloc(rect.width * rect.height * BPP); - memset(surface.buffer, 0, rect.width * rect.height * BPP); - int text_x = 0; - int text_y = loaded_font->ascender; - enum EpdDrawError res = epd_write_default(loaded_font, text, &text_x, &text_y, &surface); - free(text); - if (res != EPD_DRAW_SUCCESS) { - fprintf(stderr, "Failed to draw text. Error code: %i\n", res); - return; - } - - item->primitive = Image; - item->width = surface.width; - item->height = surface.height; - item->brcolor = 0; - //FIXME: surface buffer leak - item->data.image_data.pix = surface.buffer; -#else - fprintf(stderr, "unsupported font: "); - term_display(stderr, font, ctx); - fprintf(stderr, "\n"); - item->primitive = Text; - item->height = 16; - item->width = strlen(text) * 8; - item->brcolor = brcolor; - item->data.text_data.fgcolor = fgcolor; - item->data.text_data.text = text; +void init_item(BaseDisplayItem *item, term req, Context *ctx); +void destroy_items(BaseDisplayItem *items, int items_count); #endif - } - - } else { - fprintf(stderr, "unexpected display list command: "); - term_display(stderr, req, ctx); - fprintf(stderr, "\n"); - - item->primitive = Invalid; - item->x = -1; - item->y = -1; - item->width = 1; - item->height = 1; - } -} -static void destroy_items(BaseDisplayItem *items, int items_count) -{ - for (int i = 0; i < items_count; i++) { - BaseDisplayItem *item = &items[i]; - - switch (item->primitive) { - case Image: - break; - - case Rect: - break; - - case Text: - free((char *) item->data.text_data.text); - break; - - default: { - break; - } - } - } - - free(items); -} diff --git a/sdl_display/CMakeLists.txt b/sdl_display/CMakeLists.txt index 19c24d6..9099cac 100644 --- a/sdl_display/CMakeLists.txt +++ b/sdl_display/CMakeLists.txt @@ -43,7 +43,7 @@ endif() set(CMAKE_SHARED_LIBRARY_PREFIX "") -add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c) +add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c ../display_items.c) if (AVM_DISABLE_SMP) target_compile_definitions(avm_display_port_driver PUBLIC AVM_NO_SMP) From 70fa8eb137d1286bbea115c0f937613e6ffef154 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 09:31:36 +0000 Subject: [PATCH 05/52] display_message: Add shared message module Extract the send_message helper (identical across 6 ESP32 drivers and message_helpers.h) into a shared display_message module. Also remove PendingReply, a vestigial struct defined in 6 files but never instantiated or referenced. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 14 +------------- CMakeLists.txt | 1 + display_message.c | 27 +++++++++++++++++++++++++++ display_message.h | 29 +++++++++++++++++++++++++++++ ili934x_display_driver.c | 15 +-------------- ili948x_display_driver.c | 15 +-------------- memory_display_driver.c | 15 +-------------- message_helpers.h | 6 ------ sdl_display/CMakeLists.txt | 2 +- sdl_display/display.c | 7 +------ st7789_display_driver.c | 15 +-------------- 11 files changed, 64 insertions(+), 82 deletions(-) create mode 100644 display_message.c create mode 100644 display_message.h diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index f621c95..9e5f11f 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -41,6 +41,7 @@ #define DISPLAY_HEIGHT 448 #include "display_items.h" +#include "display_message.h" #include "display_common.h" #include "draw_common.h" #include "font_data.h" @@ -55,7 +56,6 @@ static const char *TAG = "5in65_acep_7c_display_driver"; -static void send_message(term pid, term message, GlobalContext *global); static void clear_screen(Context *ctx, int color); struct SPI @@ -72,12 +72,6 @@ struct SPI uint64_t last_refresh; }; -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static inline float square(float p) @@ -568,12 +562,6 @@ static void process_messages(void *arg) } } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static void clear_screen(Context *ctx, int color) { struct SPI *spi = ctx->platform_data; diff --git a/CMakeLists.txt b/CMakeLists.txt index a67fc32..411b0aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ idf_component_register(SRCS "display_common.c" "display_driver.c" "display_items.c" + "display_message.c" "5in65_acep_7c_display_driver.c" "font_data.c" "ili934x_display_driver.c" diff --git a/display_message.c b/display_message.c new file mode 100644 index 0000000..4186c5b --- /dev/null +++ b/display_message.c @@ -0,0 +1,27 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_message.h" + +void send_message(term pid, term message, GlobalContext *global) +{ + int local_process_id = term_to_local_process_id(pid); + globalcontext_send_message(global, local_process_id, message); +} diff --git a/display_message.h b/display_message.h new file mode 100644 index 0000000..e46f964 --- /dev/null +++ b/display_message.h @@ -0,0 +1,29 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DISPLAY_MESSAGE_H_ +#define _DISPLAY_MESSAGE_H_ + +#include +#include + +void send_message(term pid, term message, GlobalContext *global); + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 3c6fd74..10c31b0 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -51,6 +51,7 @@ #include "backlight_gpio.h" #include "display_common.h" #include "display_items.h" +#include "display_message.h" #include "image_helpers.h" #include "spi_display.h" @@ -111,8 +112,6 @@ static const char *TAG = "ili934x_display_driver"; -static void send_message(term pid, term message, GlobalContext *global); - struct SPI { struct SPIDisplay spi_disp; @@ -173,12 +172,6 @@ static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) return rgb565_color_to_surface(s, color16); } -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); @@ -646,12 +639,6 @@ Context *ili934x_display_create_port(GlobalContext *global, term opts) return ctx; } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static void display_init(Context *ctx, term opts) { screen = malloc(sizeof(struct Screen)); diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 696315b..1131970 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -54,6 +54,7 @@ #include "backlight_gpio.h" #include "display_common.h" #include "display_items.h" +#include "display_message.h" #include "image_helpers.h" #include "spi_display.h" @@ -106,8 +107,6 @@ static const char *TAG = "ili948x_display_driver"; -static void send_message(term pid, term message, GlobalContext *global); - struct SPI { struct SPIDisplay spi_disp; @@ -197,12 +196,6 @@ static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *sr } } -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); @@ -746,12 +739,6 @@ Context *ili948x_display_create_port(GlobalContext *global, term opts) return ctx; } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static void display_init(Context *ctx, term opts) { screen = malloc(sizeof(struct Screen)); diff --git a/memory_display_driver.c b/memory_display_driver.c index cb0a70c..8837703 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -69,6 +69,7 @@ struct SPI }; #include "display_items.h" +#include "display_message.h" #include "draw_common.h" #include "image_helpers.h" #include "monochrome.h" @@ -86,12 +87,6 @@ struct Screen static struct Screen *screen; -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); @@ -179,8 +174,6 @@ static void do_update(Context *ctx, term display_list) destroy_items(items, len); } -static void send_message(term pid, term message, GlobalContext *global); - static void process_message(Message *message, Context *ctx) { GenMessage gen_message; @@ -254,12 +247,6 @@ Context *memory_lcd_display_create_port(GlobalContext *global, term opts) return ctx; } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static void display_init(Context *ctx, term opts) { screen = malloc(sizeof(struct Screen)); diff --git a/message_helpers.h b/message_helpers.h index aa4895a..e81430a 100644 --- a/message_helpers.h +++ b/message_helpers.h @@ -47,12 +47,6 @@ #include "image_helpers.h" -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); diff --git a/sdl_display/CMakeLists.txt b/sdl_display/CMakeLists.txt index 9099cac..9521125 100644 --- a/sdl_display/CMakeLists.txt +++ b/sdl_display/CMakeLists.txt @@ -43,7 +43,7 @@ endif() set(CMAKE_SHARED_LIBRARY_PREFIX "") -add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c ../display_items.c) +add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c ../display_items.c ../display_message.c) if (AVM_DISABLE_SMP) target_compile_definitions(avm_display_port_driver PUBLIC AVM_NO_SMP) diff --git a/sdl_display/display.c b/sdl_display/display.c index 6358b04..21aed37 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -40,6 +40,7 @@ #define CHAR_WIDTH 8 #include "../display_items.h" +#include "../display_message.h" #include "../font_data.h" #include "../image_helpers.h" @@ -651,12 +652,6 @@ static NativeHandlerResult consume_display_mailbox(Context *ctx) return NativeContinue; } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static inline int replace_new_line(int c) { return c == '\r' ? '\n' : c; diff --git a/st7789_display_driver.c b/st7789_display_driver.c index 2aa7d9b..0ab05a0 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -51,6 +51,7 @@ #include "backlight_gpio.h" #include "display_common.h" #include "display_items.h" +#include "display_message.h" #include "image_helpers.h" #include "spi_display.h" @@ -100,8 +101,6 @@ static const char *TAG = "st7789_display_driver"; -static void send_message(term pid, term message, GlobalContext *global); - static inline void delay(int ms) { vTaskDelay(ms / portTICK_PERIOD_MS); @@ -169,12 +168,6 @@ static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) return rgb565_color_to_surface(s, color16); } -struct PendingReply -{ - uint64_t pending_call_ref_ticks; - term pending_call_pid; -}; - static QueueHandle_t display_messages_queue; static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); @@ -641,12 +634,6 @@ Context *st7789_display_create_port(GlobalContext *global, term opts) return ctx; } -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} - static void display_init(Context *ctx, term opts) { term width_term = interop_kv_get_value_default( From c3d6cba8879024f3278e582a2fe9f64ac922e270 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 10:29:28 +0000 Subject: [PATCH 06/52] display_task: Add shared dispatch module Extract the FreeRTOS queue, consume_mailbox callback, and process_messages task into a shared display_task module. All seven ESP32 drivers now embed struct DisplayTaskArgs and use the shared dispatch infrastructure via CONTAINER_OF. The consume_mailbox implementation adopts the ili948x variant (non-blocking enqueue with drop-oldest-on-overflow and proper disposal of dropped messages). Also removes unused display_enqueue_message from ili934x and stale mailbox.h include from display_driver.h. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 49 ++++--------- CMakeLists.txt | 1 + display_driver.h | 2 - display_task.c | 66 +++++++++++++++++ display_task.h | 40 +++++++++++ ili934x_display_driver.c | 53 ++++---------- ili948x_display_driver.c | 63 ++++------------- memory_display_driver.c | 46 ++++-------- message_helpers.h | 125 --------------------------------- ssd1306_display_driver.c | 67 ++++++++++++++++-- st7789_display_driver.c | 48 ++++--------- 11 files changed, 237 insertions(+), 323 deletions(-) create mode 100644 display_task.c create mode 100644 display_task.h delete mode 100644 message_helpers.h diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 9e5f11f..c5c90ba 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -42,6 +42,7 @@ #include "display_items.h" #include "display_message.h" +#include "display_task.h" #include "display_common.h" #include "draw_common.h" #include "font_data.h" @@ -70,9 +71,12 @@ struct SPI int count_to_refresh; uint64_t last_refresh; + + struct DisplayTaskArgs display_args; }; -static QueueHandle_t display_messages_queue; +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) static inline float square(float p) { @@ -388,7 +392,7 @@ static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, void wait_some_time(Context *ctx) { - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); @@ -403,7 +407,7 @@ void wait_some_time(Context *ctx) void update_last_refresh_ts(Context *ctx) { - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); @@ -412,7 +416,7 @@ void update_last_refresh_ts(Context *ctx) void maybe_refresh(Context *ctx) { - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); spi->count_to_refresh--; if (spi->count_to_refresh <= 0) { @@ -443,7 +447,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = DISPLAY_WIDTH; int screen_height = DISPLAY_HEIGHT; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); struct SPIDisplay *spi_disp = &spi->spi_disp; spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); @@ -547,24 +551,9 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - static void clear_screen(Context *ctx, int color) { - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); @@ -678,7 +667,7 @@ static void display_spi_init(Context *ctx, term opts) writedata(spi, 0x37); spi_device_release_bus(spi->spi_disp.handle); - ctx->platform_data = spi; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; @@ -696,21 +685,13 @@ static void display_spi_init(Context *ctx, term opts) while (1) ; #else - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); #endif } -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - xQueueSend(display_messages_queue, &msg, 1); - - return NativeContinue; -} - Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); diff --git a/CMakeLists.txt b/CMakeLists.txt index 411b0aa..0307b2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ idf_component_register(SRCS "display_driver.c" "display_items.c" "display_message.c" + "display_task.c" "5in65_acep_7c_display_driver.c" "font_data.c" "ili934x_display_driver.c" diff --git a/display_driver.h b/display_driver.h index ac450e9..a859162 100644 --- a/display_driver.h +++ b/display_driver.h @@ -22,10 +22,8 @@ #define _DISPLAY_DRIVER_H_ #include -#include #include Context *display_create_port(GlobalContext *global, term opts); -void display_enqueue_message(Message *message); #endif diff --git a/display_task.c b/display_task.c new file mode 100644 index 0000000..b12f428 --- /dev/null +++ b/display_task.c @@ -0,0 +1,66 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_task.h" + +#include +#include + +NativeHandlerResult display_driver_consume_mailbox(Context *ctx) +{ + struct DisplayTaskArgs *args = ctx->platform_data; + + MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); + Message *msg = CONTAINER_OF(mbox_msg, Message, base); + + // Non-blocking enqueue; drop oldest on overflow. + if (xQueueSend(args->messages_queue, &msg, 0) != pdTRUE) { + + Message *old = NULL; + if (xQueueReceive(args->messages_queue, &old, 0) == pdTRUE && old) { + BEGIN_WITH_STACK_HEAP(1, temp_heap); + mailbox_message_dispose(&old->base, &temp_heap); + END_WITH_STACK_HEAP(temp_heap, ctx->global); + } + + if (xQueueSend(args->messages_queue, &msg, 0) != pdTRUE) { + BEGIN_WITH_STACK_HEAP(1, temp_heap2); + mailbox_message_dispose(&msg->base, &temp_heap2); + END_WITH_STACK_HEAP(temp_heap2, ctx->global); + } + } + + return NativeContinue; +} + +void display_process_messages(void *arg) +{ + struct DisplayTaskArgs *args = arg; + + while (true) { + Message *message; + xQueueReceive(args->messages_queue, &message, portMAX_DELAY); + args->process_message_fn(message, args->ctx); + + BEGIN_WITH_STACK_HEAP(1, temp_heap); + mailbox_message_dispose(&message->base, &temp_heap); + END_WITH_STACK_HEAP(temp_heap, args->ctx->global); + } +} diff --git a/display_task.h b/display_task.h new file mode 100644 index 0000000..b6597eb --- /dev/null +++ b/display_task.h @@ -0,0 +1,40 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DISPLAY_TASK_H_ +#define _DISPLAY_TASK_H_ + +#include +#include + +#include +#include + +struct DisplayTaskArgs +{ + QueueHandle_t messages_queue; + void (*process_message_fn)(Message *message, Context *ctx); + Context *ctx; +}; + +NativeHandlerResult display_driver_consume_mailbox(Context *ctx); +void display_process_messages(void *arg); + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 10c31b0..801ff24 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -52,6 +52,7 @@ #include "display_common.h" #include "display_items.h" #include "display_message.h" +#include "display_task.h" #include "image_helpers.h" #include "spi_display.h" @@ -121,8 +122,13 @@ struct SPI avm_int_t rotation; Context *ctx; + + struct DisplayTaskArgs display_args; }; +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) + // This struct is just for compatibility reasons with the SDL display driver // so it is possible to easily copy & paste code from there. struct Screen @@ -172,9 +178,6 @@ static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) return rgb565_color_to_surface(s, color16); } -static QueueHandle_t display_messages_queue; - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); static void display_init41(struct SPI *spi); @@ -461,7 +464,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); writecommand(spi, TFT_RAMWR); @@ -551,7 +554,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -593,36 +596,6 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - -void display_enqueue_message(Message *message) -{ - xQueueSend(display_messages_queue, &message, 1); -} - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - xQueueSend(display_messages_queue, &msg, 1); - - return NativeContinue; -} - static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { @@ -648,10 +621,12 @@ static void display_init(Context *ctx, term opts) screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - struct SPI *spi = malloc(sizeof(struct SPI)); - ctx->platform_data = spi; + + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; @@ -728,7 +703,7 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); } static void display_init41(struct SPI *spi) diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 1131970..9c6a8ed 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -55,6 +55,7 @@ #include "display_common.h" #include "display_items.h" #include "display_message.h" +#include "display_task.h" #include "image_helpers.h" #include "spi_display.h" @@ -119,8 +120,13 @@ struct SPI bool madctl_bgr; Context *ctx; + + struct DisplayTaskArgs display_args; }; +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) + // Double-buffered scanline buffers. struct Screen { @@ -196,9 +202,6 @@ static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *sr } } -static QueueHandle_t display_messages_queue; - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); static void display_init(Context *ctx, term opts); static void display_init9486(struct SPI *spi); @@ -485,7 +488,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); writecommand(spi, ILI948X_RAMWR); @@ -618,7 +621,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -661,46 +664,6 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - // Non-blocking enqueue; drop oldest on overflow. - if (xQueueSend(display_messages_queue, &msg, 0) != pdTRUE) { - - Message *old = NULL; - if (xQueueReceive(display_messages_queue, &old, 0) == pdTRUE && old) { - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&old->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, ctx->global); - } - - if (xQueueSend(display_messages_queue, &msg, 0) != pdTRUE) { - BEGIN_WITH_STACK_HEAP(1, temp_heap2); - mailbox_message_dispose(&msg->base, &temp_heap2); - END_WITH_STACK_HEAP(temp_heap2, ctx->global); - } - } - - return NativeContinue; -} - static void set_rotation(struct SPI *spi, int rotation) { uint8_t madctl = 0; @@ -743,10 +706,12 @@ static void display_init(Context *ctx, term opts) { screen = malloc(sizeof(struct Screen)); - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - struct SPI *spi = malloc(sizeof(struct SPI)); - ctx->platform_data = spi; + + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; @@ -872,7 +837,7 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); } static void display_init9486(struct SPI *spi) diff --git a/memory_display_driver.c b/memory_display_driver.c index 8837703..08c2951 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -51,6 +51,7 @@ #include #include "display_common.h" +#include "display_task.h" #include "spi_display.h" #define CHAR_WIDTH 8 @@ -66,8 +67,13 @@ struct SPI { struct SPIDisplay spi_disp; Context *ctx; + + struct DisplayTaskArgs display_args; }; +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) + #include "display_items.h" #include "display_message.h" #include "draw_common.h" @@ -87,9 +93,6 @@ struct Screen static struct Screen *screen; -static QueueHandle_t display_messages_queue; - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); static void display_init(Context *ctx, term opts); int vcom = 0x0; @@ -120,7 +123,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); int memsize = 2 + 400 / 8 + 2; uint8_t *buf = screen->pixels; @@ -214,31 +217,6 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - xQueueSend(display_messages_queue, &msg, 1); - - return NativeContinue; -} - Context *memory_lcd_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); @@ -267,12 +245,14 @@ static void display_init(Context *ctx, term opts) abort(); } - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - GlobalContext *glb = ctx->global; struct SPI *spi = malloc(sizeof(struct SPI)); - ctx->platform_data = spi; + + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; @@ -295,5 +275,5 @@ static void display_init(Context *ctx, term opts) gpio_set_level(en_gpio, 1); } - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); } diff --git a/message_helpers.h b/message_helpers.h deleted file mode 100644 index e81430a..0000000 --- a/message_helpers.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2022 Davide Bettio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include "image_helpers.h" - -static QueueHandle_t display_messages_queue; - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); - -static void send_message(term pid, term message, GlobalContext *global); - -static void process_message(Message *message, Context *ctx) -{ - GenMessage gen_message; - if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { - fprintf(stderr, "Received invalid message."); - AVM_ABORT(); - } - - term req = gen_message.req; - if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { - AVM_ABORT(); - } - term cmd = term_get_tuple_element(req, 0); - - if (cmd == context_make_atom(ctx, "\x6" - "update")) { - term display_list = term_get_tuple_element(req, 1); - do_update(ctx, display_list); - - } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" "load_image")) { - handle_load_image(req, gen_message.ref, gen_message.pid, ctx); - return; - - } else { -#if REPORT_UNEXPECTED_MSGS - fprintf(stderr, "display: "); - term_display(stderr, req, ctx); - fprintf(stderr, "\n"); -#endif - } - - BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); - term return_tuple = term_alloc_tuple(2, &heap); - term_put_tuple_element(return_tuple, 0, gen_message.ref); - term_put_tuple_element(return_tuple, 1, OK_ATOM); - - send_message(gen_message.pid, return_tuple, ctx->global); - END_WITH_STACK_HEAP(heap, ctx->global); -} - -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - xQueueSend(display_messages_queue, &msg, 1); - - return NativeContinue; -} - -static void send_message(term pid, term message, GlobalContext *global) -{ - int local_process_id = term_to_local_process_id(pid); - globalcontext_send_message(global, local_process_id, message); -} diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index 527b805..c435b9e 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -27,11 +27,20 @@ #include #include +#include +#include #include +#include +#include +#include +#include #include #include "display_common.h" +#include "display_message.h" +#include "display_task.h" +#include "image_helpers.h" #define TAG "SSD1306" @@ -66,15 +75,17 @@ struct SPI term i2c_host; display_type_t type; Context *ctx; + + struct DisplayTaskArgs display_args; }; -static void do_update(Context *ctx, term display_list); +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) #include "font_data.h" #include "display_items.h" #include "draw_common.h" #include "monochrome.h" -#include "message_helpers.h" static void do_update(Context *ctx, term display_list) { @@ -91,7 +102,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = DISPLAY_WIDTH; int screen_height = DISPLAY_HEIGHT; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); int memsize = (DISPLAY_WIDTH * (PAGE_HEIGHT + 1)) / sizeof(uint8_t); uint8_t *buf = malloc(memsize); @@ -165,6 +176,46 @@ static void do_update(Context *ctx, term display_list) destroy_items(items, len); } +static void process_message(Message *message, Context *ctx) +{ + GenMessage gen_message; + if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { + fprintf(stderr, "Received invalid message."); + AVM_ABORT(); + } + + term req = gen_message.req; + if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { + AVM_ABORT(); + } + term cmd = term_get_tuple_element(req, 0); + + if (cmd == context_make_atom(ctx, "\x6" + "update")) { + term display_list = term_get_tuple_element(req, 1); + do_update(ctx, display_list); + + } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" "load_image")) { + handle_load_image(req, gen_message.ref, gen_message.pid, ctx); + return; + + } else { +#if REPORT_UNEXPECTED_MSGS + fprintf(stderr, "display: "); + term_display(stderr, req, ctx); + fprintf(stderr, "\n"); +#endif + } + + BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); + term return_tuple = term_alloc_tuple(2, &heap); + term_put_tuple_element(return_tuple, 0, gen_message.ref); + term_put_tuple_element(return_tuple, 1, OK_ATOM); + + send_message(gen_message.pid, return_tuple, ctx->global); + END_WITH_STACK_HEAP(heap, ctx->global); +} + static void display_init(Context *ctx, term opts) { GlobalContext *glb = ctx->global; @@ -178,10 +229,12 @@ static void display_init(Context *ctx, term opts) bool invert = interop_kv_get_value(opts, ATOM_STR("\x6", "invert"), glb) == TRUE_ATOM; - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - struct SPI *spi = malloc(sizeof(struct SPI)); - ctx->platform_data = spi; + + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; spi->type = DISPLAY_SSD1306; // Default to SSD1306 @@ -289,7 +342,7 @@ static void display_init(Context *ctx, term opts) if (res != ESP_OK) { ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res); } else { - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); } i2c_cmd_link_delete(cmd); diff --git a/st7789_display_driver.c b/st7789_display_driver.c index 0ab05a0..b3fdcad 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -52,6 +52,7 @@ #include "display_common.h" #include "display_items.h" #include "display_message.h" +#include "display_task.h" #include "image_helpers.h" #include "spi_display.h" @@ -115,8 +116,13 @@ struct SPI avm_int_t rotation; Context *ctx; + + struct DisplayTaskArgs display_args; }; +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) + // This struct is just for compatibility reasons with the SDL display driver // so it is possible to easily copy & paste code from there. struct Screen @@ -168,9 +174,6 @@ static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) return rgb565_color_to_surface(s, color16); } -static QueueHandle_t display_messages_queue; - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx); static void display_init(Context *ctx, term opts); static void display_init_alt_gamma_2(struct SPI *spi); static void display_init_std(struct SPI *spi); @@ -461,7 +464,7 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); writecommand(spi, ST7789_RAMWR); @@ -551,7 +554,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = ctx->platform_data; + struct SPI *spi = SPI_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -593,31 +596,6 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void process_messages(void *arg) -{ - struct SPI *args = arg; - - while (true) { - Message *message; - xQueueReceive(display_messages_queue, &message, portMAX_DELAY); - process_message(message, args->ctx); - - BEGIN_WITH_STACK_HEAP(1, temp_heap); - mailbox_message_dispose(&message->base, &temp_heap); - END_WITH_STACK_HEAP(temp_heap, args->ctx->global); - } -} - -static NativeHandlerResult display_driver_consume_mailbox(Context *ctx) -{ - MailboxMessage *mbox_msg = mailbox_take_message(&ctx->mailbox); - Message *msg = CONTAINER_OF(mbox_msg, Message, base); - - xQueueSend(display_messages_queue, &msg, 1); - - return NativeContinue; -} - static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { @@ -647,10 +625,12 @@ static void display_init(Context *ctx, term opts) screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - display_messages_queue = xQueueCreate(32, sizeof(Message *)); - struct SPI *spi = malloc(sizeof(struct SPI)); - ctx->platform_data = spi; + + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + ctx->platform_data = &spi->display_args; spi->ctx = ctx; @@ -743,7 +723,7 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(process_messages, "display", 10000, spi, 1, NULL); + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); } static void display_init_alt_gamma_2(struct SPI *spi) From d174bb53113616a875d931ee218953a43425df86 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 15:15:56 +0000 Subject: [PATCH 07/52] ssd1306: Add stderr warning for unknown commands Signed-off-by: Davide Bettio --- ssd1306_display_driver.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index c435b9e..8585297 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -200,11 +200,9 @@ static void process_message(Message *message, Context *ctx) return; } else { -#if REPORT_UNEXPECTED_MSGS fprintf(stderr, "display: "); term_display(stderr, req, ctx); fprintf(stderr, "\n"); -#endif } BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); From 6d096172ec0a0f863759cb64c40d2705c14531e8 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 15:58:54 +0000 Subject: [PATCH 08/52] spi_dc_driver: Add shared SPI+DC transport Extract writedata, writecommand, and writecmddata from the DCS LCD drivers into a shared module using struct SPIDCBus. Migrate st7789, ili934x, and ili948x to embed the bus sub-struct. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + ili934x_display_driver.c | 412 +++++++++++++++++++-------------------- ili948x_display_driver.c | 372 +++++++++++++++++------------------ spi_dc_driver.c | 48 +++++ spi_dc_driver.h | 39 ++++ st7789_display_driver.c | 388 +++++++++++++++++------------------- 6 files changed, 649 insertions(+), 611 deletions(-) create mode 100644 spi_dc_driver.c create mode 100644 spi_dc_driver.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0307b2c..e5aa1d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ idf_component_register(SRCS "memory_display_driver.c" "ssd1306_display_driver.c" "st7789_display_driver.c" + "spi_dc_driver.c" "spi_display.c" "backlight_gpio.c" "image_helpers.c" diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 801ff24..2e75cef 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -54,6 +54,7 @@ #include "display_message.h" #include "display_task.h" #include "image_helpers.h" +#include "spi_dc_driver.h" #include "spi_display.h" #define SPI_CLOCK_HZ 27000000 @@ -115,8 +116,7 @@ static const char *TAG = "ili934x_display_driver"; struct SPI { - struct SPIDisplay spi_disp; - int dc_gpio; + struct SPIDCBus bus; int reset_gpio; avm_int_t rotation; @@ -182,31 +182,17 @@ static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); static void display_init41(struct SPI *spi); -static inline void writedata(struct SPI *spi, uint32_t data) -{ - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 8, data); - spi_device_release_bus(spi->spi_disp.handle); -} - -static inline void writecommand(struct SPI *spi, uint8_t command) -{ - gpio_set_level(spi->dc_gpio, 0); - writedata(spi, command); - gpio_set_level(spi->dc_gpio, 1); -} - static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) { - writecommand(spi, TFT_CASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->spi_disp.handle); - - writecommand(spi, TFT_PASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->spi_disp.handle); + spi_dc_writecommand(&spi->bus, TFT_CASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); + + spi_dc_writecommand(&spi->bus, TFT_PASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); } static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) @@ -467,8 +453,8 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - writecommand(spi, TFT_RAMWR); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_dc_writecommand(&spi->bus, TFT_RAMWR); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -483,23 +469,23 @@ static void do_update(Context *ctx, term display_list) spi_transaction_t *trans; // I did a quick measurement, and most of the time is spent waiting for DMA transaction // eg. 23 us spent in draw_x, 188 us spent in spi_device_get_trans_result - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } //NEW CODE void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dmawrite(&spi->spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); destroy_items(items, len); } @@ -510,7 +496,7 @@ void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const voi set_screen_paint_area(spi, x, y, width, height); - writecommand(spi, TFT_RAMWR); + spi_dc_writecommand(&spi->bus, TFT_RAMWR); int dest_size = width * height; int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; @@ -519,13 +505,13 @@ void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const voi uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); for (int i = 0; i < chunks; i++) { const uint16_t *data_b = data + 1024 * i; for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); } int last_chunk_size = dest_size - chunks * 1024; if (last_chunk_size) { @@ -533,9 +519,9 @@ void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const voi for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); free(tmpbuf); } @@ -599,8 +585,8 @@ static void process_message(Message *message, Context *ctx) static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - writecommand(spi, TFT_MADCTL); - writedata(spi, TFT_MAD_BGR | TFT_MAD_MY | TFT_MAD_MV); + spi_dc_writecommand(&spi->bus, TFT_MADCTL); + spi_dc_writedata(&spi->bus, TFT_MAD_BGR | TFT_MAD_MY | TFT_MAD_MV); } } @@ -635,9 +621,9 @@ static void display_init(Context *ctx, term opts) spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&spi->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); @@ -665,18 +651,18 @@ static void display_init(Context *ctx, term opts) } // Reset - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); gpio_set_level(spi->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); - gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - writecommand(spi, TFT_SWRST); + spi_dc_writecommand(&spi->bus, TFT_SWRST); vTaskDelay(5 / portTICK_PERIOD_MS); @@ -686,14 +672,14 @@ static void display_init(Context *ctx, term opts) display_init41(spi); } - writecommand(spi, ILI9341_SLPOUT); + spi_dc_writecommand(&spi->bus, ILI9341_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - writecommand(spi, ILI9341_DISPON); + spi_dc_writecommand(&spi->bus, ILI9341_DISPON); if (enable_tft_invon) { - writecommand(spi, TFT_INVON); + spi_dc_writecommand(&spi->bus, TFT_INVON); } set_rotation(spi, spi->rotation); @@ -708,174 +694,174 @@ static void display_init(Context *ctx, term opts) static void display_init41(struct SPI *spi) { - writecommand(spi, 0xEF); - writedata(spi, 0x03); - writedata(spi, 0x80); - writedata(spi, 0x02); - - writecommand(spi, 0xCF); - writedata(spi, 0x00); - writedata(spi, 0xC1); - writedata(spi, 0x30); - - writecommand(spi, 0xED); - writedata(spi, 0x64); - writedata(spi, 0x03); - writedata(spi, 0x12); - writedata(spi, 0x81); - - writecommand(spi, 0xE8); - writedata(spi, 0x85); - writedata(spi, 0x00); - writedata(spi, 0x78); - - writecommand(spi, 0xCB); - writedata(spi, 0x39); - writedata(spi, 0x2C); - writedata(spi, 0x00); - writedata(spi, 0x34); - writedata(spi, 0x02); - - writecommand(spi, 0xF7); - writedata(spi, 0x20); - - writecommand(spi, 0xEA); - writedata(spi, 0x00); - writedata(spi, 0x00); - - writecommand(spi, ILI9341_PWCTR1); - writedata(spi, 0x23); - - writecommand(spi, ILI9341_PWCTR2); - writedata(spi, 0x10); - - writecommand(spi, ILI9341_VMCTR1); - writedata(spi, 0x3E); - writedata(spi, 0x28); - - writecommand(spi, ILI9341_VMCTR2); - writedata(spi, 0x86); - - writecommand(spi, ILI9341_MADCTL); - writedata(spi, 0x08); - - writecommand(spi, ILI9341_PIXFMT); - writedata(spi, 0x55); - - writecommand(spi, ILI9341_FRMCTR1); - writedata(spi, 0x00); - writedata(spi, 0x13); - - writecommand(spi, ILI9341_DFUNCTR); - writedata(spi, 0x0A); - writedata(spi, 0xA2); - writedata(spi, 0x27); - - writecommand(spi, 0xF2); - writedata(spi, 0x00); - - writecommand(spi, ILI9341_GAMMASET); - writedata(spi, 0x01); - - writecommand(spi, ILI9341_GMCTRP1); - writedata(spi, 0x0F); - writedata(spi, 0x31); - writedata(spi, 0x2B); - writedata(spi, 0x0C); - writedata(spi, 0x0E); - writedata(spi, 0x08); - writedata(spi, 0x4E); - writedata(spi, 0xF1); - writedata(spi, 0x37); - writedata(spi, 0x07); - writedata(spi, 0x10); - writedata(spi, 0x03); - writedata(spi, 0x0E); - writedata(spi, 0x09); - writedata(spi, 0x00); - - writecommand(spi, ILI9341_GMCTRN1); - writedata(spi, 0x00); - writedata(spi, 0x0E); - writedata(spi, 0x14); - writedata(spi, 0x03); - writedata(spi, 0x11); - writedata(spi, 0x07); - writedata(spi, 0x31); - writedata(spi, 0xC1); - writedata(spi, 0x48); - writedata(spi, 0x08); - writedata(spi, 0x0F); - writedata(spi, 0x0C); - writedata(spi, 0x31); - writedata(spi, 0x36); - writedata(spi, 0x0F); + spi_dc_writecommand(&spi->bus, 0xEF); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x80); + spi_dc_writedata(&spi->bus, 0x02); + + spi_dc_writecommand(&spi->bus, 0xCF); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0xC1); + spi_dc_writedata(&spi->bus, 0x30); + + spi_dc_writecommand(&spi->bus, 0xED); + spi_dc_writedata(&spi->bus, 0x64); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x12); + spi_dc_writedata(&spi->bus, 0x81); + + spi_dc_writecommand(&spi->bus, 0xE8); + spi_dc_writedata(&spi->bus, 0x85); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x78); + + spi_dc_writecommand(&spi->bus, 0xCB); + spi_dc_writedata(&spi->bus, 0x39); + spi_dc_writedata(&spi->bus, 0x2C); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x34); + spi_dc_writedata(&spi->bus, 0x02); + + spi_dc_writecommand(&spi->bus, 0xF7); + spi_dc_writedata(&spi->bus, 0x20); + + spi_dc_writecommand(&spi->bus, 0xEA); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI9341_PWCTR1); + spi_dc_writedata(&spi->bus, 0x23); + + spi_dc_writecommand(&spi->bus, ILI9341_PWCTR2); + spi_dc_writedata(&spi->bus, 0x10); + + spi_dc_writecommand(&spi->bus, ILI9341_VMCTR1); + spi_dc_writedata(&spi->bus, 0x3E); + spi_dc_writedata(&spi->bus, 0x28); + + spi_dc_writecommand(&spi->bus, ILI9341_VMCTR2); + spi_dc_writedata(&spi->bus, 0x86); + + spi_dc_writecommand(&spi->bus, ILI9341_MADCTL); + spi_dc_writedata(&spi->bus, 0x08); + + spi_dc_writecommand(&spi->bus, ILI9341_PIXFMT); + spi_dc_writedata(&spi->bus, 0x55); + + spi_dc_writecommand(&spi->bus, ILI9341_FRMCTR1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x13); + + spi_dc_writecommand(&spi->bus, ILI9341_DFUNCTR); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0xA2); + spi_dc_writedata(&spi->bus, 0x27); + + spi_dc_writecommand(&spi->bus, 0xF2); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI9341_GAMMASET); + spi_dc_writedata(&spi->bus, 0x01); + + spi_dc_writecommand(&spi->bus, ILI9341_GMCTRP1); + spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_writedata(&spi->bus, 0x31); + spi_dc_writedata(&spi->bus, 0x2B); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x4E); + spi_dc_writedata(&spi->bus, 0xF1); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x10); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI9341_GMCTRN1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x14); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x31); + spi_dc_writedata(&spi->bus, 0xC1); + spi_dc_writedata(&spi->bus, 0x48); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x31); + spi_dc_writedata(&spi->bus, 0x36); + spi_dc_writedata(&spi->bus, 0x0F); } static void display_init42c(struct SPI *spi) { - writecommand(spi, 0xC8); - writedata(spi, 0xFF); - writedata(spi, 0x93); - writedata(spi, 0x42); - - writecommand(spi, ILI9341_PWCTR1); - writedata(spi, 0x12); - writedata(spi, 0x12); - - writecommand(spi, ILI9341_PWCTR2); - writedata(spi, 0x03); - - writecommand(spi, 0xB0); - writedata(spi, 0xE0); - - writecommand(spi, 0xF6); - writedata(spi, 0x00); - writedata(spi, 0x01); - writedata(spi, 0x01); - - writecommand(spi, ILI9341_MADCTL); - writedata(spi, TFT_MAD_MY | TFT_MAD_MV); - - writecommand(spi, ILI9341_PIXFMT); - writedata(spi, 0x55); - - writecommand(spi, ILI9341_DFUNCTR); - writedata(spi, 0x08); - writedata(spi, 0x82); - writedata(spi, 0x27); - - writecommand(spi, ILI9341_GMCTRP1); - writedata(spi, 0x00); - writedata(spi, 0x0C); - writedata(spi, 0x11); - writedata(spi, 0x04); - writedata(spi, 0x11); - writedata(spi, 0x08); - writedata(spi, 0x37); - writedata(spi, 0x89); - writedata(spi, 0x4C); - writedata(spi, 0x06); - writedata(spi, 0x0C); - writedata(spi, 0x0A); - writedata(spi, 0x2E); - writedata(spi, 0x34); - writedata(spi, 0x0F); - - writecommand(spi, ILI9341_GMCTRN1); - writedata(spi, 0x00); - writedata(spi, 0x0B); - writedata(spi, 0x11); - writedata(spi, 0x05); - writedata(spi, 0x13); - writedata(spi, 0x09); - writedata(spi, 0x33); - writedata(spi, 0x67); - writedata(spi, 0x48); - writedata(spi, 0x07); - writedata(spi, 0x0E); - writedata(spi, 0x0B); - writedata(spi, 0x2E); - writedata(spi, 0x33); - writedata(spi, 0x0F); + spi_dc_writecommand(&spi->bus, 0xC8); + spi_dc_writedata(&spi->bus, 0xFF); + spi_dc_writedata(&spi->bus, 0x93); + spi_dc_writedata(&spi->bus, 0x42); + + spi_dc_writecommand(&spi->bus, ILI9341_PWCTR1); + spi_dc_writedata(&spi->bus, 0x12); + spi_dc_writedata(&spi->bus, 0x12); + + spi_dc_writecommand(&spi->bus, ILI9341_PWCTR2); + spi_dc_writedata(&spi->bus, 0x03); + + spi_dc_writecommand(&spi->bus, 0xB0); + spi_dc_writedata(&spi->bus, 0xE0); + + spi_dc_writecommand(&spi->bus, 0xF6); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0x01); + + spi_dc_writecommand(&spi->bus, ILI9341_MADCTL); + spi_dc_writedata(&spi->bus, TFT_MAD_MY | TFT_MAD_MV); + + spi_dc_writecommand(&spi->bus, ILI9341_PIXFMT); + spi_dc_writedata(&spi->bus, 0x55); + + spi_dc_writecommand(&spi->bus, ILI9341_DFUNCTR); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x82); + spi_dc_writedata(&spi->bus, 0x27); + + spi_dc_writecommand(&spi->bus, ILI9341_GMCTRP1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x04); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x89); + spi_dc_writedata(&spi->bus, 0x4C); + spi_dc_writedata(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x2E); + spi_dc_writedata(&spi->bus, 0x34); + spi_dc_writedata(&spi->bus, 0x0F); + + spi_dc_writecommand(&spi->bus, ILI9341_GMCTRN1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x0B); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x05); + spi_dc_writedata(&spi->bus, 0x13); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x33); + spi_dc_writedata(&spi->bus, 0x67); + spi_dc_writedata(&spi->bus, 0x48); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x0B); + spi_dc_writedata(&spi->bus, 0x2E); + spi_dc_writedata(&spi->bus, 0x33); + spi_dc_writedata(&spi->bus, 0x0F); } diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 9c6a8ed..fd94f3c 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -57,6 +57,7 @@ #include "display_message.h" #include "display_task.h" #include "image_helpers.h" +#include "spi_dc_driver.h" #include "spi_display.h" #define SPI_CLOCK_HZ 27000000 @@ -110,8 +111,7 @@ static const char *TAG = "ili948x_display_driver"; struct SPI { - struct SPIDisplay spi_disp; - int dc_gpio; + struct SPIDCBus bus; int reset_gpio; avm_int_t rotation; @@ -207,31 +207,17 @@ static void display_init(Context *ctx, term opts); static void display_init9486(struct SPI *spi); static void display_init9488(struct SPI *spi); -static inline void writedata(struct SPI *spi, uint32_t data) -{ - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 8, data); - spi_device_release_bus(spi->spi_disp.handle); -} - -static inline void writecommand(struct SPI *spi, uint8_t command) -{ - gpio_set_level(spi->dc_gpio, 0); - writedata(spi, command); - gpio_set_level(spi->dc_gpio, 1); -} - static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) { - writecommand(spi, ILI948X_CASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->spi_disp.handle); - - writecommand(spi, ILI948X_PASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->spi_disp.handle); + spi_dc_writecommand(&spi->bus, ILI948X_CASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); + + spi_dc_writecommand(&spi->bus, ILI948X_PASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); } static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) @@ -491,8 +477,8 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - writecommand(spi, ILI948X_RAMWR); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_dc_writecommand(&spi->bus, ILI948X_RAMWR); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -505,7 +491,7 @@ static void do_update(Context *ctx, term display_list) if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } // Swap scanline buffers. @@ -514,14 +500,14 @@ static void do_update(Context *ctx, term display_list) screen->pixels_out = tmp; if (!spi->is_ili9488) { - spi_display_dmawrite(&spi->spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); } else { void *tmpb = screen->bytes; screen->bytes = screen->bytes_out; screen->bytes_out = tmpb; rgb565swapped_line_to_rgb888(screen->bytes_out, screen->pixels_out, screen_width); - spi_display_dmawrite(&spi->spi_disp, screen_width * 3, screen->bytes_out); + spi_display_dmawrite(&spi->bus.spi_disp, screen_width * 3, screen->bytes_out); } transaction_in_progress = true; @@ -529,10 +515,10 @@ static void do_update(Context *ctx, term display_list) if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); destroy_items(items, len); } @@ -543,12 +529,12 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co set_screen_paint_area(spi, x, y, width, height); - writecommand(spi, ILI948X_RAMWR); + spi_dc_writecommand(&spi->bus, ILI948X_RAMWR); int dest_size = width * height; int chunks = dest_size / 1024; - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); if (!spi->is_ili9488) { int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; @@ -559,7 +545,7 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, 1024 * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, 1024 * sizeof(uint16_t), tmpbuf); } int last_chunk_size = dest_size - chunks * 1024; @@ -568,7 +554,7 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co for (int j = 0; j < last_chunk_size; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); } free(tmpbuf); @@ -597,14 +583,14 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co tmpbuf[j * 3 + 2] = b8; } - spi_display_dmawrite(&spi->spi_disp, n * 3, tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, n * 3, tmpbuf); i += n; } free(tmpbuf); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); } static void process_message(Message *message, Context *ctx) @@ -690,8 +676,8 @@ static void set_rotation(struct SPI *spi, int rotation) break; } - writecommand(spi, ILI948X_MADCTL); - writedata(spi, madctl); + spi_dc_writecommand(&spi->bus, ILI948X_MADCTL); + spi_dc_writedata(&spi->bus, madctl); } Context *ili948x_display_create_port(GlobalContext *global, term opts) @@ -720,9 +706,9 @@ static void display_init(Context *ctx, term opts) spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&spi->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); @@ -797,18 +783,18 @@ static void display_init(Context *ctx, term opts) } // Reset. - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); gpio_set_level(spi->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); - gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - writecommand(spi, ILI948X_SWRESET); + spi_dc_writecommand(&spi->bus, ILI948X_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); @@ -818,16 +804,16 @@ static void display_init(Context *ctx, term opts) display_init9486(spi); } - writecommand(spi, ILI948X_SLPOUT); + spi_dc_writecommand(&spi->bus, ILI948X_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - writecommand(spi, ILI948X_DISPON); + spi_dc_writecommand(&spi->bus, ILI948X_DISPON); if (enable_tft_invon) { - writecommand(spi, ILI948X_INVON); + spi_dc_writecommand(&spi->bus, ILI948X_INVON); } else { - writecommand(spi, ILI948X_INVOFF); + spi_dc_writecommand(&spi->bus, ILI948X_INVOFF); } set_rotation(spi, spi->rotation); @@ -842,152 +828,152 @@ static void display_init(Context *ctx, term opts) static void display_init9486(struct SPI *spi) { - writecommand(spi, ILI948X_IFMODE); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_PIXFMT); - writedata(spi, 0x55); - - writecommand(spi, ILI948X_PWRCTR3); - writedata(spi, 0x44); - - writecommand(spi, ILI948X_VMCTR1); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_PGAMCTRL); - writedata(spi, 0x0f); - writedata(spi, 0x1f); - writedata(spi, 0x1c); - writedata(spi, 0x0c); - writedata(spi, 0x0f); - writedata(spi, 0x08); - writedata(spi, 0x48); - writedata(spi, 0x98); - writedata(spi, 0x37); - writedata(spi, 0x0a); - writedata(spi, 0x13); - writedata(spi, 0x04); - writedata(spi, 0x11); - writedata(spi, 0x0d); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_NGAMCTRL); - writedata(spi, 0x0f); - writedata(spi, 0x32); - writedata(spi, 0x2e); - writedata(spi, 0x0b); - writedata(spi, 0x0d); - writedata(spi, 0x05); - writedata(spi, 0x47); - writedata(spi, 0x75); - writedata(spi, 0x37); - writedata(spi, 0x06); - writedata(spi, 0x10); - writedata(spi, 0x03); - writedata(spi, 0x24); - writedata(spi, 0x20); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_DGAMCTRL); - writedata(spi, 0x0f); - writedata(spi, 0x32); - writedata(spi, 0x2e); - writedata(spi, 0x0b); - writedata(spi, 0x0d); - writedata(spi, 0x05); - writedata(spi, 0x47); - writedata(spi, 0x75); - writedata(spi, 0x37); - writedata(spi, 0x06); - writedata(spi, 0x10); - writedata(spi, 0x03); - writedata(spi, 0x24); - writedata(spi, 0x20); - writedata(spi, 0x00); + spi_dc_writecommand(&spi->bus, ILI948X_IFMODE); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_PIXFMT); + spi_dc_writedata(&spi->bus, 0x55); + + spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR3); + spi_dc_writedata(&spi->bus, 0x44); + + spi_dc_writecommand(&spi->bus, ILI948X_VMCTR1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_PGAMCTRL); + spi_dc_writedata(&spi->bus, 0x0f); + spi_dc_writedata(&spi->bus, 0x1f); + spi_dc_writedata(&spi->bus, 0x1c); + spi_dc_writedata(&spi->bus, 0x0c); + spi_dc_writedata(&spi->bus, 0x0f); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x48); + spi_dc_writedata(&spi->bus, 0x98); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x0a); + spi_dc_writedata(&spi->bus, 0x13); + spi_dc_writedata(&spi->bus, 0x04); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x0d); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_NGAMCTRL); + spi_dc_writedata(&spi->bus, 0x0f); + spi_dc_writedata(&spi->bus, 0x32); + spi_dc_writedata(&spi->bus, 0x2e); + spi_dc_writedata(&spi->bus, 0x0b); + spi_dc_writedata(&spi->bus, 0x0d); + spi_dc_writedata(&spi->bus, 0x05); + spi_dc_writedata(&spi->bus, 0x47); + spi_dc_writedata(&spi->bus, 0x75); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0x10); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x24); + spi_dc_writedata(&spi->bus, 0x20); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_DGAMCTRL); + spi_dc_writedata(&spi->bus, 0x0f); + spi_dc_writedata(&spi->bus, 0x32); + spi_dc_writedata(&spi->bus, 0x2e); + spi_dc_writedata(&spi->bus, 0x0b); + spi_dc_writedata(&spi->bus, 0x0d); + spi_dc_writedata(&spi->bus, 0x05); + spi_dc_writedata(&spi->bus, 0x47); + spi_dc_writedata(&spi->bus, 0x75); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0x10); + spi_dc_writedata(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x24); + spi_dc_writedata(&spi->bus, 0x20); + spi_dc_writedata(&spi->bus, 0x00); } static void display_init9488(struct SPI *spi) { // ILI9488: RGB666 over SPI (3 bytes/pixel). - writecommand(spi, ILI948X_IFMODE); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_ADJCTRL3); - writedata(spi, 0xA9); - writedata(spi, 0x51); - writedata(spi, 0x2C); - writedata(spi, 0x82); - - writecommand(spi, ILI948X_PWRCTR1); - writedata(spi, 0x11); - writedata(spi, 0x09); - - writecommand(spi, ILI948X_PWRCTR2); - writedata(spi, 0x41); - - writecommand(spi, ILI948X_VMCTR1); - writedata(spi, 0x00); - writedata(spi, 0x0A); - writedata(spi, 0x80); - - writecommand(spi, ILI948X_FRMCTR1); - writedata(spi, 0xB0); - writedata(spi, 0x11); - - writecommand(spi, ILI948X_INVCTR); - writedata(spi, 0x02); - - writecommand(spi, ILI948X_DFUNCTR); - writedata(spi, 0x02); - writedata(spi, 0x02); - - writecommand(spi, ILI948X_ETMOD); - writedata(spi, 0xC6); - - writecommand(spi, ILI948X_HS_LANES_CTRL); - writedata(spi, 0x00); - writedata(spi, 0x04); - - writecommand(spi, ILI948X_IMAGE_FUNCTION); - writedata(spi, 0x00); - - writecommand(spi, ILI948X_PIXFMT); - writedata(spi, 0x66); - - writecommand(spi, ILI948X_PGAMCTRL); - writedata(spi, 0x00); - writedata(spi, 0x07); - writedata(spi, 0x10); - writedata(spi, 0x09); - writedata(spi, 0x17); - writedata(spi, 0x0B); - writedata(spi, 0x41); - writedata(spi, 0x89); - writedata(spi, 0x4B); - writedata(spi, 0x0A); - writedata(spi, 0x0C); - writedata(spi, 0x0E); - writedata(spi, 0x18); - writedata(spi, 0x1B); - writedata(spi, 0x0F); - - writecommand(spi, ILI948X_NGAMCTRL); - writedata(spi, 0x00); - writedata(spi, 0x17); - writedata(spi, 0x1A); - writedata(spi, 0x04); - writedata(spi, 0x0E); - writedata(spi, 0x06); - writedata(spi, 0x2F); - writedata(spi, 0x45); - writedata(spi, 0x43); - writedata(spi, 0x02); - writedata(spi, 0x0A); - writedata(spi, 0x09); - writedata(spi, 0x32); - writedata(spi, 0x36); - writedata(spi, 0x0F); + spi_dc_writecommand(&spi->bus, ILI948X_IFMODE); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_ADJCTRL3); + spi_dc_writedata(&spi->bus, 0xA9); + spi_dc_writedata(&spi->bus, 0x51); + spi_dc_writedata(&spi->bus, 0x2C); + spi_dc_writedata(&spi->bus, 0x82); + + spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR1); + spi_dc_writedata(&spi->bus, 0x11); + spi_dc_writedata(&spi->bus, 0x09); + + spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR2); + spi_dc_writedata(&spi->bus, 0x41); + + spi_dc_writecommand(&spi->bus, ILI948X_VMCTR1); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x80); + + spi_dc_writecommand(&spi->bus, ILI948X_FRMCTR1); + spi_dc_writedata(&spi->bus, 0xB0); + spi_dc_writedata(&spi->bus, 0x11); + + spi_dc_writecommand(&spi->bus, ILI948X_INVCTR); + spi_dc_writedata(&spi->bus, 0x02); + + spi_dc_writecommand(&spi->bus, ILI948X_DFUNCTR); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x02); + + spi_dc_writecommand(&spi->bus, ILI948X_ETMOD); + spi_dc_writedata(&spi->bus, 0xC6); + + spi_dc_writecommand(&spi->bus, ILI948X_HS_LANES_CTRL); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x04); + + spi_dc_writecommand(&spi->bus, ILI948X_IMAGE_FUNCTION); + spi_dc_writedata(&spi->bus, 0x00); + + spi_dc_writecommand(&spi->bus, ILI948X_PIXFMT); + spi_dc_writedata(&spi->bus, 0x66); + + spi_dc_writecommand(&spi->bus, ILI948X_PGAMCTRL); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x10); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x17); + spi_dc_writedata(&spi->bus, 0x0B); + spi_dc_writedata(&spi->bus, 0x41); + spi_dc_writedata(&spi->bus, 0x89); + spi_dc_writedata(&spi->bus, 0x4B); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x18); + spi_dc_writedata(&spi->bus, 0x1B); + spi_dc_writedata(&spi->bus, 0x0F); + + spi_dc_writecommand(&spi->bus, ILI948X_NGAMCTRL); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x17); + spi_dc_writedata(&spi->bus, 0x1A); + spi_dc_writedata(&spi->bus, 0x04); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0x2F); + spi_dc_writedata(&spi->bus, 0x45); + spi_dc_writedata(&spi->bus, 0x43); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x32); + spi_dc_writedata(&spi->bus, 0x36); + spi_dc_writedata(&spi->bus, 0x0F); } diff --git a/spi_dc_driver.c b/spi_dc_driver.c new file mode 100644 index 0000000..dd003c1 --- /dev/null +++ b/spi_dc_driver.c @@ -0,0 +1,48 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "spi_dc_driver.h" + +#include + +#include +#include + +void spi_dc_writedata(struct SPIDCBus *bus, uint32_t data) +{ + spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); + spi_display_write(&bus->spi_disp, 8, data); + spi_device_release_bus(bus->spi_disp.handle); +} + +void spi_dc_writecommand(struct SPIDCBus *bus, uint8_t cmd) +{ + gpio_set_level(bus->dc_gpio, 0); + spi_dc_writedata(bus, cmd); + gpio_set_level(bus->dc_gpio, 1); +} + +void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t length) +{ + spi_dc_writecommand(bus, cmd); + for (int i = 0; i < length; i++) { + spi_dc_writedata(bus, data[i]); + } +} diff --git a/spi_dc_driver.h b/spi_dc_driver.h new file mode 100644 index 0000000..2f1fd04 --- /dev/null +++ b/spi_dc_driver.h @@ -0,0 +1,39 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _SPI_DC_DRIVER_H_ +#define _SPI_DC_DRIVER_H_ + +#include +#include + +#include "spi_display.h" + +struct SPIDCBus +{ + struct SPIDisplay spi_disp; + int dc_gpio; +}; + +void spi_dc_writedata(struct SPIDCBus *bus, uint32_t data); +void spi_dc_writecommand(struct SPIDCBus *bus, uint8_t cmd); +void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t length); + +#endif diff --git a/st7789_display_driver.c b/st7789_display_driver.c index b3fdcad..eb8eda0 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -54,6 +54,7 @@ #include "display_message.h" #include "display_task.h" #include "image_helpers.h" +#include "spi_dc_driver.h" #include "spi_display.h" // if needed it can be lowered to 27000000, while maximum is 62.5 Mhz @@ -109,8 +110,7 @@ static inline void delay(int ms) struct SPI { - struct SPIDisplay spi_disp; - int dc_gpio; + struct SPIDCBus bus; int reset_gpio; avm_int_t rotation; @@ -179,34 +179,20 @@ static void display_init_alt_gamma_2(struct SPI *spi); static void display_init_std(struct SPI *spi); static void display_init_using_list(struct SPI *spi, term init_list); -static inline void writedata(struct SPI *spi, uint32_t data) -{ - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 8, data); - spi_device_release_bus(spi->spi_disp.handle); -} - -static inline void writecommand(struct SPI *spi, uint8_t command) -{ - gpio_set_level(spi->dc_gpio, 0); - writedata(spi, command); - gpio_set_level(spi->dc_gpio, 1); -} - static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) { x += screen->x_offset; y += screen->y_offset; - writecommand(spi, ST7789_CASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->spi_disp.handle); + spi_dc_writecommand(&spi->bus, ST7789_CASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); - writecommand(spi, ST7789_RASET); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->spi_disp.handle); + spi_dc_writecommand(&spi->bus, ST7789_RASET); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); + spi_device_release_bus(spi->bus.spi_disp.handle); } static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) @@ -467,8 +453,8 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - writecommand(spi, ST7789_RAMWR); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_dc_writecommand(&spi->bus, ST7789_RAMWR); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -483,23 +469,23 @@ static void do_update(Context *ctx, term display_list) spi_transaction_t *trans; // I did a quick measurement, and most of the time is spent waiting for DMA transaction // eg. 23 us spent in draw_x, 188 us spent in spi_device_get_trans_result - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } // NEW CODE void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dmawrite(&spi->spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); destroy_items(items, len); } @@ -510,7 +496,7 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co set_screen_paint_area(spi, x, y, width, height); - writecommand(spi, ST7789_RAMWR); + spi_dc_writecommand(&spi->bus, ST7789_RAMWR); int dest_size = width * height; int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; @@ -519,13 +505,13 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); for (int i = 0; i < chunks; i++) { const uint16_t *data_b = data + 1024 * i; for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); } int last_chunk_size = dest_size - chunks * 1024; if (last_chunk_size) { @@ -533,9 +519,9 @@ static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, co for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&spi->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); + spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); free(tmpbuf); } @@ -599,8 +585,8 @@ static void process_message(Message *message, Context *ctx) static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - writecommand(spi, ST7789_MADCTL); - writedata(spi, ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB); + spi_dc_writecommand(&spi->bus, ST7789_MADCTL); + spi_dc_writedata(&spi->bus, ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB); } } @@ -639,9 +625,9 @@ static void display_init(Context *ctx, term opts) spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&spi->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); bool reset_configured = true; if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global)) { @@ -676,20 +662,20 @@ static void display_init(Context *ctx, term opts) // Reset if (reset_configured) { - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); gpio_set_level(spi->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(spi->bus.spi_disp.handle); } - gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); if (!reset_configured) { - writecommand(spi, ST7789_SWRESET); + spi_dc_writecommand(&spi->bus, ST7789_SWRESET); delay(100); } @@ -711,11 +697,11 @@ static void display_init(Context *ctx, term opts) set_rotation(spi, spi->rotation); if (enable_tft_invon) { - writecommand(spi, ST7789_INVON); + spi_dc_writecommand(&spi->bus, ST7789_INVON); } } - writecommand(spi, ST7789_DISPON); + spi_dc_writecommand(&spi->bus, ST7789_DISPON); delay(120); struct BacklightGPIOConfig backlight_config; @@ -728,209 +714,201 @@ static void display_init(Context *ctx, term opts) static void display_init_alt_gamma_2(struct SPI *spi) { - writecommand(spi, ST7789_SLPOUT); + spi_dc_writecommand(&spi->bus, ST7789_SLPOUT); delay(120); - writecommand(spi, ST7789_NORON); + spi_dc_writecommand(&spi->bus, ST7789_NORON); // - display and color format setting - // - writecommand(spi, ST7789_MADCTL); - writedata(spi, TFT_MAD_COLOR_ORDER); + spi_dc_writecommand(&spi->bus, ST7789_MADCTL); + spi_dc_writedata(&spi->bus, TFT_MAD_COLOR_ORDER); - writecommand(spi, ST7789_COLMOD); - writedata(spi, 0x55); + spi_dc_writecommand(&spi->bus, ST7789_COLMOD); + spi_dc_writedata(&spi->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - writecommand(spi, ST7789_PORCTRL); - writedata(spi, 0x0C); - writedata(spi, 0x0C); - writedata(spi, 0x00); - writedata(spi, 0x33); - writedata(spi, 0x33); + spi_dc_writecommand(&spi->bus, ST7789_PORCTRL); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x33); + spi_dc_writedata(&spi->bus, 0x33); - writecommand(spi, ST7789_GCTRL); - writedata(spi, 0x75); + spi_dc_writecommand(&spi->bus, ST7789_GCTRL); + spi_dc_writedata(&spi->bus, 0x75); // - ST7789V power setting - // - writecommand(spi, ST7789_VCOMS); - writedata(spi, 0x1A); + spi_dc_writecommand(&spi->bus, ST7789_VCOMS); + spi_dc_writedata(&spi->bus, 0x1A); - writecommand(spi, ST7789_LCMCTRL); - writedata(spi, 0x2C); + spi_dc_writecommand(&spi->bus, ST7789_LCMCTRL); + spi_dc_writedata(&spi->bus, 0x2C); - writecommand(spi, ST7789_VDVVRHEN); - writedata(spi, 0x01); + spi_dc_writecommand(&spi->bus, ST7789_VDVVRHEN); + spi_dc_writedata(&spi->bus, 0x01); - writecommand(spi, ST7789_VRHS); - writedata(spi, 0x13); + spi_dc_writecommand(&spi->bus, ST7789_VRHS); + spi_dc_writedata(&spi->bus, 0x13); - writecommand(spi, ST7789_VDVSET); - writedata(spi, 0x20); + spi_dc_writecommand(&spi->bus, ST7789_VDVSET); + spi_dc_writedata(&spi->bus, 0x20); - writecommand(spi, ST7789_FRCTR2); - writedata(spi, 0x0F); + spi_dc_writecommand(&spi->bus, ST7789_FRCTR2); + spi_dc_writedata(&spi->bus, 0x0F); - writecommand(spi, ST7789_PWCTRL1); - writedata(spi, 0xA4); - writedata(spi, 0xA1); + spi_dc_writecommand(&spi->bus, ST7789_PWCTRL1); + spi_dc_writedata(&spi->bus, 0xA4); + spi_dc_writedata(&spi->bus, 0xA1); // - ST7789V gamma setting - // - writecommand(spi, ST7789_PVGAMCTRL); - writedata(spi, 0xD0); - writedata(spi, 0x0D); - writedata(spi, 0x14); - writedata(spi, 0x0D); - writedata(spi, 0x0D); - writedata(spi, 0x09); - writedata(spi, 0x38); - writedata(spi, 0x44); - writedata(spi, 0x4E); - writedata(spi, 0x3A); - writedata(spi, 0x17); - writedata(spi, 0x18); - writedata(spi, 0x2F); - writedata(spi, 0x30); - - writecommand(spi, ST7789_NVGAMCTRL); - writedata(spi, 0xD0); - writedata(spi, 0x09); - writedata(spi, 0x0F); - writedata(spi, 0x08); - writedata(spi, 0x07); - writedata(spi, 0x14); - writedata(spi, 0x37); - writedata(spi, 0x44); - writedata(spi, 0x4D); - writedata(spi, 0x38); - writedata(spi, 0x15); - writedata(spi, 0x16); - writedata(spi, 0x2C); - writedata(spi, 0x3E); - - writecommand(spi, ST7789_CASET); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0xEF); // 239 - - writecommand(spi, ST7789_RASET); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x01); - writedata(spi, 0x3F); // 319 + spi_dc_writecommand(&spi->bus, ST7789_PVGAMCTRL); + spi_dc_writedata(&spi->bus, 0xD0); + spi_dc_writedata(&spi->bus, 0x0D); + spi_dc_writedata(&spi->bus, 0x14); + spi_dc_writedata(&spi->bus, 0x0D); + spi_dc_writedata(&spi->bus, 0x0D); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x38); + spi_dc_writedata(&spi->bus, 0x44); + spi_dc_writedata(&spi->bus, 0x4E); + spi_dc_writedata(&spi->bus, 0x3A); + spi_dc_writedata(&spi->bus, 0x17); + spi_dc_writedata(&spi->bus, 0x18); + spi_dc_writedata(&spi->bus, 0x2F); + spi_dc_writedata(&spi->bus, 0x30); + + spi_dc_writecommand(&spi->bus, ST7789_NVGAMCTRL); + spi_dc_writedata(&spi->bus, 0xD0); + spi_dc_writedata(&spi->bus, 0x09); + spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x14); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x44); + spi_dc_writedata(&spi->bus, 0x4D); + spi_dc_writedata(&spi->bus, 0x38); + spi_dc_writedata(&spi->bus, 0x15); + spi_dc_writedata(&spi->bus, 0x16); + spi_dc_writedata(&spi->bus, 0x2C); + spi_dc_writedata(&spi->bus, 0x3E); + + spi_dc_writecommand(&spi->bus, ST7789_CASET); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0xEF); // 239 + + spi_dc_writecommand(&spi->bus, ST7789_RASET); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0x3F); // 319 } static void display_init_std(struct SPI *spi) { - writecommand(spi, ST7789_SLPOUT); + spi_dc_writecommand(&spi->bus, ST7789_SLPOUT); delay(120); - writecommand(spi, ST7789_NORON); + spi_dc_writecommand(&spi->bus, ST7789_NORON); // - display and color format setting - // - writecommand(spi, ST7789_MADCTL); - writedata(spi, TFT_MAD_COLOR_ORDER); + spi_dc_writecommand(&spi->bus, ST7789_MADCTL); + spi_dc_writedata(&spi->bus, TFT_MAD_COLOR_ORDER); - writecommand(spi, 0xB6); - writedata(spi, 0x0A); - writedata(spi, 0x82); + spi_dc_writecommand(&spi->bus, 0xB6); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x82); - writecommand(spi, ST7789_RAMCTRL); - writedata(spi, 0x00); - writedata(spi, 0xE0); + spi_dc_writecommand(&spi->bus, ST7789_RAMCTRL); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0xE0); - writecommand(spi, ST7789_COLMOD); - writedata(spi, 0x55); + spi_dc_writecommand(&spi->bus, ST7789_COLMOD); + spi_dc_writedata(&spi->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - writecommand(spi, ST7789_PORCTRL); - writedata(spi, 0x0C); - writedata(spi, 0x0C); - writedata(spi, 0x00); - writedata(spi, 0x33); - writedata(spi, 0x33); + spi_dc_writecommand(&spi->bus, ST7789_PORCTRL); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x33); + spi_dc_writedata(&spi->bus, 0x33); - writecommand(spi, ST7789_GCTRL); - writedata(spi, 0x35); + spi_dc_writecommand(&spi->bus, ST7789_GCTRL); + spi_dc_writedata(&spi->bus, 0x35); // - ST7789V power setting - // - writecommand(spi, ST7789_VCOMS); - writedata(spi, 0x28); + spi_dc_writecommand(&spi->bus, ST7789_VCOMS); + spi_dc_writedata(&spi->bus, 0x28); - writecommand(spi, ST7789_LCMCTRL); - writedata(spi, 0x0C); + spi_dc_writecommand(&spi->bus, ST7789_LCMCTRL); + spi_dc_writedata(&spi->bus, 0x0C); - writecommand(spi, ST7789_VDVVRHEN); - writedata(spi, 0x01); - writedata(spi, 0xFF); + spi_dc_writecommand(&spi->bus, ST7789_VDVVRHEN); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xFF); - writecommand(spi, ST7789_VRHS); - writedata(spi, 0x10); + spi_dc_writecommand(&spi->bus, ST7789_VRHS); + spi_dc_writedata(&spi->bus, 0x10); - writecommand(spi, ST7789_VDVSET); - writedata(spi, 0x20); + spi_dc_writecommand(&spi->bus, ST7789_VDVSET); + spi_dc_writedata(&spi->bus, 0x20); - writecommand(spi, ST7789_FRCTR2); - writedata(spi, 0x0F); + spi_dc_writecommand(&spi->bus, ST7789_FRCTR2); + spi_dc_writedata(&spi->bus, 0x0F); - writecommand(spi, ST7789_PWCTRL1); - writedata(spi, 0xA4); - writedata(spi, 0xA1); + spi_dc_writecommand(&spi->bus, ST7789_PWCTRL1); + spi_dc_writedata(&spi->bus, 0xA4); + spi_dc_writedata(&spi->bus, 0xA1); // - ST7789V gamma setting - // - writecommand(spi, ST7789_PVGAMCTRL); - writedata(spi, 0xD0); - writedata(spi, 0x00); - writedata(spi, 0x02); - writedata(spi, 0x07); - writedata(spi, 0x0A); - writedata(spi, 0x28); - writedata(spi, 0x32); - writedata(spi, 0x44); - writedata(spi, 0x42); - writedata(spi, 0x06); - writedata(spi, 0x0E); - writedata(spi, 0x12); - writedata(spi, 0x14); - writedata(spi, 0x17); - - writecommand(spi, ST7789_NVGAMCTRL); - writedata(spi, 0xD0); - writedata(spi, 0x00); - writedata(spi, 0x02); - writedata(spi, 0x07); - writedata(spi, 0x0A); - writedata(spi, 0x28); - writedata(spi, 0x31); - writedata(spi, 0x54); - writedata(spi, 0x47); - writedata(spi, 0x0E); - writedata(spi, 0x1C); - writedata(spi, 0x17); - writedata(spi, 0x1B); - writedata(spi, 0x1E); - - writecommand(spi, ST7789_CASET); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0xEF); // 239 - - writecommand(spi, ST7789_RASET); - writedata(spi, 0x00); - writedata(spi, 0x00); - writedata(spi, 0x01); - writedata(spi, 0x3F); // 319 -} - -static void writecmddata(struct SPI *spi, uint8_t cmd, const uint8_t *data, size_t length) -{ - writecommand(spi, cmd); - for (int i = 0; i < length; i++) { - writedata(spi, data[i]); - } + spi_dc_writecommand(&spi->bus, ST7789_PVGAMCTRL); + spi_dc_writedata(&spi->bus, 0xD0); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x28); + spi_dc_writedata(&spi->bus, 0x32); + spi_dc_writedata(&spi->bus, 0x44); + spi_dc_writedata(&spi->bus, 0x42); + spi_dc_writedata(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x12); + spi_dc_writedata(&spi->bus, 0x14); + spi_dc_writedata(&spi->bus, 0x17); + + spi_dc_writecommand(&spi->bus, ST7789_NVGAMCTRL); + spi_dc_writedata(&spi->bus, 0xD0); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x07); + spi_dc_writedata(&spi->bus, 0x0A); + spi_dc_writedata(&spi->bus, 0x28); + spi_dc_writedata(&spi->bus, 0x31); + spi_dc_writedata(&spi->bus, 0x54); + spi_dc_writedata(&spi->bus, 0x47); + spi_dc_writedata(&spi->bus, 0x0E); + spi_dc_writedata(&spi->bus, 0x1C); + spi_dc_writedata(&spi->bus, 0x17); + spi_dc_writedata(&spi->bus, 0x1B); + spi_dc_writedata(&spi->bus, 0x1E); + + spi_dc_writecommand(&spi->bus, ST7789_CASET); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0xEF); // 239 + + spi_dc_writecommand(&spi->bus, ST7789_RASET); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0x3F); // 319 } static void display_init_using_list(struct SPI *spi, term init_list) @@ -944,7 +922,7 @@ static void display_init_using_list(struct SPI *spi, term init_list) if (term_is_integer(cmd_term) && term_is_binary(data_term)) { avm_int_t cmd = term_to_int(cmd_term); const uint8_t *data = (const uint8_t *) term_binary_data(data_term); - writecmddata(spi, cmd, data, term_binary_size(data_term)); + spi_dc_writecmddata(&spi->bus, cmd, data, term_binary_size(data_term)); } else if ((cmd_term == context_make_atom(spi->ctx, ATOM_STR("\x8", "sleep_ms"))) && term_is_integer(data_term)) { delay(term_to_int(data_term)); From da2c14e373c50e06c548003312166cf06cd05f58 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 17:44:03 +0000 Subject: [PATCH 09/52] dcs_lcd_color: Add shared RGB565 color helpers Extract alpha_blend_rgb565, rgba8888_color_to_rgb565, and related color helpers into a shared inline-header module. Drop the unused struct Screen parameter. Migrate st7789, ili934x, and ili948x. Signed-off-by: Davide Bettio --- dcs_lcd_color.h | 65 ++++++++++++++++++++++++++++++++++++++++ ili934x_display_driver.c | 60 ++++++++----------------------------- ili948x_display_driver.c | 63 ++++++++------------------------------ st7789_display_driver.c | 60 ++++++++----------------------------- 4 files changed, 101 insertions(+), 147 deletions(-) create mode 100644 dcs_lcd_color.h diff --git a/dcs_lcd_color.h b/dcs_lcd_color.h new file mode 100644 index 0000000..cb23e1f --- /dev/null +++ b/dcs_lcd_color.h @@ -0,0 +1,65 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DCS_LCD_COLOR_H_ +#define _DCS_LCD_COLOR_H_ + +#include + +#include + +// This functions is taken from: +// https://stackoverflow.com/questions/18937701/combining-two-16-bits-rgb-colors-with-alpha-blending +static inline uint16_t alpha_blend_rgb565(uint32_t fg, uint32_t bg, uint8_t alpha) +{ + alpha = (alpha + 4) >> 3; + bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111; + fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111; + uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111; + return (uint16_t) ((result >> 16) | result); +} + +static inline uint8_t rgba8888_get_alpha(uint32_t color) +{ + return color & 0xFF; +} + +static inline uint16_t rgba8888_color_to_rgb565(uint32_t color) +{ + uint8_t r = color >> 24; + uint8_t g = (color >> 16) & 0xFF; + uint8_t b = (color >> 8) & 0xFF; + + return (((uint16_t) (r >> 3)) << 11) | (((uint16_t) (g >> 2)) << 5) | ((uint16_t) b >> 3); +} + +static inline uint16_t rgb565_color_to_surface(uint16_t color16) +{ + return (uint16_t) SPI_SWAP_DATA_TX(color16, 16); +} + +static inline uint16_t uint32_color_to_surface(uint32_t color) +{ + uint16_t color16 = rgba8888_color_to_rgb565(color); + + return rgb565_color_to_surface(color16); +} + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 2e75cef..1636560 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -49,6 +49,7 @@ #include #include "backlight_gpio.h" +#include "dcs_lcd_color.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -141,43 +142,6 @@ struct Screen static struct Screen *screen; -// This functions is taken from: -// https://stackoverflow.com/questions/18937701/combining-two-16-bits-rgb-colors-with-alpha-blending -static inline uint16_t alpha_blend_rgb565(uint32_t fg, uint32_t bg, uint8_t alpha) -{ - alpha = (alpha + 4) >> 3; - bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111; - fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111; - uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111; - return (uint16_t)((result >> 16) | result); -} - -static inline uint8_t rgba8888_get_alpha(uint32_t color) -{ - return color & 0xFF; -} - -static inline uint16_t rgba8888_color_to_rgb565(struct Screen *s, uint32_t color) -{ - uint8_t r = color >> 24; - uint8_t g = (color >> 16) & 0xFF; - uint8_t b = (color >> 8) & 0xFF; - - return (((uint16_t)(r >> 3)) << 11) | (((uint16_t)(g >> 2)) << 5) | ((uint16_t) b >> 3); -} - -static inline uint16_t rgb565_color_to_surface(struct Screen *s, uint16_t color16) -{ - return (uint16_t) SPI_SWAP_DATA_TX(color16, 16); -} - -static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) -{ - uint16_t color16 = rgba8888_color_to_rgb565(s, color); - - return rgb565_color_to_surface(s, color16); -} - static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); static void display_init41(struct SPI *spi); @@ -203,7 +167,7 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -225,12 +189,12 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -249,7 +213,7 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -282,12 +246,12 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -303,7 +267,7 @@ static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int width = item->width; - uint16_t color = uint32_color_to_surface(screen, item->brcolor); + uint16_t color = uint32_color_to_surface(item->brcolor); int drawn_pixels = 0; @@ -325,11 +289,11 @@ static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(screen, item->data.text_data.fgcolor); + uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); uint16_t bgcolor; bool visible_bg; if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(screen, item->brcolor); + bgcolor = uint32_color_to_surface(item->brcolor); visible_bg = true; } else { visible_bg = false; diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index fd94f3c..8256e46 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -52,6 +52,7 @@ #include #include "backlight_gpio.h" +#include "dcs_lcd_color.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -142,46 +143,6 @@ struct Screen static struct Screen *screen; -// Alpha blending for RGB565. -static inline uint16_t alpha_blend_rgb565(uint32_t fg, uint32_t bg, uint8_t alpha) -{ - alpha = (alpha + 4) >> 3; - bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111; - fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111; - uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111; - return (uint16_t) ((result >> 16) | result); -} - -static inline uint8_t rgba8888_get_alpha(uint32_t color) -{ - return color & 0xFF; -} - -static inline uint16_t rgba8888_color_to_rgb565(struct Screen *s, uint32_t color) -{ - UNUSED(s); - - uint8_t r = color >> 24; - uint8_t g = (color >> 16) & 0xFF; - uint8_t b = (color >> 8) & 0xFF; - - return (((uint16_t) (r >> 3)) << 11) | (((uint16_t) (g >> 2)) << 5) | ((uint16_t) b >> 3); -} - -static inline uint16_t rgb565_color_to_surface(struct Screen *s, uint16_t color16) -{ - UNUSED(s); - - return (uint16_t) SPI_SWAP_DATA_TX(color16, 16); -} - -static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) -{ - uint16_t color16 = rgba8888_color_to_rgb565(s, color); - - return rgb565_color_to_surface(s, color16); -} - // ILI9488 scanline conversion: RGB565 -> RGB888 bytes. static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) { @@ -228,7 +189,7 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -250,12 +211,12 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -274,7 +235,7 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -307,12 +268,12 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -327,7 +288,7 @@ static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int width = item->width; - uint16_t color = uint32_color_to_surface(screen, item->brcolor); + uint16_t color = uint32_color_to_surface(item->brcolor); int drawn_pixels = 0; @@ -349,11 +310,11 @@ static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(screen, item->data.text_data.fgcolor); + uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); uint16_t bgcolor; bool visible_bg; if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(screen, item->brcolor); + bgcolor = uint32_color_to_surface(item->brcolor); visible_bg = true; } else { visible_bg = false; diff --git a/st7789_display_driver.c b/st7789_display_driver.c index eb8eda0..0e03d2b 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -49,6 +49,7 @@ #include #include "backlight_gpio.h" +#include "dcs_lcd_color.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -137,43 +138,6 @@ struct Screen static struct Screen *screen; -// This functions is taken from: -// https://stackoverflow.com/questions/18937701/combining-two-16-bits-rgb-colors-with-alpha-blending -static inline uint16_t alpha_blend_rgb565(uint32_t fg, uint32_t bg, uint8_t alpha) -{ - alpha = (alpha + 4) >> 3; - bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111; - fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111; - uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111; - return (uint16_t)((result >> 16) | result); -} - -static inline uint8_t rgba8888_get_alpha(uint32_t color) -{ - return color & 0xFF; -} - -static inline uint16_t rgba8888_color_to_rgb565(struct Screen *s, uint32_t color) -{ - uint8_t r = color >> 24; - uint8_t g = (color >> 16) & 0xFF; - uint8_t b = (color >> 8) & 0xFF; - - return (((uint16_t)(r >> 3)) << 11) | (((uint16_t)(g >> 2)) << 5) | ((uint16_t) b >> 3); -} - -static inline uint16_t rgb565_color_to_surface(struct Screen *s, uint16_t color16) -{ - return (uint16_t) SPI_SWAP_DATA_TX(color16, 16); -} - -static inline uint16_t uint32_color_to_surface(struct Screen *s, uint32_t color) -{ - uint16_t color16 = rgba8888_color_to_rgb565(s, color); - - return rgb565_color_to_surface(s, color16); -} - static void display_init(Context *ctx, term opts); static void display_init_alt_gamma_2(struct SPI *spi); static void display_init_std(struct SPI *spi); @@ -203,7 +167,7 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -225,12 +189,12 @@ static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *i uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -249,7 +213,7 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint16_t bgcolor = 0; bool visible_bg; if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(screen, item->brcolor); + bgcolor = rgba8888_color_to_rgb565(item->brcolor); visible_bg = true; } else { visible_bg = false; @@ -282,12 +246,12 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD uint32_t img_pixel = READ_32_UNALIGNED(pixels); uint8_t alpha = rgba8888_get_alpha(img_pixel); if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(screen, img_pixel); + uint16_t color = uint32_color_to_surface(img_pixel); pixmem16[drawn_pixels] = color; } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(screen, img_pixel); + uint16_t color = rgba8888_color_to_rgb565(img_pixel); uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(screen, blended); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); } else { return drawn_pixels; } @@ -303,7 +267,7 @@ static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int width = item->width; - uint16_t color = uint32_color_to_surface(screen, item->brcolor); + uint16_t color = uint32_color_to_surface(item->brcolor); int drawn_pixels = 0; @@ -325,11 +289,11 @@ static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it { int x = item->x; int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(screen, item->data.text_data.fgcolor); + uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); uint16_t bgcolor; bool visible_bg; if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(screen, item->brcolor); + bgcolor = uint32_color_to_surface(item->brcolor); visible_bg = true; } else { visible_bg = false; From 42d80c7da3801a7c4641f1d3f304e8055199cbcb Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 8 Apr 2026 22:30:22 +0000 Subject: [PATCH 10/52] dcs_lcd_screen: Add shared DCSLCDScreen type Unify the per-driver struct Screen into a shared DCSLCDScreen that is the superset of all three: st7789's x_offset/y_offset, ili948x's bytes/bytes_out for RGB888, and the common w/h and pixel buffers. Migrate st7789, ili934x, and ili948x. Signed-off-by: Davide Bettio --- dcs_lcd_screen.h | 41 ++++++++++++++++++++++++++++++++++++++++ ili934x_display_driver.c | 15 +++------------ ili948x_display_driver.c | 18 +++--------------- st7789_display_driver.c | 21 +++++--------------- 4 files changed, 52 insertions(+), 43 deletions(-) create mode 100644 dcs_lcd_screen.h diff --git a/dcs_lcd_screen.h b/dcs_lcd_screen.h new file mode 100644 index 0000000..071c1ed --- /dev/null +++ b/dcs_lcd_screen.h @@ -0,0 +1,41 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DCS_LCD_SCREEN_H_ +#define _DCS_LCD_SCREEN_H_ + +#include + +// Per-display state shared across the DCS LCD scanline rendering pipeline. +struct DCSLCDScreen +{ + int w; + int h; + int16_t x_offset; + int16_t y_offset; + uint16_t *pixels; + uint16_t *pixels_out; + + // ILI9488: 3 bytes/pixel. + uint8_t *bytes; + uint8_t *bytes_out; +}; + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 1636560..0357917 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -50,6 +50,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -130,17 +131,7 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) -// This struct is just for compatibility reasons with the SDL display driver -// so it is possible to easily copy & paste code from there. -struct Screen -{ - int w; - int h; - uint16_t *pixels; - uint16_t *pixels_out; -}; - -static struct Screen *screen; +static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); @@ -564,7 +555,7 @@ Context *ili934x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = malloc(sizeof(struct Screen)); + screen = malloc(sizeof(struct DCSLCDScreen)); // FIXME: hardcoded width and height screen->w = 320; screen->h = 240; diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 8256e46..d88295b 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -53,6 +53,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -128,20 +129,7 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) -// Double-buffered scanline buffers. -struct Screen -{ - int w; - int h; - uint16_t *pixels; - uint16_t *pixels_out; - - // ILI9488: 3 bytes/pixel. - uint8_t *bytes; - uint8_t *bytes_out; -}; - -static struct Screen *screen; +static struct DCSLCDScreen *screen; // ILI9488 scanline conversion: RGB565 -> RGB888 bytes. static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) @@ -651,7 +639,7 @@ Context *ili948x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = malloc(sizeof(struct Screen)); + screen = malloc(sizeof(struct DCSLCDScreen)); struct SPI *spi = malloc(sizeof(struct SPI)); diff --git a/st7789_display_driver.c b/st7789_display_driver.c index 0e03d2b..bb78b88 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -50,6 +50,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" #include "display_message.h" @@ -124,19 +125,7 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) -// This struct is just for compatibility reasons with the SDL display driver -// so it is possible to easily copy & paste code from there. -struct Screen -{ - int w; - int h; - avm_int_t x_offset; - avm_int_t y_offset; - uint16_t *pixels; - uint16_t *pixels_out; -}; - -static struct Screen *screen; +static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); static void display_init_alt_gamma_2(struct SPI *spi); @@ -569,7 +558,7 @@ static void display_init(Context *ctx, term opts) term height_term = interop_kv_get_value_default( opts, ATOM_STR("\x6", "height"), term_from_int(240), ctx->global); - screen = malloc(sizeof(struct Screen)); + screen = malloc(sizeof(struct DCSLCDScreen)); screen->w = term_to_int(width_term); screen->h = term_to_int(height_term); screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); @@ -613,8 +602,8 @@ static void display_init(Context *ctx, term opts) opts, ATOM_STR("\x8", "y_offset"), term_from_int(0), ctx->global); if (term_is_integer(x_off_term) && term_is_integer(y_off_term)) { - screen->x_offset = term_to_int(x_off_term); - screen->y_offset = term_to_int(y_off_term); + screen->x_offset = (int16_t) term_to_int(x_off_term); + screen->y_offset = (int16_t) term_to_int(y_off_term); } else { ok = false; } From bd76e2b7f0591df65eace9dc7d959e5d6f37247b Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Thu, 9 Apr 2026 08:07:36 +0000 Subject: [PATCH 11/52] dcs_lcd_commands: Add shared DCS LCD commands Extract set_paint_area (with offset support from st7789) and draw_buffer (with RGB888 path from ili948x) into a shared module. Consolidate generic MIPI DCS command macros under a DCS_LCD_ prefix. Zero-init DCSLCDScreen allocation for safe access to uninitialized offset fields. Migrate st7789, ili934x, and ili948x. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + dcs_lcd_commands.c | 116 ++++++++++++++++++++++++++++++++ dcs_lcd_commands.h | 57 ++++++++++++++++ ili934x_display_driver.c | 109 +++++------------------------- ili948x_display_driver.c | 139 +++++---------------------------------- st7789_display_driver.c | 122 +++++++--------------------------- 6 files changed, 232 insertions(+), 312 deletions(-) create mode 100644 dcs_lcd_commands.c create mode 100644 dcs_lcd_commands.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e5aa1d9..25469eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ else() endif() idf_component_register(SRCS + "dcs_lcd_commands.c" "display_common.c" "display_driver.c" "display_items.c" diff --git a/dcs_lcd_commands.c b/dcs_lcd_commands.c new file mode 100644 index 0000000..8a45208 --- /dev/null +++ b/dcs_lcd_commands.c @@ -0,0 +1,116 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "dcs_lcd_commands.h" + +#include + +#include + +#include +#include + +void dcs_lcd_set_paint_area(struct SPIDCBus *bus, const struct DCSLCDScreen *screen, + int x, int y, int width, int height) +{ + x += screen->x_offset; + y += screen->y_offset; + + spi_dc_writecommand(bus, DCS_LCD_CASET); + spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); + spi_display_write(&bus->spi_disp, 32, (x << 16) | ((x + width) - 1)); + spi_device_release_bus(bus->spi_disp.handle); + + spi_dc_writecommand(bus, DCS_LCD_PASET); + spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); + spi_display_write(&bus->spi_disp, 32, (y << 16) | ((y + height) - 1)); + spi_device_release_bus(bus->spi_disp.handle); +} + +void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen, + int pixel_bytes, int x, int y, int width, int height, const void *imgdata) +{ + const uint16_t *data = imgdata; + + dcs_lcd_set_paint_area(bus, screen, x, y, width, height); + + spi_dc_writecommand(bus, DCS_LCD_RAMWR); + + int dest_size = width * height; + int chunks = dest_size / 1024; + + spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); + + if (pixel_bytes == 2) { + int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; + uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); + + for (int i = 0; i < chunks; i++) { + const uint16_t *data_b = data + 1024 * i; + for (int j = 0; j < 1024; j++) { + tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); + } + spi_display_dmawrite(&bus->spi_disp, 1024 * sizeof(uint16_t), tmpbuf); + } + + int last_chunk_size = dest_size - chunks * 1024; + if (last_chunk_size) { + const uint16_t *data_b = data + chunks * 1024; + for (int j = 0; j < last_chunk_size; j++) { + tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); + } + spi_display_dmawrite(&bus->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); + } + + free(tmpbuf); + + } else { + // ILI9488: RGB565 -> RGB888 (3 bytes/pixel). + const int chunk_pixels = 512; + uint8_t *tmpbuf = heap_caps_malloc(chunk_pixels * 3, MALLOC_CAP_DMA); + + int i = 0; + while (i < dest_size) { + int n = (dest_size - i > chunk_pixels) ? chunk_pixels : (dest_size - i); + + for (int j = 0; j < n; j++) { + uint16_t px = data[i + j]; + uint8_t r5 = (px >> 11) & 0x1F; + uint8_t g6 = (px >> 5) & 0x3F; + uint8_t b5 = (px >> 0) & 0x1F; + + uint8_t r8 = (r5 << 3) | (r5 >> 2); + uint8_t g8 = (g6 << 2) | (g6 >> 4); + uint8_t b8 = (b5 << 3) | (b5 >> 2); + + tmpbuf[j * 3 + 0] = r8; + tmpbuf[j * 3 + 1] = g8; + tmpbuf[j * 3 + 2] = b8; + } + + spi_display_dmawrite(&bus->spi_disp, n * 3, tmpbuf); + i += n; + } + + free(tmpbuf); + } + + spi_device_release_bus(bus->spi_disp.handle); +} diff --git a/dcs_lcd_commands.h b/dcs_lcd_commands.h new file mode 100644 index 0000000..449f7f3 --- /dev/null +++ b/dcs_lcd_commands.h @@ -0,0 +1,57 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DCS_LCD_COMMANDS_H_ +#define _DCS_LCD_COMMANDS_H_ + +#include "dcs_lcd_screen.h" +#include "spi_dc_driver.h" + +// Generic MIPI DCS command set (subset used by AtomGL DCS LCD drivers). +// Vendor-specific init sequence bytes (ILI9341_FRMCTR1, ST7789_PORCTRL, +// ILI948X_HS_LANES_CTRL, etc.) stay in each driver's header block. +#define DCS_LCD_SWRESET 0x01 +#define DCS_LCD_SLPIN 0x10 +#define DCS_LCD_SLPOUT 0x11 +#define DCS_LCD_NORON 0x13 +#define DCS_LCD_INVOFF 0x20 +#define DCS_LCD_INVON 0x21 +#define DCS_LCD_DISPOFF 0x28 +#define DCS_LCD_DISPON 0x29 +#define DCS_LCD_CASET 0x2A +#define DCS_LCD_PASET 0x2B +#define DCS_LCD_RAMWR 0x2C +#define DCS_LCD_MADCTL 0x36 +#define DCS_LCD_COLMOD 0x3A + +// MADCTL bit positions. +#define DCS_LCD_MAD_MY 0x80 +#define DCS_LCD_MAD_MX 0x40 +#define DCS_LCD_MAD_MV 0x20 +#define DCS_LCD_MAD_ML 0x10 +#define DCS_LCD_MAD_BGR 0x08 + +void dcs_lcd_set_paint_area(struct SPIDCBus *bus, const struct DCSLCDScreen *screen, + int x, int y, int width, int height); + +void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen, + int pixel_bytes, int x, int y, int width, int height, const void *imgdata); + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 0357917..8dd02d4 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -50,6 +50,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_commands.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -64,22 +65,7 @@ #define CHAR_WIDTH 8 -#define ILI9341_SLPIN 0x10 -#define ILI9341_SLPOUT 0x11 -#define ILI9341_PTLON 0x12 -#define ILI9341_NORON 0x13 - -#define ILI9341_INVOFF 0x20 -#define ILI9341_INVON 0x21 #define ILI9341_GAMMASET 0x26 -#define ILI9341_DISPOFF 0x28 -#define ILI9341_DISPON 0x29 - -#define ILI9341_PTLAR 0x30 -#define ILI9341_VSCRDEF 0x33 -#define ILI9341_MADCTL 0x36 -#define ILI9341_VSCRSADD 0x37 -#define ILI9341_PIXFMT 0x3A #define ILI9341_FRMCTR1 0xB1 #define ILI9341_FRMCTR2 0xB2 @@ -98,20 +84,6 @@ #define ILI9341_GMCTRP1 0xE0 #define ILI9341_GMCTRN1 0xE1 -#define TFT_SWRST 0x01 -#define TFT_CASET 0x2A -#define TFT_PASET 0x2B -#define TFT_RAMWR 0x2C - -#define TFT_MADCTL 0x36 -#define TFT_MAD_MY 0x80 -#define TFT_MAD_MX 0x40 -#define TFT_MAD_MV 0x20 -#define TFT_MAD_BGR 0x08 - -#define TFT_INVOFF 0x20 -#define TFT_INVON 0x21 - #include "font_data.h" static const char *TAG = "ili934x_display_driver"; @@ -137,19 +109,6 @@ static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); static void display_init41(struct SPI *spi); -static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) -{ - spi_dc_writecommand(&spi->bus, TFT_CASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); - - spi_dc_writecommand(&spi->bus, TFT_PASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); -} - static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) { int x = item->x; @@ -407,8 +366,8 @@ static void do_update(Context *ctx, term display_list) int screen_height = screen->h; struct SPI *spi = SPI_FROM_CTX(ctx); - set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, TFT_RAMWR); + dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -445,42 +404,6 @@ static void do_update(Context *ctx, term display_list) destroy_items(items, len); } -void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const void *imgdata) -{ - const uint16_t *data = imgdata; - - set_screen_paint_area(spi, x, y, width, height); - - spi_dc_writecommand(&spi->bus, TFT_RAMWR); - - int dest_size = width * height; - int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; - - int chunks = dest_size / 1024; - - uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); - - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - for (int i = 0; i < chunks; i++) { - const uint16_t *data_b = data + 1024 * i; - for (int j = 0; j < 1024; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); - } - int last_chunk_size = dest_size - chunks * 1024; - if (last_chunk_size) { - const uint16_t *data_b = data + chunks * 1024; - for (int j = 0; j < 1024; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); - } - spi_device_release_bus(spi->bus.spi_disp.handle); - - free(tmpbuf); -} - static void process_message(Message *message, Context *ctx) { GenMessage gen_message; @@ -513,7 +436,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - draw_buffer(spi, x, y, width, height, data); + dcs_lcd_draw_buffer(&spi->bus, screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -540,8 +463,8 @@ static void process_message(Message *message, Context *ctx) static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - spi_dc_writecommand(&spi->bus, TFT_MADCTL); - spi_dc_writedata(&spi->bus, TFT_MAD_BGR | TFT_MAD_MY | TFT_MAD_MV); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); + spi_dc_writedata(&spi->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); } } @@ -555,7 +478,7 @@ Context *ili934x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = malloc(sizeof(struct DCSLCDScreen)); + screen = calloc(1, sizeof(struct DCSLCDScreen)); // FIXME: hardcoded width and height screen->w = 320; screen->h = 240; @@ -617,7 +540,7 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_writecommand(&spi->bus, TFT_SWRST); + spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); @@ -627,14 +550,14 @@ static void display_init(Context *ctx, term opts) display_init41(spi); } - spi_dc_writecommand(&spi->bus, ILI9341_SLPOUT); + spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_writecommand(&spi->bus, ILI9341_DISPON); + spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, TFT_INVON); + spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); } set_rotation(spi, spi->rotation); @@ -697,10 +620,10 @@ static void display_init41(struct SPI *spi) spi_dc_writecommand(&spi->bus, ILI9341_VMCTR2); spi_dc_writedata(&spi->bus, 0x86); - spi_dc_writecommand(&spi->bus, ILI9341_MADCTL); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writecommand(&spi->bus, ILI9341_PIXFMT); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x55); spi_dc_writecommand(&spi->bus, ILI9341_FRMCTR1); @@ -775,10 +698,10 @@ static void display_init42c(struct SPI *spi) spi_dc_writedata(&spi->bus, 0x01); spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writecommand(&spi->bus, ILI9341_MADCTL); - spi_dc_writedata(&spi->bus, TFT_MAD_MY | TFT_MAD_MV); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); + spi_dc_writedata(&spi->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); - spi_dc_writecommand(&spi->bus, ILI9341_PIXFMT); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x55); spi_dc_writecommand(&spi->bus, ILI9341_DFUNCTR); diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index d88295b..9a10ebc 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -53,6 +53,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_commands.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -67,27 +68,6 @@ #define CHAR_WIDTH 8 -#define ILI948X_SWRESET 0x01 -#define ILI948X_SLPIN 0x10 -#define ILI948X_SLPOUT 0x11 -#define ILI948X_DISPOFF 0x28 -#define ILI948X_DISPON 0x29 - -#define ILI948X_CASET 0x2A -#define ILI948X_PASET 0x2B -#define ILI948X_RAMWR 0x2C - -#define ILI948X_MADCTL 0x36 -#define ILI948X_MAD_MY 0x80 -#define ILI948X_MAD_MX 0x40 -#define ILI948X_MAD_MV 0x20 -#define ILI948X_MAD_BGR 0x08 - -#define ILI948X_INVOFF 0x20 -#define ILI948X_INVON 0x21 - -#define ILI948X_PIXFMT 0x3A - #define ILI948X_IFMODE 0xB0 #define ILI948X_FRMCTR1 0xB1 #define ILI948X_INVCTR 0xB4 @@ -156,19 +136,6 @@ static void display_init(Context *ctx, term opts); static void display_init9486(struct SPI *spi); static void display_init9488(struct SPI *spi); -static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) -{ - spi_dc_writecommand(&spi->bus, ILI948X_CASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); - - spi_dc_writecommand(&spi->bus, ILI948X_PASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); -} - static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) { int x = item->x; @@ -425,8 +392,8 @@ static void do_update(Context *ctx, term display_list) int screen_height = screen->h; struct SPI *spi = SPI_FROM_CTX(ctx); - set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, ILI948X_RAMWR); + dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -472,76 +439,6 @@ static void do_update(Context *ctx, term display_list) destroy_items(items, len); } -static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const void *imgdata) -{ - const uint16_t *data = imgdata; - - set_screen_paint_area(spi, x, y, width, height); - - spi_dc_writecommand(&spi->bus, ILI948X_RAMWR); - - int dest_size = width * height; - int chunks = dest_size / 1024; - - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - - if (!spi->is_ili9488) { - int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; - uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); - - for (int i = 0; i < chunks; i++) { - const uint16_t *data_b = data + 1024 * i; - for (int j = 0; j < 1024; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, 1024 * sizeof(uint16_t), tmpbuf); - } - - int last_chunk_size = dest_size - chunks * 1024; - if (last_chunk_size) { - const uint16_t *data_b = data + chunks * 1024; - for (int j = 0; j < last_chunk_size; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); - } - - free(tmpbuf); - - } else { - // ILI9488: RGB565 -> RGB888 (3 bytes/pixel). - const int chunk_pixels = 512; - uint8_t *tmpbuf = heap_caps_malloc(chunk_pixels * 3, MALLOC_CAP_DMA); - - int i = 0; - while (i < dest_size) { - int n = (dest_size - i > chunk_pixels) ? chunk_pixels : (dest_size - i); - - for (int j = 0; j < n; j++) { - uint16_t px = data[i + j]; - uint8_t r5 = (px >> 11) & 0x1F; - uint8_t g6 = (px >> 5) & 0x3F; - uint8_t b5 = (px >> 0) & 0x1F; - - uint8_t r8 = (r5 << 3) | (r5 >> 2); - uint8_t g8 = (g6 << 2) | (g6 >> 4); - uint8_t b8 = (b5 << 3) | (b5 >> 2); - - tmpbuf[j * 3 + 0] = r8; - tmpbuf[j * 3 + 1] = g8; - tmpbuf[j * 3 + 2] = b8; - } - - spi_display_dmawrite(&spi->bus.spi_disp, n * 3, tmpbuf); - i += n; - } - - free(tmpbuf); - } - - spi_device_release_bus(spi->bus.spi_disp.handle); -} - static void process_message(Message *message, Context *ctx) { GenMessage gen_message; @@ -574,7 +471,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - draw_buffer(spi, x, y, width, height, data); + dcs_lcd_draw_buffer(&spi->bus, screen, spi->is_ili9488 ? 3 : 2, x, y, width, height, data); // draw_buffer is fire-and-forget. return; @@ -604,28 +501,28 @@ static void set_rotation(struct SPI *spi, int rotation) uint8_t madctl = 0; if (spi->madctl_bgr) { - madctl |= ILI948X_MAD_BGR; + madctl |= DCS_LCD_MAD_BGR; } switch (rotation & 3) { case 0: - madctl |= ILI948X_MAD_MX; + madctl |= DCS_LCD_MAD_MX; break; case 1: - madctl |= ILI948X_MAD_MV; + madctl |= DCS_LCD_MAD_MV; break; case 2: - madctl |= ILI948X_MAD_MY; + madctl |= DCS_LCD_MAD_MY; break; case 3: - madctl |= ILI948X_MAD_MX | ILI948X_MAD_MY | ILI948X_MAD_MV; + madctl |= DCS_LCD_MAD_MX | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV; break; } - spi_dc_writecommand(&spi->bus, ILI948X_MADCTL); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); spi_dc_writedata(&spi->bus, madctl); } @@ -639,7 +536,7 @@ Context *ili948x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = malloc(sizeof(struct DCSLCDScreen)); + screen = calloc(1, sizeof(struct DCSLCDScreen)); struct SPI *spi = malloc(sizeof(struct SPI)); @@ -743,7 +640,7 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_writecommand(&spi->bus, ILI948X_SWRESET); + spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); @@ -753,16 +650,16 @@ static void display_init(Context *ctx, term opts) display_init9486(spi); } - spi_dc_writecommand(&spi->bus, ILI948X_SLPOUT); + spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_writecommand(&spi->bus, ILI948X_DISPON); + spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, ILI948X_INVON); + spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); } else { - spi_dc_writecommand(&spi->bus, ILI948X_INVOFF); + spi_dc_writecommand(&spi->bus, DCS_LCD_INVOFF); } set_rotation(spi, spi->rotation); @@ -780,7 +677,7 @@ static void display_init9486(struct SPI *spi) spi_dc_writecommand(&spi->bus, ILI948X_IFMODE); spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, ILI948X_PIXFMT); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x55); spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR3); @@ -889,7 +786,7 @@ static void display_init9488(struct SPI *spi) spi_dc_writecommand(&spi->bus, ILI948X_IMAGE_FUNCTION); spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, ILI948X_PIXFMT); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x66); spi_dc_writecommand(&spi->bus, ILI948X_PGAMCTRL); diff --git a/st7789_display_driver.c b/st7789_display_driver.c index bb78b88..c7f8298 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -50,6 +50,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" +#include "dcs_lcd_commands.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -65,18 +66,6 @@ #define CHAR_WIDTH 8 -#define ST7789_SWRESET 0x01 -#define ST7789_SLPIN 0x10 -#define ST7789_SLPOUT 0x11 -#define ST7789_NORON 0x13 -#define ST7789_INVON 0x21 -#define ST7789_DISPOFF 0x28 -#define ST7789_DISPON 0x29 -#define ST7789_CASET 0x2A -#define ST7789_RASET 0x2B -#define ST7789_RAMWR 0x2C -#define ST7789_MADCTL 0x36 -#define ST7789_COLMOD 0x3A #define ST7789_RAMCTRL 0xB0 #define ST7789_PORCTRL 0xB2 #define ST7789_GCTRL 0xB7 @@ -90,17 +79,6 @@ #define ST7789_PVGAMCTRL 0xE0 #define ST7789_NVGAMCTRL 0xE1 -// rotation -#define ST7789_MADCTL_MY 0x80 -#define ST7789_MADCTL_MX 0x40 -#define ST7789_MADCTL_MV 0x20 -#define ST7789_MADCTL_ML 0x10 -#define ST7789_MADCTL_RGB 0x00 - -#define TFT_MAD_RGB 0x00 -#define TFT_MAD_BGR 0x08 -#define TFT_MAD_COLOR_ORDER TFT_MAD_RGB - #include "font_data.h" static const char *TAG = "st7789_display_driver"; @@ -132,22 +110,6 @@ static void display_init_alt_gamma_2(struct SPI *spi); static void display_init_std(struct SPI *spi); static void display_init_using_list(struct SPI *spi, term init_list); -static inline void set_screen_paint_area(struct SPI *spi, int x, int y, int width, int height) -{ - x += screen->x_offset; - y += screen->y_offset; - - spi_dc_writecommand(&spi->bus, ST7789_CASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (x << 16) | ((x + width) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); - - spi_dc_writecommand(&spi->bus, ST7789_RASET); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - spi_display_write(&spi->bus.spi_disp, 32, (y << 16) | ((y + height) - 1)); - spi_device_release_bus(spi->bus.spi_disp.handle); -} - static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) { int x = item->x; @@ -405,8 +367,8 @@ static void do_update(Context *ctx, term display_list) int screen_height = screen->h; struct SPI *spi = SPI_FROM_CTX(ctx); - set_screen_paint_area(spi, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, ST7789_RAMWR); + dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -443,42 +405,6 @@ static void do_update(Context *ctx, term display_list) destroy_items(items, len); } -static void draw_buffer(struct SPI *spi, int x, int y, int width, int height, const void *imgdata) -{ - const uint16_t *data = imgdata; - - set_screen_paint_area(spi, x, y, width, height); - - spi_dc_writecommand(&spi->bus, ST7789_RAMWR); - - int dest_size = width * height; - int buf_pixel_size = (dest_size > 1024) ? 1024 : dest_size; - - int chunks = dest_size / 1024; - - uint16_t *tmpbuf = heap_caps_malloc(buf_pixel_size * sizeof(uint16_t), MALLOC_CAP_DMA); - - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - for (int i = 0; i < chunks; i++) { - const uint16_t *data_b = data + 1024 * i; - for (int j = 0; j < 1024; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, buf_pixel_size * sizeof(uint16_t), tmpbuf); - } - int last_chunk_size = dest_size - chunks * 1024; - if (last_chunk_size) { - const uint16_t *data_b = data + chunks * 1024; - for (int j = 0; j < 1024; j++) { - tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); - } - spi_display_dmawrite(&spi->bus.spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); - } - spi_device_release_bus(spi->bus.spi_disp.handle); - - free(tmpbuf); -} - static void process_message(Message *message, Context *ctx) { GenMessage gen_message; @@ -511,7 +437,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - draw_buffer(spi, x, y, width, height, data); + dcs_lcd_draw_buffer(&spi->bus, screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -538,8 +464,8 @@ static void process_message(Message *message, Context *ctx) static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - spi_dc_writecommand(&spi->bus, ST7789_MADCTL); - spi_dc_writedata(&spi->bus, ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); + spi_dc_writedata(&spi->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); } } @@ -558,7 +484,7 @@ static void display_init(Context *ctx, term opts) term height_term = interop_kv_get_value_default( opts, ATOM_STR("\x6", "height"), term_from_int(240), ctx->global); - screen = malloc(sizeof(struct DCSLCDScreen)); + screen = calloc(1, sizeof(struct DCSLCDScreen)); screen->w = term_to_int(width_term); screen->h = term_to_int(height_term); screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); @@ -628,7 +554,7 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); if (!reset_configured) { - spi_dc_writecommand(&spi->bus, ST7789_SWRESET); + spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); delay(100); } @@ -650,11 +576,11 @@ static void display_init(Context *ctx, term opts) set_rotation(spi, spi->rotation); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, ST7789_INVON); + spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); } } - spi_dc_writecommand(&spi->bus, ST7789_DISPON); + spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); delay(120); struct BacklightGPIOConfig backlight_config; @@ -667,16 +593,16 @@ static void display_init(Context *ctx, term opts) static void display_init_alt_gamma_2(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, ST7789_SLPOUT); + spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_writecommand(&spi->bus, ST7789_NORON); + spi_dc_writecommand(&spi->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_writecommand(&spi->bus, ST7789_MADCTL); - spi_dc_writedata(&spi->bus, TFT_MAD_COLOR_ORDER); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); + spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, ST7789_COLMOD); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x55); delay(10); @@ -747,13 +673,13 @@ static void display_init_alt_gamma_2(struct SPI *spi) spi_dc_writedata(&spi->bus, 0x2C); spi_dc_writedata(&spi->bus, 0x3E); - spi_dc_writecommand(&spi->bus, ST7789_CASET); + spi_dc_writecommand(&spi->bus, DCS_LCD_CASET); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0xEF); // 239 - spi_dc_writecommand(&spi->bus, ST7789_RASET); + spi_dc_writecommand(&spi->bus, DCS_LCD_PASET); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x01); @@ -762,14 +688,14 @@ static void display_init_alt_gamma_2(struct SPI *spi) static void display_init_std(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, ST7789_SLPOUT); + spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_writecommand(&spi->bus, ST7789_NORON); + spi_dc_writecommand(&spi->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_writecommand(&spi->bus, ST7789_MADCTL); - spi_dc_writedata(&spi->bus, TFT_MAD_COLOR_ORDER); + spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); + spi_dc_writedata(&spi->bus, 0x00); spi_dc_writecommand(&spi->bus, 0xB6); spi_dc_writedata(&spi->bus, 0x0A); @@ -779,7 +705,7 @@ static void display_init_std(struct SPI *spi) spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0xE0); - spi_dc_writecommand(&spi->bus, ST7789_COLMOD); + spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); spi_dc_writedata(&spi->bus, 0x55); delay(10); @@ -851,13 +777,13 @@ static void display_init_std(struct SPI *spi) spi_dc_writedata(&spi->bus, 0x1B); spi_dc_writedata(&spi->bus, 0x1E); - spi_dc_writecommand(&spi->bus, ST7789_CASET); + spi_dc_writecommand(&spi->bus, DCS_LCD_CASET); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0xEF); // 239 - spi_dc_writecommand(&spi->bus, ST7789_RASET); + spi_dc_writecommand(&spi->bus, DCS_LCD_PASET); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x00); spi_dc_writedata(&spi->bus, 0x01); From 931febbdcc85bf5f9383782aad62d4eed2d1a4be Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Thu, 9 Apr 2026 13:19:28 +0000 Subject: [PATCH 12/52] dcs_lcd_draw: Add shared DCS LCD draw pipeline Extract the per-pixel scanline renderer (draw_x dispatcher and five draw_*_x helpers) into a shared module. All functions take const struct DCSLCDScreen *screen as their first parameter. Migrate st7789, ili934x, and ili948x. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + dcs_lcd_draw.c | 278 +++++++++++++++++++++++++++++++++++++++ dcs_lcd_draw.h | 45 +++++++ ili934x_display_driver.c | 245 +--------------------------------- ili948x_display_driver.c | 244 +--------------------------------- st7789_display_driver.c | 245 +--------------------------------- 6 files changed, 330 insertions(+), 728 deletions(-) create mode 100644 dcs_lcd_draw.c create mode 100644 dcs_lcd_draw.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 25469eb..f7b4c21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ endif() idf_component_register(SRCS "dcs_lcd_commands.c" + "dcs_lcd_draw.c" "display_common.c" "display_driver.c" "display_items.c" diff --git a/dcs_lcd_draw.c b/dcs_lcd_draw.c new file mode 100644 index 0000000..3b5dc6a --- /dev/null +++ b/dcs_lcd_draw.c @@ -0,0 +1,278 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "dcs_lcd_draw.h" + +#include +#include +#include + +#include + +#include "dcs_lcd_color.h" +#include "font_data.h" + +#define CHAR_WIDTH 8 + +int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos) +{ + int line_len = screen->w - xpos; + + for (int i = 0; i < count; i++) { + BaseDisplayItem *item = &items[i]; + + if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { + int len_to_item = item->x - xpos; + line_len = (line_len > len_to_item) ? len_to_item : line_len; + } + } + + return line_len; +} + +int dcs_lcd_draw_image_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + uint16_t bgcolor = 0; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor = rgba8888_color_to_rgb565(item->brcolor); + visible_bg = true; + } else { + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data.pix; + + int drawn_pixels = 0; + + uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); + uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + uint8_t alpha = rgba8888_get_alpha(img_pixel); + if (alpha == 0xFF) { + uint16_t color = uint32_color_to_surface(img_pixel); + pixmem16[drawn_pixels] = color; + } else if (visible_bg) { + uint16_t color = rgba8888_color_to_rgb565(img_pixel); + uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels++; + } + + return drawn_pixels; +} + +int dcs_lcd_draw_rect_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int width = item->width; + uint16_t color = uint32_color_to_surface(item->brcolor); + + int drawn_pixels = 0; + + uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + pixmem16[drawn_pixels] = color; + drawn_pixels++; + } + + return drawn_pixels; +} + +int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); + uint16_t bgcolor; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor = uint32_color_to_surface(item->brcolor); + visible_bg = true; + } else { + visible_bg = false; + } + + char *text = (char *) item->data.text_data.text; + + int width = item->width; + + int drawn_pixels = 0; + + uint16_t *pixmem32 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + int char_index = j / CHAR_WIDTH; + char c = text[char_index]; + unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; + + unsigned char row = glyph[ypos - y]; + + bool opaque; + int k = j % CHAR_WIDTH; + if (row & (1 << (7 - k))) { + opaque = true; + } else { + opaque = false; + } + + if (opaque) { + pixmem32[drawn_pixels] = fgcolor; + } else if (visible_bg) { + pixmem32[drawn_pixels] = bgcolor; + } else { + return drawn_pixels; + } + drawn_pixels++; + } + + return drawn_pixels; +} + +int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + uint16_t bgcolor = 0; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor = rgba8888_color_to_rgb565(item->brcolor); + visible_bg = true; + } else { + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data_with_size.pix; + + int drawn_pixels = 0; + + int y_scale = item->y_scale; + int x_scale = item->x_scale; + int img_width = item->data.image_data_with_size.width; + + int source_x = item->source_x; + int source_y = item->source_y; + + uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); + uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); + + if (source_x + (width / x_scale) > img_width) { + width = (img_width - source_x) * x_scale; + } + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + uint8_t alpha = rgba8888_get_alpha(img_pixel); + if (alpha == 0xFF) { + uint16_t color = uint32_color_to_surface(img_pixel); + pixmem16[drawn_pixels] = color; + } else if (visible_bg) { + uint16_t color = rgba8888_color_to_rgb565(img_pixel); + uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); + pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); + } else { + return drawn_pixels; + } + drawn_pixels++; + // TODO: optimize here + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + } + + return drawn_pixels; +} + +int dcs_lcd_draw_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, BaseDisplayItem *items, int items_count) +{ + bool below = false; + + for (int i = 0; i < items_count; i++) { + BaseDisplayItem *item = &items[i]; + if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { + continue; + } + + int max_line_len = below ? 1 : dcs_lcd_find_max_line_len(screen, items, i, xpos, ypos); + + int drawn_pixels = 0; + switch (items[i].primitive) { + case Image: + drawn_pixels = dcs_lcd_draw_image_x(screen, xpos, ypos, max_line_len, item); + break; + + case Rect: + drawn_pixels = dcs_lcd_draw_rect_x(screen, xpos, ypos, max_line_len, item); + break; + + case ScaledCroppedImage: + drawn_pixels = dcs_lcd_draw_scaled_cropped_img_x(screen, xpos, ypos, max_line_len, item); + break; + + case Text: + drawn_pixels = dcs_lcd_draw_text_x(screen, xpos, ypos, max_line_len, item); + break; + default: { + fprintf(stderr, "unexpected display list command.\n"); + } + } + + if (drawn_pixels != 0) { + return drawn_pixels; + } + + below = true; + } + + return 1; +} diff --git a/dcs_lcd_draw.h b/dcs_lcd_draw.h new file mode 100644 index 0000000..0f773fe --- /dev/null +++ b/dcs_lcd_draw.h @@ -0,0 +1,45 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2020-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _DCS_LCD_DRAW_H_ +#define _DCS_LCD_DRAW_H_ + +#include "dcs_lcd_screen.h" +#include "display_items.h" + +int dcs_lcd_draw_image_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item); + +int dcs_lcd_draw_rect_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item); + +int dcs_lcd_draw_text_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item); + +int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, int max_line_len, BaseDisplayItem *item); + +int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos); + +int dcs_lcd_draw_x(const struct DCSLCDScreen *screen, + int xpos, int ypos, BaseDisplayItem *items, int items_count); + +#endif diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 8dd02d4..a6bb684 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -51,6 +51,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" #include "dcs_lcd_commands.h" +#include "dcs_lcd_draw.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -63,8 +64,6 @@ #define SPI_CLOCK_HZ 27000000 #define SPI_MODE 0 -#define CHAR_WIDTH 8 - #define ILI9341_GAMMASET 0x26 #define ILI9341_FRMCTR1 0xB1 @@ -109,246 +108,6 @@ static void display_init(Context *ctx, term opts); static void display_init42c(struct SPI *spi); static void display_init41(struct SPI *spi); -static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - // TODO: optimize here - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - uint16_t color = uint32_color_to_surface(item->brcolor); - - int drawn_pixels = 0; - - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - pixmem16[drawn_pixels] = color; - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); - uint16_t bgcolor; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - uint16_t *pixmem32 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - pixmem32[drawn_pixels] = fgcolor; - } else if (visible_bg) { - pixmem32[drawn_pixels] = bgcolor; - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} - -static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int ypos) -{ - int line_len = screen->w - xpos; - - for (int i = 0; i < count; i++) { - BaseDisplayItem *item = &items[i]; - - if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { - int len_to_item = item->x - xpos; - line_len = (line_len > len_to_item) ? len_to_item : line_len; - } - } - - return line_len; -} - -static int draw_x(int xpos, int ypos, BaseDisplayItem *items, int items_count) -{ - bool below = false; - - for (int i = 0; i < items_count; i++) { - BaseDisplayItem *item = &items[i]; - if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { - continue; - } - - int max_line_len = below ? 1 : find_max_line_len(items, i, xpos, ypos); - - int drawn_pixels = 0; - switch (items[i].primitive) { - case Image: - drawn_pixels = draw_image_x(xpos, ypos, max_line_len, item); - break; - - case Rect: - drawn_pixels = draw_rect_x(xpos, ypos, max_line_len, item); - break; - - case ScaledCroppedImage: - drawn_pixels = draw_scaled_cropped_img_x(xpos, ypos, max_line_len, item); - break; - - case Text: - drawn_pixels = draw_text_x(xpos, ypos, max_line_len, item); - break; - default: { - fprintf(stderr, "unexpected display list command.\n"); - } - } - - if (drawn_pixels != 0) { - return drawn_pixels; - } - - below = true; - } - - return 1; -} - static void do_update(Context *ctx, term display_list) { int proper; @@ -375,7 +134,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); xpos += drawn_pixels; } diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 9a10ebc..96c0645 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -54,6 +54,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" #include "dcs_lcd_commands.h" +#include "dcs_lcd_draw.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -66,8 +67,6 @@ #define SPI_CLOCK_HZ 27000000 #define SPI_MODE 0 -#define CHAR_WIDTH 8 - #define ILI948X_IFMODE 0xB0 #define ILI948X_FRMCTR1 0xB1 #define ILI948X_INVCTR 0xB4 @@ -136,245 +135,6 @@ static void display_init(Context *ctx, term opts); static void display_init9486(struct SPI *spi); static void display_init9488(struct SPI *spi); -static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - uint16_t color = uint32_color_to_surface(item->brcolor); - - int drawn_pixels = 0; - - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - pixmem16[drawn_pixels] = color; - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); - uint16_t bgcolor; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - uint16_t *pixmem32 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - pixmem32[drawn_pixels] = fgcolor; - } else if (visible_bg) { - pixmem32[drawn_pixels] = bgcolor; - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} - -static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int ypos) -{ - int line_len = screen->w - xpos; - - for (int i = 0; i < count; i++) { - BaseDisplayItem *item = &items[i]; - - if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { - int len_to_item = item->x - xpos; - line_len = (line_len > len_to_item) ? len_to_item : line_len; - } - } - - return line_len; -} - -static int draw_x(int xpos, int ypos, BaseDisplayItem *items, int items_count) -{ - bool below = false; - - for (int i = 0; i < items_count; i++) { - BaseDisplayItem *item = &items[i]; - if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { - continue; - } - - int max_line_len = below ? 1 : find_max_line_len(items, i, xpos, ypos); - - int drawn_pixels = 0; - switch (items[i].primitive) { - case Image: - drawn_pixels = draw_image_x(xpos, ypos, max_line_len, item); - break; - - case Rect: - drawn_pixels = draw_rect_x(xpos, ypos, max_line_len, item); - break; - - case ScaledCroppedImage: - drawn_pixels = draw_scaled_cropped_img_x(xpos, ypos, max_line_len, item); - break; - - case Text: - drawn_pixels = draw_text_x(xpos, ypos, max_line_len, item); - break; - default: - fprintf(stderr, "unexpected display list command.\n"); - break; - } - - if (drawn_pixels != 0) { - return drawn_pixels; - } - - below = true; - } - - return 1; -} - static void do_update(Context *ctx, term display_list) { int proper; @@ -401,7 +161,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); xpos += drawn_pixels; } diff --git a/st7789_display_driver.c b/st7789_display_driver.c index c7f8298..c819b1c 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -51,6 +51,7 @@ #include "backlight_gpio.h" #include "dcs_lcd_color.h" #include "dcs_lcd_commands.h" +#include "dcs_lcd_draw.h" #include "dcs_lcd_screen.h" #include "display_common.h" #include "display_items.h" @@ -64,8 +65,6 @@ #define SPI_CLOCK_HZ 40000000 #define SPI_MODE 0 -#define CHAR_WIDTH 8 - #define ST7789_RAMCTRL 0xB0 #define ST7789_PORCTRL 0xB2 #define ST7789_GCTRL 0xB7 @@ -110,246 +109,6 @@ static void display_init_alt_gamma_2(struct SPI *spi); static void display_init_std(struct SPI *spi); static void display_init_using_list(struct SPI *spi, term init_list); -static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - uint16_t bgcolor = 0; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = rgba8888_color_to_rgb565(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - uint8_t alpha = rgba8888_get_alpha(img_pixel); - if (alpha == 0xFF) { - uint16_t color = uint32_color_to_surface(img_pixel); - pixmem16[drawn_pixels] = color; - } else if (visible_bg) { - uint16_t color = rgba8888_color_to_rgb565(img_pixel); - uint16_t blended = alpha_blend_rgb565(color, bgcolor, alpha); - pixmem16[drawn_pixels] = rgb565_color_to_surface(blended); - } else { - return drawn_pixels; - } - drawn_pixels++; - // TODO: optimize here - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - uint16_t color = uint32_color_to_surface(item->brcolor); - - int drawn_pixels = 0; - - uint16_t *pixmem16 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - pixmem16[drawn_pixels] = color; - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - uint16_t fgcolor = uint32_color_to_surface(item->data.text_data.fgcolor); - uint16_t bgcolor; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor = uint32_color_to_surface(item->brcolor); - visible_bg = true; - } else { - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - uint16_t *pixmem32 = (uint16_t *) (((uint8_t *) screen->pixels) + xpos * sizeof(uint16_t)); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - pixmem32[drawn_pixels] = fgcolor; - } else if (visible_bg) { - pixmem32[drawn_pixels] = bgcolor; - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} - -static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int ypos) -{ - int line_len = screen->w - xpos; - - for (int i = 0; i < count; i++) { - BaseDisplayItem *item = &items[i]; - - if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { - int len_to_item = item->x - xpos; - line_len = (line_len > len_to_item) ? len_to_item : line_len; - } - } - - return line_len; -} - -static int draw_x(int xpos, int ypos, BaseDisplayItem *items, int items_count) -{ - bool below = false; - - for (int i = 0; i < items_count; i++) { - BaseDisplayItem *item = &items[i]; - if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { - continue; - } - - int max_line_len = below ? 1 : find_max_line_len(items, i, xpos, ypos); - - int drawn_pixels = 0; - switch (items[i].primitive) { - case Image: - drawn_pixels = draw_image_x(xpos, ypos, max_line_len, item); - break; - - case Rect: - drawn_pixels = draw_rect_x(xpos, ypos, max_line_len, item); - break; - - case ScaledCroppedImage: - drawn_pixels = draw_scaled_cropped_img_x(xpos, ypos, max_line_len, item); - break; - - case Text: - drawn_pixels = draw_text_x(xpos, ypos, max_line_len, item); - break; - default: { - fprintf(stderr, "unexpected display list command.\n"); - } - } - - if (drawn_pixels != 0) { - return drawn_pixels; - } - - below = true; - } - - return 1; -} - static void do_update(Context *ctx, term display_list) { int proper; @@ -376,7 +135,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); xpos += drawn_pixels; } From 9ed1a407182326c03c13a849a2a99b6691bc89b6 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 08:05:26 +0000 Subject: [PATCH 13/52] gdep073e01: Add 7.3" 7-color e-paper driver Add driver for the Good Display GDEP073E01 7.3-inch ACeP panel (800x480, 7-color e-ink). Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + display_driver.c | 3 + gdep073e01_display_driver.c | 775 ++++++++++++++++++++++++++++++++++++ 3 files changed, 779 insertions(+) create mode 100644 gdep073e01_display_driver.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f7b4c21..365e8b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ idf_component_register(SRCS "display_message.c" "display_task.c" "5in65_acep_7c_display_driver.c" + "gdep073e01_display_driver.c" "font_data.c" "ili934x_display_driver.c" "ili948x_display_driver.c" diff --git a/display_driver.c b/display_driver.c index d229984..1efebd5 100644 --- a/display_driver.c +++ b/display_driver.c @@ -30,6 +30,7 @@ static const char *TAG = "display_driver"; Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts); +Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts); Context *ili934x_display_create_port(GlobalContext *global, term opts); Context *ili948x_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); @@ -55,6 +56,8 @@ Context *display_create_port(GlobalContext *global, term opts) Context *ctx = NULL; if (!strcmp(compat_string, "waveshare,5in65-acep-7c")) { ctx = acep_5in65_7c_display_driver_create_port(global, opts); + } else if (!strcmp(compat_string, "gooddisplay,gdep073e01")) { + ctx = gdep073e01_display_driver_create_port(global, opts); } else if (!strcmp(compat_string, "sharp,memory-lcd")) { ctx = memory_lcd_display_create_port(global, opts); } else if (!strcmp(compat_string, "ilitek,ili9341")) { diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c new file mode 100644 index 0000000..7e98c22 --- /dev/null +++ b/gdep073e01_display_driver.c @@ -0,0 +1,775 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2025 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define DISPLAY_WIDTH 800 +#define DISPLAY_HEIGHT 480 + +#include "display_items.h" +#include "display_message.h" +#include "display_task.h" +#include "display_common.h" +#include "draw_common.h" +#include "font_data.h" +#include "image_helpers.h" +#include "spi_display.h" + +#define PSR 0x00 +#define PWRR 0x01 +#define POF 0x02 +#define POFS 0x03 +#define PON 0x04 +#define BTST1 0x05 +#define BTST2 0x06 +#define DSLP 0x07 +#define BTST3 0x08 +#define DTM 0x10 +#define DRF 0x12 +#define PLL 0x30 +#define CDI 0x50 +#define TCON 0x60 +#define TRES 0x61 +#define REV 0x70 +#define VDCS 0x82 +#define T_VDCS 0x84 +#define PWS 0xE3 + +#define CHAR_WIDTH 8 + +#define REPORT_UNEXPECTED_MSGS 0 +#define CHECK_OVERFLOW 1 +#define SELF_TEST 0 + +static const char *TAG = "gdep073e01"; + +static void clear_screen(Context *ctx, int color); + +struct SPI +{ + struct SPIDisplay spi_disp; + + int busy_gpio; + int dc_gpio; + int reset_gpio; + + Context *ctx; + + int count_to_refresh; + uint64_t last_refresh; + + struct DisplayTaskArgs display_args; +}; + +#define SPI_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) + +static inline float square(float p) +{ + return p * p; +} + +static uint8_t dither_acep7(int x, int y, uint8_t r, uint8_t g, uint8_t b) +{ + const uint8_t m[4][4] = { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } + }; + + // following r parameters have been found using standard deviation + // that gives a decent result + int r1 = r + roundf(92.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + int g1 = g + roundf(85.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + int b1 = b + roundf(65.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + + // values found by trial and error + // they try to get closer to real colors than pure saturated RGB colors + uint8_t colors[7][3] = { + { 0x00, 0x00, 0x00 }, + { 0xFF, 0xFF, 0xFF }, + { 0xFF, 0xFF, 0x00 }, + { 0xFF, 0x00, 0x00 }, + { 0x10, 0x10, 0x10 }, + { 0x00, 0x00, 0xFF }, + { 0x00, 0xFF, 0x00 } + }; + + float min = INT_MAX; + int min_index = 0; + + for (int i = 0; i < 7; i++) { + int r2 = colors[i][0]; + int g2 = colors[i][1]; + int b2 = colors[i][2]; + +#ifdef NO_WEIGHTS + float d = square((r2 - r1)) + square((g2 - g1)) + square((b2 - b1)); +#else + float d = square((r2 - r1) * 0.30) + square((g2 - g1) * 0.59) + square((b2 - b1) * 0.11); +#endif + + if (d < min) { + min = d; + min_index = i; + } + } + + return min_index; +} + +static void writecommand(struct SPI *spi, uint8_t cmd) +{ + gpio_set_level(spi->dc_gpio, 0); + spi_display_write(&spi->spi_disp, 8, cmd); + gpio_set_level(spi->dc_gpio, 1); +} + +static void writedata(struct SPI *spi, uint8_t data) +{ + gpio_set_level(spi->dc_gpio, 1); + spi_display_write(&spi->spi_disp, 8, data); +} + +static void writedatan(struct SPI *spi, uint8_t *data, size_t len) +{ + for (size_t i = 0; i < len; i++) { + writedata(spi, data[i]); + } +} + +static void display_reset(struct SPI *spi) +{ + gpio_set_level(spi->reset_gpio, 0); + vTaskDelay(100); + gpio_set_level(spi->reset_gpio, 1); +} + +static void wait_busy_level(struct SPI *spi, int level) +{ + while (gpio_get_level(spi->busy_gpio) != level) { + vTaskDelay(100); + } +} + +static inline void draw_pixel_x(uint8_t *line_buf, int xpos, uint8_t c) +{ +#if CHECK_OVERFLOW + if (xpos > DISPLAY_WIDTH) { + fprintf(stderr, "buf ovf!\n"); + return; + } +#endif + + if ((xpos & 1) == 0) { + line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF) | (c << 4); + } else { + line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF0) | c; + } +} + +static int draw_image_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data.pix; + + int drawn_pixels = 0; + + uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels++; + } + + return drawn_pixels; +} + +static int draw_scaled_cropped_img_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data_with_size.pix; + + int drawn_pixels = 0; + + int y_scale = item->y_scale; + int x_scale = item->x_scale; + int img_width = item->data.image_data_with_size.width; + + int source_x = item->source_x; + int source_y = item->source_y; + + uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); + + if (source_x + (width / x_scale) > img_width) { + width = (img_width - source_x) * x_scale; + } + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + } + + return drawn_pixels; +} + +static int draw_rect_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int width = item->width; + + uint8_t r = (item->brcolor >> 24) & 0xFF; + uint8_t g = (item->brcolor >> 16) & 0xFF; + uint8_t b = (item->brcolor >> 8) & 0xFF; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + drawn_pixels++; + } + + return drawn_pixels; +} + +static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + bool visible_bg; + + int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; + int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; + int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + char *text = (char *) item->data.text_data.text; + + int width = item->width; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + int char_index = j / CHAR_WIDTH; + char c = text[char_index]; + unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; + + unsigned char row = glyph[ypos - y]; + + bool opaque; + int k = j % CHAR_WIDTH; + if (row & (1 << (7 - k))) { + opaque = true; + } else { + opaque = false; + } + + if (opaque) { + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + draw_pixel_x(line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + } + + return drawn_pixels; +} + +static void wait_some_time(Context *ctx) +{ + struct SPI *spi = SPI_FROM_CTX(ctx); + + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t now = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); + uint64_t delta = now - spi->last_refresh; + if (delta < 2000) { + // Wait 2 seconds before allowing a new refresh + // this is not on datasheets, but without this the screen will not update. + vTaskDelay((2000 - delta) / portTICK_PERIOD_MS); + } +} + +static void update_last_refresh_ts(Context *ctx) +{ + struct SPI *spi = SPI_FROM_CTX(ctx); + + struct timeval tv; + gettimeofday(&tv, NULL); + spi->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); +} + +static void maybe_refresh(Context *ctx) +{ +#if 0 + struct SPI *spi = SPI_FROM_CTX(ctx); + + spi->count_to_refresh--; + if (spi->count_to_refresh <= 0) { + // 7 is the special "clear screen color" + clear_screen(ctx, 7); + update_last_refresh_ts(ctx); + spi->count_to_refresh = 5; + } +#endif +} + +static void do_update(Context *ctx, term display_list) +{ + maybe_refresh(ctx); + // it looks like we need to wait some time + // let's use 2 seconds + wait_some_time(ctx); + + int proper; + int len = term_list_length(display_list, &proper); + + BaseDisplayItem *items = malloc(sizeof(BaseDisplayItem) * len); + + term t = display_list; + for (int i = 0; i < len; i++) { + init_item(&items[i], term_get_list_head(t), ctx); + t = term_get_list_tail(t); + } + + int screen_width = DISPLAY_WIDTH; + int screen_height = DISPLAY_HEIGHT; + struct SPI *spi = SPI_FROM_CTX(ctx); + + struct SPIDisplay *spi_disp = &spi->spi_disp; + spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); + +#if 0 + // resolution command + writecommand(spi, 0x61); + writedata(spi, 0x02); + writedata(spi, 0x58); + writedata(spi, 0x01); + writedata(spi, 0xC0); +#endif + + // update command + writecommand(spi, 0x10); + + gpio_set_level(spi->dc_gpio, 1); + + uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); + memset(buf, 0x11, DISPLAY_WIDTH / 2); + + bool transaction_in_progress = false; + + for (int ypos = 0; ypos < screen_height; ypos++) { + if (transaction_in_progress) { + spi_transaction_t *trans = NULL; + spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + } + + int xpos = 0; + while (xpos < screen_width) { + int drawn_pixels = draw_x(buf, xpos, ypos, items, len); + xpos += drawn_pixels; + } + + spi_display_dmawrite(&spi->spi_disp, DISPLAY_WIDTH / 2, buf); + transaction_in_progress = true; + } + + if (transaction_in_progress) { + spi_transaction_t *trans = NULL; + spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + } + + free(buf); + + // not sure if we should add 0x11, which is end of data command or not + + // power on command + writecommand(spi, 0x04); + wait_busy_level(spi, 1); + + // refresh command + writecommand(spi, 0x12); + uint8_t refresh_data[] = {0x00}; + writedatan(spi, refresh_data, sizeof(refresh_data)); + wait_busy_level(spi, 1); + + // power off command + writecommand(spi, 0x02); + spi_device_release_bus(spi_disp->handle); + wait_busy_level(spi, 1); + + destroy_items(items, len); + + update_last_refresh_ts(ctx); +} + +static void process_message(Message *message, Context *ctx) +{ + GenMessage gen_message; + if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { + fprintf(stderr, "Received invalid message."); + AVM_ABORT(); + } + + term req = gen_message.req; + if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { + AVM_ABORT(); + } + term cmd = term_get_tuple_element(req, 0); + + if (cmd == context_make_atom(ctx, "\x6" + "update")) { + + term display_list = term_get_tuple_element(req, 1); + do_update(ctx, display_list); + + } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" "load_image")) { + handle_load_image(req, gen_message.ref, gen_message.pid, ctx); + return; + + } else { +#if REPORT_UNEXPECTED_MSGS + fprintf(stderr, "display: "); + term_display(stderr, req, ctx); + fprintf(stderr, "\n"); +#endif + } + + BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); + term return_tuple = term_alloc_tuple(2, &heap); + term_put_tuple_element(return_tuple, 0, gen_message.ref); + term_put_tuple_element(return_tuple, 1, OK_ATOM); + + send_message(gen_message.pid, return_tuple, ctx->global); + END_WITH_STACK_HEAP(heap, ctx->global); +} + +static void clear_screen(Context *ctx, int color) +{ + struct SPI *spi = SPI_FROM_CTX(ctx); + + uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); + + struct SPIDisplay *spi_disp = &spi->spi_disp; + spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); + +#if 0 + writecommand(spi, 0x61); + writedata(spi, 0x02); + writedata(spi, 0x58); + writedata(spi, 0x01); + writedata(spi, 0xC0); +#endif + writecommand(spi, 0x10); + + gpio_set_level(spi->dc_gpio, 1); + + bool transaction_in_progress = false; + + for (int i = 0; i < DISPLAY_HEIGHT; i++) { + if (transaction_in_progress) { + spi_transaction_t *trans = NULL; + spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + } + + // let's ensure a memset otherwise we might generate odd artifacts + memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); + spi_display_dmawrite(spi_disp, DISPLAY_WIDTH / 2, buf); + transaction_in_progress = true; + } + + if (transaction_in_progress) { + spi_transaction_t *trans = NULL; + spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + } + + writecommand(spi, 0x04); + wait_busy_level(spi, 1); + writecommand(spi, 0x12); + uint8_t refresh_data[] = {0x00}; + writedatan(spi, refresh_data, sizeof(refresh_data)); + wait_busy_level(spi, 1); + writecommand(spi, 0x02); + spi_device_release_bus(spi_disp->handle); + wait_busy_level(spi, 1); +} + +static void display_spi_init(Context *ctx, term opts) +{ + struct SPI *spi = malloc(sizeof(struct SPI)); + // TODO check here + + struct SPIDisplayConfig spi_config; + spi_display_init_config(&spi_config); + spi_config.clock_speed_hz = 4000000; + spi_display_parse_config(&spi_config, opts, ctx->global); + spi_display_init(&spi->spi_disp, &spi_config); + + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &spi->busy_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); + if (UNLIKELY(!ok)) { + ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); + return; + } + + gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(spi->reset_gpio, 1); + gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(spi->dc_gpio, GPIO_PULLUP_ENABLE); + gpio_set_direction(spi->busy_gpio, GPIO_MODE_INPUT); + gpio_set_pull_mode(spi->busy_gpio, GPIO_PULLUP_ENABLE); + gpio_set_level(spi->dc_gpio, 0); + + esp_err_t ret = spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + ESP_ERROR_CHECK(ret); + display_reset(spi); + + wait_busy_level(spi, 1); + + writecommand(spi, 0xAA); + uint8_t psr1_data[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18}; + writedatan(spi, psr1_data, sizeof(psr1_data)); + wait_busy_level(spi, 1); + + writecommand(spi, PWRR); + uint8_t pwrr_data[] = {0x3F}; + writedatan(spi, pwrr_data, sizeof(pwrr_data)); + wait_busy_level(spi, 1); + + writecommand(spi, PSR); + uint8_t psr_data[] = {0x5F, 0x69}; + writedatan(spi, psr_data, sizeof(psr_data)); + wait_busy_level(spi, 1); + + writecommand(spi, POFS); + uint8_t pofs_data[] = {0x00, 0x54, 0x00, 0x44}; + writedatan(spi, pofs_data, sizeof(pofs_data)); + wait_busy_level(spi, 1); + + writecommand(spi, BTST1); + uint8_t btst1_data[] = {0x40, 0x1F, 0x1F, 0x2C}; + writedatan(spi, btst1_data, sizeof(btst1_data)); + wait_busy_level(spi, 1); + + writecommand(spi, BTST2); + uint8_t btst2_data[] = {0x6F, 0x1F, 0x17, 0x49}; + writedatan(spi, btst2_data, sizeof(btst2_data)); + wait_busy_level(spi, 1); + + writecommand(spi, BTST3); + uint8_t btst3_data[] = {0x6F, 0x1F, 0x1F, 0x22}; + writedatan(spi, btst3_data, sizeof(btst3_data)); + wait_busy_level(spi, 1); + + writecommand(spi, PLL); + uint8_t pll_data[] = {0x00}; + writedatan(spi, pll_data, sizeof(pll_data)); + wait_busy_level(spi, 1); + + writecommand(spi, CDI); + uint8_t cdi_data[] = {0x3F}; + writedatan(spi, cdi_data, sizeof(cdi_data)); + wait_busy_level(spi, 1); + + writecommand(spi, TCON); + uint8_t tcon_data[] = {0x02, 0x00}; + writedatan(spi, tcon_data, sizeof(tcon_data)); + wait_busy_level(spi, 1); + + writecommand(spi, TRES); + uint8_t tres_data[] = {0x03, 0x20, 0x01, 0xe0}; + writedatan(spi, tres_data, sizeof(tres_data)); + wait_busy_level(spi, 1); + + writecommand(spi, T_VDCS); + uint8_t vdcs_data[] = {0x01}; + writedatan(spi, vdcs_data, sizeof(vdcs_data)); + wait_busy_level(spi, 1); + + writecommand(spi, PWS); + uint8_t pws_data[] = {0x2F}; + writedatan(spi, pws_data, sizeof(pws_data)); + wait_busy_level(spi, 1); + + // PON + writecommand(spi, 0x04); + wait_busy_level(spi, 1); + + spi_device_release_bus(spi->spi_disp.handle); + + ctx->platform_data = &spi->display_args; + + spi->ctx = ctx; + + update_last_refresh_ts(ctx); + spi->count_to_refresh = 0; + +#if SELF_TEST + for (int i = 0; i < 8; i++) { + fprintf(stderr, "color: %i\n", i); + clear_screen(ctx, i); + vTaskDelay(30000 / portTICK_PERIOD_MS); + } + clear_screen(ctx, 1); + + while (1) + ; +#else + spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + spi->display_args.process_message_fn = process_message; + spi->display_args.ctx = ctx; + xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); +#endif +} + +Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts) +{ + Context *ctx = context_new(global); + ctx->native_handler = display_driver_consume_mailbox; + display_spi_init(ctx, opts); + return ctx; +} From 369876a7a774fd6856b53c3d9d9386703e13bf18 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 08:07:13 +0000 Subject: [PATCH 14/52] Add shared e-paper modules and migrate drivers Add epaper_screen (shared type), epaper_color (palette- parameterized ACeP 7-color dithering), and epaper_draw (per-pixel draw pipeline). Extend spi_dc_driver with spi_dc_writedatan for variable-width writes. Migrate 5in65_acep_7c and gdep073e01 drivers. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 462 ++++++-------------------------- CMakeLists.txt | 2 + display_driver.c | 2 +- epaper_color.c | 89 +++++++ epaper_color.h | 32 +++ epaper_draw.c | 343 ++++++++++++++++++++++++ epaper_draw.h | 53 ++++ epaper_screen.h | 34 +++ gdep073e01_display_driver.c | 467 ++++++--------------------------- spi_dc_driver.c | 7 + spi_dc_driver.h | 1 + 11 files changed, 724 insertions(+), 768 deletions(-) create mode 100644 epaper_color.c create mode 100644 epaper_color.h create mode 100644 epaper_draw.c create mode 100644 epaper_draw.h create mode 100644 epaper_screen.h diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index c5c90ba..8ff6523 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -44,13 +44,13 @@ #include "display_message.h" #include "display_task.h" #include "display_common.h" -#include "draw_common.h" -#include "font_data.h" +#include "epaper_color.h" +#include "epaper_draw.h" +#include "epaper_screen.h" #include "image_helpers.h" +#include "spi_dc_driver.h" #include "spi_display.h" -#define CHAR_WIDTH 8 - #define REPORT_UNEXPECTED_MSGS 0 #define CHECK_OVERFLOW 1 #define SELF_TEST 0 @@ -61,10 +61,9 @@ static void clear_screen(Context *ctx, int color); struct SPI { - struct SPIDisplay spi_disp; + struct SPIDCBus bus; int busy_gpio; - int dc_gpio; int reset_gpio; Context *ctx; @@ -78,72 +77,7 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) -static inline float square(float p) -{ - return p * p; -} - -static uint8_t dither_acep7(int x, int y, uint8_t r, uint8_t g, uint8_t b) -{ - const uint8_t m[4][4] = { - { 0, 8, 2, 10 }, - { 12, 4, 14, 6 }, - { 3, 11, 1, 9 }, - { 15, 7, 13, 5 } - }; - - // following r parameters have been found using standard deviation - // that gives a decent result - int r1 = r + roundf(92.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - int g1 = g + roundf(85.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - int b1 = b + roundf(65.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - - // values found by trial and error - // they try to get closer to real colors than pure saturated RGB colors - uint8_t colors[7][3] = { - { 0x00, 0x00, 0x00 }, - { 0xFF, 0xFF, 0xFF }, - { 0x00, 0xFF, 0x00 }, - { 0x00, 0x00, 0xFF }, - { 0xFF, 0x00, 0x00 }, - { 0xFF, 0xFF, 0x00 }, - { 0xFF, 0x80, 0x00 } - }; - - float min = INT_MAX; - int min_index = 0; - - for (int i = 0; i < 7; i++) { - int r2 = colors[i][0]; - int g2 = colors[i][1]; - int b2 = colors[i][2]; - -#ifdef NO_WEIGHTS - float d = square((r2 - r1)) + square((g2 - g1)) + square((b2 - b1)); -#else - float d = square((r2 - r1) * 0.30) + square((g2 - g1) * 0.59) + square((b2 - b1) * 0.11); -#endif - - if (d < min) { - min = d; - min_index = i; - } - } - - return min_index; -} - -static void writecommand(struct SPI *spi, uint8_t cmd) -{ - gpio_set_level(spi->dc_gpio, 0); - spi_display_write(&spi->spi_disp, 8, cmd); -} - -static void writedata(struct SPI *spi, uint8_t data) -{ - gpio_set_level(spi->dc_gpio, 1); - spi_display_write(&spi->spi_disp, 8, data); -} +static struct EpaperScreen *screen; static void display_reset(struct SPI *spi) { @@ -159,236 +93,6 @@ static void wait_busy_level(struct SPI *spi, int level) } } -static inline void draw_pixel_x(uint8_t *line_buf, int xpos, uint8_t c) -{ -#if CHECK_OVERFLOW - if (xpos > DISPLAY_WIDTH) { - fprintf(stderr, "buf ovf!\n"); - return; - } -#endif - - if ((xpos & 1) == 0) { - line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF) | (c << 4); - } else { - line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF0) | c; - } -} - -static int draw_image_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - - uint8_t r = (item->brcolor >> 24) & 0xFF; - uint8_t g = (item->brcolor >> 16) & 0xFF; - uint8_t b = (item->brcolor >> 8) & 0xFF; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - bool visible_bg; - - int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; - int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; - int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} void wait_some_time(Context *ctx) { @@ -449,60 +153,58 @@ static void do_update(Context *ctx, term display_list) int screen_height = DISPLAY_HEIGHT; struct SPI *spi = SPI_FROM_CTX(ctx); - struct SPIDisplay *spi_disp = &spi->spi_disp; - spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); - // resolution command - writecommand(spi, 0x61); - writedata(spi, 0x02); - writedata(spi, 0x58); - writedata(spi, 0x01); - writedata(spi, 0xC0); + spi_dc_writecommand(&spi->bus, 0x61); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x58); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xC0); // update command - writecommand(spi, 0x10); - - gpio_set_level(spi->dc_gpio, 1); + spi_dc_writecommand(&spi->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); bool transaction_in_progress = false; + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + for (int ypos = 0; ypos < screen_height; ypos++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(buf, xpos, ypos, items, len); + int drawn_pixels = epaper_draw_x(screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } - spi_display_dmawrite(&spi->spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } + spi_device_release_bus(spi->bus.spi_disp.handle); + // not sure if we should add 0x11, which is end of data command or not // power on command - writecommand(spi, 0x04); + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); // refresh command - writecommand(spi, 0x12); + spi_dc_writecommand(&spi->bus, 0x12); wait_busy_level(spi, 1); // power off command - writecommand(spi, 0x02); - spi_device_release_bus(spi_disp->handle); + spi_dc_writecommand(&spi->bus, 0x02); wait_busy_level(spi, 0); destroy_items(items, len); @@ -557,42 +259,41 @@ static void clear_screen(Context *ctx, int color) uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - struct SPIDisplay *spi_disp = &spi->spi_disp; - spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); - writecommand(spi, 0x61); - writedata(spi, 0x02); - writedata(spi, 0x58); - writedata(spi, 0x01); - writedata(spi, 0xC0); - writecommand(spi, 0x10); - - gpio_set_level(spi->dc_gpio, 1); + spi_dc_writecommand(&spi->bus, 0x61); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x58); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xC0); + spi_dc_writecommand(&spi->bus, 0x10); bool transaction_in_progress = false; + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + for (int i = 0; i < DISPLAY_HEIGHT; i++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dmawrite(spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } - writecommand(spi, 0x04); + spi_device_release_bus(spi->bus.spi_disp.handle); + + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); - writecommand(spi, 0x12); + spi_dc_writecommand(&spi->bus, 0x12); wait_busy_level(spi, 1); - writecommand(spi, 0x02); - spi_device_release_bus(spi_disp->handle); + spi_dc_writecommand(&spi->bus, 0x02); wait_busy_level(spi, 0); } @@ -605,10 +306,10 @@ static void display_spi_init(Context *ctx, term opts) spi_display_init_config(&spi_config); spi_config.clock_speed_hz = 1000000; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&spi->bus.spi_disp, &spi_config); bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &spi->busy_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); if (UNLIKELY(!ok)) { ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); @@ -617,60 +318,63 @@ static void display_spi_init(Context *ctx, term opts) gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); gpio_set_level(spi->reset_gpio, 1); - gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); - gpio_set_pull_mode(spi->dc_gpio, GPIO_PULLUP_ENABLE); + gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(spi->bus.dc_gpio, GPIO_PULLUP_ENABLE); gpio_set_direction(spi->busy_gpio, GPIO_MODE_INPUT); gpio_set_pull_mode(spi->busy_gpio, GPIO_PULLUP_ENABLE); - gpio_set_level(spi->dc_gpio, 0); + gpio_set_level(spi->bus.dc_gpio, 0); - esp_err_t ret = spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - ESP_ERROR_CHECK(ret); display_reset(spi); wait_busy_level(spi, 1); - writecommand(spi, 0x00); - writedata(spi, 0xEF); - writedata(spi, 0x08); - writecommand(spi, 0x01); - writedata(spi, 0x37); - writedata(spi, 0x00); - writedata(spi, 0x23); //datasheet says: 0x05 - writedata(spi, 0x23); //datasheet says: 0x05 - writecommand(spi, 0x03); - writedata(spi, 0x00); - writecommand(spi, 0x06); - writedata(spi, 0xC7); - writedata(spi, 0xC7); - writedata(spi, 0x1D); - writecommand(spi, 0x30); - writedata(spi, 0x3C); - writecommand(spi, 0x40); //datasheet says: 0x41 - writedata(spi, 0x00); - writecommand(spi, 0x50); - writedata(spi, 0x3F); //datasheet says: 0x37 - writecommand(spi, 0x60); - writedata(spi, 0x22); - writecommand(spi, 0x61); - writedata(spi, 0x02); - writedata(spi, 0x58); - writedata(spi, 0x01); - writedata(spi, 0xC0); - writecommand(spi, 0xE3); - writedata(spi, 0xAA); - writecommand(spi, 0x82); - writedata(spi, 0x80); + spi_dc_writecommand(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0xEF); + spi_dc_writedata(&spi->bus, 0x08); + spi_dc_writecommand(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0x37); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writedata(&spi->bus, 0x23); //datasheet says: 0x05 + spi_dc_writedata(&spi->bus, 0x23); //datasheet says: 0x05 + spi_dc_writecommand(&spi->bus, 0x03); + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writecommand(&spi->bus, 0x06); + spi_dc_writedata(&spi->bus, 0xC7); + spi_dc_writedata(&spi->bus, 0xC7); + spi_dc_writedata(&spi->bus, 0x1D); + spi_dc_writecommand(&spi->bus, 0x30); + spi_dc_writedata(&spi->bus, 0x3C); + spi_dc_writecommand(&spi->bus, 0x40); //datasheet says: 0x41 + spi_dc_writedata(&spi->bus, 0x00); + spi_dc_writecommand(&spi->bus, 0x50); + spi_dc_writedata(&spi->bus, 0x3F); //datasheet says: 0x37 + spi_dc_writecommand(&spi->bus, 0x60); + spi_dc_writedata(&spi->bus, 0x22); + spi_dc_writecommand(&spi->bus, 0x61); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x58); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xC0); + spi_dc_writecommand(&spi->bus, 0xE3); + spi_dc_writedata(&spi->bus, 0xAA); + spi_dc_writecommand(&spi->bus, 0x82); + spi_dc_writedata(&spi->bus, 0x80); vTaskDelay(10); - writecommand(spi, 0x50); - writedata(spi, 0x37); - spi_device_release_bus(spi->spi_disp.handle); + spi_dc_writecommand(&spi->bus, 0x50); + spi_dc_writedata(&spi->bus, 0x37); ctx->platform_data = &spi->display_args; spi->ctx = ctx; + screen = calloc(1, sizeof(struct EpaperScreen)); + screen->w = DISPLAY_WIDTH; + screen->h = DISPLAY_HEIGHT; + screen->palette = epaper_acep_palette; + screen->palette_size = 7; + update_last_refresh_ts(ctx); spi->count_to_refresh = 0; diff --git a/CMakeLists.txt b/CMakeLists.txt index 365e8b7..1f81b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ idf_component_register(SRCS "display_items.c" "display_message.c" "display_task.c" + "epaper_color.c" + "epaper_draw.c" "5in65_acep_7c_display_driver.c" "gdep073e01_display_driver.c" "font_data.c" diff --git a/display_driver.c b/display_driver.c index 1efebd5..7b2936a 100644 --- a/display_driver.c +++ b/display_driver.c @@ -56,7 +56,7 @@ Context *display_create_port(GlobalContext *global, term opts) Context *ctx = NULL; if (!strcmp(compat_string, "waveshare,5in65-acep-7c")) { ctx = acep_5in65_7c_display_driver_create_port(global, opts); - } else if (!strcmp(compat_string, "gooddisplay,gdep073e01")) { + } else if (!strcmp(compat_string, "good-display/gdep073e01")) { ctx = gdep073e01_display_driver_create_port(global, opts); } else if (!strcmp(compat_string, "sharp,memory-lcd")) { ctx = memory_lcd_display_create_port(global, opts); diff --git a/epaper_color.c b/epaper_color.c new file mode 100644 index 0000000..c680545 --- /dev/null +++ b/epaper_color.c @@ -0,0 +1,89 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "epaper_color.h" + +#include +#include +#include + +const uint8_t epaper_acep_palette[7][3] = { + { 0x00, 0x00, 0x00 }, + { 0xFF, 0xFF, 0xFF }, + { 0x00, 0xFF, 0x00 }, + { 0x00, 0x00, 0xFF }, + { 0xFF, 0x00, 0x00 }, + { 0xFF, 0xFF, 0x00 }, + { 0xFF, 0x80, 0x00 } +}; + +const uint8_t epaper_gdep073e01_palette[7][3] = { + { 0x00, 0x00, 0x00 }, + { 0xFF, 0xFF, 0xFF }, + { 0xFF, 0xFF, 0x00 }, + { 0xFF, 0x00, 0x00 }, + { 0x10, 0x10, 0x10 }, + { 0x00, 0x00, 0xFF }, + { 0x00, 0xFF, 0x00 } +}; + +static inline float square(float p) +{ + return p * p; +} + +uint8_t epaper_dither_acep7(int x, int y, uint8_t r, uint8_t g, uint8_t b, + const uint8_t palette[][3], int palette_size) +{ + const uint8_t m[4][4] = { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } + }; + + // following r parameters have been found using standard deviation + // that gives a decent result + int r1 = r + roundf(92.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + int g1 = g + roundf(85.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + int b1 = b + roundf(65.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + + float min = INT_MAX; + int min_index = 0; + + for (int i = 0; i < palette_size; i++) { + int r2 = palette[i][0]; + int g2 = palette[i][1]; + int b2 = palette[i][2]; + +#ifdef NO_WEIGHTS + float d = square((r2 - r1)) + square((g2 - g1)) + square((b2 - b1)); +#else + float d = square((r2 - r1) * 0.30) + square((g2 - g1) * 0.59) + square((b2 - b1) * 0.11); +#endif + + if (d < min) { + min = d; + min_index = i; + } + } + + return min_index; +} diff --git a/epaper_color.h b/epaper_color.h new file mode 100644 index 0000000..57c24a9 --- /dev/null +++ b/epaper_color.h @@ -0,0 +1,32 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _EPAPER_COLOR_H_ +#define _EPAPER_COLOR_H_ + +#include + +extern const uint8_t epaper_acep_palette[7][3]; +extern const uint8_t epaper_gdep073e01_palette[7][3]; + +uint8_t epaper_dither_acep7(int x, int y, uint8_t r, uint8_t g, uint8_t b, + const uint8_t palette[][3], int palette_size); + +#endif diff --git a/epaper_draw.c b/epaper_draw.c new file mode 100644 index 0000000..25cdaae --- /dev/null +++ b/epaper_draw.c @@ -0,0 +1,343 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "epaper_draw.h" + +#include +#include +#include + +#include + +#include "display_items.h" +#include "epaper_color.h" +#include "epaper_screen.h" +#include "font_data.h" + +#define CHAR_WIDTH 8 + +void epaper_draw_pixel_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, uint8_t c) +{ + if (xpos > screen->w) { + fprintf(stderr, "buf ovf!\n"); + return; + } + + if ((xpos & 1) == 0) { + line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF) | (c << 4); + } else { + line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF0) | c; + } +} + +int epaper_find_max_line_len(const struct EpaperScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos) +{ + int line_len = screen->w - xpos; + + for (int i = 0; i < count; i++) { + BaseDisplayItem *item = &items[i]; + + if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { + int len_to_item = item->x - xpos; + line_len = (line_len > len_to_item) ? len_to_item : line_len; + } + } + + return line_len; +} + +int epaper_draw_image_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data.pix; + + int drawn_pixels = 0; + + uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, r, g, b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels++; + } + + return drawn_pixels; +} + +int epaper_draw_rect_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int width = item->width; + + uint8_t r = (item->brcolor >> 24) & 0xFF; + uint8_t g = (item->brcolor >> 16) & 0xFF; + uint8_t b = (item->brcolor >> 8) & 0xFF; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, r, g, b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + drawn_pixels++; + } + + return drawn_pixels; +} + +int epaper_draw_text_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + bool visible_bg; + + int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; + int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; + int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + char *text = (char *) item->data.text_data.text; + + int width = item->width; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + int char_index = j / CHAR_WIDTH; + char c = text[char_index]; + unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; + + unsigned char row = glyph[ypos - y]; + + bool opaque; + int k = j % CHAR_WIDTH; + if (row & (1 << (7 - k))) { + opaque = true; + } else { + opaque = false; + } + + if (opaque) { + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + } + + return drawn_pixels; +} + +int epaper_draw_scaled_cropped_img_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data_with_size.pix; + + int drawn_pixels = 0; + + int y_scale = item->y_scale; + int x_scale = item->x_scale; + int img_width = item->data.image_data_with_size.width; + + int source_x = item->source_x; + int source_y = item->source_y; + + uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); + + if (source_x + (width / x_scale) > img_width) { + width = (img_width - source_x) * x_scale; + } + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, r, g, b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + uint8_t c = epaper_dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b, + screen->palette, screen->palette_size); + epaper_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + } + + return drawn_pixels; +} + +int epaper_draw_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, + BaseDisplayItem *items, int items_count) +{ + bool below = false; + + for (int i = 0; i < items_count; i++) { + BaseDisplayItem *item = &items[i]; + if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { + continue; + } + + int max_line_len = below ? 1 : epaper_find_max_line_len(screen, items, i, xpos, ypos); + + int drawn_pixels = 0; + switch (items[i].primitive) { + case Image: + drawn_pixels = epaper_draw_image_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case ScaledCroppedImage: + drawn_pixels = epaper_draw_scaled_cropped_img_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case Rect: + drawn_pixels = epaper_draw_rect_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case Text: + drawn_pixels = epaper_draw_text_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + default: { + fprintf(stderr, "unexpected display list command.\n"); + } + } + + if (drawn_pixels != 0) { + return drawn_pixels; + } + + below = true; + } + + return 1; +} diff --git a/epaper_draw.h b/epaper_draw.h new file mode 100644 index 0000000..3e914cb --- /dev/null +++ b/epaper_draw.h @@ -0,0 +1,53 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _EPAPER_DRAW_H_ +#define _EPAPER_DRAW_H_ + +#include "epaper_screen.h" +#include "display_items.h" + +void epaper_draw_pixel_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, uint8_t c); + +int epaper_draw_image_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int epaper_draw_rect_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int epaper_draw_text_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int epaper_draw_scaled_cropped_img_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int epaper_find_max_line_len(const struct EpaperScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos); + +int epaper_draw_x(const struct EpaperScreen *screen, + uint8_t *line_buf, int xpos, int ypos, + BaseDisplayItem *items, int items_count); + +#endif diff --git a/epaper_screen.h b/epaper_screen.h new file mode 100644 index 0000000..8e8d87f --- /dev/null +++ b/epaper_screen.h @@ -0,0 +1,34 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _EPAPER_SCREEN_H_ +#define _EPAPER_SCREEN_H_ + +#include + +struct EpaperScreen +{ + int w; + int h; + const uint8_t (*palette)[3]; + int palette_size; +}; + +#endif diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index 7e98c22..b805564 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -44,9 +43,11 @@ #include "display_message.h" #include "display_task.h" #include "display_common.h" -#include "draw_common.h" -#include "font_data.h" +#include "epaper_color.h" +#include "epaper_draw.h" +#include "epaper_screen.h" #include "image_helpers.h" +#include "spi_dc_driver.h" #include "spi_display.h" #define PSR 0x00 @@ -69,10 +70,7 @@ #define T_VDCS 0x84 #define PWS 0xE3 -#define CHAR_WIDTH 8 - #define REPORT_UNEXPECTED_MSGS 0 -#define CHECK_OVERFLOW 1 #define SELF_TEST 0 static const char *TAG = "gdep073e01"; @@ -81,10 +79,9 @@ static void clear_screen(Context *ctx, int color); struct SPI { - struct SPIDisplay spi_disp; + struct SPIDCBus bus; int busy_gpio; - int dc_gpio; int reset_gpio; Context *ctx; @@ -98,80 +95,7 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) -static inline float square(float p) -{ - return p * p; -} - -static uint8_t dither_acep7(int x, int y, uint8_t r, uint8_t g, uint8_t b) -{ - const uint8_t m[4][4] = { - { 0, 8, 2, 10 }, - { 12, 4, 14, 6 }, - { 3, 11, 1, 9 }, - { 15, 7, 13, 5 } - }; - - // following r parameters have been found using standard deviation - // that gives a decent result - int r1 = r + roundf(92.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - int g1 = g + roundf(85.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - int b1 = b + roundf(65.0 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - - // values found by trial and error - // they try to get closer to real colors than pure saturated RGB colors - uint8_t colors[7][3] = { - { 0x00, 0x00, 0x00 }, - { 0xFF, 0xFF, 0xFF }, - { 0xFF, 0xFF, 0x00 }, - { 0xFF, 0x00, 0x00 }, - { 0x10, 0x10, 0x10 }, - { 0x00, 0x00, 0xFF }, - { 0x00, 0xFF, 0x00 } - }; - - float min = INT_MAX; - int min_index = 0; - - for (int i = 0; i < 7; i++) { - int r2 = colors[i][0]; - int g2 = colors[i][1]; - int b2 = colors[i][2]; - -#ifdef NO_WEIGHTS - float d = square((r2 - r1)) + square((g2 - g1)) + square((b2 - b1)); -#else - float d = square((r2 - r1) * 0.30) + square((g2 - g1) * 0.59) + square((b2 - b1) * 0.11); -#endif - - if (d < min) { - min = d; - min_index = i; - } - } - - return min_index; -} - -static void writecommand(struct SPI *spi, uint8_t cmd) -{ - gpio_set_level(spi->dc_gpio, 0); - spi_display_write(&spi->spi_disp, 8, cmd); - gpio_set_level(spi->dc_gpio, 1); -} - -static void writedata(struct SPI *spi, uint8_t data) -{ - gpio_set_level(spi->dc_gpio, 1); - spi_display_write(&spi->spi_disp, 8, data); -} - -static void writedatan(struct SPI *spi, uint8_t *data, size_t len) -{ - for (size_t i = 0; i < len; i++) { - writedata(spi, data[i]); - } -} +static struct EpaperScreen *screen; static void display_reset(struct SPI *spi) { @@ -187,237 +111,6 @@ static void wait_busy_level(struct SPI *spi, int level) } } -static inline void draw_pixel_x(uint8_t *line_buf, int xpos, uint8_t c) -{ -#if CHECK_OVERFLOW - if (xpos > DISPLAY_WIDTH) { - fprintf(stderr, "buf ovf!\n"); - return; - } -#endif - - if ((xpos & 1) == 0) { - line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF) | (c << 4); - } else { - line_buf[xpos / 2] = (line_buf[xpos / 2] & 0xF0) | c; - } -} - -static int draw_image_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - - uint8_t r = (item->brcolor >> 24) & 0xFF; - uint8_t g = (item->brcolor >> 16) & 0xFF; - uint8_t b = (item->brcolor >> 8) & 0xFF; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - bool visible_bg; - - int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; - int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; - int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = dither_acep7(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} - static void wait_some_time(Context *ctx) { struct SPI *spi = SPI_FROM_CTX(ctx); @@ -479,66 +172,64 @@ static void do_update(Context *ctx, term display_list) int screen_height = DISPLAY_HEIGHT; struct SPI *spi = SPI_FROM_CTX(ctx); - struct SPIDisplay *spi_disp = &spi->spi_disp; - spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); - #if 0 // resolution command - writecommand(spi, 0x61); - writedata(spi, 0x02); - writedata(spi, 0x58); - writedata(spi, 0x01); - writedata(spi, 0xC0); + spi_dc_writecommand(&spi->bus, 0x61); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x58); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xC0); #endif // update command - writecommand(spi, 0x10); - - gpio_set_level(spi->dc_gpio, 1); + spi_dc_writecommand(&spi->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); bool transaction_in_progress = false; + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + for (int ypos = 0; ypos < screen_height; ypos++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(buf, xpos, ypos, items, len); + int drawn_pixels = epaper_draw_x(screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } - spi_display_dmawrite(&spi->spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } + spi_device_release_bus(spi->bus.spi_disp.handle); + free(buf); // not sure if we should add 0x11, which is end of data command or not // power on command - writecommand(spi, 0x04); + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); // refresh command - writecommand(spi, 0x12); + spi_dc_writecommand(&spi->bus, 0x12); uint8_t refresh_data[] = {0x00}; - writedatan(spi, refresh_data, sizeof(refresh_data)); + spi_dc_writedatan(&spi->bus, refresh_data, sizeof(refresh_data)); wait_busy_level(spi, 1); // power off command - writecommand(spi, 0x02); - spi_device_release_bus(spi_disp->handle); + spi_dc_writecommand(&spi->bus, 0x02); wait_busy_level(spi, 1); destroy_items(items, len); @@ -593,47 +284,45 @@ static void clear_screen(Context *ctx, int color) uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - struct SPIDisplay *spi_disp = &spi->spi_disp; - spi_device_acquire_bus(spi_disp->handle, portMAX_DELAY); - #if 0 - writecommand(spi, 0x61); - writedata(spi, 0x02); - writedata(spi, 0x58); - writedata(spi, 0x01); - writedata(spi, 0xC0); + spi_dc_writecommand(&spi->bus, 0x61); + spi_dc_writedata(&spi->bus, 0x02); + spi_dc_writedata(&spi->bus, 0x58); + spi_dc_writedata(&spi->bus, 0x01); + spi_dc_writedata(&spi->bus, 0xC0); #endif - writecommand(spi, 0x10); - - gpio_set_level(spi->dc_gpio, 1); + spi_dc_writecommand(&spi->bus, 0x10); bool transaction_in_progress = false; + spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + for (int i = 0; i < DISPLAY_HEIGHT; i++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dmawrite(spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); } - writecommand(spi, 0x04); + spi_device_release_bus(spi->bus.spi_disp.handle); + + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); - writecommand(spi, 0x12); + spi_dc_writecommand(&spi->bus, 0x12); uint8_t refresh_data[] = {0x00}; - writedatan(spi, refresh_data, sizeof(refresh_data)); + spi_dc_writedatan(&spi->bus, refresh_data, sizeof(refresh_data)); wait_busy_level(spi, 1); - writecommand(spi, 0x02); - spi_device_release_bus(spi_disp->handle); + spi_dc_writecommand(&spi->bus, 0x02); wait_busy_level(spi, 1); } @@ -646,10 +335,10 @@ static void display_spi_init(Context *ctx, term opts) spi_display_init_config(&spi_config); spi_config.clock_speed_hz = 4000000; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&spi->bus.spi_disp, &spi_config); bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &spi->busy_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); if (UNLIKELY(!ok)) { ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); @@ -658,93 +347,95 @@ static void display_spi_init(Context *ctx, term opts) gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); gpio_set_level(spi->reset_gpio, 1); - gpio_set_direction(spi->dc_gpio, GPIO_MODE_OUTPUT); - gpio_set_pull_mode(spi->dc_gpio, GPIO_PULLUP_ENABLE); + gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(spi->bus.dc_gpio, GPIO_PULLUP_ENABLE); gpio_set_direction(spi->busy_gpio, GPIO_MODE_INPUT); gpio_set_pull_mode(spi->busy_gpio, GPIO_PULLUP_ENABLE); - gpio_set_level(spi->dc_gpio, 0); + gpio_set_level(spi->bus.dc_gpio, 0); - esp_err_t ret = spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); - ESP_ERROR_CHECK(ret); display_reset(spi); wait_busy_level(spi, 1); - writecommand(spi, 0xAA); + spi_dc_writecommand(&spi->bus, 0xAA); uint8_t psr1_data[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18}; - writedatan(spi, psr1_data, sizeof(psr1_data)); + spi_dc_writedatan(&spi->bus, psr1_data, sizeof(psr1_data)); wait_busy_level(spi, 1); - writecommand(spi, PWRR); + spi_dc_writecommand(&spi->bus, PWRR); uint8_t pwrr_data[] = {0x3F}; - writedatan(spi, pwrr_data, sizeof(pwrr_data)); + spi_dc_writedatan(&spi->bus, pwrr_data, sizeof(pwrr_data)); wait_busy_level(spi, 1); - writecommand(spi, PSR); + spi_dc_writecommand(&spi->bus, PSR); uint8_t psr_data[] = {0x5F, 0x69}; - writedatan(spi, psr_data, sizeof(psr_data)); + spi_dc_writedatan(&spi->bus, psr_data, sizeof(psr_data)); wait_busy_level(spi, 1); - writecommand(spi, POFS); + spi_dc_writecommand(&spi->bus, POFS); uint8_t pofs_data[] = {0x00, 0x54, 0x00, 0x44}; - writedatan(spi, pofs_data, sizeof(pofs_data)); + spi_dc_writedatan(&spi->bus, pofs_data, sizeof(pofs_data)); wait_busy_level(spi, 1); - writecommand(spi, BTST1); + spi_dc_writecommand(&spi->bus, BTST1); uint8_t btst1_data[] = {0x40, 0x1F, 0x1F, 0x2C}; - writedatan(spi, btst1_data, sizeof(btst1_data)); + spi_dc_writedatan(&spi->bus, btst1_data, sizeof(btst1_data)); wait_busy_level(spi, 1); - writecommand(spi, BTST2); + spi_dc_writecommand(&spi->bus, BTST2); uint8_t btst2_data[] = {0x6F, 0x1F, 0x17, 0x49}; - writedatan(spi, btst2_data, sizeof(btst2_data)); + spi_dc_writedatan(&spi->bus, btst2_data, sizeof(btst2_data)); wait_busy_level(spi, 1); - writecommand(spi, BTST3); + spi_dc_writecommand(&spi->bus, BTST3); uint8_t btst3_data[] = {0x6F, 0x1F, 0x1F, 0x22}; - writedatan(spi, btst3_data, sizeof(btst3_data)); + spi_dc_writedatan(&spi->bus, btst3_data, sizeof(btst3_data)); wait_busy_level(spi, 1); - writecommand(spi, PLL); + spi_dc_writecommand(&spi->bus, PLL); uint8_t pll_data[] = {0x00}; - writedatan(spi, pll_data, sizeof(pll_data)); + spi_dc_writedatan(&spi->bus, pll_data, sizeof(pll_data)); wait_busy_level(spi, 1); - writecommand(spi, CDI); + spi_dc_writecommand(&spi->bus, CDI); uint8_t cdi_data[] = {0x3F}; - writedatan(spi, cdi_data, sizeof(cdi_data)); + spi_dc_writedatan(&spi->bus, cdi_data, sizeof(cdi_data)); wait_busy_level(spi, 1); - writecommand(spi, TCON); + spi_dc_writecommand(&spi->bus, TCON); uint8_t tcon_data[] = {0x02, 0x00}; - writedatan(spi, tcon_data, sizeof(tcon_data)); + spi_dc_writedatan(&spi->bus, tcon_data, sizeof(tcon_data)); wait_busy_level(spi, 1); - writecommand(spi, TRES); + spi_dc_writecommand(&spi->bus, TRES); uint8_t tres_data[] = {0x03, 0x20, 0x01, 0xe0}; - writedatan(spi, tres_data, sizeof(tres_data)); + spi_dc_writedatan(&spi->bus, tres_data, sizeof(tres_data)); wait_busy_level(spi, 1); - writecommand(spi, T_VDCS); + spi_dc_writecommand(&spi->bus, T_VDCS); uint8_t vdcs_data[] = {0x01}; - writedatan(spi, vdcs_data, sizeof(vdcs_data)); + spi_dc_writedatan(&spi->bus, vdcs_data, sizeof(vdcs_data)); wait_busy_level(spi, 1); - writecommand(spi, PWS); + spi_dc_writecommand(&spi->bus, PWS); uint8_t pws_data[] = {0x2F}; - writedatan(spi, pws_data, sizeof(pws_data)); + spi_dc_writedatan(&spi->bus, pws_data, sizeof(pws_data)); wait_busy_level(spi, 1); // PON - writecommand(spi, 0x04); + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); - spi_device_release_bus(spi->spi_disp.handle); - ctx->platform_data = &spi->display_args; spi->ctx = ctx; + screen = calloc(1, sizeof(struct EpaperScreen)); + screen->w = DISPLAY_WIDTH; + screen->h = DISPLAY_HEIGHT; + screen->palette = epaper_gdep073e01_palette; + screen->palette_size = 7; + update_last_refresh_ts(ctx); spi->count_to_refresh = 0; diff --git a/spi_dc_driver.c b/spi_dc_driver.c index dd003c1..bc183b8 100644 --- a/spi_dc_driver.c +++ b/spi_dc_driver.c @@ -46,3 +46,10 @@ void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, spi_dc_writedata(bus, data[i]); } } + +void spi_dc_writedatan(struct SPIDCBus *bus, const uint8_t *data, size_t length) +{ + for (size_t i = 0; i < length; i++) { + spi_dc_writedata(bus, data[i]); + } +} diff --git a/spi_dc_driver.h b/spi_dc_driver.h index 2f1fd04..4208fcc 100644 --- a/spi_dc_driver.h +++ b/spi_dc_driver.h @@ -35,5 +35,6 @@ struct SPIDCBus void spi_dc_writedata(struct SPIDCBus *bus, uint32_t data); void spi_dc_writecommand(struct SPIDCBus *bus, uint8_t cmd); void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t length); +void spi_dc_writedatan(struct SPIDCBus *bus, const uint8_t *data, size_t length); #endif From 2ddc5253b10056841d20190ef5fecd35c7c229f5 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 12:57:32 +0000 Subject: [PATCH 15/52] mono_draw: Add shared monochrome draw module Extract the monochrome draw_x dispatcher, five draw_*_x helpers, and Bayer 4x4 dithering into a shared module with struct MonoScreen. Migrate memory_display and ssd1306 drivers. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + memory_display_driver.c | 15 +- mono_draw.c | 373 +++++++++++++++++++++++++++++++++++++++ mono_draw.h | 58 ++++++ ssd1306_display_driver.c | 12 +- 5 files changed, 449 insertions(+), 10 deletions(-) create mode 100644 mono_draw.c create mode 100644 mono_draw.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f81b72..141b5a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ idf_component_register(SRCS "display_task.c" "epaper_color.c" "epaper_draw.c" + "mono_draw.c" "5in65_acep_7c_display_driver.c" "gdep073e01_display_driver.c" "font_data.c" diff --git a/memory_display_driver.c b/memory_display_driver.c index 08c2951..68de601 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -54,11 +54,9 @@ #include "display_task.h" #include "spi_display.h" -#define CHAR_WIDTH 8 - #define DISPLAY_WIDTH 400 +#define DISPLAY_HEIGHT 240 -#define CHECK_OVERFLOW 1 #define REPORT_UNEXPECTED_MSGS 0 #include "font_data.h" @@ -76,9 +74,8 @@ struct SPI #include "display_items.h" #include "display_message.h" -#include "draw_common.h" #include "image_helpers.h" -#include "monochrome.h" +#include "mono_draw.h" // This struct is just for compatibility reasons with the SDL display driver // so it is possible to easily copy & paste code from there. @@ -92,6 +89,7 @@ struct Screen }; static struct Screen *screen; +static struct MonoScreen *mono_screen; static void display_init(Context *ctx, term opts); @@ -141,7 +139,7 @@ static void do_update(Context *ctx, term display_list) int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(buf + 2, xpos, ypos, items, len); + int drawn_pixels = mono_draw_x(mono_screen, buf + 2, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -231,6 +229,11 @@ static void display_init(Context *ctx, term opts) // FIXME: hardcoded width and height screen->w = 400; screen->h = 240; + + mono_screen = calloc(1, sizeof(struct MonoScreen)); + mono_screen->w = DISPLAY_WIDTH; + mono_screen->h = DISPLAY_HEIGHT; + int memsize = 2 + 400 / 8 + 2; screen->pixels = heap_caps_malloc(memsize, MALLOC_CAP_DMA); diff --git a/mono_draw.c b/mono_draw.c new file mode 100644 index 0000000..9f0145b --- /dev/null +++ b/mono_draw.c @@ -0,0 +1,373 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mono_draw.h" + +#include +#include +#include + +#include + +#include "display_items.h" +#include "font_data.h" + +#define CHAR_WIDTH 8 + +static int get_color(int x, int y, uint8_t r, uint8_t g, uint8_t b) +{ + // dither + + /* + * Original bayer matrix + * { 0, 8, 2, 10 }, + * { 12, 4, 14, 6 }, + * { 3, 11, 1, 9 }, + * { 15, 7, 13, 5 } + * + * The following is calculated applying the following code element by element + * r = 255 / values / 4 + * roundf(63.75 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); + */ + const int m[4][4] = { + { -32, 0, -24, 8 }, + { 16, -16, 24, -8 }, + { -20, 12, -28, 4 }, + { 28, -4, 20, -12 } + }; + + int v = m[x % 4][y % 4]; + int out_r = r + v; + int out_g = g + v; + int out_b = b + v; + // end of dither + + // get closest + // float yval = 0.2126 * out_r + 0.7152 * out_g + 0.0722 * out_b; + // the following is a fast formula + int yval = ((out_r << 1) + out_r + (out_g << 2) + out_b) >> 3; + + return yval >= 128; +} + +void mono_draw_pixel_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int color) +{ + if (xpos > screen->w) { + fprintf(stderr, "display buffer overflow: %i!\n", xpos); + return; + } + + int bpos = (xpos % 8); + line_buf[xpos / 8] = (line_buf[xpos / 8] & ~(0x1 << bpos)) | (color << bpos); +} + +int mono_find_max_line_len(const struct MonoScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos) +{ + int line_len = screen->w - xpos; + + for (int i = 0; i < count; i++) { + BaseDisplayItem *item = &items[i]; + + if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { + int len_to_item = item->x - xpos; + line_len = (line_len > len_to_item) ? len_to_item : line_len; + } + } + + return line_len; +} + +int mono_draw_image_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data.pix; + + int drawn_pixels = 0; + + uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + int c = get_color(xpos + drawn_pixels, ypos, r, g, b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + int c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + pixels++; + } + + return drawn_pixels; +} + +int mono_draw_rect_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int width = item->width; + + uint8_t r = (item->brcolor >> 24) & 0xFF; + uint8_t g = (item->brcolor >> 16) & 0xFF; + uint8_t b = (item->brcolor >> 8) & 0xFF; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + int c = get_color(xpos + drawn_pixels, ypos, r, g, b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + drawn_pixels++; + } + + return drawn_pixels; +} + +int mono_draw_text_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + bool visible_bg; + + int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; + int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; + int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + char *text = (char *) item->data.text_data.text; + + int width = item->width; + + int drawn_pixels = 0; + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + int char_index = j / CHAR_WIDTH; + char c = text[char_index]; + unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; + + unsigned char row = glyph[ypos - y]; + + bool opaque; + int k = j % CHAR_WIDTH; + if (row & (1 << (7 - k))) { + opaque = true; + } else { + opaque = false; + } + + if (opaque) { + int c = get_color(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + int c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + } + + return drawn_pixels; +} + +int mono_draw_scaled_cropped_img_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item) +{ + int x = item->x; + int y = item->y; + + int bgcolor_r; + int bgcolor_g; + int bgcolor_b; + bool visible_bg; + if (item->brcolor != 0) { + bgcolor_r = (item->brcolor >> 24) & 0xFF; + bgcolor_g = (item->brcolor >> 16) & 0xFF; + bgcolor_b = (item->brcolor >> 8) & 0xFF; + visible_bg = true; + } else { + bgcolor_r = 0; + bgcolor_g = 0; + bgcolor_b = 0; + visible_bg = false; + } + + int width = item->width; + const char *data = item->data.image_data_with_size.pix; + + int drawn_pixels = 0; + + int y_scale = item->y_scale; + int x_scale = item->x_scale; + int img_width = item->data.image_data_with_size.width; + + int source_x = item->source_x; + int source_y = item->source_y; + + uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); + + if (source_x + (width / x_scale) > img_width) { + width = (img_width - source_x) * x_scale; + } + + if (width > xpos - x + max_line_len) { + width = xpos - x + max_line_len; + } + + for (int j = xpos - x; j < width; j++) { + uint32_t img_pixel = READ_32_UNALIGNED(pixels); + if ((*pixels >> 24) & 0xFF) { + uint8_t r = img_pixel >> 24; + uint8_t g = (img_pixel >> 16) & 0xFF; + uint8_t b = (img_pixel >> 8) & 0xFF; + + int c = get_color(xpos + drawn_pixels, ypos, r, g, b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else if (visible_bg) { + int c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); + mono_draw_pixel_x(screen, line_buf, xpos + drawn_pixels, c); + + } else { + return drawn_pixels; + } + drawn_pixels++; + //TODO: optimize here + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + } + + return drawn_pixels; +} + +int mono_draw_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, + BaseDisplayItem *items, int items_count) +{ + bool below = false; + + for (int i = 0; i < items_count; i++) { + BaseDisplayItem *item = &items[i]; + if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { + continue; + } + + int max_line_len = below ? 1 : mono_find_max_line_len(screen, items, i, xpos, ypos); + + int drawn_pixels = 0; + switch (items[i].primitive) { + case Image: + //fprintf(stderr, "Image\n"); + drawn_pixels = mono_draw_image_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case ScaledCroppedImage: + //fprintf(stderr, "ScaledCroppedImage\n"); + drawn_pixels = mono_draw_scaled_cropped_img_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case Rect: + //fprintf(stderr, "Rect\n"); + drawn_pixels = mono_draw_rect_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + case Text: + //fprintf(stderr, "Text\n"); + drawn_pixels = mono_draw_text_x(screen, line_buf, xpos, ypos, max_line_len, item); + break; + + default: { + fprintf(stderr, "unexpected display list command.\n"); + } + } + + if (drawn_pixels != 0) { + return drawn_pixels; + } + + below = true; + } + + return 1; +} diff --git a/mono_draw.h b/mono_draw.h new file mode 100644 index 0000000..498616a --- /dev/null +++ b/mono_draw.h @@ -0,0 +1,58 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2022-2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _MONO_DRAW_H_ +#define _MONO_DRAW_H_ + +#include "display_items.h" + +struct MonoScreen +{ + int w; + int h; +}; + +void mono_draw_pixel_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int color); + +int mono_draw_image_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int mono_draw_rect_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int mono_draw_text_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int mono_draw_scaled_cropped_img_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, int max_line_len, + BaseDisplayItem *item); + +int mono_find_max_line_len(const struct MonoScreen *screen, + BaseDisplayItem *items, int count, int xpos, int ypos); + +int mono_draw_x(const struct MonoScreen *screen, + uint8_t *line_buf, int xpos, int ypos, + BaseDisplayItem *items, int items_count); + +#endif diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index 8585297..716ad99 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -48,7 +48,6 @@ #define DISPLAY_HEIGHT 64 #define PAGE_HEIGHT 8 #define PAGES_NUM 8 -#define CHAR_WIDTH 8 #define I2C_ADDRESS 0x3C @@ -82,10 +81,11 @@ struct SPI #define SPI_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +static struct MonoScreen *mono_screen; + #include "font_data.h" #include "display_items.h" -#include "draw_common.h" -#include "monochrome.h" +#include "mono_draw.h" static void do_update(Context *ctx, term display_list) { @@ -117,7 +117,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = draw_x(buf, xpos, ypos, items, len); + int drawn_pixels = mono_draw_x(mono_screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -227,6 +227,10 @@ static void display_init(Context *ctx, term opts) bool invert = interop_kv_get_value(opts, ATOM_STR("\x6", "invert"), glb) == TRUE_ATOM; + mono_screen = calloc(1, sizeof(struct MonoScreen)); + mono_screen->w = DISPLAY_WIDTH; + mono_screen->h = DISPLAY_HEIGHT; + struct SPI *spi = malloc(sizeof(struct SPI)); spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); From 7ebd2d1326b0987e423b027bdeacf09dae2ae410 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 16:53:33 +0000 Subject: [PATCH 16/52] Fix off-by-one in draw_scaled_cropped_img_x The pixels pointer update at the bottom of the inner loop used j / x_scale, but j is the current iteration's index. After the for-loop increments j, pixels still pointed at the old source column. Use (j + 1) / x_scale so the pointer is correct for the next iteration. Fixes a leftmost-column artifact visible on 4x-scaled images. Signed-off-by: Davide Bettio --- dcs_lcd_draw.c | 3 +-- epaper_draw.c | 2 +- mono_draw.c | 3 +-- sdl_display/display.c | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dcs_lcd_draw.c b/dcs_lcd_draw.c index 3b5dc6a..6c74b39 100644 --- a/dcs_lcd_draw.c +++ b/dcs_lcd_draw.c @@ -225,8 +225,7 @@ int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen, return drawn_pixels; } drawn_pixels++; - // TODO: optimize here - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((j + 1) / x_scale); } return drawn_pixels; diff --git a/epaper_draw.c b/epaper_draw.c index 25cdaae..7d8ee54 100644 --- a/epaper_draw.c +++ b/epaper_draw.c @@ -289,7 +289,7 @@ int epaper_draw_scaled_cropped_img_x(const struct EpaperScreen *screen, return drawn_pixels; } drawn_pixels++; - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((j + 1) / x_scale); } return drawn_pixels; diff --git a/mono_draw.c b/mono_draw.c index 9f0145b..98d1971 100644 --- a/mono_draw.c +++ b/mono_draw.c @@ -314,8 +314,7 @@ int mono_draw_scaled_cropped_img_x(const struct MonoScreen *screen, return drawn_pixels; } drawn_pixels++; - //TODO: optimize here - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((j + 1) / x_scale); } return drawn_pixels; diff --git a/sdl_display/display.c b/sdl_display/display.c index 21aed37..8f0ae09 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -344,7 +344,7 @@ static int draw_scaled_cropped_img_x(int xpos, int ypos, int max_line_len, BaseD return drawn_pixels; } drawn_pixels++; - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); + pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((j + 1) / x_scale); } return drawn_pixels; From c5c5de86607505f8f9e30d66099d67cf83f4408c Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 16:58:22 +0000 Subject: [PATCH 17/52] Fix memory leaks in e-paper do_update Both e-paper drivers allocated a DMA scanline buffer with heap_caps_malloc but never freed it. Add the missing free(buf) after spi_device_release_bus in each function. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 4 ++++ gdep073e01_display_driver.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 8ff6523..3fb3b97 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -193,6 +193,8 @@ static void do_update(Context *ctx, term display_list) spi_device_release_bus(spi->bus.spi_disp.handle); + free(buf); + // not sure if we should add 0x11, which is end of data command or not // power on command @@ -289,6 +291,8 @@ static void clear_screen(Context *ctx, int color) spi_device_release_bus(spi->bus.spi_disp.handle); + free(buf); + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); spi_dc_writecommand(&spi->bus, 0x12); diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index b805564..d4df5b1 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -316,6 +316,8 @@ static void clear_screen(Context *ctx, int color) spi_device_release_bus(spi->bus.spi_disp.handle); + free(buf); + spi_dc_writecommand(&spi->bus, 0x04); wait_busy_level(spi, 1); spi_dc_writecommand(&spi->bus, 0x12); From 83cdcbcef98089c61e0ec786a133d68113eb7285 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 16:59:55 +0000 Subject: [PATCH 18/52] epaper_color: Change gdep073e01 palette Replace the placeholder pure-primary RGB values with real-world measurements taken from the physical panel. The calibrated values produce more accurate nearest-color matching during dithering. Signed-off-by: Davide Bettio --- epaper_color.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/epaper_color.c b/epaper_color.c index c680545..ddd27b7 100644 --- a/epaper_color.c +++ b/epaper_color.c @@ -35,13 +35,13 @@ const uint8_t epaper_acep_palette[7][3] = { }; const uint8_t epaper_gdep073e01_palette[7][3] = { - { 0x00, 0x00, 0x00 }, - { 0xFF, 0xFF, 0xFF }, - { 0xFF, 0xFF, 0x00 }, - { 0xFF, 0x00, 0x00 }, - { 0x10, 0x10, 0x10 }, - { 0x00, 0x00, 0xFF }, - { 0x00, 0xFF, 0x00 } + { 0x19, 0x1E, 0x21 }, + { 0xE8, 0xE8, 0xE8 }, + { 0xEF, 0xDE, 0x44 }, + { 0xB2, 0x13, 0x18 }, + { 0xE8, 0xE8, 0xE8 }, + { 0x21, 0x57, 0xBA }, + { 0x12, 0x5F, 0x20 } }; static inline float square(float p) From 13d3aa7a7d86e0184b3c2ad5ee3abb1f78d2c073 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 17:04:49 +0000 Subject: [PATCH 19/52] Remove dead draw_common.h and monochrome.h Both headers have zero includers after the monochrome draw functions were extracted into mono_draw. The shared per-pixel draw pipeline and Bayer dither now live in mono_draw.c/h. Signed-off-by: Davide Bettio --- draw_common.h | 92 ---------------- monochrome.h | 288 -------------------------------------------------- 2 files changed, 380 deletions(-) delete mode 100644 draw_common.h delete mode 100644 monochrome.h diff --git a/draw_common.h b/draw_common.h deleted file mode 100644 index c64d581..0000000 --- a/draw_common.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2020-2022 Davide Bettio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -static int draw_image_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item); -static int draw_scaled_cropped_img_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item); -static int draw_rect_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item); -static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item); - -static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int ypos) -{ - int line_len = DISPLAY_WIDTH - xpos; - - for (int i = 0; i < count; i++) { - BaseDisplayItem *item = &items[i]; - - if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { - int len_to_item = item->x - xpos; - line_len = (line_len > len_to_item) ? len_to_item : line_len; - } - } - - return line_len; -} - -static int draw_x(uint8_t *line_buf, int xpos, int ypos, BaseDisplayItem *items, int items_count) -{ - bool below = false; - - for (int i = 0; i < items_count; i++) { - BaseDisplayItem *item = &items[i]; - if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { - continue; - } - - int max_line_len = below ? 1 : find_max_line_len(items, i, xpos, ypos); - - int drawn_pixels = 0; - switch (items[i].primitive) { - case Image: - //fprintf(stderr, "Image\n"); - drawn_pixels = draw_image_x(line_buf, xpos, ypos, max_line_len, item); - break; - - case ScaledCroppedImage: - //fprintf(stderr, "ScaledCroppedImage\n"); - drawn_pixels = draw_scaled_cropped_img_x(line_buf, xpos, ypos, max_line_len, item); - break; - - case Rect: - //fprintf(stderr, "Rect\n"); - drawn_pixels = draw_rect_x(line_buf, xpos, ypos, max_line_len, item); - break; - - case Text: - //fprintf(stderr, "Text\n"); - drawn_pixels = draw_text_x(line_buf, xpos, ypos, max_line_len, item); - break; - - default: { - fprintf(stderr, "unexpected display list command.\n"); - } - } - - if (drawn_pixels != 0) { - return drawn_pixels; - } - - below = true; - } - - return 1; -} diff --git a/monochrome.h b/monochrome.h deleted file mode 100644 index 78e49b1..0000000 --- a/monochrome.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2022-2024 Davide Bettio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -static int get_color(int x, int y, uint8_t r, uint8_t g, uint8_t b) -{ - // dither - - /* - * Original bayer matrix - * { 0, 8, 2, 10 }, - * { 12, 4, 14, 6 }, - * { 3, 11, 1, 9 }, - * { 15, 7, 13, 5 } - * - * The following is calculated applying the following code element by element - * r = 255 / values / 4 - * roundf(63.75 * ((float) m[x % 4][y % 4] * 0.0625 - 0.5)); - */ - const int m[4][4] = { - { -32, 0, -24, 8 }, - { 16, -16, 24, -8 }, - { -20, 12, -28, 4 }, - { 28, -4, 20, -12 } - }; - - int v = m[x % 4][y % 4]; - int out_r = r + v; - int out_g = g + v; - int out_b = b + v; - // end of dither - - // get closest - // float yval = 0.2126 * out_r + 0.7152 * out_g + 0.0722 * out_b; - // the following is a fast formula - int yval = ((out_r << 1) + out_r + (out_g << 2) + out_b) >> 3; - - return yval >= 128; -} - -static inline void draw_pixel_x(uint8_t *line_buf, int xpos, int color) -{ -#if CHECK_OVERFLOW - if (xpos > DISPLAY_WIDTH) { - fprintf(stderr, "display buffer overflow: %i!\n", xpos); - return; - } -#endif - - int bpos = (xpos % 8); - line_buf[xpos / 8] = (line_buf[xpos / 8] & ~(0x1 << bpos)) | (color << bpos); -} - -static int draw_image_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data.pix; - - int drawn_pixels = 0; - - uint32_t *pixels = ((uint32_t *) data) + (ypos - y) * width + (xpos - x); - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = get_color(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - pixels++; - } - - return drawn_pixels; -} - -static int draw_scaled_cropped_img_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - bool visible_bg; - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - int width = item->width; - const char *data = item->data.image_data_with_size.pix; - - int drawn_pixels = 0; - - int y_scale = item->y_scale; - int x_scale = item->x_scale; - int img_width = item->data.image_data_with_size.width; - - int source_x = item->source_x; - int source_y = item->source_y; - - uint32_t *pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + ((xpos - x) / x_scale); - - if (source_x + (width / x_scale) > img_width) { - width = (img_width - source_x) * x_scale; - } - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint32_t img_pixel = READ_32_UNALIGNED(pixels); - if ((*pixels >> 24) & 0xFF) { - uint8_t r = img_pixel >> 24; - uint8_t g = (img_pixel >> 16) & 0xFF; - uint8_t b = (img_pixel >> 8) & 0xFF; - - uint8_t c = get_color(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - //TODO: optimize here - pixels = ((uint32_t *) data) + (source_y + ((ypos - y) / y_scale)) * img_width + source_x + (j / x_scale); - } - - return drawn_pixels; -} - -static int draw_rect_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int width = item->width; - - uint8_t r = (item->brcolor >> 24) & 0xFF; - uint8_t g = (item->brcolor >> 16) & 0xFF; - uint8_t b = (item->brcolor >> 8) & 0xFF; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - uint8_t c = get_color(xpos + drawn_pixels, ypos, r, g, b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - drawn_pixels++; - } - - return drawn_pixels; -} - -static int draw_text_x(uint8_t *line_buf, int xpos, int ypos, int max_line_len, BaseDisplayItem *item) -{ - int x = item->x; - int y = item->y; - bool visible_bg; - - int fgcolor_r = (item->data.text_data.fgcolor >> 24) & 0xFF; - int fgcolor_g = (item->data.text_data.fgcolor >> 16) & 0xFF; - int fgcolor_b = (item->data.text_data.fgcolor >> 8) & 0xFF; - - int bgcolor_r; - int bgcolor_g; - int bgcolor_b; - - if (item->brcolor != 0) { - bgcolor_r = (item->brcolor >> 24) & 0xFF; - bgcolor_g = (item->brcolor >> 16) & 0xFF; - bgcolor_b = (item->brcolor >> 8) & 0xFF; - visible_bg = true; - } else { - bgcolor_r = 0; - bgcolor_g = 0; - bgcolor_b = 0; - visible_bg = false; - } - - char *text = (char *) item->data.text_data.text; - - int width = item->width; - - int drawn_pixels = 0; - - if (width > xpos - x + max_line_len) { - width = xpos - x + max_line_len; - } - - for (int j = xpos - x; j < width; j++) { - int char_index = j / CHAR_WIDTH; - char c = text[char_index]; - unsigned const char *glyph = fontdata + ((unsigned char) c) * 16; - - unsigned char row = glyph[ypos - y]; - - bool opaque; - int k = j % CHAR_WIDTH; - if (row & (1 << (7 - k))) { - opaque = true; - } else { - opaque = false; - } - - if (opaque) { - uint8_t c = get_color(xpos + drawn_pixels, ypos, fgcolor_r, fgcolor_g, fgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else if (visible_bg) { - uint8_t c = get_color(xpos + drawn_pixels, ypos, bgcolor_r, bgcolor_g, bgcolor_b); - draw_pixel_x(line_buf, xpos + drawn_pixels, c); - - } else { - return drawn_pixels; - } - drawn_pixels++; - } - - return drawn_pixels; -} From aac13c2caf5f211c79ec9d3ef8e2bf7b345a7263 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Fri, 10 Apr 2026 23:09:31 +0000 Subject: [PATCH 20/52] Add uFontLib support for all ESP32 drivers Move ufontlib from sdl_display/ to the component root. Fix the inverted magic check in ufont_parse and accept standard IFF container layout (FORM + size + uFL0 form type). Wire up register_font as a pre-dispatch handler in the shared display task, ufont_manager initialization, and the epd_draw_pixel rendering callback. Honor FgColor for anti-aliased text. Signed-off-by: Davide Bettio --- CMakeLists.txt | 3 +- display_items.c | 63 +++++++++++++++++++++++++--- display_items.h | 6 ++- display_task.c | 56 ++++++++++++++++++++++++- sdl_display/CMakeLists.txt | 2 +- sdl_display/display.c | 2 +- sdl_display/ufontlib.c => ufontlib.c | 13 +++++- sdl_display/ufontlib.h => ufontlib.h | 0 8 files changed, 133 insertions(+), 12 deletions(-) rename sdl_display/ufontlib.c => ufontlib.c (97%) rename sdl_display/ufontlib.h => ufontlib.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 141b5a4..53b027c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ idf_component_register(SRCS "st7789_display_driver.c" "spi_dc_driver.c" "spi_display.c" + "ufontlib.c" "backlight_gpio.c" "image_helpers.c" "spng.c" @@ -55,7 +56,7 @@ idf_component_register(SRCS ${OPTIONAL_WHOLE_ARCHIVE} ) -target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-DSPNG_USE_MINIZ=1") +target_compile_definitions(${COMPONENT_TARGET} PRIVATE "-DSPNG_USE_MINIZ=1" "-DENABLE_UFONT") if (IDF_VERSION_MAJOR EQUAL 4) idf_build_set_property( diff --git a/display_items.c b/display_items.c index 69dbcb6..875bf6d 100644 --- a/display_items.c +++ b/display_items.c @@ -26,8 +26,50 @@ #include +#ifdef ENABLE_UFONT +#include "ufontlib.h" +extern UFontManager *ufont_manager; + +#ifdef ESP_PLATFORM +struct Surface +{ + int width; + int height; + void *buffer; + uint32_t fg_color; // RGBA8888 little-endian byte order with the + // alpha byte cleared; ORed with the per-pixel + // alpha in epd_draw_pixel. +}; + +#define BPP 4 + +void epd_draw_pixel(int xpos, int ypos, uint8_t color, void *buffer) +{ + struct Surface *surface = buffer; + + if (xpos < 0 || ypos < 0 || xpos >= surface->width + || ypos >= surface->height) { + return; + } + + uint32_t *pixel = (uint32_t *) (((uint8_t *) surface->buffer) + + (surface->width * ypos + xpos) * sizeof(uint32_t)); + + // The `color` parameter is the LUT-mapped glyph value from + // draw_char: 0 = full foreground (fg_color=0 in default props), + // 240 = full background (bg_color=15), in steps of 16. Render + // the foreground RGB on transparent with anti-aliased alpha + // derived from the inverted grayscale. + uint8_t alpha = (15 - (color >> 4)) * 17; + *pixel = ((uint32_t) alpha << 24) | (surface->fg_color & 0x00FFFFFFu); +} +#endif /* ESP_PLATFORM */ +#endif /* ENABLE_UFONT */ + void init_item(BaseDisplayItem *item, term req, Context *ctx) { + item->owns_data = false; + term cmd = term_get_tuple_element(req, 0); if (cmd == context_make_atom(ctx, "\x5" @@ -136,10 +178,12 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) } else { #ifdef ENABLE_UFONT - AtomString handle_atom = globalcontext_atomstring_from_term(ctx->global, font); - char handle[255]; - atom_string_to_c(handle_atom, handle, sizeof(handle)); - EpdFont *loaded_font = ufont_manager_find_by_handle(ufont_manager, handle); + char *handle = interop_atom_to_string(ctx, font); + EpdFont *loaded_font = NULL; + if (handle != NULL) { + loaded_font = ufont_manager_find_by_handle(ufont_manager, handle); + free(handle); + } if (!loaded_font) { fprintf(stderr, "unsupported font: "); @@ -156,6 +200,12 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) surface.height = rect.height; surface.buffer = malloc(rect.width * rect.height * BPP); memset(surface.buffer, 0, rect.width * rect.height * BPP); + // Convert Erlang fgcolor (0xRRGGBBAA) to RGBA8888 little- + // endian byte order (R in low byte, alpha byte cleared) so + // epd_draw_pixel can OR it with the per-pixel alpha. + surface.fg_color = ((fgcolor >> 24) & 0xFFu) + | (((fgcolor >> 16) & 0xFFu) << 8) + | (((fgcolor >> 8) & 0xFFu) << 16); int text_x = 0; int text_y = loaded_font->ascender; enum EpdDrawError res = epd_write_default(loaded_font, text, &text_x, &text_y, &surface); @@ -169,8 +219,8 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) item->width = surface.width; item->height = surface.height; item->brcolor = 0; - //FIXME: surface buffer leak item->data.image_data.pix = surface.buffer; + item->owns_data = true; #else fprintf(stderr, "unsupported font: "); term_display(stderr, font, ctx); @@ -205,6 +255,9 @@ void destroy_items(BaseDisplayItem *items, int items_count) switch (item->primitive) { case Image: + if (item->owns_data) { + free((void *) item->data.image_data.pix); + } break; case Rect: diff --git a/display_items.h b/display_items.h index 268099b..7587438 100644 --- a/display_items.h +++ b/display_items.h @@ -21,9 +21,11 @@ #ifndef _DISPLAY_ITEMS_H_ #define _DISPLAY_ITEMS_H_ -#include +#include #include +#include + // TODO: deprecated helper, remove this static inline term context_make_atom(Context *ctx, AtomString string) { @@ -77,6 +79,8 @@ struct BaseDisplayItem int source_y; int x_scale; int y_scale; + + bool owns_data; }; typedef struct BaseDisplayItem BaseDisplayItem; diff --git a/display_task.c b/display_task.c index b12f428..7f29c84 100644 --- a/display_task.c +++ b/display_task.c @@ -20,9 +20,18 @@ #include "display_task.h" +#include +#include #include +#include +#include #include +#include "display_message.h" +#include "ufontlib.h" + +UFontManager *ufont_manager; + NativeHandlerResult display_driver_consume_mailbox(Context *ctx) { struct DisplayTaskArgs *args = ctx->platform_data; @@ -50,14 +59,59 @@ NativeHandlerResult display_driver_consume_mailbox(Context *ctx) return NativeContinue; } +static bool try_handle_register_font(Message *message, Context *ctx) +{ + GenMessage gen_message; + if (UNLIKELY(port_parse_gen_message(message->message, + &gen_message) != GenCallMessage)) { + return false; + } + + term req = gen_message.req; + if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { + return false; + } + term cmd = term_get_tuple_element(req, 0); + + if (cmd != globalcontext_make_atom(ctx->global, + "\xD" "register_font")) { + return false; + } + + term font_bin = term_get_tuple_element(req, 2); + EpdFont *loaded_font = ufont_parse( + term_binary_data(font_bin), term_binary_size(font_bin)); + + char *handle = interop_atom_to_string(ctx, + term_get_tuple_element(req, 1)); + if (loaded_font != NULL && handle != NULL) { + ufont_manager_register(ufont_manager, handle, loaded_font); + } + free(handle); + + BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); + term return_tuple = term_alloc_tuple(2, &heap); + term_put_tuple_element(return_tuple, 0, gen_message.ref); + term_put_tuple_element(return_tuple, 1, OK_ATOM); + send_message(gen_message.pid, return_tuple, ctx->global); + END_WITH_STACK_HEAP(heap, ctx->global); + + return true; +} + void display_process_messages(void *arg) { struct DisplayTaskArgs *args = arg; + ufont_manager = ufont_manager_new(); + while (true) { Message *message; xQueueReceive(args->messages_queue, &message, portMAX_DELAY); - args->process_message_fn(message, args->ctx); + + if (!try_handle_register_font(message, args->ctx)) { + args->process_message_fn(message, args->ctx); + } BEGIN_WITH_STACK_HEAP(1, temp_heap); mailbox_message_dispose(&message->base, &temp_heap); diff --git a/sdl_display/CMakeLists.txt b/sdl_display/CMakeLists.txt index 9521125..21918d0 100644 --- a/sdl_display/CMakeLists.txt +++ b/sdl_display/CMakeLists.txt @@ -43,7 +43,7 @@ endif() set(CMAKE_SHARED_LIBRARY_PREFIX "") -add_library(avm_display_port_driver SHARED display.c ufontlib.c ../image_helpers.c ../spng.c ../font_data.c ../display_items.c ../display_message.c) +add_library(avm_display_port_driver SHARED display.c ../ufontlib.c ../image_helpers.c ../spng.c ../font_data.c ../display_items.c ../display_message.c) if (AVM_DISABLE_SMP) target_compile_definitions(avm_display_port_driver PUBLIC AVM_NO_SMP) diff --git a/sdl_display/display.c b/sdl_display/display.c index 8f0ae09..3fa9796 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -31,7 +31,7 @@ #include #include -#include "ufontlib.h" +#include "../ufontlib.h" #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 diff --git a/sdl_display/ufontlib.c b/ufontlib.c similarity index 97% rename from sdl_display/ufontlib.c rename to ufontlib.c index 6569865..82dea12 100644 --- a/sdl_display/ufontlib.c +++ b/ufontlib.c @@ -25,7 +25,9 @@ #ifdef WITH_ZLIB #include #else +#ifndef ESP_PLATFORM #include "miniz.c" +#endif #include "miniz.h" #endif #include @@ -587,12 +589,19 @@ static uint32_t ufont_iff_align(uint32_t size) static int ufont_iff_is_valid_ufl(const void *iff) { - return memcmp(iff, "UFL0", 4) == 0; + // Standard IFF layout produced by fontconvert.py: + // offset 0: "FORM" (IFF group magic) + // offset 4: file size (big-endian) + // offset 8: "uFL0" (form type, lowercase u) + // offset 12+: IFF records (uFH0, uFP0, uFI0, uFB0) + const uint8_t *data = iff; + return memcmp(data, "FORM", 4) == 0 + && memcmp(data + 8, "uFL0", 4) == 0; } EpdFont *ufont_parse(const void *iff_binary, int buf_size) { - if (ufont_iff_is_valid_ufl(iff_binary)) { + if (!ufont_iff_is_valid_ufl(iff_binary)) { return NULL; } diff --git a/sdl_display/ufontlib.h b/ufontlib.h similarity index 100% rename from sdl_display/ufontlib.h rename to ufontlib.h From 052d3f0ba4f20a938323948fbf502ae55912840f Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sat, 11 Apr 2026 23:03:53 +0000 Subject: [PATCH 21/52] Refactor names to follow AVMCCS conventions Apply lower_snake_case to SPI and SPI+DC helpers, add module prefixes to display_items, display_task, and display_message public functions, fix word boundaries in controller-specific init function names, convert enum values to PascalCase, and make file-local helpers static. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 122 +++++------ dcs_lcd_commands.c | 12 +- dcs_lcd_draw.c | 8 +- display_items.c | 24 +-- display_items.h | 20 +- display_message.c | 2 +- display_message.h | 2 +- display_task.c | 6 +- display_task.h | 4 +- epaper_draw.c | 8 +- gdep073e01_display_driver.c | 108 +++++----- ili934x_display_driver.c | 370 ++++++++++++++++----------------- ili948x_display_driver.c | 332 ++++++++++++++--------------- memory_display_driver.c | 14 +- mono_draw.c | 8 +- sdl_display/display.c | 24 +-- spi_dc_driver.c | 20 +- spi_dc_driver.h | 8 +- spi_display.c | 4 +- spi_display.h | 2 +- ssd1306_display_driver.c | 30 +-- st7789_display_driver.c | 328 ++++++++++++++--------------- 22 files changed, 728 insertions(+), 728 deletions(-) diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 3fb3b97..a712926 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -94,7 +94,7 @@ static void wait_busy_level(struct SPI *spi, int level) } -void wait_some_time(Context *ctx) +static void wait_some_time(Context *ctx) { struct SPI *spi = SPI_FROM_CTX(ctx); @@ -109,7 +109,7 @@ void wait_some_time(Context *ctx) } } -void update_last_refresh_ts(Context *ctx) +static void update_last_refresh_ts(Context *ctx) { struct SPI *spi = SPI_FROM_CTX(ctx); @@ -118,7 +118,7 @@ void update_last_refresh_ts(Context *ctx) spi->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); } -void maybe_refresh(Context *ctx) +static void maybe_refresh(Context *ctx) { struct SPI *spi = SPI_FROM_CTX(ctx); @@ -145,7 +145,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -154,14 +154,14 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); // resolution command - spi_dc_writecommand(&spi->bus, 0x61); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x58); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xC0); + spi_dc_write_command(&spi->bus, 0x61); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x58); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xC0); // update command - spi_dc_writecommand(&spi->bus, 0x10); + spi_dc_write_command(&spi->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); @@ -182,7 +182,7 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } @@ -198,18 +198,18 @@ static void do_update(Context *ctx, term display_list) // not sure if we should add 0x11, which is end of data command or not // power on command - spi_dc_writecommand(&spi->bus, 0x04); + spi_dc_write_command(&spi->bus, 0x04); wait_busy_level(spi, 1); // refresh command - spi_dc_writecommand(&spi->bus, 0x12); + spi_dc_write_command(&spi->bus, 0x12); wait_busy_level(spi, 1); // power off command - spi_dc_writecommand(&spi->bus, 0x02); + spi_dc_write_command(&spi->bus, 0x02); wait_busy_level(spi, 0); - destroy_items(items, len); + display_items_delete(items, len); update_last_refresh_ts(ctx); } @@ -251,7 +251,7 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } @@ -261,12 +261,12 @@ static void clear_screen(Context *ctx, int color) uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - spi_dc_writecommand(&spi->bus, 0x61); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x58); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xC0); - spi_dc_writecommand(&spi->bus, 0x10); + spi_dc_write_command(&spi->bus, 0x61); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x58); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xC0); + spi_dc_write_command(&spi->bus, 0x10); bool transaction_in_progress = false; @@ -280,7 +280,7 @@ static void clear_screen(Context *ctx, int color) // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } @@ -293,11 +293,11 @@ static void clear_screen(Context *ctx, int color) free(buf); - spi_dc_writecommand(&spi->bus, 0x04); + spi_dc_write_command(&spi->bus, 0x04); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0x12); + spi_dc_write_command(&spi->bus, 0x12); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0x02); + spi_dc_write_command(&spi->bus, 0x02); wait_busy_level(spi, 0); } @@ -332,42 +332,42 @@ static void display_spi_init(Context *ctx, term opts) wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0xEF); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writecommand(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x23); //datasheet says: 0x05 - spi_dc_writedata(&spi->bus, 0x23); //datasheet says: 0x05 - spi_dc_writecommand(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0xC7); - spi_dc_writedata(&spi->bus, 0xC7); - spi_dc_writedata(&spi->bus, 0x1D); - spi_dc_writecommand(&spi->bus, 0x30); - spi_dc_writedata(&spi->bus, 0x3C); - spi_dc_writecommand(&spi->bus, 0x40); //datasheet says: 0x41 - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, 0x50); - spi_dc_writedata(&spi->bus, 0x3F); //datasheet says: 0x37 - spi_dc_writecommand(&spi->bus, 0x60); - spi_dc_writedata(&spi->bus, 0x22); - spi_dc_writecommand(&spi->bus, 0x61); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x58); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xC0); - spi_dc_writecommand(&spi->bus, 0xE3); - spi_dc_writedata(&spi->bus, 0xAA); - spi_dc_writecommand(&spi->bus, 0x82); - spi_dc_writedata(&spi->bus, 0x80); + spi_dc_write_command(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0xEF); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_command(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x23); //datasheet says: 0x05 + spi_dc_write_data(&spi->bus, 0x23); //datasheet says: 0x05 + spi_dc_write_command(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_command(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0xC7); + spi_dc_write_data(&spi->bus, 0xC7); + spi_dc_write_data(&spi->bus, 0x1D); + spi_dc_write_command(&spi->bus, 0x30); + spi_dc_write_data(&spi->bus, 0x3C); + spi_dc_write_command(&spi->bus, 0x40); //datasheet says: 0x41 + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_command(&spi->bus, 0x50); + spi_dc_write_data(&spi->bus, 0x3F); //datasheet says: 0x37 + spi_dc_write_command(&spi->bus, 0x60); + spi_dc_write_data(&spi->bus, 0x22); + spi_dc_write_command(&spi->bus, 0x61); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x58); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xC0); + spi_dc_write_command(&spi->bus, 0xE3); + spi_dc_write_data(&spi->bus, 0xAA); + spi_dc_write_command(&spi->bus, 0x82); + spi_dc_write_data(&spi->bus, 0x80); vTaskDelay(10); - spi_dc_writecommand(&spi->bus, 0x50); - spi_dc_writedata(&spi->bus, 0x37); + spi_dc_write_command(&spi->bus, 0x50); + spi_dc_write_data(&spi->bus, 0x37); ctx->platform_data = &spi->display_args; @@ -396,14 +396,14 @@ static void display_spi_init(Context *ctx, term opts) spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); spi->display_args.process_message_fn = process_message; spi->display_args.ctx = ctx; - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); #endif } Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_spi_init(ctx, opts); return ctx; } diff --git a/dcs_lcd_commands.c b/dcs_lcd_commands.c index 8a45208..8b4eb8c 100644 --- a/dcs_lcd_commands.c +++ b/dcs_lcd_commands.c @@ -33,12 +33,12 @@ void dcs_lcd_set_paint_area(struct SPIDCBus *bus, const struct DCSLCDScreen *scr x += screen->x_offset; y += screen->y_offset; - spi_dc_writecommand(bus, DCS_LCD_CASET); + spi_dc_write_command(bus, DCS_LCD_CASET); spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); spi_display_write(&bus->spi_disp, 32, (x << 16) | ((x + width) - 1)); spi_device_release_bus(bus->spi_disp.handle); - spi_dc_writecommand(bus, DCS_LCD_PASET); + spi_dc_write_command(bus, DCS_LCD_PASET); spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); spi_display_write(&bus->spi_disp, 32, (y << 16) | ((y + height) - 1)); spi_device_release_bus(bus->spi_disp.handle); @@ -51,7 +51,7 @@ void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen dcs_lcd_set_paint_area(bus, screen, x, y, width, height); - spi_dc_writecommand(bus, DCS_LCD_RAMWR); + spi_dc_write_command(bus, DCS_LCD_RAMWR); int dest_size = width * height; int chunks = dest_size / 1024; @@ -67,7 +67,7 @@ void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen for (int j = 0; j < 1024; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&bus->spi_disp, 1024 * sizeof(uint16_t), tmpbuf); + spi_display_dma_write(&bus->spi_disp, 1024 * sizeof(uint16_t), tmpbuf); } int last_chunk_size = dest_size - chunks * 1024; @@ -76,7 +76,7 @@ void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen for (int j = 0; j < last_chunk_size; j++) { tmpbuf[j] = SPI_SWAP_DATA_TX(data_b[j], 16); } - spi_display_dmawrite(&bus->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); + spi_display_dma_write(&bus->spi_disp, last_chunk_size * sizeof(uint16_t), tmpbuf); } free(tmpbuf); @@ -105,7 +105,7 @@ void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen tmpbuf[j * 3 + 2] = b8; } - spi_display_dmawrite(&bus->spi_disp, n * 3, tmpbuf); + spi_display_dma_write(&bus->spi_disp, n * 3, tmpbuf); i += n; } diff --git a/dcs_lcd_draw.c b/dcs_lcd_draw.c index 6c74b39..542c931 100644 --- a/dcs_lcd_draw.c +++ b/dcs_lcd_draw.c @@ -246,19 +246,19 @@ int dcs_lcd_draw_x(const struct DCSLCDScreen *screen, int drawn_pixels = 0; switch (items[i].primitive) { - case Image: + case PrimitiveImage: drawn_pixels = dcs_lcd_draw_image_x(screen, xpos, ypos, max_line_len, item); break; - case Rect: + case PrimitiveRect: drawn_pixels = dcs_lcd_draw_rect_x(screen, xpos, ypos, max_line_len, item); break; - case ScaledCroppedImage: + case PrimitiveScaledCroppedImage: drawn_pixels = dcs_lcd_draw_scaled_cropped_img_x(screen, xpos, ypos, max_line_len, item); break; - case Text: + case PrimitiveText: drawn_pixels = dcs_lcd_draw_text_x(screen, xpos, ypos, max_line_len, item); break; default: { diff --git a/display_items.c b/display_items.c index 875bf6d..aad0db5 100644 --- a/display_items.c +++ b/display_items.c @@ -66,7 +66,7 @@ void epd_draw_pixel(int xpos, int ypos, uint8_t color, void *buffer) #endif /* ESP_PLATFORM */ #endif /* ENABLE_UFONT */ -void init_item(BaseDisplayItem *item, term req, Context *ctx) +void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx) { item->owns_data = false; @@ -74,7 +74,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) if (cmd == context_make_atom(ctx, "\x5" "image")) { - item->primitive = Image; + item->primitive = PrimitiveImage; item->x = term_to_int(term_get_tuple_element(req, 1)); item->y = term_to_int(term_get_tuple_element(req, 2)); @@ -101,7 +101,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) item->data.image_data.pix = term_binary_data(term_get_tuple_element(img, 3)); } else if (cmd == globalcontext_make_atom(ctx->global, ATOM_STR("\x14", "scaled_cropped_image"))) { - item->primitive = ScaledCroppedImage; + item->primitive = PrimitiveScaledCroppedImage; item->x = term_to_int(term_get_tuple_element(req, 1)); item->y = term_to_int(term_get_tuple_element(req, 2)); item->width = term_to_int(term_get_tuple_element(req, 3)); @@ -138,7 +138,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) } else if (cmd == context_make_atom(ctx, "\x4" "rect")) { - item->primitive = Rect; + item->primitive = PrimitiveRect; item->x = term_to_int(term_get_tuple_element(req, 1)); item->y = term_to_int(term_get_tuple_element(req, 2)); item->width = term_to_int(term_get_tuple_element(req, 3)); @@ -169,7 +169,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) term font = term_get_tuple_element(req, 3); if (font == globalcontext_make_atom(ctx->global, "\xB" "default16px")) { - item->primitive = Text; + item->primitive = PrimitiveText; item->height = 16; item->width = strlen(text) * 8; item->brcolor = brcolor; @@ -215,7 +215,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) return; } - item->primitive = Image; + item->primitive = PrimitiveImage; item->width = surface.width; item->height = surface.height; item->brcolor = 0; @@ -225,7 +225,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) fprintf(stderr, "unsupported font: "); term_display(stderr, font, ctx); fprintf(stderr, "\n"); - item->primitive = Text; + item->primitive = PrimitiveText; item->height = 16; item->width = strlen(text) * 8; item->brcolor = brcolor; @@ -240,7 +240,7 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) term_display(stderr, req, ctx); fprintf(stderr, "\n"); - item->primitive = Invalid; + item->primitive = PrimitiveInvalid; item->x = -1; item->y = -1; item->width = 1; @@ -248,22 +248,22 @@ void init_item(BaseDisplayItem *item, term req, Context *ctx) } } -void destroy_items(BaseDisplayItem *items, int items_count) +void display_items_delete(BaseDisplayItem *items, int items_count) { for (int i = 0; i < items_count; i++) { BaseDisplayItem *item = &items[i]; switch (item->primitive) { - case Image: + case PrimitiveImage: if (item->owns_data) { free((void *) item->data.image_data.pix); } break; - case Rect: + case PrimitiveRect: break; - case Text: + case PrimitiveText: free((char *) item->data.text_data.text); break; diff --git a/display_items.h b/display_items.h index 7587438..8fca87a 100644 --- a/display_items.h +++ b/display_items.h @@ -32,14 +32,14 @@ static inline term context_make_atom(Context *ctx, AtomString string) return globalcontext_make_atom(ctx->global, string); } -enum primitive +typedef enum { - Invalid = 0, - Image, - ScaledCroppedImage, - Rect, - Text -}; + PrimitiveInvalid = 0, + PrimitiveImage, + PrimitiveScaledCroppedImage, + PrimitiveRect, + PrimitiveText +} primitive_t; struct TextData { @@ -61,7 +61,7 @@ struct ImageDataWithSize struct BaseDisplayItem { - enum primitive primitive; + primitive_t primitive; int x; int y; int width; @@ -85,8 +85,8 @@ struct BaseDisplayItem typedef struct BaseDisplayItem BaseDisplayItem; -void init_item(BaseDisplayItem *item, term req, Context *ctx); -void destroy_items(BaseDisplayItem *items, int items_count); +void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx); +void display_items_delete(BaseDisplayItem *items, int items_count); #endif diff --git a/display_message.c b/display_message.c index 4186c5b..b810034 100644 --- a/display_message.c +++ b/display_message.c @@ -20,7 +20,7 @@ #include "display_message.h" -void send_message(term pid, term message, GlobalContext *global) +void display_message_send(term pid, term message, GlobalContext *global) { int local_process_id = term_to_local_process_id(pid); globalcontext_send_message(global, local_process_id, message); diff --git a/display_message.h b/display_message.h index e46f964..16fc1d7 100644 --- a/display_message.h +++ b/display_message.h @@ -24,6 +24,6 @@ #include #include -void send_message(term pid, term message, GlobalContext *global); +void display_message_send(term pid, term message, GlobalContext *global); #endif diff --git a/display_task.c b/display_task.c index 7f29c84..c69d6b7 100644 --- a/display_task.c +++ b/display_task.c @@ -32,7 +32,7 @@ UFontManager *ufont_manager; -NativeHandlerResult display_driver_consume_mailbox(Context *ctx) +NativeHandlerResult display_task_consume_mailbox(Context *ctx) { struct DisplayTaskArgs *args = ctx->platform_data; @@ -93,13 +93,13 @@ static bool try_handle_register_font(Message *message, Context *ctx) term return_tuple = term_alloc_tuple(2, &heap); term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); return true; } -void display_process_messages(void *arg) +void display_task_process_messages(void *arg) { struct DisplayTaskArgs *args = arg; diff --git a/display_task.h b/display_task.h index b6597eb..1e1933f 100644 --- a/display_task.h +++ b/display_task.h @@ -34,7 +34,7 @@ struct DisplayTaskArgs Context *ctx; }; -NativeHandlerResult display_driver_consume_mailbox(Context *ctx); -void display_process_messages(void *arg); +NativeHandlerResult display_task_consume_mailbox(Context *ctx); +void display_task_process_messages(void *arg); #endif diff --git a/epaper_draw.c b/epaper_draw.c index 7d8ee54..ce77ffb 100644 --- a/epaper_draw.c +++ b/epaper_draw.c @@ -311,19 +311,19 @@ int epaper_draw_x(const struct EpaperScreen *screen, int drawn_pixels = 0; switch (items[i].primitive) { - case Image: + case PrimitiveImage: drawn_pixels = epaper_draw_image_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case ScaledCroppedImage: + case PrimitiveScaledCroppedImage: drawn_pixels = epaper_draw_scaled_cropped_img_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case Rect: + case PrimitiveRect: drawn_pixels = epaper_draw_rect_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case Text: + case PrimitiveText: drawn_pixels = epaper_draw_text_x(screen, line_buf, xpos, ypos, max_line_len, item); break; diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index d4df5b1..554a476 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -164,7 +164,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -174,15 +174,15 @@ static void do_update(Context *ctx, term display_list) #if 0 // resolution command - spi_dc_writecommand(&spi->bus, 0x61); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x58); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xC0); + spi_dc_write_command(&spi->bus, 0x61); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x58); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xC0); #endif // update command - spi_dc_writecommand(&spi->bus, 0x10); + spi_dc_write_command(&spi->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); @@ -203,7 +203,7 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } @@ -219,20 +219,20 @@ static void do_update(Context *ctx, term display_list) // not sure if we should add 0x11, which is end of data command or not // power on command - spi_dc_writecommand(&spi->bus, 0x04); + spi_dc_write_command(&spi->bus, 0x04); wait_busy_level(spi, 1); // refresh command - spi_dc_writecommand(&spi->bus, 0x12); + spi_dc_write_command(&spi->bus, 0x12); uint8_t refresh_data[] = {0x00}; - spi_dc_writedatan(&spi->bus, refresh_data, sizeof(refresh_data)); + spi_dc_write_data_n(&spi->bus, refresh_data, sizeof(refresh_data)); wait_busy_level(spi, 1); // power off command - spi_dc_writecommand(&spi->bus, 0x02); + spi_dc_write_command(&spi->bus, 0x02); wait_busy_level(spi, 1); - destroy_items(items, len); + display_items_delete(items, len); update_last_refresh_ts(ctx); } @@ -274,7 +274,7 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } @@ -285,13 +285,13 @@ static void clear_screen(Context *ctx, int color) uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); #if 0 - spi_dc_writecommand(&spi->bus, 0x61); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x58); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xC0); + spi_dc_write_command(&spi->bus, 0x61); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x58); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xC0); #endif - spi_dc_writecommand(&spi->bus, 0x10); + spi_dc_write_command(&spi->bus, 0x10); bool transaction_in_progress = false; @@ -305,7 +305,7 @@ static void clear_screen(Context *ctx, int color) // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dmawrite(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } @@ -318,13 +318,13 @@ static void clear_screen(Context *ctx, int color) free(buf); - spi_dc_writecommand(&spi->bus, 0x04); + spi_dc_write_command(&spi->bus, 0x04); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0x12); + spi_dc_write_command(&spi->bus, 0x12); uint8_t refresh_data[] = {0x00}; - spi_dc_writedatan(&spi->bus, refresh_data, sizeof(refresh_data)); + spi_dc_write_data_n(&spi->bus, refresh_data, sizeof(refresh_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0x02); + spi_dc_write_command(&spi->bus, 0x02); wait_busy_level(spi, 1); } @@ -359,73 +359,73 @@ static void display_spi_init(Context *ctx, term opts) wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, 0xAA); + spi_dc_write_command(&spi->bus, 0xAA); uint8_t psr1_data[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18}; - spi_dc_writedatan(&spi->bus, psr1_data, sizeof(psr1_data)); + spi_dc_write_data_n(&spi->bus, psr1_data, sizeof(psr1_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, PWRR); + spi_dc_write_command(&spi->bus, PWRR); uint8_t pwrr_data[] = {0x3F}; - spi_dc_writedatan(&spi->bus, pwrr_data, sizeof(pwrr_data)); + spi_dc_write_data_n(&spi->bus, pwrr_data, sizeof(pwrr_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, PSR); + spi_dc_write_command(&spi->bus, PSR); uint8_t psr_data[] = {0x5F, 0x69}; - spi_dc_writedatan(&spi->bus, psr_data, sizeof(psr_data)); + spi_dc_write_data_n(&spi->bus, psr_data, sizeof(psr_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, POFS); + spi_dc_write_command(&spi->bus, POFS); uint8_t pofs_data[] = {0x00, 0x54, 0x00, 0x44}; - spi_dc_writedatan(&spi->bus, pofs_data, sizeof(pofs_data)); + spi_dc_write_data_n(&spi->bus, pofs_data, sizeof(pofs_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, BTST1); + spi_dc_write_command(&spi->bus, BTST1); uint8_t btst1_data[] = {0x40, 0x1F, 0x1F, 0x2C}; - spi_dc_writedatan(&spi->bus, btst1_data, sizeof(btst1_data)); + spi_dc_write_data_n(&spi->bus, btst1_data, sizeof(btst1_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, BTST2); + spi_dc_write_command(&spi->bus, BTST2); uint8_t btst2_data[] = {0x6F, 0x1F, 0x17, 0x49}; - spi_dc_writedatan(&spi->bus, btst2_data, sizeof(btst2_data)); + spi_dc_write_data_n(&spi->bus, btst2_data, sizeof(btst2_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, BTST3); + spi_dc_write_command(&spi->bus, BTST3); uint8_t btst3_data[] = {0x6F, 0x1F, 0x1F, 0x22}; - spi_dc_writedatan(&spi->bus, btst3_data, sizeof(btst3_data)); + spi_dc_write_data_n(&spi->bus, btst3_data, sizeof(btst3_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, PLL); + spi_dc_write_command(&spi->bus, PLL); uint8_t pll_data[] = {0x00}; - spi_dc_writedatan(&spi->bus, pll_data, sizeof(pll_data)); + spi_dc_write_data_n(&spi->bus, pll_data, sizeof(pll_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, CDI); + spi_dc_write_command(&spi->bus, CDI); uint8_t cdi_data[] = {0x3F}; - spi_dc_writedatan(&spi->bus, cdi_data, sizeof(cdi_data)); + spi_dc_write_data_n(&spi->bus, cdi_data, sizeof(cdi_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, TCON); + spi_dc_write_command(&spi->bus, TCON); uint8_t tcon_data[] = {0x02, 0x00}; - spi_dc_writedatan(&spi->bus, tcon_data, sizeof(tcon_data)); + spi_dc_write_data_n(&spi->bus, tcon_data, sizeof(tcon_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, TRES); + spi_dc_write_command(&spi->bus, TRES); uint8_t tres_data[] = {0x03, 0x20, 0x01, 0xe0}; - spi_dc_writedatan(&spi->bus, tres_data, sizeof(tres_data)); + spi_dc_write_data_n(&spi->bus, tres_data, sizeof(tres_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, T_VDCS); + spi_dc_write_command(&spi->bus, T_VDCS); uint8_t vdcs_data[] = {0x01}; - spi_dc_writedatan(&spi->bus, vdcs_data, sizeof(vdcs_data)); + spi_dc_write_data_n(&spi->bus, vdcs_data, sizeof(vdcs_data)); wait_busy_level(spi, 1); - spi_dc_writecommand(&spi->bus, PWS); + spi_dc_write_command(&spi->bus, PWS); uint8_t pws_data[] = {0x2F}; - spi_dc_writedatan(&spi->bus, pws_data, sizeof(pws_data)); + spi_dc_write_data_n(&spi->bus, pws_data, sizeof(pws_data)); wait_busy_level(spi, 1); // PON - spi_dc_writecommand(&spi->bus, 0x04); + spi_dc_write_command(&spi->bus, 0x04); wait_busy_level(spi, 1); ctx->platform_data = &spi->display_args; @@ -455,14 +455,14 @@ static void display_spi_init(Context *ctx, term opts) spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); spi->display_args.process_message_fn = process_message; spi->display_args.ctx = ctx; - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); #endif } Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_spi_init(ctx, opts); return ctx; } diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index a6bb684..4bc2420 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -105,8 +105,8 @@ struct SPI static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); -static void display_init42c(struct SPI *spi); -static void display_init41(struct SPI *spi); +static void display_init_9342c(struct SPI *spi); +static void display_init_9341(struct SPI *spi); static void do_update(Context *ctx, term display_list) { @@ -117,7 +117,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -126,7 +126,7 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); + spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -149,7 +149,7 @@ static void do_update(Context *ctx, term display_list) void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } @@ -160,7 +160,7 @@ static void do_update(Context *ctx, term display_list) spi_device_release_bus(spi->bus.spi_disp.handle); - destroy_items(items, len); + display_items_delete(items, len); } static void process_message(Message *message, Context *ctx) @@ -215,22 +215,22 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); } } Context *ili934x_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_init(ctx, opts); return ctx; } @@ -299,24 +299,24 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); if (enable_ili93442c) { - display_init42c(spi); + display_init_9342c(spi); } else { - display_init41(spi); + display_init_9341(spi); } - spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&spi->bus, DCS_LCD_INVON); } set_rotation(spi, spi->rotation); @@ -326,179 +326,179 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); } -static void display_init41(struct SPI *spi) +static void display_init_9341(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, 0xEF); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x80); - spi_dc_writedata(&spi->bus, 0x02); - - spi_dc_writecommand(&spi->bus, 0xCF); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0xC1); - spi_dc_writedata(&spi->bus, 0x30); - - spi_dc_writecommand(&spi->bus, 0xED); - spi_dc_writedata(&spi->bus, 0x64); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x12); - spi_dc_writedata(&spi->bus, 0x81); - - spi_dc_writecommand(&spi->bus, 0xE8); - spi_dc_writedata(&spi->bus, 0x85); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x78); - - spi_dc_writecommand(&spi->bus, 0xCB); - spi_dc_writedata(&spi->bus, 0x39); - spi_dc_writedata(&spi->bus, 0x2C); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x34); - spi_dc_writedata(&spi->bus, 0x02); - - spi_dc_writecommand(&spi->bus, 0xF7); - spi_dc_writedata(&spi->bus, 0x20); - - spi_dc_writecommand(&spi->bus, 0xEA); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI9341_PWCTR1); - spi_dc_writedata(&spi->bus, 0x23); - - spi_dc_writecommand(&spi->bus, ILI9341_PWCTR2); - spi_dc_writedata(&spi->bus, 0x10); - - spi_dc_writecommand(&spi->bus, ILI9341_VMCTR1); - spi_dc_writedata(&spi->bus, 0x3E); - spi_dc_writedata(&spi->bus, 0x28); - - spi_dc_writecommand(&spi->bus, ILI9341_VMCTR2); - spi_dc_writedata(&spi->bus, 0x86); - - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, 0x08); - - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x55); - - spi_dc_writecommand(&spi->bus, ILI9341_FRMCTR1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x13); - - spi_dc_writecommand(&spi->bus, ILI9341_DFUNCTR); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0xA2); - spi_dc_writedata(&spi->bus, 0x27); - - spi_dc_writecommand(&spi->bus, 0xF2); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI9341_GAMMASET); - spi_dc_writedata(&spi->bus, 0x01); - - spi_dc_writecommand(&spi->bus, ILI9341_GMCTRP1); - spi_dc_writedata(&spi->bus, 0x0F); - spi_dc_writedata(&spi->bus, 0x31); - spi_dc_writedata(&spi->bus, 0x2B); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x4E); - spi_dc_writedata(&spi->bus, 0xF1); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x10); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI9341_GMCTRN1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x14); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x31); - spi_dc_writedata(&spi->bus, 0xC1); - spi_dc_writedata(&spi->bus, 0x48); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x0F); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x31); - spi_dc_writedata(&spi->bus, 0x36); - spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_write_command(&spi->bus, 0xEF); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x80); + spi_dc_write_data(&spi->bus, 0x02); + + spi_dc_write_command(&spi->bus, 0xCF); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0xC1); + spi_dc_write_data(&spi->bus, 0x30); + + spi_dc_write_command(&spi->bus, 0xED); + spi_dc_write_data(&spi->bus, 0x64); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x12); + spi_dc_write_data(&spi->bus, 0x81); + + spi_dc_write_command(&spi->bus, 0xE8); + spi_dc_write_data(&spi->bus, 0x85); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x78); + + spi_dc_write_command(&spi->bus, 0xCB); + spi_dc_write_data(&spi->bus, 0x39); + spi_dc_write_data(&spi->bus, 0x2C); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x34); + spi_dc_write_data(&spi->bus, 0x02); + + spi_dc_write_command(&spi->bus, 0xF7); + spi_dc_write_data(&spi->bus, 0x20); + + spi_dc_write_command(&spi->bus, 0xEA); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI9341_PWCTR1); + spi_dc_write_data(&spi->bus, 0x23); + + spi_dc_write_command(&spi->bus, ILI9341_PWCTR2); + spi_dc_write_data(&spi->bus, 0x10); + + spi_dc_write_command(&spi->bus, ILI9341_VMCTR1); + spi_dc_write_data(&spi->bus, 0x3E); + spi_dc_write_data(&spi->bus, 0x28); + + spi_dc_write_command(&spi->bus, ILI9341_VMCTR2); + spi_dc_write_data(&spi->bus, 0x86); + + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, 0x08); + + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x55); + + spi_dc_write_command(&spi->bus, ILI9341_FRMCTR1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x13); + + spi_dc_write_command(&spi->bus, ILI9341_DFUNCTR); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0xA2); + spi_dc_write_data(&spi->bus, 0x27); + + spi_dc_write_command(&spi->bus, 0xF2); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI9341_GAMMASET); + spi_dc_write_data(&spi->bus, 0x01); + + spi_dc_write_command(&spi->bus, ILI9341_GMCTRP1); + spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_data(&spi->bus, 0x31); + spi_dc_write_data(&spi->bus, 0x2B); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x4E); + spi_dc_write_data(&spi->bus, 0xF1); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x10); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI9341_GMCTRN1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x14); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x31); + spi_dc_write_data(&spi->bus, 0xC1); + spi_dc_write_data(&spi->bus, 0x48); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x31); + spi_dc_write_data(&spi->bus, 0x36); + spi_dc_write_data(&spi->bus, 0x0F); } -static void display_init42c(struct SPI *spi) +static void display_init_9342c(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, 0xC8); - spi_dc_writedata(&spi->bus, 0xFF); - spi_dc_writedata(&spi->bus, 0x93); - spi_dc_writedata(&spi->bus, 0x42); - - spi_dc_writecommand(&spi->bus, ILI9341_PWCTR1); - spi_dc_writedata(&spi->bus, 0x12); - spi_dc_writedata(&spi->bus, 0x12); - - spi_dc_writecommand(&spi->bus, ILI9341_PWCTR2); - spi_dc_writedata(&spi->bus, 0x03); - - spi_dc_writecommand(&spi->bus, 0xB0); - spi_dc_writedata(&spi->bus, 0xE0); - - spi_dc_writecommand(&spi->bus, 0xF6); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0x01); - - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); - - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x55); - - spi_dc_writecommand(&spi->bus, ILI9341_DFUNCTR); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x82); - spi_dc_writedata(&spi->bus, 0x27); - - spi_dc_writecommand(&spi->bus, ILI9341_GMCTRP1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x04); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x89); - spi_dc_writedata(&spi->bus, 0x4C); - spi_dc_writedata(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x2E); - spi_dc_writedata(&spi->bus, 0x34); - spi_dc_writedata(&spi->bus, 0x0F); - - spi_dc_writecommand(&spi->bus, ILI9341_GMCTRN1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x0B); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x05); - spi_dc_writedata(&spi->bus, 0x13); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x33); - spi_dc_writedata(&spi->bus, 0x67); - spi_dc_writedata(&spi->bus, 0x48); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x0B); - spi_dc_writedata(&spi->bus, 0x2E); - spi_dc_writedata(&spi->bus, 0x33); - spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_write_command(&spi->bus, 0xC8); + spi_dc_write_data(&spi->bus, 0xFF); + spi_dc_write_data(&spi->bus, 0x93); + spi_dc_write_data(&spi->bus, 0x42); + + spi_dc_write_command(&spi->bus, ILI9341_PWCTR1); + spi_dc_write_data(&spi->bus, 0x12); + spi_dc_write_data(&spi->bus, 0x12); + + spi_dc_write_command(&spi->bus, ILI9341_PWCTR2); + spi_dc_write_data(&spi->bus, 0x03); + + spi_dc_write_command(&spi->bus, 0xB0); + spi_dc_write_data(&spi->bus, 0xE0); + + spi_dc_write_command(&spi->bus, 0xF6); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0x01); + + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); + + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x55); + + spi_dc_write_command(&spi->bus, ILI9341_DFUNCTR); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x82); + spi_dc_write_data(&spi->bus, 0x27); + + spi_dc_write_command(&spi->bus, ILI9341_GMCTRP1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x04); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x89); + spi_dc_write_data(&spi->bus, 0x4C); + spi_dc_write_data(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x2E); + spi_dc_write_data(&spi->bus, 0x34); + spi_dc_write_data(&spi->bus, 0x0F); + + spi_dc_write_command(&spi->bus, ILI9341_GMCTRN1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x0B); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x05); + spi_dc_write_data(&spi->bus, 0x13); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_data(&spi->bus, 0x67); + spi_dc_write_data(&spi->bus, 0x48); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x0B); + spi_dc_write_data(&spi->bus, 0x2E); + spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_data(&spi->bus, 0x0F); } diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 96c0645..9e2b4c2 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -111,7 +111,7 @@ struct SPI static struct DCSLCDScreen *screen; // ILI9488 scanline conversion: RGB565 -> RGB888 bytes. -static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) +static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) { for (int i = 0; i < n_pixels; i++) { uint16_t px = (uint16_t) SPI_SWAP_DATA_TX(src_swapped[i], 16); @@ -132,8 +132,8 @@ static inline void rgb565swapped_line_to_rgb888(uint8_t *dst, const uint16_t *sr static void display_init(Context *ctx, term opts); -static void display_init9486(struct SPI *spi); -static void display_init9488(struct SPI *spi); +static void display_init_9486(struct SPI *spi); +static void display_init_9488(struct SPI *spi); static void do_update(Context *ctx, term display_list) { @@ -144,7 +144,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -153,7 +153,7 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); + spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -176,14 +176,14 @@ static void do_update(Context *ctx, term display_list) screen->pixels_out = tmp; if (!spi->is_ili9488) { - spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); } else { void *tmpb = screen->bytes; screen->bytes = screen->bytes_out; screen->bytes_out = tmpb; - rgb565swapped_line_to_rgb888(screen->bytes_out, screen->pixels_out, screen_width); - spi_display_dmawrite(&spi->bus.spi_disp, screen_width * 3, screen->bytes_out); + rgb565_swapped_line_to_rgb888(screen->bytes_out, screen->pixels_out, screen_width); + spi_display_dma_write(&spi->bus.spi_disp, screen_width * 3, screen->bytes_out); } transaction_in_progress = true; @@ -196,7 +196,7 @@ static void do_update(Context *ctx, term display_list) spi_device_release_bus(spi->bus.spi_disp.handle); - destroy_items(items, len); + display_items_delete(items, len); } static void process_message(Message *message, Context *ctx) @@ -252,7 +252,7 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } @@ -282,14 +282,14 @@ static void set_rotation(struct SPI *spi, int rotation) break; } - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, madctl); + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, madctl); } Context *ili948x_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_init(ctx, opts); return ctx; } @@ -400,26 +400,26 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); if (spi->is_ili9488) { - display_init9488(spi); + display_init_9488(spi); } else { - display_init9486(spi); + display_init_9486(spi); } - spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&spi->bus, DCS_LCD_INVON); } else { - spi_dc_writecommand(&spi->bus, DCS_LCD_INVOFF); + spi_dc_write_command(&spi->bus, DCS_LCD_INVOFF); } set_rotation(spi, spi->rotation); @@ -429,157 +429,157 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); } -static void display_init9486(struct SPI *spi) +static void display_init_9486(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, ILI948X_IFMODE); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x55); - - spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR3); - spi_dc_writedata(&spi->bus, 0x44); - - spi_dc_writecommand(&spi->bus, ILI948X_VMCTR1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI948X_PGAMCTRL); - spi_dc_writedata(&spi->bus, 0x0f); - spi_dc_writedata(&spi->bus, 0x1f); - spi_dc_writedata(&spi->bus, 0x1c); - spi_dc_writedata(&spi->bus, 0x0c); - spi_dc_writedata(&spi->bus, 0x0f); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x48); - spi_dc_writedata(&spi->bus, 0x98); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x0a); - spi_dc_writedata(&spi->bus, 0x13); - spi_dc_writedata(&spi->bus, 0x04); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x0d); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI948X_NGAMCTRL); - spi_dc_writedata(&spi->bus, 0x0f); - spi_dc_writedata(&spi->bus, 0x32); - spi_dc_writedata(&spi->bus, 0x2e); - spi_dc_writedata(&spi->bus, 0x0b); - spi_dc_writedata(&spi->bus, 0x0d); - spi_dc_writedata(&spi->bus, 0x05); - spi_dc_writedata(&spi->bus, 0x47); - spi_dc_writedata(&spi->bus, 0x75); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0x10); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x24); - spi_dc_writedata(&spi->bus, 0x20); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI948X_DGAMCTRL); - spi_dc_writedata(&spi->bus, 0x0f); - spi_dc_writedata(&spi->bus, 0x32); - spi_dc_writedata(&spi->bus, 0x2e); - spi_dc_writedata(&spi->bus, 0x0b); - spi_dc_writedata(&spi->bus, 0x0d); - spi_dc_writedata(&spi->bus, 0x05); - spi_dc_writedata(&spi->bus, 0x47); - spi_dc_writedata(&spi->bus, 0x75); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0x10); - spi_dc_writedata(&spi->bus, 0x03); - spi_dc_writedata(&spi->bus, 0x24); - spi_dc_writedata(&spi->bus, 0x20); - spi_dc_writedata(&spi->bus, 0x00); + spi_dc_write_command(&spi->bus, ILI948X_IFMODE); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x55); + + spi_dc_write_command(&spi->bus, ILI948X_PWRCTR3); + spi_dc_write_data(&spi->bus, 0x44); + + spi_dc_write_command(&spi->bus, ILI948X_VMCTR1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI948X_PGAMCTRL); + spi_dc_write_data(&spi->bus, 0x0f); + spi_dc_write_data(&spi->bus, 0x1f); + spi_dc_write_data(&spi->bus, 0x1c); + spi_dc_write_data(&spi->bus, 0x0c); + spi_dc_write_data(&spi->bus, 0x0f); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x48); + spi_dc_write_data(&spi->bus, 0x98); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x0a); + spi_dc_write_data(&spi->bus, 0x13); + spi_dc_write_data(&spi->bus, 0x04); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x0d); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI948X_NGAMCTRL); + spi_dc_write_data(&spi->bus, 0x0f); + spi_dc_write_data(&spi->bus, 0x32); + spi_dc_write_data(&spi->bus, 0x2e); + spi_dc_write_data(&spi->bus, 0x0b); + spi_dc_write_data(&spi->bus, 0x0d); + spi_dc_write_data(&spi->bus, 0x05); + spi_dc_write_data(&spi->bus, 0x47); + spi_dc_write_data(&spi->bus, 0x75); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0x10); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x24); + spi_dc_write_data(&spi->bus, 0x20); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI948X_DGAMCTRL); + spi_dc_write_data(&spi->bus, 0x0f); + spi_dc_write_data(&spi->bus, 0x32); + spi_dc_write_data(&spi->bus, 0x2e); + spi_dc_write_data(&spi->bus, 0x0b); + spi_dc_write_data(&spi->bus, 0x0d); + spi_dc_write_data(&spi->bus, 0x05); + spi_dc_write_data(&spi->bus, 0x47); + spi_dc_write_data(&spi->bus, 0x75); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0x10); + spi_dc_write_data(&spi->bus, 0x03); + spi_dc_write_data(&spi->bus, 0x24); + spi_dc_write_data(&spi->bus, 0x20); + spi_dc_write_data(&spi->bus, 0x00); } -static void display_init9488(struct SPI *spi) +static void display_init_9488(struct SPI *spi) { // ILI9488: RGB666 over SPI (3 bytes/pixel). - spi_dc_writecommand(&spi->bus, ILI948X_IFMODE); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, ILI948X_ADJCTRL3); - spi_dc_writedata(&spi->bus, 0xA9); - spi_dc_writedata(&spi->bus, 0x51); - spi_dc_writedata(&spi->bus, 0x2C); - spi_dc_writedata(&spi->bus, 0x82); - - spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR1); - spi_dc_writedata(&spi->bus, 0x11); - spi_dc_writedata(&spi->bus, 0x09); - - spi_dc_writecommand(&spi->bus, ILI948X_PWRCTR2); - spi_dc_writedata(&spi->bus, 0x41); - - spi_dc_writecommand(&spi->bus, ILI948X_VMCTR1); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x80); - - spi_dc_writecommand(&spi->bus, ILI948X_FRMCTR1); - spi_dc_writedata(&spi->bus, 0xB0); - spi_dc_writedata(&spi->bus, 0x11); - - spi_dc_writecommand(&spi->bus, ILI948X_INVCTR); - spi_dc_writedata(&spi->bus, 0x02); - - spi_dc_writecommand(&spi->bus, ILI948X_DFUNCTR); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x02); - - spi_dc_writecommand(&spi->bus, ILI948X_ETMOD); - spi_dc_writedata(&spi->bus, 0xC6); - - spi_dc_writecommand(&spi->bus, ILI948X_HS_LANES_CTRL); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x04); - - spi_dc_writecommand(&spi->bus, ILI948X_IMAGE_FUNCTION); - spi_dc_writedata(&spi->bus, 0x00); - - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x66); - - spi_dc_writecommand(&spi->bus, ILI948X_PGAMCTRL); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x10); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x17); - spi_dc_writedata(&spi->bus, 0x0B); - spi_dc_writedata(&spi->bus, 0x41); - spi_dc_writedata(&spi->bus, 0x89); - spi_dc_writedata(&spi->bus, 0x4B); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x18); - spi_dc_writedata(&spi->bus, 0x1B); - spi_dc_writedata(&spi->bus, 0x0F); - - spi_dc_writecommand(&spi->bus, ILI948X_NGAMCTRL); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x17); - spi_dc_writedata(&spi->bus, 0x1A); - spi_dc_writedata(&spi->bus, 0x04); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0x2F); - spi_dc_writedata(&spi->bus, 0x45); - spi_dc_writedata(&spi->bus, 0x43); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x32); - spi_dc_writedata(&spi->bus, 0x36); - spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_write_command(&spi->bus, ILI948X_IFMODE); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, ILI948X_ADJCTRL3); + spi_dc_write_data(&spi->bus, 0xA9); + spi_dc_write_data(&spi->bus, 0x51); + spi_dc_write_data(&spi->bus, 0x2C); + spi_dc_write_data(&spi->bus, 0x82); + + spi_dc_write_command(&spi->bus, ILI948X_PWRCTR1); + spi_dc_write_data(&spi->bus, 0x11); + spi_dc_write_data(&spi->bus, 0x09); + + spi_dc_write_command(&spi->bus, ILI948X_PWRCTR2); + spi_dc_write_data(&spi->bus, 0x41); + + spi_dc_write_command(&spi->bus, ILI948X_VMCTR1); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x80); + + spi_dc_write_command(&spi->bus, ILI948X_FRMCTR1); + spi_dc_write_data(&spi->bus, 0xB0); + spi_dc_write_data(&spi->bus, 0x11); + + spi_dc_write_command(&spi->bus, ILI948X_INVCTR); + spi_dc_write_data(&spi->bus, 0x02); + + spi_dc_write_command(&spi->bus, ILI948X_DFUNCTR); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x02); + + spi_dc_write_command(&spi->bus, ILI948X_ETMOD); + spi_dc_write_data(&spi->bus, 0xC6); + + spi_dc_write_command(&spi->bus, ILI948X_HS_LANES_CTRL); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x04); + + spi_dc_write_command(&spi->bus, ILI948X_IMAGE_FUNCTION); + spi_dc_write_data(&spi->bus, 0x00); + + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x66); + + spi_dc_write_command(&spi->bus, ILI948X_PGAMCTRL); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x10); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x17); + spi_dc_write_data(&spi->bus, 0x0B); + spi_dc_write_data(&spi->bus, 0x41); + spi_dc_write_data(&spi->bus, 0x89); + spi_dc_write_data(&spi->bus, 0x4B); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x18); + spi_dc_write_data(&spi->bus, 0x1B); + spi_dc_write_data(&spi->bus, 0x0F); + + spi_dc_write_command(&spi->bus, ILI948X_NGAMCTRL); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x17); + spi_dc_write_data(&spi->bus, 0x1A); + spi_dc_write_data(&spi->bus, 0x04); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0x2F); + spi_dc_write_data(&spi->bus, 0x45); + spi_dc_write_data(&spi->bus, 0x43); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x32); + spi_dc_write_data(&spi->bus, 0x36); + spi_dc_write_data(&spi->bus, 0x0F); } diff --git a/memory_display_driver.c b/memory_display_driver.c index 68de601..4e6f093 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -115,7 +115,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -158,9 +158,9 @@ static void do_update(Context *ctx, term display_list) buf = screen->pixels; screen->dma_out = tmp; - spi_display_dmawrite(&spi->spi_disp, memsize, screen->dma_out); + spi_display_dma_write(&spi->spi_disp, memsize, screen->dma_out); } else { - spi_display_dmawrite(&spi->spi_disp, memsize, buf); + spi_display_dma_write(&spi->spi_disp, memsize, buf); } transaction_in_progress = true; @@ -172,7 +172,7 @@ static void do_update(Context *ctx, term display_list) } spi_device_release_bus(spi->spi_disp.handle); - destroy_items(items, len); + display_items_delete(items, len); } static void process_message(Message *message, Context *ctx) @@ -211,14 +211,14 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } Context *memory_lcd_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_init(ctx, opts); return ctx; } @@ -278,5 +278,5 @@ static void display_init(Context *ctx, term opts) gpio_set_level(en_gpio, 1); } - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); } diff --git a/mono_draw.c b/mono_draw.c index 98d1971..bac6e35 100644 --- a/mono_draw.c +++ b/mono_draw.c @@ -336,22 +336,22 @@ int mono_draw_x(const struct MonoScreen *screen, int drawn_pixels = 0; switch (items[i].primitive) { - case Image: + case PrimitiveImage: //fprintf(stderr, "Image\n"); drawn_pixels = mono_draw_image_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case ScaledCroppedImage: + case PrimitiveScaledCroppedImage: //fprintf(stderr, "ScaledCroppedImage\n"); drawn_pixels = mono_draw_scaled_cropped_img_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case Rect: + case PrimitiveRect: //fprintf(stderr, "Rect\n"); drawn_pixels = mono_draw_rect_x(screen, line_buf, xpos, ypos, max_line_len, item); break; - case Text: + case PrimitiveText: //fprintf(stderr, "Text\n"); drawn_pixels = mono_draw_text_x(screen, line_buf, xpos, ypos, max_line_len, item); break; diff --git a/sdl_display/display.c b/sdl_display/display.c index 3fa9796..a0a9327 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -126,17 +126,17 @@ static bool cmp_display_item(BaseDisplayItem *a, BaseDisplayItem *b) } switch (a->primitive) { - case Image: + case PrimitiveImage: return a->data.image_data.pix == b->data.image_data.pix; - case Rect: + case PrimitiveRect: return true; - case Text: + case PrimitiveText: return (a->data.text_data.fgcolor == b->data.text_data.fgcolor) && !strcmp(a->data.text_data.text, b->data.text_data.text); - case ScaledCroppedImage: + case PrimitiveScaledCroppedImage: return (a->data.image_data.pix == b->data.image_data.pix) && (a->x_scale == b->x_scale) && (a->y_scale == b->y_scale) && (a->source_x == b->source_x) && (a->source_y == b->source_y); @@ -456,19 +456,19 @@ static int draw_x(int xpos, int ypos, BaseDisplayItem *items, int items_count) int drawn_pixels = 0; switch (items[i].primitive) { - case Image: + case PrimitiveImage: drawn_pixels = draw_image_x(xpos, ypos, max_line_len, item); break; - case ScaledCroppedImage: + case PrimitiveScaledCroppedImage: drawn_pixels = draw_scaled_cropped_img_x(xpos, ypos, max_line_len, item); break; - case Rect: + case PrimitiveRect: drawn_pixels = draw_rect_x(xpos, ypos, max_line_len, item); break; - case Text: + case PrimitiveText: drawn_pixels = draw_text_x(xpos, ypos, max_line_len, item); break; @@ -496,7 +496,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -504,7 +504,7 @@ static void do_update(Context *ctx, term display_list) damaged.valid = false; dumb_diff(prev_items, prev_items_len, items, len, &damaged); if (prev_items) { - destroy_items(prev_items, prev_items_len); + display_items_delete(prev_items, prev_items_len); destroy_message(prev_message, ctx->global); } prev_items = items; @@ -735,7 +735,7 @@ void send_keyboard_event(struct KeyboardEvent *keyb, Context *ctx) term_put_tuple_element(event_tuple, 2, term_from_int(millis)); term_put_tuple_element(event_tuple, 3, event_data_tuple); - send_message(keyboard_pid, event_tuple, glb); + display_message_send(keyboard_pid, event_tuple, glb); END_WITH_STACK_HEAP(heap, glb); } @@ -807,7 +807,7 @@ void send_mouse_event(struct MouseEvent *mouse, Context *ctx) term_put_tuple_element(event_tuple, 2, term_from_int(millis)); term_put_tuple_element(event_tuple, 3, event_data_tuple); - send_message(keyboard_pid, event_tuple, glb); + display_message_send(keyboard_pid, event_tuple, glb); END_WITH_STACK_HEAP(heap, glb); } diff --git a/spi_dc_driver.c b/spi_dc_driver.c index bc183b8..e9933e9 100644 --- a/spi_dc_driver.c +++ b/spi_dc_driver.c @@ -25,31 +25,31 @@ #include #include -void spi_dc_writedata(struct SPIDCBus *bus, uint32_t data) +void spi_dc_write_data(struct SPIDCBus *bus, uint32_t data) { spi_device_acquire_bus(bus->spi_disp.handle, portMAX_DELAY); spi_display_write(&bus->spi_disp, 8, data); spi_device_release_bus(bus->spi_disp.handle); } -void spi_dc_writecommand(struct SPIDCBus *bus, uint8_t cmd) +void spi_dc_write_command(struct SPIDCBus *bus, uint8_t cmd) { gpio_set_level(bus->dc_gpio, 0); - spi_dc_writedata(bus, cmd); + spi_dc_write_data(bus, cmd); gpio_set_level(bus->dc_gpio, 1); } -void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t length) +void spi_dc_write_cmd_data(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t data_len) { - spi_dc_writecommand(bus, cmd); - for (int i = 0; i < length; i++) { - spi_dc_writedata(bus, data[i]); + spi_dc_write_command(bus, cmd); + for (int i = 0; i < data_len; i++) { + spi_dc_write_data(bus, data[i]); } } -void spi_dc_writedatan(struct SPIDCBus *bus, const uint8_t *data, size_t length) +void spi_dc_write_data_n(struct SPIDCBus *bus, const uint8_t *data, size_t data_len) { - for (size_t i = 0; i < length; i++) { - spi_dc_writedata(bus, data[i]); + for (size_t i = 0; i < data_len; i++) { + spi_dc_write_data(bus, data[i]); } } diff --git a/spi_dc_driver.h b/spi_dc_driver.h index 4208fcc..248fb25 100644 --- a/spi_dc_driver.h +++ b/spi_dc_driver.h @@ -32,9 +32,9 @@ struct SPIDCBus int dc_gpio; }; -void spi_dc_writedata(struct SPIDCBus *bus, uint32_t data); -void spi_dc_writecommand(struct SPIDCBus *bus, uint8_t cmd); -void spi_dc_writecmddata(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t length); -void spi_dc_writedatan(struct SPIDCBus *bus, const uint8_t *data, size_t length); +void spi_dc_write_data(struct SPIDCBus *bus, uint32_t data); +void spi_dc_write_command(struct SPIDCBus *bus, uint8_t cmd); +void spi_dc_write_cmd_data(struct SPIDCBus *bus, uint8_t cmd, const uint8_t *data, size_t data_len); +void spi_dc_write_data_n(struct SPIDCBus *bus, const uint8_t *data, size_t data_len); #endif diff --git a/spi_display.c b/spi_display.c index c09e002..8710114 100644 --- a/spi_display.c +++ b/spi_display.c @@ -33,7 +33,7 @@ #include "display_common.h" -bool spi_display_dmawrite(struct SPIDisplay *spi_data, int data_len, const void *data) +bool spi_display_dma_write(struct SPIDisplay *spi_data, int data_len, const void *data) { memset(&spi_data->transaction, 0, sizeof(spi_transaction_t)); @@ -44,7 +44,7 @@ bool spi_display_dmawrite(struct SPIDisplay *spi_data, int data_len, const void int ret = spi_device_queue_trans(spi_data->handle, &spi_data->transaction, portMAX_DELAY); if (UNLIKELY(ret != ESP_OK)) { - fprintf(stderr, "spidmawrite: transmit error\n"); + fprintf(stderr, "spi_display_dma_write: transmit error\n"); return false; } diff --git a/spi_display.h b/spi_display.h index b3b6a98..07bfd31 100644 --- a/spi_display.h +++ b/spi_display.h @@ -46,7 +46,7 @@ struct SPIDisplayConfig }; bool spi_display_init(struct SPIDisplay *spi_disp, struct SPIDisplayConfig *spi_config); -bool spi_display_dmawrite(struct SPIDisplay *spi_data, int data_len, const void *data); +bool spi_display_dma_write(struct SPIDisplay *spi_data, int data_len, const void *data); bool spi_display_write(struct SPIDisplay *spi_data, int data_len, uint32_t data); void spi_display_init_config(struct SPIDisplayConfig *spi_config); bool spi_display_parse_config(struct SPIDisplayConfig *spi_config, term opts, GlobalContext *global); diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index 716ad99..c9eb261 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -63,9 +63,9 @@ typedef enum { - DISPLAY_SSD1306, - DISPLAY_SSD1315, - DISPLAY_SH1106, + DisplayTypeSsd1306, + DisplayTypeSsd1315, + DisplayTypeSh1106, } display_type_t; // TODO: let's change name, since also non SPI display are supported now @@ -96,7 +96,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -135,7 +135,7 @@ static void do_update(Context *ctx, term display_list) i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0xB0 | ypos / 8, true); - if (spi->type == DISPLAY_SH1106 || spi->type == DISPLAY_SSD1315) { + if (spi->type == DisplayTypeSh1106 || spi->type == DisplayTypeSsd1315) { // SSD1315 and SH1106 require explicit column address reset i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0x00, true); @@ -145,7 +145,7 @@ static void do_update(Context *ctx, term display_list) i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true); - if (spi->type == DISPLAY_SH1106) { + if (spi->type == DisplayTypeSh1106) { // add 2 empty pages on sh1106 since it can have up to 132 pixels // and 128 pixel screen starts at (2, 0) i2c_master_write_byte(cmd, 0, true); @@ -157,7 +157,7 @@ static void do_update(Context *ctx, term display_list) } // no need to send the last 2 page, the position will be set on next line again - // if (spi->type == DISPLAY_SH1106) { + // if (spi->type == DisplayTypeSh1106) { // i2c_master_write_byte(cmd, 0, true); // i2c_master_write_byte(cmd, 0, true); // } @@ -173,7 +173,7 @@ static void do_update(Context *ctx, term display_list) i2c_driver_release(spi->i2c_host, ctx->global); free(buf); - destroy_items(items, len); + display_items_delete(items, len); } static void process_message(Message *message, Context *ctx) @@ -210,7 +210,7 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } @@ -239,7 +239,7 @@ static void display_init(Context *ctx, term opts) ctx->platform_data = &spi->display_args; spi->ctx = ctx; - spi->type = DISPLAY_SSD1306; // Default to SSD1306 + spi->type = DisplayTypeSsd1306; // Default to SSD1306 term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); int str_ok; @@ -251,9 +251,9 @@ static void display_init(Context *ctx, term opts) } if (!strcmp(compat_string, "sino-wealth,sh1106")) { - spi->type = DISPLAY_SH1106; + spi->type = DisplayTypeSh1106; } else if (!strcmp(compat_string, "solomon-systech,ssd1315")) { - spi->type = DISPLAY_SSD1315; + spi->type = DisplayTypeSsd1315; } free(compat_string); @@ -280,7 +280,7 @@ static void display_init(Context *ctx, term opts) i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true); - if (spi->type == DISPLAY_SSD1315) { + if (spi->type == DisplayTypeSsd1315) { /* * Init sequence derived from u8g2 project (BSD-2-Clause). * Source: https://github.com/olikraus/u8g2 @@ -344,7 +344,7 @@ static void display_init(Context *ctx, term opts) if (res != ESP_OK) { ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res); } else { - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); } i2c_cmd_link_delete(cmd); @@ -354,7 +354,7 @@ static void display_init(Context *ctx, term opts) Context *ssd1306_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_init(ctx, opts); return ctx; diff --git a/st7789_display_driver.c b/st7789_display_driver.c index c819b1c..62a2e92 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -118,7 +118,7 @@ static void do_update(Context *ctx, term display_list) term t = display_list; for (int i = 0; i < len; i++) { - init_item(&items[i], term_get_list_head(t), ctx); + display_items_init_item(&items[i], term_get_list_head(t), ctx); t = term_get_list_tail(t); } @@ -127,7 +127,7 @@ static void do_update(Context *ctx, term display_list) struct SPI *spi = SPI_FROM_CTX(ctx); dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_writecommand(&spi->bus, DCS_LCD_RAMWR); + spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -150,7 +150,7 @@ static void do_update(Context *ctx, term display_list) void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dmawrite(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } @@ -161,7 +161,7 @@ static void do_update(Context *ctx, term display_list) spi_device_release_bus(spi->bus.spi_disp.handle); - destroy_items(items, len); + display_items_delete(items, len); } static void process_message(Message *message, Context *ctx) @@ -216,22 +216,22 @@ static void process_message(Message *message, Context *ctx) term_put_tuple_element(return_tuple, 0, gen_message.ref); term_put_tuple_element(return_tuple, 1, OK_ATOM); - send_message(gen_message.pid, return_tuple, ctx->global); + display_message_send(gen_message.pid, return_tuple, ctx->global); END_WITH_STACK_HEAP(heap, ctx->global); } static void set_rotation(struct SPI *spi, int rotation) { if (rotation == 1) { - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); } } Context *st7789_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); - ctx->native_handler = display_driver_consume_mailbox; + ctx->native_handler = display_task_consume_mailbox; display_init(ctx, opts); return ctx; } @@ -313,7 +313,7 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); if (!reset_configured) { - spi_dc_writecommand(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); delay(100); } @@ -335,11 +335,11 @@ static void display_init(Context *ctx, term opts) set_rotation(spi, spi->rotation); if (enable_tft_invon) { - spi_dc_writecommand(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&spi->bus, DCS_LCD_INVON); } } - spi_dc_writecommand(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); delay(120); struct BacklightGPIOConfig backlight_config; @@ -347,206 +347,206 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); } static void display_init_alt_gamma_2(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_writecommand(&spi->bus, DCS_LCD_NORON); + spi_dc_write_command(&spi->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, 0x00); + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x55); + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - spi_dc_writecommand(&spi->bus, ST7789_PORCTRL); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x33); - spi_dc_writedata(&spi->bus, 0x33); + spi_dc_write_command(&spi->bus, ST7789_PORCTRL); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_data(&spi->bus, 0x33); - spi_dc_writecommand(&spi->bus, ST7789_GCTRL); - spi_dc_writedata(&spi->bus, 0x75); + spi_dc_write_command(&spi->bus, ST7789_GCTRL); + spi_dc_write_data(&spi->bus, 0x75); // - ST7789V power setting - // - spi_dc_writecommand(&spi->bus, ST7789_VCOMS); - spi_dc_writedata(&spi->bus, 0x1A); + spi_dc_write_command(&spi->bus, ST7789_VCOMS); + spi_dc_write_data(&spi->bus, 0x1A); - spi_dc_writecommand(&spi->bus, ST7789_LCMCTRL); - spi_dc_writedata(&spi->bus, 0x2C); + spi_dc_write_command(&spi->bus, ST7789_LCMCTRL); + spi_dc_write_data(&spi->bus, 0x2C); - spi_dc_writecommand(&spi->bus, ST7789_VDVVRHEN); - spi_dc_writedata(&spi->bus, 0x01); + spi_dc_write_command(&spi->bus, ST7789_VDVVRHEN); + spi_dc_write_data(&spi->bus, 0x01); - spi_dc_writecommand(&spi->bus, ST7789_VRHS); - spi_dc_writedata(&spi->bus, 0x13); + spi_dc_write_command(&spi->bus, ST7789_VRHS); + spi_dc_write_data(&spi->bus, 0x13); - spi_dc_writecommand(&spi->bus, ST7789_VDVSET); - spi_dc_writedata(&spi->bus, 0x20); + spi_dc_write_command(&spi->bus, ST7789_VDVSET); + spi_dc_write_data(&spi->bus, 0x20); - spi_dc_writecommand(&spi->bus, ST7789_FRCTR2); - spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_write_command(&spi->bus, ST7789_FRCTR2); + spi_dc_write_data(&spi->bus, 0x0F); - spi_dc_writecommand(&spi->bus, ST7789_PWCTRL1); - spi_dc_writedata(&spi->bus, 0xA4); - spi_dc_writedata(&spi->bus, 0xA1); + spi_dc_write_command(&spi->bus, ST7789_PWCTRL1); + spi_dc_write_data(&spi->bus, 0xA4); + spi_dc_write_data(&spi->bus, 0xA1); // - ST7789V gamma setting - // - spi_dc_writecommand(&spi->bus, ST7789_PVGAMCTRL); - spi_dc_writedata(&spi->bus, 0xD0); - spi_dc_writedata(&spi->bus, 0x0D); - spi_dc_writedata(&spi->bus, 0x14); - spi_dc_writedata(&spi->bus, 0x0D); - spi_dc_writedata(&spi->bus, 0x0D); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x38); - spi_dc_writedata(&spi->bus, 0x44); - spi_dc_writedata(&spi->bus, 0x4E); - spi_dc_writedata(&spi->bus, 0x3A); - spi_dc_writedata(&spi->bus, 0x17); - spi_dc_writedata(&spi->bus, 0x18); - spi_dc_writedata(&spi->bus, 0x2F); - spi_dc_writedata(&spi->bus, 0x30); - - spi_dc_writecommand(&spi->bus, ST7789_NVGAMCTRL); - spi_dc_writedata(&spi->bus, 0xD0); - spi_dc_writedata(&spi->bus, 0x09); - spi_dc_writedata(&spi->bus, 0x0F); - spi_dc_writedata(&spi->bus, 0x08); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x14); - spi_dc_writedata(&spi->bus, 0x37); - spi_dc_writedata(&spi->bus, 0x44); - spi_dc_writedata(&spi->bus, 0x4D); - spi_dc_writedata(&spi->bus, 0x38); - spi_dc_writedata(&spi->bus, 0x15); - spi_dc_writedata(&spi->bus, 0x16); - spi_dc_writedata(&spi->bus, 0x2C); - spi_dc_writedata(&spi->bus, 0x3E); - - spi_dc_writecommand(&spi->bus, DCS_LCD_CASET); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0xEF); // 239 - - spi_dc_writecommand(&spi->bus, DCS_LCD_PASET); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0x3F); // 319 + spi_dc_write_command(&spi->bus, ST7789_PVGAMCTRL); + spi_dc_write_data(&spi->bus, 0xD0); + spi_dc_write_data(&spi->bus, 0x0D); + spi_dc_write_data(&spi->bus, 0x14); + spi_dc_write_data(&spi->bus, 0x0D); + spi_dc_write_data(&spi->bus, 0x0D); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x38); + spi_dc_write_data(&spi->bus, 0x44); + spi_dc_write_data(&spi->bus, 0x4E); + spi_dc_write_data(&spi->bus, 0x3A); + spi_dc_write_data(&spi->bus, 0x17); + spi_dc_write_data(&spi->bus, 0x18); + spi_dc_write_data(&spi->bus, 0x2F); + spi_dc_write_data(&spi->bus, 0x30); + + spi_dc_write_command(&spi->bus, ST7789_NVGAMCTRL); + spi_dc_write_data(&spi->bus, 0xD0); + spi_dc_write_data(&spi->bus, 0x09); + spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_data(&spi->bus, 0x08); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x14); + spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_data(&spi->bus, 0x44); + spi_dc_write_data(&spi->bus, 0x4D); + spi_dc_write_data(&spi->bus, 0x38); + spi_dc_write_data(&spi->bus, 0x15); + spi_dc_write_data(&spi->bus, 0x16); + spi_dc_write_data(&spi->bus, 0x2C); + spi_dc_write_data(&spi->bus, 0x3E); + + spi_dc_write_command(&spi->bus, DCS_LCD_CASET); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0xEF); // 239 + + spi_dc_write_command(&spi->bus, DCS_LCD_PASET); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0x3F); // 319 } static void display_init_std(struct SPI *spi) { - spi_dc_writecommand(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_writecommand(&spi->bus, DCS_LCD_NORON); + spi_dc_write_command(&spi->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_writecommand(&spi->bus, DCS_LCD_MADCTL); - spi_dc_writedata(&spi->bus, 0x00); + spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&spi->bus, 0x00); - spi_dc_writecommand(&spi->bus, 0xB6); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x82); + spi_dc_write_command(&spi->bus, 0xB6); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x82); - spi_dc_writecommand(&spi->bus, ST7789_RAMCTRL); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0xE0); + spi_dc_write_command(&spi->bus, ST7789_RAMCTRL); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0xE0); - spi_dc_writecommand(&spi->bus, DCS_LCD_COLMOD); - spi_dc_writedata(&spi->bus, 0x55); + spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&spi->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - spi_dc_writecommand(&spi->bus, ST7789_PORCTRL); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x0C); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x33); - spi_dc_writedata(&spi->bus, 0x33); + spi_dc_write_command(&spi->bus, ST7789_PORCTRL); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_data(&spi->bus, 0x33); - spi_dc_writecommand(&spi->bus, ST7789_GCTRL); - spi_dc_writedata(&spi->bus, 0x35); + spi_dc_write_command(&spi->bus, ST7789_GCTRL); + spi_dc_write_data(&spi->bus, 0x35); // - ST7789V power setting - // - spi_dc_writecommand(&spi->bus, ST7789_VCOMS); - spi_dc_writedata(&spi->bus, 0x28); + spi_dc_write_command(&spi->bus, ST7789_VCOMS); + spi_dc_write_data(&spi->bus, 0x28); - spi_dc_writecommand(&spi->bus, ST7789_LCMCTRL); - spi_dc_writedata(&spi->bus, 0x0C); + spi_dc_write_command(&spi->bus, ST7789_LCMCTRL); + spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_writecommand(&spi->bus, ST7789_VDVVRHEN); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0xFF); + spi_dc_write_command(&spi->bus, ST7789_VDVVRHEN); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0xFF); - spi_dc_writecommand(&spi->bus, ST7789_VRHS); - spi_dc_writedata(&spi->bus, 0x10); + spi_dc_write_command(&spi->bus, ST7789_VRHS); + spi_dc_write_data(&spi->bus, 0x10); - spi_dc_writecommand(&spi->bus, ST7789_VDVSET); - spi_dc_writedata(&spi->bus, 0x20); + spi_dc_write_command(&spi->bus, ST7789_VDVSET); + spi_dc_write_data(&spi->bus, 0x20); - spi_dc_writecommand(&spi->bus, ST7789_FRCTR2); - spi_dc_writedata(&spi->bus, 0x0F); + spi_dc_write_command(&spi->bus, ST7789_FRCTR2); + spi_dc_write_data(&spi->bus, 0x0F); - spi_dc_writecommand(&spi->bus, ST7789_PWCTRL1); - spi_dc_writedata(&spi->bus, 0xA4); - spi_dc_writedata(&spi->bus, 0xA1); + spi_dc_write_command(&spi->bus, ST7789_PWCTRL1); + spi_dc_write_data(&spi->bus, 0xA4); + spi_dc_write_data(&spi->bus, 0xA1); // - ST7789V gamma setting - // - spi_dc_writecommand(&spi->bus, ST7789_PVGAMCTRL); - spi_dc_writedata(&spi->bus, 0xD0); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x28); - spi_dc_writedata(&spi->bus, 0x32); - spi_dc_writedata(&spi->bus, 0x44); - spi_dc_writedata(&spi->bus, 0x42); - spi_dc_writedata(&spi->bus, 0x06); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x12); - spi_dc_writedata(&spi->bus, 0x14); - spi_dc_writedata(&spi->bus, 0x17); - - spi_dc_writecommand(&spi->bus, ST7789_NVGAMCTRL); - spi_dc_writedata(&spi->bus, 0xD0); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x02); - spi_dc_writedata(&spi->bus, 0x07); - spi_dc_writedata(&spi->bus, 0x0A); - spi_dc_writedata(&spi->bus, 0x28); - spi_dc_writedata(&spi->bus, 0x31); - spi_dc_writedata(&spi->bus, 0x54); - spi_dc_writedata(&spi->bus, 0x47); - spi_dc_writedata(&spi->bus, 0x0E); - spi_dc_writedata(&spi->bus, 0x1C); - spi_dc_writedata(&spi->bus, 0x17); - spi_dc_writedata(&spi->bus, 0x1B); - spi_dc_writedata(&spi->bus, 0x1E); - - spi_dc_writecommand(&spi->bus, DCS_LCD_CASET); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0xEF); // 239 - - spi_dc_writecommand(&spi->bus, DCS_LCD_PASET); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x00); - spi_dc_writedata(&spi->bus, 0x01); - spi_dc_writedata(&spi->bus, 0x3F); // 319 + spi_dc_write_command(&spi->bus, ST7789_PVGAMCTRL); + spi_dc_write_data(&spi->bus, 0xD0); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x28); + spi_dc_write_data(&spi->bus, 0x32); + spi_dc_write_data(&spi->bus, 0x44); + spi_dc_write_data(&spi->bus, 0x42); + spi_dc_write_data(&spi->bus, 0x06); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x12); + spi_dc_write_data(&spi->bus, 0x14); + spi_dc_write_data(&spi->bus, 0x17); + + spi_dc_write_command(&spi->bus, ST7789_NVGAMCTRL); + spi_dc_write_data(&spi->bus, 0xD0); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x02); + spi_dc_write_data(&spi->bus, 0x07); + spi_dc_write_data(&spi->bus, 0x0A); + spi_dc_write_data(&spi->bus, 0x28); + spi_dc_write_data(&spi->bus, 0x31); + spi_dc_write_data(&spi->bus, 0x54); + spi_dc_write_data(&spi->bus, 0x47); + spi_dc_write_data(&spi->bus, 0x0E); + spi_dc_write_data(&spi->bus, 0x1C); + spi_dc_write_data(&spi->bus, 0x17); + spi_dc_write_data(&spi->bus, 0x1B); + spi_dc_write_data(&spi->bus, 0x1E); + + spi_dc_write_command(&spi->bus, DCS_LCD_CASET); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0xEF); // 239 + + spi_dc_write_command(&spi->bus, DCS_LCD_PASET); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_data(&spi->bus, 0x3F); // 319 } static void display_init_using_list(struct SPI *spi, term init_list) @@ -560,7 +560,7 @@ static void display_init_using_list(struct SPI *spi, term init_list) if (term_is_integer(cmd_term) && term_is_binary(data_term)) { avm_int_t cmd = term_to_int(cmd_term); const uint8_t *data = (const uint8_t *) term_binary_data(data_term); - spi_dc_writecmddata(&spi->bus, cmd, data, term_binary_size(data_term)); + spi_dc_write_cmd_data(&spi->bus, cmd, data, term_binary_size(data_term)); } else if ((cmd_term == context_make_atom(spi->ctx, ATOM_STR("\x8", "sleep_ms"))) && term_is_integer(data_term)) { delay(term_to_int(data_term)); From 0ca1ce60f5f914314db74db77eb4e512e99e4b4f Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 12 Apr 2026 09:58:51 +0000 Subject: [PATCH 22/52] font_data: Document style exception Add a Style Exception comment explaining that FONTDATAMAX and fontdata keep their original Linux kernel names (from lib/fonts/font_8x16.c) to simplify comparison with upstream. Per C_CODING_STYLE.md "Style Exception Documentation" section. No code changes. Signed-off-by: Davide Bettio --- font_data.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/font_data.h b/font_data.h index b3a7e46..849931c 100644 --- a/font_data.h +++ b/font_data.h @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Linux kernel developers et al. // See also lib/fonts/font_8x16.c +// +// Style Exception (AVMCCS-N001/N005/N008): the identifiers FONTDATAMAX +// and fontdata are kept verbatim from the Linux kernel font_8x16 source +// to simplify comparison with upstream. Do not rename them. #ifndef _FONT_DATA_H_ #define _FONT_DATA_H_ From 3b69a6ecc4c9310e0a5bc296ae8bdfc290d915d7 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 12 Apr 2026 10:02:11 +0000 Subject: [PATCH 23/52] Refactor array lengths to use size_t Convert data_len in spi_display_dma_write/spi_display_write and items_len in display_items_delete/find_max_line_len/draw_x from int to size_t. Rename spi_data parameter to spi_disp. Signed-off-by: Davide Bettio --- dcs_lcd_draw.c | 8 ++++---- dcs_lcd_draw.h | 4 ++-- display_items.c | 4 ++-- display_items.h | 3 ++- epaper_draw.c | 8 ++++---- epaper_draw.h | 4 ++-- mono_draw.c | 8 ++++---- mono_draw.h | 4 ++-- sdl_display/display.c | 8 ++++---- spi_display.c | 34 +++++++++++++++++----------------- spi_display.h | 5 +++-- 11 files changed, 46 insertions(+), 44 deletions(-) diff --git a/dcs_lcd_draw.c b/dcs_lcd_draw.c index 542c931..adfcb5b 100644 --- a/dcs_lcd_draw.c +++ b/dcs_lcd_draw.c @@ -32,11 +32,11 @@ #define CHAR_WIDTH 8 int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos) + BaseDisplayItem items[], size_t items_len, int xpos, int ypos) { int line_len = screen->w - xpos; - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { @@ -232,11 +232,11 @@ int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen, } int dcs_lcd_draw_x(const struct DCSLCDScreen *screen, - int xpos, int ypos, BaseDisplayItem *items, int items_count) + int xpos, int ypos, BaseDisplayItem items[], size_t items_len) { bool below = false; - for (int i = 0; i < items_count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { continue; diff --git a/dcs_lcd_draw.h b/dcs_lcd_draw.h index 0f773fe..a99b407 100644 --- a/dcs_lcd_draw.h +++ b/dcs_lcd_draw.h @@ -37,9 +37,9 @@ int dcs_lcd_draw_scaled_cropped_img_x(const struct DCSLCDScreen *screen, int xpos, int ypos, int max_line_len, BaseDisplayItem *item); int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos); + BaseDisplayItem items[], size_t items_len, int xpos, int ypos); int dcs_lcd_draw_x(const struct DCSLCDScreen *screen, - int xpos, int ypos, BaseDisplayItem *items, int items_count); + int xpos, int ypos, BaseDisplayItem items[], size_t items_len); #endif diff --git a/display_items.c b/display_items.c index aad0db5..64351a6 100644 --- a/display_items.c +++ b/display_items.c @@ -248,9 +248,9 @@ void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx) } } -void display_items_delete(BaseDisplayItem *items, int items_count) +void display_items_delete(BaseDisplayItem items[], size_t items_len) { - for (int i = 0; i < items_count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; switch (item->primitive) { diff --git a/display_items.h b/display_items.h index 8fca87a..b5d505f 100644 --- a/display_items.h +++ b/display_items.h @@ -22,6 +22,7 @@ #define _DISPLAY_ITEMS_H_ #include +#include #include #include @@ -86,7 +87,7 @@ struct BaseDisplayItem typedef struct BaseDisplayItem BaseDisplayItem; void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx); -void display_items_delete(BaseDisplayItem *items, int items_count); +void display_items_delete(BaseDisplayItem items[], size_t items_len); #endif diff --git a/epaper_draw.c b/epaper_draw.c index ce77ffb..35b054a 100644 --- a/epaper_draw.c +++ b/epaper_draw.c @@ -49,11 +49,11 @@ void epaper_draw_pixel_x(const struct EpaperScreen *screen, } int epaper_find_max_line_len(const struct EpaperScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos) + BaseDisplayItem items[], size_t items_len, int xpos, int ypos) { int line_len = screen->w - xpos; - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { @@ -297,11 +297,11 @@ int epaper_draw_scaled_cropped_img_x(const struct EpaperScreen *screen, int epaper_draw_x(const struct EpaperScreen *screen, uint8_t *line_buf, int xpos, int ypos, - BaseDisplayItem *items, int items_count) + BaseDisplayItem items[], size_t items_len) { bool below = false; - for (int i = 0; i < items_count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { continue; diff --git a/epaper_draw.h b/epaper_draw.h index 3e914cb..22df4d0 100644 --- a/epaper_draw.h +++ b/epaper_draw.h @@ -44,10 +44,10 @@ int epaper_draw_scaled_cropped_img_x(const struct EpaperScreen *screen, BaseDisplayItem *item); int epaper_find_max_line_len(const struct EpaperScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos); + BaseDisplayItem items[], size_t items_len, int xpos, int ypos); int epaper_draw_x(const struct EpaperScreen *screen, uint8_t *line_buf, int xpos, int ypos, - BaseDisplayItem *items, int items_count); + BaseDisplayItem items[], size_t items_len); #endif diff --git a/mono_draw.c b/mono_draw.c index bac6e35..f4f5fcf 100644 --- a/mono_draw.c +++ b/mono_draw.c @@ -80,11 +80,11 @@ void mono_draw_pixel_x(const struct MonoScreen *screen, } int mono_find_max_line_len(const struct MonoScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos) + BaseDisplayItem items[], size_t items_len, int xpos, int ypos) { int line_len = screen->w - xpos; - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { @@ -322,11 +322,11 @@ int mono_draw_scaled_cropped_img_x(const struct MonoScreen *screen, int mono_draw_x(const struct MonoScreen *screen, uint8_t *line_buf, int xpos, int ypos, - BaseDisplayItem *items, int items_count) + BaseDisplayItem items[], size_t items_len) { bool below = false; - for (int i = 0; i < items_count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { continue; diff --git a/mono_draw.h b/mono_draw.h index 498616a..0cda8c8 100644 --- a/mono_draw.h +++ b/mono_draw.h @@ -49,10 +49,10 @@ int mono_draw_scaled_cropped_img_x(const struct MonoScreen *screen, BaseDisplayItem *item); int mono_find_max_line_len(const struct MonoScreen *screen, - BaseDisplayItem *items, int count, int xpos, int ypos); + BaseDisplayItem items[], size_t items_len, int xpos, int ypos); int mono_draw_x(const struct MonoScreen *screen, uint8_t *line_buf, int xpos, int ypos, - BaseDisplayItem *items, int items_count); + BaseDisplayItem items[], size_t items_len); #endif diff --git a/sdl_display/display.c b/sdl_display/display.c index a0a9327..c1807b6 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -426,11 +426,11 @@ static int draw_text_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *it return drawn_pixels; } -static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int ypos) +static int find_max_line_len(BaseDisplayItem items[], size_t items_len, int xpos, int ypos) { int line_len = screen->w - xpos; - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) && (ypos >= item->y) && (ypos < item->y + item->height)) { @@ -442,11 +442,11 @@ static int find_max_line_len(BaseDisplayItem *items, int count, int xpos, int yp return line_len; } -static int draw_x(int xpos, int ypos, BaseDisplayItem *items, int items_count) +static int draw_x(int xpos, int ypos, BaseDisplayItem items[], size_t items_len) { bool below = false; - for (int i = 0; i < items_count; i++) { + for (size_t i = 0; i < items_len; i++) { BaseDisplayItem *item = &items[i]; if ((xpos < item->x) || (xpos >= item->x + item->width) || (ypos < item->y) || (ypos >= item->y + item->height)) { continue; diff --git a/spi_display.c b/spi_display.c index 8710114..f3b4a61 100644 --- a/spi_display.c +++ b/spi_display.c @@ -33,16 +33,16 @@ #include "display_common.h" -bool spi_display_dma_write(struct SPIDisplay *spi_data, int data_len, const void *data) +bool spi_display_dma_write(struct SPIDisplay *spi_disp, size_t data_len, const void *data) { - memset(&spi_data->transaction, 0, sizeof(spi_transaction_t)); + memset(&spi_disp->transaction, 0, sizeof(spi_transaction_t)); - spi_data->transaction.flags = 0; - spi_data->transaction.length = data_len * 8; - spi_data->transaction.addr = 0; - spi_data->transaction.tx_buffer = data; + spi_disp->transaction.flags = 0; + spi_disp->transaction.length = data_len * 8; + spi_disp->transaction.addr = 0; + spi_disp->transaction.tx_buffer = data; - int ret = spi_device_queue_trans(spi_data->handle, &spi_data->transaction, portMAX_DELAY); + int ret = spi_device_queue_trans(spi_disp->handle, &spi_disp->transaction, portMAX_DELAY); if (UNLIKELY(ret != ESP_OK)) { fprintf(stderr, "spi_display_dma_write: transmit error\n"); return false; @@ -51,22 +51,22 @@ bool spi_display_dma_write(struct SPIDisplay *spi_data, int data_len, const void return true; } -bool spi_display_write(struct SPIDisplay *spi_data, int data_len, uint32_t data) +bool spi_display_write(struct SPIDisplay *spi_disp, size_t data_len, uint32_t data) { - memset(&spi_data->transaction, 0, sizeof(spi_transaction_t)); + memset(&spi_disp->transaction, 0, sizeof(spi_transaction_t)); uint32_t tx_data = SPI_SWAP_DATA_TX(data, data_len); - spi_data->transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; - spi_data->transaction.length = data_len; - spi_data->transaction.addr = 0; - spi_data->transaction.tx_data[0] = tx_data; - spi_data->transaction.tx_data[1] = (tx_data >> 8) & 0xFF; - spi_data->transaction.tx_data[2] = (tx_data >> 16) & 0xFF; - spi_data->transaction.tx_data[3] = (tx_data >> 24) & 0xFF; + spi_disp->transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA; + spi_disp->transaction.length = data_len; + spi_disp->transaction.addr = 0; + spi_disp->transaction.tx_data[0] = tx_data; + spi_disp->transaction.tx_data[1] = (tx_data >> 8) & 0xFF; + spi_disp->transaction.tx_data[2] = (tx_data >> 16) & 0xFF; + spi_disp->transaction.tx_data[3] = (tx_data >> 24) & 0xFF; // this function is meant for small amount of data so polling is fine here - int ret = spi_device_polling_transmit(spi_data->handle, &spi_data->transaction); + int ret = spi_device_polling_transmit(spi_disp->handle, &spi_disp->transaction); if (UNLIKELY(ret != ESP_OK)) { fprintf(stderr, "spiwrite: transmit error\n"); return false; diff --git a/spi_display.h b/spi_display.h index 07bfd31..925c78f 100644 --- a/spi_display.h +++ b/spi_display.h @@ -24,6 +24,7 @@ #include #include +#include #include @@ -46,8 +47,8 @@ struct SPIDisplayConfig }; bool spi_display_init(struct SPIDisplay *spi_disp, struct SPIDisplayConfig *spi_config); -bool spi_display_dma_write(struct SPIDisplay *spi_data, int data_len, const void *data); -bool spi_display_write(struct SPIDisplay *spi_data, int data_len, uint32_t data); +bool spi_display_dma_write(struct SPIDisplay *spi_disp, size_t data_len, const void *data); +bool spi_display_write(struct SPIDisplay *spi_disp, size_t data_len, uint32_t data); void spi_display_init_config(struct SPIDisplayConfig *spi_config); bool spi_display_parse_config(struct SPIDisplayConfig *spi_config, term opts, GlobalContext *global); From b5f9df013cd251e89631911a237895cebf845786 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 12 Apr 2026 23:05:19 +0000 Subject: [PATCH 24/52] sdl: Add uFontLib custom font support The SDL build compiled ufontlib.c but never defined ENABLE_UFONT, so the rendering path in display_items.c was compiled out. The SDL driver also carried an outdated duplicate of struct Surface and epd_draw_pixel that was missing the fg_color field and hardcoding black. Remove the ESP_PLATFORM guard from the shared epd_draw_pixel in display_items.c, remove the stale SDL-local copy, and add -DENABLE_UFONT to the SDL CMake build. Signed-off-by: Davide Bettio --- display_items.c | 2 -- sdl_display/CMakeLists.txt | 1 + sdl_display/display.c | 22 ---------------------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/display_items.c b/display_items.c index 64351a6..c507666 100644 --- a/display_items.c +++ b/display_items.c @@ -30,7 +30,6 @@ #include "ufontlib.h" extern UFontManager *ufont_manager; -#ifdef ESP_PLATFORM struct Surface { int width; @@ -63,7 +62,6 @@ void epd_draw_pixel(int xpos, int ypos, uint8_t color, void *buffer) uint8_t alpha = (15 - (color >> 4)) * 17; *pixel = ((uint32_t) alpha << 24) | (surface->fg_color & 0x00FFFFFFu); } -#endif /* ESP_PLATFORM */ #endif /* ENABLE_UFONT */ void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx) diff --git a/sdl_display/CMakeLists.txt b/sdl_display/CMakeLists.txt index 21918d0..0d95983 100644 --- a/sdl_display/CMakeLists.txt +++ b/sdl_display/CMakeLists.txt @@ -65,6 +65,7 @@ if (ATOMIC_POINTER_LOCK_FREE_IS_TWO) target_compile_definitions(avm_display_port_driver PUBLIC HAVE_ATOMIC) endif() +target_compile_definitions(avm_display_port_driver PRIVATE ENABLE_UFONT) target_link_libraries(avm_display_port_driver ${SDL_LIBRARY} ${ZLIB_LIBRARIES}) set_property(TARGET avm_display_port_driver PROPERTY C_STANDARD 11) set_property(TARGET avm_display_port_driver PROPERTY PREFIX "") diff --git a/sdl_display/display.c b/sdl_display/display.c index c1807b6..b9aec07 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -231,28 +231,6 @@ static inline Uint32 uint32_color_to_surface(struct Screen *screen, uint32_t col return SDL_MapRGB(screen->format, (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF); } -struct Surface -{ - int width; - int height; - void *buffer; -}; - -void epd_draw_pixel(int xpos, int ypos, uint8_t color, void *buffer) -{ - struct Surface *surface = buffer; - - if (xpos < 0 || ypos < 0 || xpos >= surface->width || ypos >= surface->height) { - return; - } - - Uint32 *pixmem32b = (Uint32 *) (((uint8_t *) surface->buffer) + surface->width * ypos * BPP + xpos * BPP); - - //TODO: handle other colors than black - UNUSED(color); - *pixmem32b = 0xFF000000; -} - static int draw_image_x(int xpos, int ypos, int max_line_len, BaseDisplayItem *item) { int x = item->x; From e77553b44e23e9de670ba5b5d074c7bf36cfb1f6 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Sun, 12 Apr 2026 23:05:30 +0000 Subject: [PATCH 25/52] Fix ufont text ignoring background color The ufont rendering path hardcoded brcolor to 0 (transparent) when storing the pre-rendered glyph surface as a PrimitiveImage. This caused the scanline draw_image_x to early-return on the first transparent pixel, making the text invisible unless every pixel in the glyph bounding box had non-zero alpha. Use the background color from the display list tuple instead. Signed-off-by: Davide Bettio --- display_items.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display_items.c b/display_items.c index c507666..f06732c 100644 --- a/display_items.c +++ b/display_items.c @@ -216,7 +216,7 @@ void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx) item->primitive = PrimitiveImage; item->width = surface.width; item->height = surface.height; - item->brcolor = 0; + item->brcolor = brcolor; item->data.image_data.pix = surface.buffer; item->owns_data = true; #else From cf556f95775886b5da791e32ea6272bcc0abeccc Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 13 Apr 2026 11:11:44 +0000 Subject: [PATCH 26/52] Rename struct SPI to family-specific types struct SPI was the shared state struct across all drivers. After the family split, the generic name no longer described the shape of any given driver. The SSD1306 family also drove it further out of truth: the driver uses I2C, not SPI. Rename to family-specific types: - DCS LCD drivers (ili934x, ili948x, st7789) -> DCSLCDDriver - E-paper drivers (5in65_acep_7c, gdep073e01) -> EpaperDriver - SSD1306 family -> OLEDDriver - memory_lcd -> MemoryLCDDriver Rename the matching FROM_CTX macros and the local `spi` variable to `driver`. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 218 ++++++++--------- gdep073e01_display_driver.c | 232 +++++++++--------- ili934x_display_driver.c | 422 ++++++++++++++++----------------- ili948x_display_driver.c | 398 +++++++++++++++---------------- memory_display_driver.c | 38 +-- ssd1306_display_driver.c | 43 ++-- st7789_display_driver.c | 400 +++++++++++++++---------------- 7 files changed, 875 insertions(+), 876 deletions(-) diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index a712926..25dd27c 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -59,7 +59,7 @@ static const char *TAG = "5in65_acep_7c_display_driver"; static void clear_screen(Context *ctx, int color); -struct SPI +struct EpaperDriver { struct SPIDCBus bus; @@ -74,21 +74,21 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define EPAPER_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) static struct EpaperScreen *screen; -static void display_reset(struct SPI *spi) +static void display_reset(struct EpaperDriver *driver) { - gpio_set_level(spi->reset_gpio, 0); + gpio_set_level(driver->reset_gpio, 0); vTaskDelay(100); - gpio_set_level(spi->reset_gpio, 1); + gpio_set_level(driver->reset_gpio, 1); } -static void wait_busy_level(struct SPI *spi, int level) +static void wait_busy_level(struct EpaperDriver *driver, int level) { - while (gpio_get_level(spi->busy_gpio) != level) { + while (gpio_get_level(driver->busy_gpio) != level) { vTaskDelay(100); } } @@ -96,12 +96,12 @@ static void wait_busy_level(struct SPI *spi, int level) static void wait_some_time(Context *ctx) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); uint64_t now = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); - uint64_t delta = now - spi->last_refresh; + uint64_t delta = now - driver->last_refresh; if (delta < 2000) { // Wait 2 seconds before allowing a new refresh // this is not on datasheets, but without this the screen will not update. @@ -111,23 +111,23 @@ static void wait_some_time(Context *ctx) static void update_last_refresh_ts(Context *ctx) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); - spi->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); + driver->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); } static void maybe_refresh(Context *ctx) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - spi->count_to_refresh--; - if (spi->count_to_refresh <= 0) { + driver->count_to_refresh--; + if (driver->count_to_refresh <= 0) { // 7 is the special "clear screen color" clear_screen(ctx, 7); update_last_refresh_ts(ctx); - spi->count_to_refresh = 5; + driver->count_to_refresh = 5; } } @@ -151,29 +151,29 @@ static void do_update(Context *ctx, term display_list) int screen_width = DISPLAY_WIDTH; int screen_height = DISPLAY_HEIGHT; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); // resolution command - spi_dc_write_command(&spi->bus, 0x61); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x58); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xC0); + spi_dc_write_command(&driver->bus, 0x61); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x58); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xC0); // update command - spi_dc_write_command(&spi->bus, 0x10); + spi_dc_write_command(&driver->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); bool transaction_in_progress = false; - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); for (int ypos = 0; ypos < screen_height; ypos++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } int xpos = 0; @@ -182,32 +182,32 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); free(buf); // not sure if we should add 0x11, which is end of data command or not // power on command - spi_dc_write_command(&spi->bus, 0x04); - wait_busy_level(spi, 1); + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); // refresh command - spi_dc_write_command(&spi->bus, 0x12); - wait_busy_level(spi, 1); + spi_dc_write_command(&driver->bus, 0x12); + wait_busy_level(driver, 1); // power off command - spi_dc_write_command(&spi->bus, 0x02); - wait_busy_level(spi, 0); + spi_dc_write_command(&driver->bus, 0x02); + wait_busy_level(driver, 0); display_items_delete(items, len); @@ -257,121 +257,121 @@ static void process_message(Message *message, Context *ctx) static void clear_screen(Context *ctx, int color) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - spi_dc_write_command(&spi->bus, 0x61); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x58); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xC0); - spi_dc_write_command(&spi->bus, 0x10); + spi_dc_write_command(&driver->bus, 0x61); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x58); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xC0); + spi_dc_write_command(&driver->bus, 0x10); bool transaction_in_progress = false; - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); for (int i = 0; i < DISPLAY_HEIGHT; i++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); free(buf); - spi_dc_write_command(&spi->bus, 0x04); - wait_busy_level(spi, 1); - spi_dc_write_command(&spi->bus, 0x12); - wait_busy_level(spi, 1); - spi_dc_write_command(&spi->bus, 0x02); - wait_busy_level(spi, 0); + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); + spi_dc_write_command(&driver->bus, 0x12); + wait_busy_level(driver, 1); + spi_dc_write_command(&driver->bus, 0x02); + wait_busy_level(driver, 0); } static void display_spi_init(Context *ctx, term opts) { - struct SPI *spi = malloc(sizeof(struct SPI)); + struct EpaperDriver *driver = malloc(sizeof(struct EpaperDriver)); // TODO check here struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.clock_speed_hz = 1000000; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->bus.spi_disp, &spi_config); + spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &spi->busy_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &driver->busy_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); if (UNLIKELY(!ok)) { ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); return; } - gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(spi->reset_gpio, 1); - gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - gpio_set_pull_mode(spi->bus.dc_gpio, GPIO_PULLUP_ENABLE); - gpio_set_direction(spi->busy_gpio, GPIO_MODE_INPUT); - gpio_set_pull_mode(spi->busy_gpio, GPIO_PULLUP_ENABLE); - gpio_set_level(spi->bus.dc_gpio, 0); - - display_reset(spi); - - wait_busy_level(spi, 1); - - spi_dc_write_command(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0xEF); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_command(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x23); //datasheet says: 0x05 - spi_dc_write_data(&spi->bus, 0x23); //datasheet says: 0x05 - spi_dc_write_command(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_command(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0xC7); - spi_dc_write_data(&spi->bus, 0xC7); - spi_dc_write_data(&spi->bus, 0x1D); - spi_dc_write_command(&spi->bus, 0x30); - spi_dc_write_data(&spi->bus, 0x3C); - spi_dc_write_command(&spi->bus, 0x40); //datasheet says: 0x41 - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_command(&spi->bus, 0x50); - spi_dc_write_data(&spi->bus, 0x3F); //datasheet says: 0x37 - spi_dc_write_command(&spi->bus, 0x60); - spi_dc_write_data(&spi->bus, 0x22); - spi_dc_write_command(&spi->bus, 0x61); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x58); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xC0); - spi_dc_write_command(&spi->bus, 0xE3); - spi_dc_write_data(&spi->bus, 0xAA); - spi_dc_write_command(&spi->bus, 0x82); - spi_dc_write_data(&spi->bus, 0x80); + gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(driver->reset_gpio, 1); + gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(driver->bus.dc_gpio, GPIO_PULLUP_ENABLE); + gpio_set_direction(driver->busy_gpio, GPIO_MODE_INPUT); + gpio_set_pull_mode(driver->busy_gpio, GPIO_PULLUP_ENABLE); + gpio_set_level(driver->bus.dc_gpio, 0); + + display_reset(driver); + + wait_busy_level(driver, 1); + + spi_dc_write_command(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0xEF); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_command(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x23); //datasheet says: 0x05 + spi_dc_write_data(&driver->bus, 0x23); //datasheet says: 0x05 + spi_dc_write_command(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_command(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0xC7); + spi_dc_write_data(&driver->bus, 0xC7); + spi_dc_write_data(&driver->bus, 0x1D); + spi_dc_write_command(&driver->bus, 0x30); + spi_dc_write_data(&driver->bus, 0x3C); + spi_dc_write_command(&driver->bus, 0x40); //datasheet says: 0x41 + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_command(&driver->bus, 0x50); + spi_dc_write_data(&driver->bus, 0x3F); //datasheet says: 0x37 + spi_dc_write_command(&driver->bus, 0x60); + spi_dc_write_data(&driver->bus, 0x22); + spi_dc_write_command(&driver->bus, 0x61); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x58); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xC0); + spi_dc_write_command(&driver->bus, 0xE3); + spi_dc_write_data(&driver->bus, 0xAA); + spi_dc_write_command(&driver->bus, 0x82); + spi_dc_write_data(&driver->bus, 0x80); vTaskDelay(10); - spi_dc_write_command(&spi->bus, 0x50); - spi_dc_write_data(&spi->bus, 0x37); + spi_dc_write_command(&driver->bus, 0x50); + spi_dc_write_data(&driver->bus, 0x37); - ctx->platform_data = &spi->display_args; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; screen = calloc(1, sizeof(struct EpaperScreen)); screen->w = DISPLAY_WIDTH; @@ -380,7 +380,7 @@ static void display_spi_init(Context *ctx, term opts) screen->palette_size = 7; update_last_refresh_ts(ctx); - spi->count_to_refresh = 0; + driver->count_to_refresh = 0; #if SELF_TEST for (int i = 0; i < 8; i++) { @@ -393,10 +393,10 @@ static void display_spi_init(Context *ctx, term opts) while (1) ; #else - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); #endif } diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index 554a476..79abbb6 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -77,7 +77,7 @@ static const char *TAG = "gdep073e01"; static void clear_screen(Context *ctx, int color); -struct SPI +struct EpaperDriver { struct SPIDCBus bus; @@ -92,33 +92,33 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define EPAPER_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) static struct EpaperScreen *screen; -static void display_reset(struct SPI *spi) +static void display_reset(struct EpaperDriver *driver) { - gpio_set_level(spi->reset_gpio, 0); + gpio_set_level(driver->reset_gpio, 0); vTaskDelay(100); - gpio_set_level(spi->reset_gpio, 1); + gpio_set_level(driver->reset_gpio, 1); } -static void wait_busy_level(struct SPI *spi, int level) +static void wait_busy_level(struct EpaperDriver *driver, int level) { - while (gpio_get_level(spi->busy_gpio) != level) { + while (gpio_get_level(driver->busy_gpio) != level) { vTaskDelay(100); } } static void wait_some_time(Context *ctx) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); uint64_t now = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); - uint64_t delta = now - spi->last_refresh; + uint64_t delta = now - driver->last_refresh; if (delta < 2000) { // Wait 2 seconds before allowing a new refresh // this is not on datasheets, but without this the screen will not update. @@ -128,24 +128,24 @@ static void wait_some_time(Context *ctx) static void update_last_refresh_ts(Context *ctx) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); struct timeval tv; gettimeofday(&tv, NULL); - spi->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); + driver->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); } static void maybe_refresh(Context *ctx) { #if 0 - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - spi->count_to_refresh--; - if (spi->count_to_refresh <= 0) { + driver->count_to_refresh--; + if (driver->count_to_refresh <= 0) { // 7 is the special "clear screen color" clear_screen(ctx, 7); update_last_refresh_ts(ctx); - spi->count_to_refresh = 5; + driver->count_to_refresh = 5; } #endif } @@ -170,31 +170,31 @@ static void do_update(Context *ctx, term display_list) int screen_width = DISPLAY_WIDTH; int screen_height = DISPLAY_HEIGHT; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); #if 0 // resolution command - spi_dc_write_command(&spi->bus, 0x61); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x58); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xC0); + spi_dc_write_command(&driver->bus, 0x61); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x58); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xC0); #endif // update command - spi_dc_write_command(&spi->bus, 0x10); + spi_dc_write_command(&driver->bus, 0x10); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); memset(buf, 0x11, DISPLAY_WIDTH / 2); bool transaction_in_progress = false; - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); for (int ypos = 0; ypos < screen_height; ypos++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } int xpos = 0; @@ -203,34 +203,34 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); free(buf); // not sure if we should add 0x11, which is end of data command or not // power on command - spi_dc_write_command(&spi->bus, 0x04); - wait_busy_level(spi, 1); + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); // refresh command - spi_dc_write_command(&spi->bus, 0x12); + spi_dc_write_command(&driver->bus, 0x12); uint8_t refresh_data[] = {0x00}; - spi_dc_write_data_n(&spi->bus, refresh_data, sizeof(refresh_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, refresh_data, sizeof(refresh_data)); + wait_busy_level(driver, 1); // power off command - spi_dc_write_command(&spi->bus, 0x02); - wait_busy_level(spi, 1); + spi_dc_write_command(&driver->bus, 0x02); + wait_busy_level(driver, 1); display_items_delete(items, len); @@ -280,157 +280,157 @@ static void process_message(Message *message, Context *ctx) static void clear_screen(Context *ctx, int color) { - struct SPI *spi = SPI_FROM_CTX(ctx); + struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); #if 0 - spi_dc_write_command(&spi->bus, 0x61); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x58); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xC0); + spi_dc_write_command(&driver->bus, 0x61); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x58); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xC0); #endif - spi_dc_write_command(&spi->bus, 0x10); + spi_dc_write_command(&driver->bus, 0x10); bool transaction_in_progress = false; - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); for (int i = 0; i < DISPLAY_HEIGHT; i++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } // let's ensure a memset otherwise we might generate odd artifacts memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dma_write(&spi->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); free(buf); - spi_dc_write_command(&spi->bus, 0x04); - wait_busy_level(spi, 1); - spi_dc_write_command(&spi->bus, 0x12); + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); + spi_dc_write_command(&driver->bus, 0x12); uint8_t refresh_data[] = {0x00}; - spi_dc_write_data_n(&spi->bus, refresh_data, sizeof(refresh_data)); - wait_busy_level(spi, 1); - spi_dc_write_command(&spi->bus, 0x02); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, refresh_data, sizeof(refresh_data)); + wait_busy_level(driver, 1); + spi_dc_write_command(&driver->bus, 0x02); + wait_busy_level(driver, 1); } static void display_spi_init(Context *ctx, term opts) { - struct SPI *spi = malloc(sizeof(struct SPI)); + struct EpaperDriver *driver = malloc(sizeof(struct EpaperDriver)); // TODO check here struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.clock_speed_hz = 4000000; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->bus.spi_disp, &spi_config); + spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &spi->busy_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &driver->busy_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); if (UNLIKELY(!ok)) { ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); return; } - gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(spi->reset_gpio, 1); - gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); - gpio_set_pull_mode(spi->bus.dc_gpio, GPIO_PULLUP_ENABLE); - gpio_set_direction(spi->busy_gpio, GPIO_MODE_INPUT); - gpio_set_pull_mode(spi->busy_gpio, GPIO_PULLUP_ENABLE); - gpio_set_level(spi->bus.dc_gpio, 0); + gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(driver->reset_gpio, 1); + gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_pull_mode(driver->bus.dc_gpio, GPIO_PULLUP_ENABLE); + gpio_set_direction(driver->busy_gpio, GPIO_MODE_INPUT); + gpio_set_pull_mode(driver->busy_gpio, GPIO_PULLUP_ENABLE); + gpio_set_level(driver->bus.dc_gpio, 0); - display_reset(spi); + display_reset(driver); - wait_busy_level(spi, 1); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, 0xAA); + spi_dc_write_command(&driver->bus, 0xAA); uint8_t psr1_data[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18}; - spi_dc_write_data_n(&spi->bus, psr1_data, sizeof(psr1_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, psr1_data, sizeof(psr1_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, PWRR); + spi_dc_write_command(&driver->bus, PWRR); uint8_t pwrr_data[] = {0x3F}; - spi_dc_write_data_n(&spi->bus, pwrr_data, sizeof(pwrr_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, pwrr_data, sizeof(pwrr_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, PSR); + spi_dc_write_command(&driver->bus, PSR); uint8_t psr_data[] = {0x5F, 0x69}; - spi_dc_write_data_n(&spi->bus, psr_data, sizeof(psr_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, psr_data, sizeof(psr_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, POFS); + spi_dc_write_command(&driver->bus, POFS); uint8_t pofs_data[] = {0x00, 0x54, 0x00, 0x44}; - spi_dc_write_data_n(&spi->bus, pofs_data, sizeof(pofs_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, pofs_data, sizeof(pofs_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, BTST1); + spi_dc_write_command(&driver->bus, BTST1); uint8_t btst1_data[] = {0x40, 0x1F, 0x1F, 0x2C}; - spi_dc_write_data_n(&spi->bus, btst1_data, sizeof(btst1_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, btst1_data, sizeof(btst1_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, BTST2); + spi_dc_write_command(&driver->bus, BTST2); uint8_t btst2_data[] = {0x6F, 0x1F, 0x17, 0x49}; - spi_dc_write_data_n(&spi->bus, btst2_data, sizeof(btst2_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, btst2_data, sizeof(btst2_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, BTST3); + spi_dc_write_command(&driver->bus, BTST3); uint8_t btst3_data[] = {0x6F, 0x1F, 0x1F, 0x22}; - spi_dc_write_data_n(&spi->bus, btst3_data, sizeof(btst3_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, btst3_data, sizeof(btst3_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, PLL); + spi_dc_write_command(&driver->bus, PLL); uint8_t pll_data[] = {0x00}; - spi_dc_write_data_n(&spi->bus, pll_data, sizeof(pll_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, pll_data, sizeof(pll_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, CDI); + spi_dc_write_command(&driver->bus, CDI); uint8_t cdi_data[] = {0x3F}; - spi_dc_write_data_n(&spi->bus, cdi_data, sizeof(cdi_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, cdi_data, sizeof(cdi_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, TCON); + spi_dc_write_command(&driver->bus, TCON); uint8_t tcon_data[] = {0x02, 0x00}; - spi_dc_write_data_n(&spi->bus, tcon_data, sizeof(tcon_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, tcon_data, sizeof(tcon_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, TRES); + spi_dc_write_command(&driver->bus, TRES); uint8_t tres_data[] = {0x03, 0x20, 0x01, 0xe0}; - spi_dc_write_data_n(&spi->bus, tres_data, sizeof(tres_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, tres_data, sizeof(tres_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, T_VDCS); + spi_dc_write_command(&driver->bus, T_VDCS); uint8_t vdcs_data[] = {0x01}; - spi_dc_write_data_n(&spi->bus, vdcs_data, sizeof(vdcs_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, vdcs_data, sizeof(vdcs_data)); + wait_busy_level(driver, 1); - spi_dc_write_command(&spi->bus, PWS); + spi_dc_write_command(&driver->bus, PWS); uint8_t pws_data[] = {0x2F}; - spi_dc_write_data_n(&spi->bus, pws_data, sizeof(pws_data)); - wait_busy_level(spi, 1); + spi_dc_write_data_n(&driver->bus, pws_data, sizeof(pws_data)); + wait_busy_level(driver, 1); // PON - spi_dc_write_command(&spi->bus, 0x04); - wait_busy_level(spi, 1); + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); - ctx->platform_data = &spi->display_args; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; screen = calloc(1, sizeof(struct EpaperScreen)); screen->w = DISPLAY_WIDTH; @@ -439,7 +439,7 @@ static void display_spi_init(Context *ctx, term opts) screen->palette_size = 7; update_last_refresh_ts(ctx); - spi->count_to_refresh = 0; + driver->count_to_refresh = 0; #if SELF_TEST for (int i = 0; i < 8; i++) { @@ -452,10 +452,10 @@ static void display_spi_init(Context *ctx, term opts) while (1) ; #else - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); #endif } diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 4bc2420..6a86bb1 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -87,7 +87,7 @@ static const char *TAG = "ili934x_display_driver"; -struct SPI +struct DCSLCDDriver { struct SPIDCBus bus; int reset_gpio; @@ -99,14 +99,14 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define DCS_LCD_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); -static void display_init_9342c(struct SPI *spi); -static void display_init_9341(struct SPI *spi); +static void display_init_9342c(struct DCSLCDDriver *driver); +static void display_init_9341(struct DCSLCDDriver *driver); static void do_update(Context *ctx, term display_list) { @@ -123,11 +123,11 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -142,23 +142,23 @@ static void do_update(Context *ctx, term display_list) spi_transaction_t *trans; // I did a quick measurement, and most of the time is spent waiting for DMA transaction // eg. 23 us spent in draw_x, 188 us spent in spi_device_get_trans_result - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } //NEW CODE void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); display_items_delete(items, len); } @@ -177,7 +177,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -195,7 +195,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&spi->bus, screen, 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -219,11 +219,11 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void set_rotation(struct SPI *spi, int rotation) +static void set_rotation(struct DCSLCDDriver *driver, int rotation) { if (rotation == 1) { - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); } } @@ -244,24 +244,24 @@ static void display_init(Context *ctx, term opts) screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - struct SPI *spi = malloc(sizeof(struct SPI)); + struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - ctx->platform_data = &spi->display_args; + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->bus.spi_disp, &spi_config); + spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); int str_ok; @@ -276,7 +276,7 @@ static void display_init(Context *ctx, term opts) term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); ok = ok && term_is_integer(rotation); - spi->rotation = term_to_int(rotation); + driver->rotation = term_to_int(rotation); term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); @@ -288,217 +288,217 @@ static void display_init(Context *ctx, term opts) } // Reset - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(spi->reset_gpio, 1); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); + gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(driver->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 0); + gpio_set_level(driver->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->bus.spi_disp.handle); + gpio_set_level(driver->reset_gpio, 1); + spi_device_release_bus(driver->bus.spi_disp.handle); - gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); if (enable_ili93442c) { - display_init_9342c(spi); + display_init_9342c(driver); } else { - display_init_9341(spi); + display_init_9341(driver); } - spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_write_command(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&driver->bus, DCS_LCD_INVON); } - set_rotation(spi, spi->rotation); + set_rotation(driver, driver->rotation); struct BacklightGPIOConfig backlight_config; backlight_gpio_init_config(&backlight_config); backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_9341(struct SPI *spi) +static void display_init_9341(struct DCSLCDDriver *driver) { - spi_dc_write_command(&spi->bus, 0xEF); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x80); - spi_dc_write_data(&spi->bus, 0x02); - - spi_dc_write_command(&spi->bus, 0xCF); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0xC1); - spi_dc_write_data(&spi->bus, 0x30); - - spi_dc_write_command(&spi->bus, 0xED); - spi_dc_write_data(&spi->bus, 0x64); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x12); - spi_dc_write_data(&spi->bus, 0x81); - - spi_dc_write_command(&spi->bus, 0xE8); - spi_dc_write_data(&spi->bus, 0x85); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x78); - - spi_dc_write_command(&spi->bus, 0xCB); - spi_dc_write_data(&spi->bus, 0x39); - spi_dc_write_data(&spi->bus, 0x2C); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x34); - spi_dc_write_data(&spi->bus, 0x02); - - spi_dc_write_command(&spi->bus, 0xF7); - spi_dc_write_data(&spi->bus, 0x20); - - spi_dc_write_command(&spi->bus, 0xEA); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI9341_PWCTR1); - spi_dc_write_data(&spi->bus, 0x23); - - spi_dc_write_command(&spi->bus, ILI9341_PWCTR2); - spi_dc_write_data(&spi->bus, 0x10); - - spi_dc_write_command(&spi->bus, ILI9341_VMCTR1); - spi_dc_write_data(&spi->bus, 0x3E); - spi_dc_write_data(&spi->bus, 0x28); - - spi_dc_write_command(&spi->bus, ILI9341_VMCTR2); - spi_dc_write_data(&spi->bus, 0x86); - - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, 0x08); - - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x55); - - spi_dc_write_command(&spi->bus, ILI9341_FRMCTR1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x13); - - spi_dc_write_command(&spi->bus, ILI9341_DFUNCTR); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0xA2); - spi_dc_write_data(&spi->bus, 0x27); - - spi_dc_write_command(&spi->bus, 0xF2); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI9341_GAMMASET); - spi_dc_write_data(&spi->bus, 0x01); - - spi_dc_write_command(&spi->bus, ILI9341_GMCTRP1); - spi_dc_write_data(&spi->bus, 0x0F); - spi_dc_write_data(&spi->bus, 0x31); - spi_dc_write_data(&spi->bus, 0x2B); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x4E); - spi_dc_write_data(&spi->bus, 0xF1); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x10); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI9341_GMCTRN1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x14); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x31); - spi_dc_write_data(&spi->bus, 0xC1); - spi_dc_write_data(&spi->bus, 0x48); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x0F); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x31); - spi_dc_write_data(&spi->bus, 0x36); - spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_command(&driver->bus, 0xEF); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x80); + spi_dc_write_data(&driver->bus, 0x02); + + spi_dc_write_command(&driver->bus, 0xCF); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0xC1); + spi_dc_write_data(&driver->bus, 0x30); + + spi_dc_write_command(&driver->bus, 0xED); + spi_dc_write_data(&driver->bus, 0x64); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x12); + spi_dc_write_data(&driver->bus, 0x81); + + spi_dc_write_command(&driver->bus, 0xE8); + spi_dc_write_data(&driver->bus, 0x85); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x78); + + spi_dc_write_command(&driver->bus, 0xCB); + spi_dc_write_data(&driver->bus, 0x39); + spi_dc_write_data(&driver->bus, 0x2C); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x34); + spi_dc_write_data(&driver->bus, 0x02); + + spi_dc_write_command(&driver->bus, 0xF7); + spi_dc_write_data(&driver->bus, 0x20); + + spi_dc_write_command(&driver->bus, 0xEA); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI9341_PWCTR1); + spi_dc_write_data(&driver->bus, 0x23); + + spi_dc_write_command(&driver->bus, ILI9341_PWCTR2); + spi_dc_write_data(&driver->bus, 0x10); + + spi_dc_write_command(&driver->bus, ILI9341_VMCTR1); + spi_dc_write_data(&driver->bus, 0x3E); + spi_dc_write_data(&driver->bus, 0x28); + + spi_dc_write_command(&driver->bus, ILI9341_VMCTR2); + spi_dc_write_data(&driver->bus, 0x86); + + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, 0x08); + + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x55); + + spi_dc_write_command(&driver->bus, ILI9341_FRMCTR1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x13); + + spi_dc_write_command(&driver->bus, ILI9341_DFUNCTR); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0xA2); + spi_dc_write_data(&driver->bus, 0x27); + + spi_dc_write_command(&driver->bus, 0xF2); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI9341_GAMMASET); + spi_dc_write_data(&driver->bus, 0x01); + + spi_dc_write_command(&driver->bus, ILI9341_GMCTRP1); + spi_dc_write_data(&driver->bus, 0x0F); + spi_dc_write_data(&driver->bus, 0x31); + spi_dc_write_data(&driver->bus, 0x2B); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x4E); + spi_dc_write_data(&driver->bus, 0xF1); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x10); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI9341_GMCTRN1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x14); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x31); + spi_dc_write_data(&driver->bus, 0xC1); + spi_dc_write_data(&driver->bus, 0x48); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x0F); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x31); + spi_dc_write_data(&driver->bus, 0x36); + spi_dc_write_data(&driver->bus, 0x0F); } -static void display_init_9342c(struct SPI *spi) +static void display_init_9342c(struct DCSLCDDriver *driver) { - spi_dc_write_command(&spi->bus, 0xC8); - spi_dc_write_data(&spi->bus, 0xFF); - spi_dc_write_data(&spi->bus, 0x93); - spi_dc_write_data(&spi->bus, 0x42); - - spi_dc_write_command(&spi->bus, ILI9341_PWCTR1); - spi_dc_write_data(&spi->bus, 0x12); - spi_dc_write_data(&spi->bus, 0x12); - - spi_dc_write_command(&spi->bus, ILI9341_PWCTR2); - spi_dc_write_data(&spi->bus, 0x03); - - spi_dc_write_command(&spi->bus, 0xB0); - spi_dc_write_data(&spi->bus, 0xE0); - - spi_dc_write_command(&spi->bus, 0xF6); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0x01); - - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); - - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x55); - - spi_dc_write_command(&spi->bus, ILI9341_DFUNCTR); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x82); - spi_dc_write_data(&spi->bus, 0x27); - - spi_dc_write_command(&spi->bus, ILI9341_GMCTRP1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x04); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x89); - spi_dc_write_data(&spi->bus, 0x4C); - spi_dc_write_data(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x2E); - spi_dc_write_data(&spi->bus, 0x34); - spi_dc_write_data(&spi->bus, 0x0F); - - spi_dc_write_command(&spi->bus, ILI9341_GMCTRN1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x0B); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x05); - spi_dc_write_data(&spi->bus, 0x13); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x33); - spi_dc_write_data(&spi->bus, 0x67); - spi_dc_write_data(&spi->bus, 0x48); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x0B); - spi_dc_write_data(&spi->bus, 0x2E); - spi_dc_write_data(&spi->bus, 0x33); - spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_command(&driver->bus, 0xC8); + spi_dc_write_data(&driver->bus, 0xFF); + spi_dc_write_data(&driver->bus, 0x93); + spi_dc_write_data(&driver->bus, 0x42); + + spi_dc_write_command(&driver->bus, ILI9341_PWCTR1); + spi_dc_write_data(&driver->bus, 0x12); + spi_dc_write_data(&driver->bus, 0x12); + + spi_dc_write_command(&driver->bus, ILI9341_PWCTR2); + spi_dc_write_data(&driver->bus, 0x03); + + spi_dc_write_command(&driver->bus, 0xB0); + spi_dc_write_data(&driver->bus, 0xE0); + + spi_dc_write_command(&driver->bus, 0xF6); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0x01); + + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); + + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x55); + + spi_dc_write_command(&driver->bus, ILI9341_DFUNCTR); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x82); + spi_dc_write_data(&driver->bus, 0x27); + + spi_dc_write_command(&driver->bus, ILI9341_GMCTRP1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x04); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x89); + spi_dc_write_data(&driver->bus, 0x4C); + spi_dc_write_data(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x2E); + spi_dc_write_data(&driver->bus, 0x34); + spi_dc_write_data(&driver->bus, 0x0F); + + spi_dc_write_command(&driver->bus, ILI9341_GMCTRN1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x0B); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x05); + spi_dc_write_data(&driver->bus, 0x13); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x33); + spi_dc_write_data(&driver->bus, 0x67); + spi_dc_write_data(&driver->bus, 0x48); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x0B); + spi_dc_write_data(&driver->bus, 0x2E); + spi_dc_write_data(&driver->bus, 0x33); + spi_dc_write_data(&driver->bus, 0x0F); } diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 9e2b4c2..cd85cc2 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -90,7 +90,7 @@ static const char *TAG = "ili948x_display_driver"; -struct SPI +struct DCSLCDDriver { struct SPIDCBus bus; int reset_gpio; @@ -105,8 +105,8 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define DCS_LCD_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) static struct DCSLCDScreen *screen; @@ -132,8 +132,8 @@ static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *s static void display_init(Context *ctx, term opts); -static void display_init_9486(struct SPI *spi); -static void display_init_9488(struct SPI *spi); +static void display_init_9486(struct DCSLCDDriver *driver); +static void display_init_9488(struct DCSLCDDriver *driver); static void do_update(Context *ctx, term display_list) { @@ -150,11 +150,11 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -167,7 +167,7 @@ static void do_update(Context *ctx, term display_list) if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } // Swap scanline buffers. @@ -175,15 +175,15 @@ static void do_update(Context *ctx, term display_list) screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - if (!spi->is_ili9488) { - spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + if (!driver->is_ili9488) { + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); } else { void *tmpb = screen->bytes; screen->bytes = screen->bytes_out; screen->bytes_out = tmpb; rgb565_swapped_line_to_rgb888(screen->bytes_out, screen->pixels_out, screen_width); - spi_display_dma_write(&spi->bus.spi_disp, screen_width * 3, screen->bytes_out); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * 3, screen->bytes_out); } transaction_in_progress = true; @@ -191,10 +191,10 @@ static void do_update(Context *ctx, term display_list) if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); display_items_delete(items, len); } @@ -213,7 +213,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -231,7 +231,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&spi->bus, screen, spi->is_ili9488 ? 3 : 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, screen, driver->is_ili9488 ? 3 : 2, x, y, width, height, data); // draw_buffer is fire-and-forget. return; @@ -256,11 +256,11 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void set_rotation(struct SPI *spi, int rotation) +static void set_rotation(struct DCSLCDDriver *driver, int rotation) { uint8_t madctl = 0; - if (spi->madctl_bgr) { + if (driver->madctl_bgr) { madctl |= DCS_LCD_MAD_BGR; } @@ -282,8 +282,8 @@ static void set_rotation(struct SPI *spi, int rotation) break; } - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, madctl); + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, madctl); } Context *ili948x_display_create_port(GlobalContext *global, term opts) @@ -298,24 +298,24 @@ static void display_init(Context *ctx, term opts) { screen = calloc(1, sizeof(struct DCSLCDScreen)); - struct SPI *spi = malloc(sizeof(struct SPI)); + struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - ctx->platform_data = &spi->display_args; + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->bus.spi_disp, &spi_config); + spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); int str_ok; @@ -334,20 +334,20 @@ static void display_init(Context *ctx, term opts) if (!is_ili9486 && !is_ili9488) { ok = false; } - spi->is_ili9488 = is_ili9488; + driver->is_ili9488 = is_ili9488; // color_order: rgb|bgr (default: bgr) term color_order_term = interop_kv_get_value_default(opts, ATOM_STR("\xB", "color_order"), term_nil(), ctx->global); if (term_is_nil(color_order_term)) { - spi->madctl_bgr = true; + driver->madctl_bgr = true; } else if (term_is_atom(color_order_term)) { if (color_order_term == context_make_atom(ctx, "\x3" "rgb")) { - spi->madctl_bgr = false; + driver->madctl_bgr = false; } else if (color_order_term == context_make_atom(ctx, "\x3" "bgr")) { - spi->madctl_bgr = true; + driver->madctl_bgr = true; } else { ok = false; } @@ -357,7 +357,7 @@ static void display_init(Context *ctx, term opts) term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); ok = ok && term_is_integer(rotation); - spi->rotation = term_to_int(rotation); + driver->rotation = term_to_int(rotation); term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); @@ -369,7 +369,7 @@ static void display_init(Context *ctx, term opts) } // Swap w/h for 90/270. - if (spi->rotation & 1) { + if (driver->rotation & 1) { screen->w = ILI948X_TFTHEIGHT; screen->h = ILI948X_TFTWIDTH; } else { @@ -380,7 +380,7 @@ static void display_init(Context *ctx, term opts) screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - if (spi->is_ili9488) { + if (driver->is_ili9488) { screen->bytes = heap_caps_malloc(screen->w * 3, MALLOC_CAP_DMA); screen->bytes_out = heap_caps_malloc(screen->w * 3, MALLOC_CAP_DMA); } else { @@ -389,197 +389,197 @@ static void display_init(Context *ctx, term opts) } // Reset. - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(spi->reset_gpio, 1); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); + gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(driver->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 0); + gpio_set_level(driver->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->bus.spi_disp.handle); + gpio_set_level(driver->reset_gpio, 1); + spi_device_release_bus(driver->bus.spi_disp.handle); - gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); vTaskDelay(5 / portTICK_PERIOD_MS); - if (spi->is_ili9488) { - display_init_9488(spi); + if (driver->is_ili9488) { + display_init_9488(driver); } else { - display_init_9486(spi); + display_init_9486(driver); } - spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); vTaskDelay(120 / portTICK_PERIOD_MS); - spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); if (enable_tft_invon) { - spi_dc_write_command(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&driver->bus, DCS_LCD_INVON); } else { - spi_dc_write_command(&spi->bus, DCS_LCD_INVOFF); + spi_dc_write_command(&driver->bus, DCS_LCD_INVOFF); } - set_rotation(spi, spi->rotation); + set_rotation(driver, driver->rotation); struct BacklightGPIOConfig backlight_config; backlight_gpio_init_config(&backlight_config); backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_9486(struct SPI *spi) +static void display_init_9486(struct DCSLCDDriver *driver) { - spi_dc_write_command(&spi->bus, ILI948X_IFMODE); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x55); - - spi_dc_write_command(&spi->bus, ILI948X_PWRCTR3); - spi_dc_write_data(&spi->bus, 0x44); - - spi_dc_write_command(&spi->bus, ILI948X_VMCTR1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI948X_PGAMCTRL); - spi_dc_write_data(&spi->bus, 0x0f); - spi_dc_write_data(&spi->bus, 0x1f); - spi_dc_write_data(&spi->bus, 0x1c); - spi_dc_write_data(&spi->bus, 0x0c); - spi_dc_write_data(&spi->bus, 0x0f); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x48); - spi_dc_write_data(&spi->bus, 0x98); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x0a); - spi_dc_write_data(&spi->bus, 0x13); - spi_dc_write_data(&spi->bus, 0x04); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x0d); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI948X_NGAMCTRL); - spi_dc_write_data(&spi->bus, 0x0f); - spi_dc_write_data(&spi->bus, 0x32); - spi_dc_write_data(&spi->bus, 0x2e); - spi_dc_write_data(&spi->bus, 0x0b); - spi_dc_write_data(&spi->bus, 0x0d); - spi_dc_write_data(&spi->bus, 0x05); - spi_dc_write_data(&spi->bus, 0x47); - spi_dc_write_data(&spi->bus, 0x75); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0x10); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x24); - spi_dc_write_data(&spi->bus, 0x20); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI948X_DGAMCTRL); - spi_dc_write_data(&spi->bus, 0x0f); - spi_dc_write_data(&spi->bus, 0x32); - spi_dc_write_data(&spi->bus, 0x2e); - spi_dc_write_data(&spi->bus, 0x0b); - spi_dc_write_data(&spi->bus, 0x0d); - spi_dc_write_data(&spi->bus, 0x05); - spi_dc_write_data(&spi->bus, 0x47); - spi_dc_write_data(&spi->bus, 0x75); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0x10); - spi_dc_write_data(&spi->bus, 0x03); - spi_dc_write_data(&spi->bus, 0x24); - spi_dc_write_data(&spi->bus, 0x20); - spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_command(&driver->bus, ILI948X_IFMODE); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x55); + + spi_dc_write_command(&driver->bus, ILI948X_PWRCTR3); + spi_dc_write_data(&driver->bus, 0x44); + + spi_dc_write_command(&driver->bus, ILI948X_VMCTR1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI948X_PGAMCTRL); + spi_dc_write_data(&driver->bus, 0x0f); + spi_dc_write_data(&driver->bus, 0x1f); + spi_dc_write_data(&driver->bus, 0x1c); + spi_dc_write_data(&driver->bus, 0x0c); + spi_dc_write_data(&driver->bus, 0x0f); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x48); + spi_dc_write_data(&driver->bus, 0x98); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x0a); + spi_dc_write_data(&driver->bus, 0x13); + spi_dc_write_data(&driver->bus, 0x04); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x0d); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI948X_NGAMCTRL); + spi_dc_write_data(&driver->bus, 0x0f); + spi_dc_write_data(&driver->bus, 0x32); + spi_dc_write_data(&driver->bus, 0x2e); + spi_dc_write_data(&driver->bus, 0x0b); + spi_dc_write_data(&driver->bus, 0x0d); + spi_dc_write_data(&driver->bus, 0x05); + spi_dc_write_data(&driver->bus, 0x47); + spi_dc_write_data(&driver->bus, 0x75); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0x10); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x24); + spi_dc_write_data(&driver->bus, 0x20); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI948X_DGAMCTRL); + spi_dc_write_data(&driver->bus, 0x0f); + spi_dc_write_data(&driver->bus, 0x32); + spi_dc_write_data(&driver->bus, 0x2e); + spi_dc_write_data(&driver->bus, 0x0b); + spi_dc_write_data(&driver->bus, 0x0d); + spi_dc_write_data(&driver->bus, 0x05); + spi_dc_write_data(&driver->bus, 0x47); + spi_dc_write_data(&driver->bus, 0x75); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0x10); + spi_dc_write_data(&driver->bus, 0x03); + spi_dc_write_data(&driver->bus, 0x24); + spi_dc_write_data(&driver->bus, 0x20); + spi_dc_write_data(&driver->bus, 0x00); } -static void display_init_9488(struct SPI *spi) +static void display_init_9488(struct DCSLCDDriver *driver) { // ILI9488: RGB666 over SPI (3 bytes/pixel). - spi_dc_write_command(&spi->bus, ILI948X_IFMODE); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, ILI948X_ADJCTRL3); - spi_dc_write_data(&spi->bus, 0xA9); - spi_dc_write_data(&spi->bus, 0x51); - spi_dc_write_data(&spi->bus, 0x2C); - spi_dc_write_data(&spi->bus, 0x82); - - spi_dc_write_command(&spi->bus, ILI948X_PWRCTR1); - spi_dc_write_data(&spi->bus, 0x11); - spi_dc_write_data(&spi->bus, 0x09); - - spi_dc_write_command(&spi->bus, ILI948X_PWRCTR2); - spi_dc_write_data(&spi->bus, 0x41); - - spi_dc_write_command(&spi->bus, ILI948X_VMCTR1); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x80); - - spi_dc_write_command(&spi->bus, ILI948X_FRMCTR1); - spi_dc_write_data(&spi->bus, 0xB0); - spi_dc_write_data(&spi->bus, 0x11); - - spi_dc_write_command(&spi->bus, ILI948X_INVCTR); - spi_dc_write_data(&spi->bus, 0x02); - - spi_dc_write_command(&spi->bus, ILI948X_DFUNCTR); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x02); - - spi_dc_write_command(&spi->bus, ILI948X_ETMOD); - spi_dc_write_data(&spi->bus, 0xC6); - - spi_dc_write_command(&spi->bus, ILI948X_HS_LANES_CTRL); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x04); - - spi_dc_write_command(&spi->bus, ILI948X_IMAGE_FUNCTION); - spi_dc_write_data(&spi->bus, 0x00); - - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x66); - - spi_dc_write_command(&spi->bus, ILI948X_PGAMCTRL); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x10); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x17); - spi_dc_write_data(&spi->bus, 0x0B); - spi_dc_write_data(&spi->bus, 0x41); - spi_dc_write_data(&spi->bus, 0x89); - spi_dc_write_data(&spi->bus, 0x4B); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x18); - spi_dc_write_data(&spi->bus, 0x1B); - spi_dc_write_data(&spi->bus, 0x0F); - - spi_dc_write_command(&spi->bus, ILI948X_NGAMCTRL); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x17); - spi_dc_write_data(&spi->bus, 0x1A); - spi_dc_write_data(&spi->bus, 0x04); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0x2F); - spi_dc_write_data(&spi->bus, 0x45); - spi_dc_write_data(&spi->bus, 0x43); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x32); - spi_dc_write_data(&spi->bus, 0x36); - spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_command(&driver->bus, ILI948X_IFMODE); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, ILI948X_ADJCTRL3); + spi_dc_write_data(&driver->bus, 0xA9); + spi_dc_write_data(&driver->bus, 0x51); + spi_dc_write_data(&driver->bus, 0x2C); + spi_dc_write_data(&driver->bus, 0x82); + + spi_dc_write_command(&driver->bus, ILI948X_PWRCTR1); + spi_dc_write_data(&driver->bus, 0x11); + spi_dc_write_data(&driver->bus, 0x09); + + spi_dc_write_command(&driver->bus, ILI948X_PWRCTR2); + spi_dc_write_data(&driver->bus, 0x41); + + spi_dc_write_command(&driver->bus, ILI948X_VMCTR1); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x80); + + spi_dc_write_command(&driver->bus, ILI948X_FRMCTR1); + spi_dc_write_data(&driver->bus, 0xB0); + spi_dc_write_data(&driver->bus, 0x11); + + spi_dc_write_command(&driver->bus, ILI948X_INVCTR); + spi_dc_write_data(&driver->bus, 0x02); + + spi_dc_write_command(&driver->bus, ILI948X_DFUNCTR); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x02); + + spi_dc_write_command(&driver->bus, ILI948X_ETMOD); + spi_dc_write_data(&driver->bus, 0xC6); + + spi_dc_write_command(&driver->bus, ILI948X_HS_LANES_CTRL); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x04); + + spi_dc_write_command(&driver->bus, ILI948X_IMAGE_FUNCTION); + spi_dc_write_data(&driver->bus, 0x00); + + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x66); + + spi_dc_write_command(&driver->bus, ILI948X_PGAMCTRL); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x10); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x17); + spi_dc_write_data(&driver->bus, 0x0B); + spi_dc_write_data(&driver->bus, 0x41); + spi_dc_write_data(&driver->bus, 0x89); + spi_dc_write_data(&driver->bus, 0x4B); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x18); + spi_dc_write_data(&driver->bus, 0x1B); + spi_dc_write_data(&driver->bus, 0x0F); + + spi_dc_write_command(&driver->bus, ILI948X_NGAMCTRL); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x17); + spi_dc_write_data(&driver->bus, 0x1A); + spi_dc_write_data(&driver->bus, 0x04); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0x2F); + spi_dc_write_data(&driver->bus, 0x45); + spi_dc_write_data(&driver->bus, 0x43); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x32); + spi_dc_write_data(&driver->bus, 0x36); + spi_dc_write_data(&driver->bus, 0x0F); } diff --git a/memory_display_driver.c b/memory_display_driver.c index 4e6f093..fc65485 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -61,7 +61,7 @@ #include "font_data.h" -struct SPI +struct MemoryLCDDriver { struct SPIDisplay spi_disp; Context *ctx; @@ -69,8 +69,8 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define MEMORY_LCD_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct MemoryLCDDriver, display_args) #include "display_items.h" #include "display_message.h" @@ -121,18 +121,18 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct MemoryLCDDriver *driver = MEMORY_LCD_DRIVER_FROM_CTX(ctx); int memsize = 2 + 400 / 8 + 2; uint8_t *buf = screen->pixels; - spi_device_acquire_bus(spi->spi_disp.handle, portMAX_DELAY); + spi_device_acquire_bus(driver->spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; for (int ypos = 0; ypos < screen_height; ypos++) { if (!screen->dma_out && transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->spi_disp.handle, &trans, portMAX_DELAY); } memset(buf + 2, 0xFF, DISPLAY_WIDTH / 8); @@ -151,16 +151,16 @@ static void do_update(Context *ctx, term display_list) if (screen->dma_out) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->spi_disp.handle, &trans, portMAX_DELAY); } void *tmp = screen->pixels; screen->pixels = screen->dma_out; buf = screen->pixels; screen->dma_out = tmp; - spi_display_dma_write(&spi->spi_disp, memsize, screen->dma_out); + spi_display_dma_write(&driver->spi_disp, memsize, screen->dma_out); } else { - spi_display_dma_write(&spi->spi_disp, memsize, buf); + spi_display_dma_write(&driver->spi_disp, memsize, buf); } transaction_in_progress = true; @@ -168,10 +168,10 @@ static void do_update(Context *ctx, term display_list) if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->spi_disp.handle); + spi_device_release_bus(driver->spi_disp.handle); display_items_delete(items, len); } @@ -250,14 +250,14 @@ static void display_init(Context *ctx, term opts) GlobalContext *glb = ctx->global; - struct SPI *spi = malloc(sizeof(struct SPI)); + struct MemoryLCDDriver *driver = malloc(sizeof(struct MemoryLCDDriver)); - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - ctx->platform_data = &spi->display_args; + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); @@ -268,7 +268,7 @@ static void display_init(Context *ctx, term opts) spi_config.cs_ena_pretrans = 4; // it should be at least 3us spi_config.cs_ena_posttrans = 2; // it should be at least 1us spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->spi_disp, &spi_config); + spi_display_init(&driver->spi_disp, &spi_config); int en_gpio; bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "en"), &en_gpio, glb); @@ -278,5 +278,5 @@ static void display_init(Context *ctx, term opts) gpio_set_level(en_gpio, 1); } - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index c9eb261..c02ff4e 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -68,8 +68,7 @@ typedef enum DisplayTypeSh1106, } display_type_t; -// TODO: let's change name, since also non SPI display are supported now -struct SPI +struct OLEDDriver { term i2c_host; display_type_t type; @@ -78,8 +77,8 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define OLED_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct OLEDDriver, display_args) static struct MonoScreen *mono_screen; @@ -102,14 +101,14 @@ static void do_update(Context *ctx, term display_list) int screen_width = DISPLAY_WIDTH; int screen_height = DISPLAY_HEIGHT; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct OLEDDriver *driver = OLED_DRIVER_FROM_CTX(ctx); int memsize = (DISPLAY_WIDTH * (PAGE_HEIGHT + 1)) / sizeof(uint8_t); uint8_t *buf = malloc(memsize); memset(buf, 0, memsize); i2c_port_t i2c_num; - if (i2c_driver_acquire(spi->i2c_host, &i2c_num, ctx->global) != I2CAcquireOk) { + if (i2c_driver_acquire(driver->i2c_host, &i2c_num, ctx->global) != I2CAcquireOk) { fprintf(stderr, "Invalid I2C peripheral\n"); return; } @@ -135,7 +134,7 @@ static void do_update(Context *ctx, term display_list) i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0xB0 | ypos / 8, true); - if (spi->type == DisplayTypeSh1106 || spi->type == DisplayTypeSsd1315) { + if (driver->type == DisplayTypeSh1106 || driver->type == DisplayTypeSsd1315) { // SSD1315 and SH1106 require explicit column address reset i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0x00, true); @@ -145,7 +144,7 @@ static void do_update(Context *ctx, term display_list) i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true); - if (spi->type == DisplayTypeSh1106) { + if (driver->type == DisplayTypeSh1106) { // add 2 empty pages on sh1106 since it can have up to 132 pixels // and 128 pixel screen starts at (2, 0) i2c_master_write_byte(cmd, 0, true); @@ -157,7 +156,7 @@ static void do_update(Context *ctx, term display_list) } // no need to send the last 2 page, the position will be set on next line again - // if (spi->type == DisplayTypeSh1106) { + // if (driver->type == DisplayTypeSh1106) { // i2c_master_write_byte(cmd, 0, true); // i2c_master_write_byte(cmd, 0, true); // } @@ -170,7 +169,7 @@ static void do_update(Context *ctx, term display_list) } } - i2c_driver_release(spi->i2c_host, ctx->global); + i2c_driver_release(driver->i2c_host, ctx->global); free(buf); display_items_delete(items, len); @@ -231,15 +230,15 @@ static void display_init(Context *ctx, term opts) mono_screen->w = DISPLAY_WIDTH; mono_screen->h = DISPLAY_HEIGHT; - struct SPI *spi = malloc(sizeof(struct SPI)); + struct OLEDDriver *driver = malloc(sizeof(struct OLEDDriver)); - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - ctx->platform_data = &spi->display_args; + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; - spi->type = DisplayTypeSsd1306; // Default to SSD1306 + driver->ctx = ctx; + driver->type = DisplayTypeSsd1306; // Default to SSD1306 term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); int str_ok; @@ -251,9 +250,9 @@ static void display_init(Context *ctx, term opts) } if (!strcmp(compat_string, "sino-wealth,sh1106")) { - spi->type = DisplayTypeSh1106; + driver->type = DisplayTypeSh1106; } else if (!strcmp(compat_string, "solomon-systech,ssd1315")) { - spi->type = DisplayTypeSsd1315; + driver->type = DisplayTypeSsd1315; } free(compat_string); @@ -273,14 +272,14 @@ static void display_init(Context *ctx, term opts) fprintf(stderr, "Invalid I2C peripheral\n"); return; } - spi->i2c_host = i2c_host; + driver->i2c_host = i2c_host; i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true); - if (spi->type == DisplayTypeSsd1315) { + if (driver->type == DisplayTypeSsd1315) { /* * Init sequence derived from u8g2 project (BSD-2-Clause). * Source: https://github.com/olikraus/u8g2 @@ -344,7 +343,7 @@ static void display_init(Context *ctx, term opts) if (res != ESP_OK) { ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res); } else { - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } i2c_cmd_link_delete(cmd); diff --git a/st7789_display_driver.c b/st7789_display_driver.c index 62a2e92..f17ee7f 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -87,7 +87,7 @@ static inline void delay(int ms) vTaskDelay(ms / portTICK_PERIOD_MS); } -struct SPI +struct DCSLCDDriver { struct SPIDCBus bus; int reset_gpio; @@ -99,15 +99,15 @@ struct SPI struct DisplayTaskArgs display_args; }; -#define SPI_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct SPI, display_args) +#define DCS_LCD_DRIVER_FROM_CTX(ctx) \ + CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); -static void display_init_alt_gamma_2(struct SPI *spi); -static void display_init_std(struct SPI *spi); -static void display_init_using_list(struct SPI *spi, term init_list); +static void display_init_alt_gamma_2(struct DCSLCDDriver *driver); +static void display_init_std(struct DCSLCDDriver *driver); +static void display_init_using_list(struct DCSLCDDriver *driver, term init_list); static void do_update(Context *ctx, term display_list) { @@ -124,11 +124,11 @@ static void do_update(Context *ctx, term display_list) int screen_width = screen->w; int screen_height = screen->h; - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - dcs_lcd_set_paint_area(&spi->bus, screen, 0, 0, screen_width, screen_height); - spi_dc_write_command(&spi->bus, DCS_LCD_RAMWR); - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); + dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; @@ -143,23 +143,23 @@ static void do_update(Context *ctx, term display_list) spi_transaction_t *trans; // I did a quick measurement, and most of the time is spent waiting for DMA transaction // eg. 23 us spent in draw_x, 188 us spent in spi_device_get_trans_result - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } // NEW CODE void *tmp = screen->pixels; screen->pixels = screen->pixels_out; screen->pixels_out = tmp; - spi_display_dma_write(&spi->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); transaction_in_progress = true; } if (transaction_in_progress) { spi_transaction_t *trans; - spi_device_get_trans_result(spi->bus.spi_disp.handle, &trans, portMAX_DELAY); + spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - spi_device_release_bus(spi->bus.spi_disp.handle); + spi_device_release_bus(driver->bus.spi_disp.handle); display_items_delete(items, len); } @@ -178,7 +178,7 @@ static void process_message(Message *message, Context *ctx) } term cmd = term_get_tuple_element(req, 0); - struct SPI *spi = SPI_FROM_CTX(ctx); + struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); if (cmd == context_make_atom(ctx, "\x6" "update")) { @@ -196,7 +196,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&spi->bus, screen, 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -220,11 +220,11 @@ static void process_message(Message *message, Context *ctx) END_WITH_STACK_HEAP(heap, ctx->global); } -static void set_rotation(struct SPI *spi, int rotation) +static void set_rotation(struct DCSLCDDriver *driver, int rotation) { if (rotation == 1) { - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); } } @@ -249,33 +249,33 @@ static void display_init(Context *ctx, term opts) screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - struct SPI *spi = malloc(sizeof(struct SPI)); + struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); - spi->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - spi->display_args.process_message_fn = process_message; - spi->display_args.ctx = ctx; - ctx->platform_data = &spi->display_args; + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; - spi->ctx = ctx; + driver->ctx = ctx; struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.mode = SPI_MODE; spi_config.clock_speed_hz = SPI_CLOCK_HZ; spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&spi->bus.spi_disp, &spi_config); + spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &spi->bus.dc_gpio, ctx->global); + bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); bool reset_configured = true; - if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &spi->reset_gpio, ctx->global)) { + if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global)) { ESP_LOGI(TAG, "Reset GPIO not configured."); reset_configured = false; } term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); ok = ok && term_is_integer(rotation); - spi->rotation = term_to_int(rotation); + driver->rotation = term_to_int(rotation); term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); @@ -300,46 +300,46 @@ static void display_init(Context *ctx, term opts) // Reset if (reset_configured) { - spi_device_acquire_bus(spi->bus.spi_disp.handle, portMAX_DELAY); - gpio_set_direction(spi->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(spi->reset_gpio, 1); + spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); + gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); + gpio_set_level(driver->reset_gpio, 1); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 0); + gpio_set_level(driver->reset_gpio, 0); vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(spi->reset_gpio, 1); - spi_device_release_bus(spi->bus.spi_disp.handle); + gpio_set_level(driver->reset_gpio, 1); + spi_device_release_bus(driver->bus.spi_disp.handle); } - gpio_set_direction(spi->bus.dc_gpio, GPIO_MODE_OUTPUT); + gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); if (!reset_configured) { - spi_dc_write_command(&spi->bus, DCS_LCD_SWRESET); + spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); delay(100); } term maybe_init_list = interop_kv_get_value_default(opts, ATOM_STR("\x9", "init_list"), term_nil(), ctx->global); if (maybe_init_list != term_nil()) { - display_init_using_list(spi, maybe_init_list); + display_init_using_list(driver, maybe_init_list); } else { term init_seq_type_term = interop_kv_get_value_default(opts, ATOM_STR("\xD", "init_seq_type"), term_nil(), ctx->global); int str_ok; char *init_seq_type_string = interop_term_to_string(init_seq_type_term, &str_ok); if (str_ok && !strcmp(init_seq_type_string, "alt_gamma_2")) { - display_init_alt_gamma_2(spi); + display_init_alt_gamma_2(driver); free(init_seq_type_string); } else { - display_init_std(spi); + display_init_std(driver); } - set_rotation(spi, spi->rotation); + set_rotation(driver, driver->rotation); if (enable_tft_invon) { - spi_dc_write_command(&spi->bus, DCS_LCD_INVON); + spi_dc_write_command(&driver->bus, DCS_LCD_INVON); } } - spi_dc_write_command(&spi->bus, DCS_LCD_DISPON); + spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); delay(120); struct BacklightGPIOConfig backlight_config; @@ -347,209 +347,209 @@ static void display_init(Context *ctx, term opts) backlight_gpio_parse_config(&backlight_config, opts, ctx->global); backlight_gpio_init(&backlight_config); - xTaskCreate(display_task_process_messages, "display", 10000, &spi->display_args, 1, NULL); + xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_alt_gamma_2(struct SPI *spi) +static void display_init_alt_gamma_2(struct DCSLCDDriver *driver) { - spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_write_command(&spi->bus, DCS_LCD_NORON); + spi_dc_write_command(&driver->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x55); + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - spi_dc_write_command(&spi->bus, ST7789_PORCTRL); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x33); - spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_command(&driver->bus, ST7789_PORCTRL); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x33); + spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_command(&spi->bus, ST7789_GCTRL); - spi_dc_write_data(&spi->bus, 0x75); + spi_dc_write_command(&driver->bus, ST7789_GCTRL); + spi_dc_write_data(&driver->bus, 0x75); // - ST7789V power setting - // - spi_dc_write_command(&spi->bus, ST7789_VCOMS); - spi_dc_write_data(&spi->bus, 0x1A); + spi_dc_write_command(&driver->bus, ST7789_VCOMS); + spi_dc_write_data(&driver->bus, 0x1A); - spi_dc_write_command(&spi->bus, ST7789_LCMCTRL); - spi_dc_write_data(&spi->bus, 0x2C); + spi_dc_write_command(&driver->bus, ST7789_LCMCTRL); + spi_dc_write_data(&driver->bus, 0x2C); - spi_dc_write_command(&spi->bus, ST7789_VDVVRHEN); - spi_dc_write_data(&spi->bus, 0x01); + spi_dc_write_command(&driver->bus, ST7789_VDVVRHEN); + spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_command(&spi->bus, ST7789_VRHS); - spi_dc_write_data(&spi->bus, 0x13); + spi_dc_write_command(&driver->bus, ST7789_VRHS); + spi_dc_write_data(&driver->bus, 0x13); - spi_dc_write_command(&spi->bus, ST7789_VDVSET); - spi_dc_write_data(&spi->bus, 0x20); + spi_dc_write_command(&driver->bus, ST7789_VDVSET); + spi_dc_write_data(&driver->bus, 0x20); - spi_dc_write_command(&spi->bus, ST7789_FRCTR2); - spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_command(&driver->bus, ST7789_FRCTR2); + spi_dc_write_data(&driver->bus, 0x0F); - spi_dc_write_command(&spi->bus, ST7789_PWCTRL1); - spi_dc_write_data(&spi->bus, 0xA4); - spi_dc_write_data(&spi->bus, 0xA1); + spi_dc_write_command(&driver->bus, ST7789_PWCTRL1); + spi_dc_write_data(&driver->bus, 0xA4); + spi_dc_write_data(&driver->bus, 0xA1); // - ST7789V gamma setting - // - spi_dc_write_command(&spi->bus, ST7789_PVGAMCTRL); - spi_dc_write_data(&spi->bus, 0xD0); - spi_dc_write_data(&spi->bus, 0x0D); - spi_dc_write_data(&spi->bus, 0x14); - spi_dc_write_data(&spi->bus, 0x0D); - spi_dc_write_data(&spi->bus, 0x0D); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x38); - spi_dc_write_data(&spi->bus, 0x44); - spi_dc_write_data(&spi->bus, 0x4E); - spi_dc_write_data(&spi->bus, 0x3A); - spi_dc_write_data(&spi->bus, 0x17); - spi_dc_write_data(&spi->bus, 0x18); - spi_dc_write_data(&spi->bus, 0x2F); - spi_dc_write_data(&spi->bus, 0x30); - - spi_dc_write_command(&spi->bus, ST7789_NVGAMCTRL); - spi_dc_write_data(&spi->bus, 0xD0); - spi_dc_write_data(&spi->bus, 0x09); - spi_dc_write_data(&spi->bus, 0x0F); - spi_dc_write_data(&spi->bus, 0x08); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x14); - spi_dc_write_data(&spi->bus, 0x37); - spi_dc_write_data(&spi->bus, 0x44); - spi_dc_write_data(&spi->bus, 0x4D); - spi_dc_write_data(&spi->bus, 0x38); - spi_dc_write_data(&spi->bus, 0x15); - spi_dc_write_data(&spi->bus, 0x16); - spi_dc_write_data(&spi->bus, 0x2C); - spi_dc_write_data(&spi->bus, 0x3E); - - spi_dc_write_command(&spi->bus, DCS_LCD_CASET); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0xEF); // 239 - - spi_dc_write_command(&spi->bus, DCS_LCD_PASET); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0x3F); // 319 + spi_dc_write_command(&driver->bus, ST7789_PVGAMCTRL); + spi_dc_write_data(&driver->bus, 0xD0); + spi_dc_write_data(&driver->bus, 0x0D); + spi_dc_write_data(&driver->bus, 0x14); + spi_dc_write_data(&driver->bus, 0x0D); + spi_dc_write_data(&driver->bus, 0x0D); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x38); + spi_dc_write_data(&driver->bus, 0x44); + spi_dc_write_data(&driver->bus, 0x4E); + spi_dc_write_data(&driver->bus, 0x3A); + spi_dc_write_data(&driver->bus, 0x17); + spi_dc_write_data(&driver->bus, 0x18); + spi_dc_write_data(&driver->bus, 0x2F); + spi_dc_write_data(&driver->bus, 0x30); + + spi_dc_write_command(&driver->bus, ST7789_NVGAMCTRL); + spi_dc_write_data(&driver->bus, 0xD0); + spi_dc_write_data(&driver->bus, 0x09); + spi_dc_write_data(&driver->bus, 0x0F); + spi_dc_write_data(&driver->bus, 0x08); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x14); + spi_dc_write_data(&driver->bus, 0x37); + spi_dc_write_data(&driver->bus, 0x44); + spi_dc_write_data(&driver->bus, 0x4D); + spi_dc_write_data(&driver->bus, 0x38); + spi_dc_write_data(&driver->bus, 0x15); + spi_dc_write_data(&driver->bus, 0x16); + spi_dc_write_data(&driver->bus, 0x2C); + spi_dc_write_data(&driver->bus, 0x3E); + + spi_dc_write_command(&driver->bus, DCS_LCD_CASET); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0xEF); // 239 + + spi_dc_write_command(&driver->bus, DCS_LCD_PASET); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0x3F); // 319 } -static void display_init_std(struct SPI *spi) +static void display_init_std(struct DCSLCDDriver *driver) { - spi_dc_write_command(&spi->bus, DCS_LCD_SLPOUT); + spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); delay(120); - spi_dc_write_command(&spi->bus, DCS_LCD_NORON); + spi_dc_write_command(&driver->bus, DCS_LCD_NORON); // - display and color format setting - // - spi_dc_write_command(&spi->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&spi->bus, 0x00); + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_command(&spi->bus, 0xB6); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x82); + spi_dc_write_command(&driver->bus, 0xB6); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x82); - spi_dc_write_command(&spi->bus, ST7789_RAMCTRL); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0xE0); + spi_dc_write_command(&driver->bus, ST7789_RAMCTRL); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0xE0); - spi_dc_write_command(&spi->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&spi->bus, 0x55); + spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); + spi_dc_write_data(&driver->bus, 0x55); delay(10); // - ST7789V frame rate setting - // - spi_dc_write_command(&spi->bus, ST7789_PORCTRL); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x0C); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x33); - spi_dc_write_data(&spi->bus, 0x33); + spi_dc_write_command(&driver->bus, ST7789_PORCTRL); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x0C); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x33); + spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_command(&spi->bus, ST7789_GCTRL); - spi_dc_write_data(&spi->bus, 0x35); + spi_dc_write_command(&driver->bus, ST7789_GCTRL); + spi_dc_write_data(&driver->bus, 0x35); // - ST7789V power setting - // - spi_dc_write_command(&spi->bus, ST7789_VCOMS); - spi_dc_write_data(&spi->bus, 0x28); + spi_dc_write_command(&driver->bus, ST7789_VCOMS); + spi_dc_write_data(&driver->bus, 0x28); - spi_dc_write_command(&spi->bus, ST7789_LCMCTRL); - spi_dc_write_data(&spi->bus, 0x0C); + spi_dc_write_command(&driver->bus, ST7789_LCMCTRL); + spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_command(&spi->bus, ST7789_VDVVRHEN); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0xFF); + spi_dc_write_command(&driver->bus, ST7789_VDVVRHEN); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0xFF); - spi_dc_write_command(&spi->bus, ST7789_VRHS); - spi_dc_write_data(&spi->bus, 0x10); + spi_dc_write_command(&driver->bus, ST7789_VRHS); + spi_dc_write_data(&driver->bus, 0x10); - spi_dc_write_command(&spi->bus, ST7789_VDVSET); - spi_dc_write_data(&spi->bus, 0x20); + spi_dc_write_command(&driver->bus, ST7789_VDVSET); + spi_dc_write_data(&driver->bus, 0x20); - spi_dc_write_command(&spi->bus, ST7789_FRCTR2); - spi_dc_write_data(&spi->bus, 0x0F); + spi_dc_write_command(&driver->bus, ST7789_FRCTR2); + spi_dc_write_data(&driver->bus, 0x0F); - spi_dc_write_command(&spi->bus, ST7789_PWCTRL1); - spi_dc_write_data(&spi->bus, 0xA4); - spi_dc_write_data(&spi->bus, 0xA1); + spi_dc_write_command(&driver->bus, ST7789_PWCTRL1); + spi_dc_write_data(&driver->bus, 0xA4); + spi_dc_write_data(&driver->bus, 0xA1); // - ST7789V gamma setting - // - spi_dc_write_command(&spi->bus, ST7789_PVGAMCTRL); - spi_dc_write_data(&spi->bus, 0xD0); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x28); - spi_dc_write_data(&spi->bus, 0x32); - spi_dc_write_data(&spi->bus, 0x44); - spi_dc_write_data(&spi->bus, 0x42); - spi_dc_write_data(&spi->bus, 0x06); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x12); - spi_dc_write_data(&spi->bus, 0x14); - spi_dc_write_data(&spi->bus, 0x17); - - spi_dc_write_command(&spi->bus, ST7789_NVGAMCTRL); - spi_dc_write_data(&spi->bus, 0xD0); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x02); - spi_dc_write_data(&spi->bus, 0x07); - spi_dc_write_data(&spi->bus, 0x0A); - spi_dc_write_data(&spi->bus, 0x28); - spi_dc_write_data(&spi->bus, 0x31); - spi_dc_write_data(&spi->bus, 0x54); - spi_dc_write_data(&spi->bus, 0x47); - spi_dc_write_data(&spi->bus, 0x0E); - spi_dc_write_data(&spi->bus, 0x1C); - spi_dc_write_data(&spi->bus, 0x17); - spi_dc_write_data(&spi->bus, 0x1B); - spi_dc_write_data(&spi->bus, 0x1E); - - spi_dc_write_command(&spi->bus, DCS_LCD_CASET); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0xEF); // 239 - - spi_dc_write_command(&spi->bus, DCS_LCD_PASET); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x00); - spi_dc_write_data(&spi->bus, 0x01); - spi_dc_write_data(&spi->bus, 0x3F); // 319 + spi_dc_write_command(&driver->bus, ST7789_PVGAMCTRL); + spi_dc_write_data(&driver->bus, 0xD0); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x28); + spi_dc_write_data(&driver->bus, 0x32); + spi_dc_write_data(&driver->bus, 0x44); + spi_dc_write_data(&driver->bus, 0x42); + spi_dc_write_data(&driver->bus, 0x06); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x12); + spi_dc_write_data(&driver->bus, 0x14); + spi_dc_write_data(&driver->bus, 0x17); + + spi_dc_write_command(&driver->bus, ST7789_NVGAMCTRL); + spi_dc_write_data(&driver->bus, 0xD0); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x02); + spi_dc_write_data(&driver->bus, 0x07); + spi_dc_write_data(&driver->bus, 0x0A); + spi_dc_write_data(&driver->bus, 0x28); + spi_dc_write_data(&driver->bus, 0x31); + spi_dc_write_data(&driver->bus, 0x54); + spi_dc_write_data(&driver->bus, 0x47); + spi_dc_write_data(&driver->bus, 0x0E); + spi_dc_write_data(&driver->bus, 0x1C); + spi_dc_write_data(&driver->bus, 0x17); + spi_dc_write_data(&driver->bus, 0x1B); + spi_dc_write_data(&driver->bus, 0x1E); + + spi_dc_write_command(&driver->bus, DCS_LCD_CASET); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0xEF); // 239 + + spi_dc_write_command(&driver->bus, DCS_LCD_PASET); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x00); + spi_dc_write_data(&driver->bus, 0x01); + spi_dc_write_data(&driver->bus, 0x3F); // 319 } -static void display_init_using_list(struct SPI *spi, term init_list) +static void display_init_using_list(struct DCSLCDDriver *driver, term init_list) { term t = init_list; while (term_is_nonempty_list(t)) { @@ -560,8 +560,8 @@ static void display_init_using_list(struct SPI *spi, term init_list) if (term_is_integer(cmd_term) && term_is_binary(data_term)) { avm_int_t cmd = term_to_int(cmd_term); const uint8_t *data = (const uint8_t *) term_binary_data(data_term); - spi_dc_write_cmd_data(&spi->bus, cmd, data, term_binary_size(data_term)); - } else if ((cmd_term == context_make_atom(spi->ctx, ATOM_STR("\x8", "sleep_ms"))) + spi_dc_write_cmd_data(&driver->bus, cmd, data, term_binary_size(data_term)); + } else if ((cmd_term == context_make_atom(driver->ctx, ATOM_STR("\x8", "sleep_ms"))) && term_is_integer(data_term)) { delay(term_to_int(data_term)); } else { From d06e282f93c45fa7538809318f455f4955e844b5 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 13 Apr 2026 11:46:03 +0000 Subject: [PATCH 27/52] font_data: Refactor CHAR_WIDTH to shared header The built-in font width constant was duplicated in four draw modules. Move it next to FONTDATAMAX in font_data.h where it belongs with the font geometry. Signed-off-by: Davide Bettio --- dcs_lcd_draw.c | 2 -- epaper_draw.c | 2 -- font_data.h | 1 + mono_draw.c | 2 -- sdl_display/display.c | 1 - 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/dcs_lcd_draw.c b/dcs_lcd_draw.c index adfcb5b..ad03da3 100644 --- a/dcs_lcd_draw.c +++ b/dcs_lcd_draw.c @@ -29,8 +29,6 @@ #include "dcs_lcd_color.h" #include "font_data.h" -#define CHAR_WIDTH 8 - int dcs_lcd_find_max_line_len(const struct DCSLCDScreen *screen, BaseDisplayItem items[], size_t items_len, int xpos, int ypos) { diff --git a/epaper_draw.c b/epaper_draw.c index 35b054a..e394097 100644 --- a/epaper_draw.c +++ b/epaper_draw.c @@ -31,8 +31,6 @@ #include "epaper_screen.h" #include "font_data.h" -#define CHAR_WIDTH 8 - void epaper_draw_pixel_x(const struct EpaperScreen *screen, uint8_t *line_buf, int xpos, uint8_t c) { diff --git a/font_data.h b/font_data.h index 849931c..60f9b43 100644 --- a/font_data.h +++ b/font_data.h @@ -10,6 +10,7 @@ #define _FONT_DATA_H_ #define FONTDATAMAX 4096 +#define CHAR_WIDTH 8 extern const unsigned char fontdata[FONTDATAMAX]; diff --git a/mono_draw.c b/mono_draw.c index f4f5fcf..fc57e13 100644 --- a/mono_draw.c +++ b/mono_draw.c @@ -29,8 +29,6 @@ #include "display_items.h" #include "font_data.h" -#define CHAR_WIDTH 8 - static int get_color(int x, int y, uint8_t r, uint8_t g, uint8_t b) { // dither diff --git a/sdl_display/display.c b/sdl_display/display.c index b9aec07..8dc0838 100644 --- a/sdl_display/display.c +++ b/sdl_display/display.c @@ -38,7 +38,6 @@ #define BPP 4 #define DEPTH 32 -#define CHAR_WIDTH 8 #include "../display_items.h" #include "../display_message.h" #include "../font_data.h" From 36042fd9acc41701b8c6e6cd20576157dc3eb55a Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 13 Apr 2026 12:20:56 +0000 Subject: [PATCH 28/52] Fix ufont text crash on allocation failure The ufont text rendering path allocates a surface buffer proportional to the bounding box. On ESP32 this can exceed available heap for large text, and the missing NULL check crashes in memset. Signed-off-by: Davide Bettio --- display_items.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/display_items.c b/display_items.c index f06732c..6794c64 100644 --- a/display_items.c +++ b/display_items.c @@ -197,6 +197,12 @@ void display_items_init_item(BaseDisplayItem *item, term req, Context *ctx) surface.width = rect.width; surface.height = rect.height; surface.buffer = malloc(rect.width * rect.height * BPP); + if (!surface.buffer) { + fprintf(stderr, "Failed to allocate ufont surface (%ix%i)\n", + rect.width, rect.height); + free(text); + return; + } memset(surface.buffer, 0, rect.width * rect.height * BPP); // Convert Erlang fgcolor (0xRRGGBBAA) to RGBA8888 little- // endian byte order (R in low byte, alpha byte cleared) so From ba2e31db631109d38a9e491a78c68adca2034193 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 13 Apr 2026 22:22:50 +0000 Subject: [PATCH 29/52] Add DCS LCD init sequence tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a compact byte-array format for controller init sequences and an interpreter (dcs_lcd_execute_init_seq) that walks the table sending commands via spi_dc_write_cmd_data. Transcribe all 6 existing display_init_*() functions into const byte arrays with _Static_assert on each array size to catch any miscount at compile time. Arrays: ILI9341, ILI9342C, ILI9486, ILI9488, ST7789 std, ST7789 alt gamma 2. No caller changes yet — the old static init functions are still in use. Signed-off-by: Davide Bettio --- dcs_lcd_commands.c | 176 +++++++++++++++++++++++++++++++++++++++++++++ dcs_lcd_commands.h | 26 +++++++ 2 files changed, 202 insertions(+) diff --git a/dcs_lcd_commands.c b/dcs_lcd_commands.c index 8b4eb8c..f9ac1e6 100644 --- a/dcs_lcd_commands.c +++ b/dcs_lcd_commands.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -114,3 +115,178 @@ void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen spi_device_release_bus(bus->spi_disp.handle); } + +// --- Init sequence interpreter --- + +void dcs_lcd_execute_init_seq(struct SPIDCBus *bus, const uint8_t *seq) +{ + while (*seq != DCS_LCD_INIT_SEQ_END) { + uint8_t cmd = *seq++; + uint8_t flags_len = *seq++; + uint8_t len = flags_len & 0x7F; + + spi_dc_write_cmd_data(bus, cmd, seq, len); + seq += len; + + if (flags_len & DCS_LCD_INIT_SEQ_DELAY) { + vTaskDelay(*seq++ / portTICK_PERIOD_MS); + } + } +} + +// --- Built-in init sequences --- +// +// Each row: CMD, FLAGS_LEN, DATA... (see dcs_lcd_commands.h for format). +// Transcribed mechanically from the original display_init_*() functions. +// _Static_assert on sizeof guards against miscounted data bytes. + +// clang-format off + +// ILI9341 — from display_init_9341() in ili934x_display_driver.c +// 19 command groups, no delays. +const uint8_t dcs_lcd_init_seq_ili9341[] = { + 0xEF, 3, 0x03, 0x80, 0x02, // (3) + 0xCF, 3, 0x00, 0xC1, 0x30, // Power ctrl B (3) + 0xED, 4, 0x64, 0x03, 0x12, 0x81, // Power on seq (4) + 0xE8, 3, 0x85, 0x00, 0x78, // Driver timing A (3) + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, // Power ctrl A (5) + 0xF7, 1, 0x20, // Pump ratio (1) + 0xEA, 2, 0x00, 0x00, // Driver timing B (2) + 0xC0, 1, 0x23, // PWCTR1 (1) + 0xC1, 1, 0x10, // PWCTR2 (1) + 0xC5, 2, 0x3E, 0x28, // VMCTR1 (2) + 0xC7, 1, 0x86, // VMCTR2 (1) + 0x36, 1, 0x08, // MADCTL (1) + 0x3A, 1, 0x55, // COLMOD (1) + 0xB1, 2, 0x00, 0x13, // FRMCTR1 (2) + 0xB6, 3, 0x0A, 0xA2, 0x27, // DFUNCTR (3) + 0xF2, 1, 0x00, // Gamma fn dis (1) + 0x26, 1, 0x01, // GAMMASET (1) + 0xE0, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, // GMCTRP1 (15) + 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, + 0xE1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, // GMCTRN1 (15) + 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_ili9341) == 104, + "ili9341 init sequence size mismatch"); + +// ILI9342C — from display_init_9342c() in ili934x_display_driver.c +// 10 command groups, no delays. +const uint8_t dcs_lcd_init_seq_ili9342c[] = { + 0xC8, 3, 0xFF, 0x93, 0x42, // Vendor enable (3) + 0xC0, 2, 0x12, 0x12, // PWCTR1 (2) + 0xC1, 1, 0x03, // PWCTR2 (1) + 0xB0, 1, 0xE0, // (1) + 0xF6, 3, 0x00, 0x01, 0x01, // Interface ctrl (3) + 0x36, 1, 0xA0, // MADCTL MY|MV (1) + 0x3A, 1, 0x55, // COLMOD (1) + 0xB6, 3, 0x08, 0x82, 0x27, // DFUNCTR (3) + 0xE0, 15, 0x00, 0x0C, 0x11, 0x04, 0x11, 0x08, 0x37, 0x89, // GMCTRP1 (15) + 0x4C, 0x06, 0x0C, 0x0A, 0x2E, 0x34, 0x0F, + 0xE1, 15, 0x00, 0x0B, 0x11, 0x05, 0x13, 0x09, 0x33, 0x67, // GMCTRN1 (15) + 0x48, 0x07, 0x0E, 0x0B, 0x2E, 0x33, 0x0F, + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_ili9342c) == 66, + "ili9342c init sequence size mismatch"); + +// ILI9486 — from display_init_9486() in ili948x_display_driver.c +// 7 command groups, no delays. +const uint8_t dcs_lcd_init_seq_ili9486[] = { + 0xB0, 1, 0x00, // IFMODE (1) + 0x3A, 1, 0x55, // COLMOD (1) + 0xC2, 1, 0x44, // PWRCTR3 (1) + 0xC5, 4, 0x00, 0x00, 0x00, 0x00, // VMCTR1 (4) + 0xE0, 15, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, // PGAMCTRL (15) + 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00, + 0xE1, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, // NGAMCTRL (15) + 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, + 0xE2, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, // DGAMCTRL (15) + 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_ili9486) == 67, + "ili9486 init sequence size mismatch"); + +// ILI9488 — from display_init_9488() in ili948x_display_driver.c +// 14 command groups, no delays. RGB666 (COLMOD 0x66, 3 bytes/pixel). +const uint8_t dcs_lcd_init_seq_ili9488[] = { + 0xB0, 1, 0x00, // IFMODE (1) + 0xF7, 4, 0xA9, 0x51, 0x2C, 0x82, // ADJCTRL3 (4) + 0xC0, 2, 0x11, 0x09, // PWRCTR1 (2) + 0xC1, 1, 0x41, // PWRCTR2 (1) + 0xC5, 3, 0x00, 0x0A, 0x80, // VMCTR1 (3) + 0xB1, 2, 0xB0, 0x11, // FRMCTR1 (2) + 0xB4, 1, 0x02, // INVCTR (1) + 0xB6, 2, 0x02, 0x02, // DFUNCTR (2) + 0xB7, 1, 0xC6, // ETMOD (1) + 0xBE, 2, 0x00, 0x04, // HS lanes ctrl (2) + 0xE9, 1, 0x00, // Image fn (1) + 0x3A, 1, 0x66, // COLMOD RGB666 (1) + 0xE0, 15, 0x00, 0x07, 0x10, 0x09, 0x17, 0x0B, 0x41, 0x89, // PGAMCTRL (15) + 0x4B, 0x0A, 0x0C, 0x0E, 0x18, 0x1B, 0x0F, + 0xE1, 15, 0x00, 0x17, 0x1A, 0x04, 0x0E, 0x06, 0x2F, 0x45, // NGAMCTRL (15) + 0x43, 0x02, 0x0A, 0x09, 0x32, 0x36, 0x0F, + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_ili9488) == 80, + "ili9488 init sequence size mismatch"); + +// ST7789 standard — from display_init_std() in st7789_display_driver.c +// 19 command groups, 2 delays (SLPOUT 120ms, COLMOD 10ms). +const uint8_t dcs_lcd_init_seq_st7789_std[] = { + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x13, 0, // NORON (0) + 0x36, 1, 0x00, // MADCTL (1) + 0xB6, 2, 0x0A, 0x82, // (2) + 0xB0, 2, 0x00, 0xE0, // RAMCTRL (2) + 0x3A, DCS_LCD_INIT_SEQ_DELAY | 1, 0x55, 10, // COLMOD +10ms (1) + 0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, // PORCTRL (5) + 0xB7, 1, 0x35, // GCTRL (1) + 0xBB, 1, 0x28, // VCOMS (1) + 0xC0, 1, 0x0C, // LCMCTRL (1) + 0xC2, 2, 0x01, 0xFF, // VDVVRHEN (2) + 0xC3, 1, 0x10, // VRHS (1) + 0xC4, 1, 0x20, // VDVSET (1) + 0xC6, 1, 0x0F, // FRCTR2 (1) + 0xD0, 2, 0xA4, 0xA1, // PWCTRL1 (2) + 0xE0, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, // PVGAMCTRL (14) + 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17, + 0xE1, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, // NVGAMCTRL (14) + 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E, + 0x2A, 4, 0x00, 0x00, 0x00, 0xEF, // CASET 0-239 (4) + 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, // PASET 0-319 (4) + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_st7789_std) == 98, + "st7789 std init sequence size mismatch"); + +// ST7789 alt gamma 2 — from display_init_alt_gamma_2() in st7789_display_driver.c +// 17 command groups, 2 delays (SLPOUT 120ms, COLMOD 10ms). +const uint8_t dcs_lcd_init_seq_st7789_alt[] = { + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x13, 0, // NORON (0) + 0x36, 1, 0x00, // MADCTL (1) + 0x3A, DCS_LCD_INIT_SEQ_DELAY | 1, 0x55, 10, // COLMOD +10ms (1) + 0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, // PORCTRL (5) + 0xB7, 1, 0x75, // GCTRL (1) + 0xBB, 1, 0x1A, // VCOMS (1) + 0xC0, 1, 0x2C, // LCMCTRL (1) + 0xC2, 1, 0x01, // VDVVRHEN (1) + 0xC3, 1, 0x13, // VRHS (1) + 0xC4, 1, 0x20, // VDVSET (1) + 0xC6, 1, 0x0F, // FRCTR2 (1) + 0xD0, 2, 0xA4, 0xA1, // PWCTRL1 (2) + 0xE0, 14, 0xD0, 0x0D, 0x14, 0x0D, 0x0D, 0x09, 0x38, 0x44, // PVGAMCTRL (14) + 0x4E, 0x3A, 0x17, 0x18, 0x2F, 0x30, + 0xE1, 14, 0xD0, 0x09, 0x0F, 0x08, 0x07, 0x14, 0x37, 0x44, // NVGAMCTRL (14) + 0x4D, 0x38, 0x15, 0x16, 0x2C, 0x3E, + 0x2A, 4, 0x00, 0x00, 0x00, 0xEF, // CASET 0-239 (4) + 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, // PASET 0-319 (4) + DCS_LCD_INIT_SEQ_END +}; +_Static_assert(sizeof(dcs_lcd_init_seq_st7789_alt) == 89, + "st7789 alt gamma 2 init sequence size mismatch"); + +// clang-format on diff --git a/dcs_lcd_commands.h b/dcs_lcd_commands.h index 449f7f3..c926f7b 100644 --- a/dcs_lcd_commands.h +++ b/dcs_lcd_commands.h @@ -21,6 +21,9 @@ #ifndef _DCS_LCD_COMMANDS_H_ #define _DCS_LCD_COMMANDS_H_ +#include +#include + #include "dcs_lcd_screen.h" #include "spi_dc_driver.h" @@ -54,4 +57,27 @@ void dcs_lcd_set_paint_area(struct SPIDCBus *bus, const struct DCSLCDScreen *scr void dcs_lcd_draw_buffer(struct SPIDCBus *bus, const struct DCSLCDScreen *screen, int pixel_bytes, int x, int y, int width, int height, const void *imgdata); +// --- Init sequence byte-array format --- +// +// Each entry: [CMD] [FLAGS_LEN] [DATA_0 ... DATA_N] [DELAY_MS] +// CMD: command byte (0x01-0xFF) +// FLAGS_LEN: bits 6:0 = data byte count (0-127) +// bit 7 = delay flag (DELAY_MS byte follows data) +// DELAY_MS: delay in milliseconds (0-255), present only if flag set +// +// End marker: single DCS_LCD_INIT_SEQ_END (0x00) byte. + +#define DCS_LCD_INIT_SEQ_END 0x00 +#define DCS_LCD_INIT_SEQ_DELAY 0x80 + +void dcs_lcd_execute_init_seq(struct SPIDCBus *bus, const uint8_t *seq); + +// Built-in init sequences. +extern const uint8_t dcs_lcd_init_seq_ili9341[]; +extern const uint8_t dcs_lcd_init_seq_ili9342c[]; +extern const uint8_t dcs_lcd_init_seq_ili9486[]; +extern const uint8_t dcs_lcd_init_seq_ili9488[]; +extern const uint8_t dcs_lcd_init_seq_st7789_std[]; +extern const uint8_t dcs_lcd_init_seq_st7789_alt[]; + #endif From 3772e4b63b16a6d6af6fbdf6ca209ba9e9530b90 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Mon, 13 Apr 2026 22:35:12 +0000 Subject: [PATCH 30/52] DCS LCD: Use init sequence tables Replace display_init_9341(), display_init_9342c(), display_init_9486(), display_init_9488(), display_init_std(), and display_init_alt_gamma_2() with calls to dcs_lcd_execute_init_seq() reading the byte-array tables from dcs_lcd_commands.c. Remove the six static init functions and their now-unused controller register defines (keep TFTWIDTH/TFTHEIGHT). The display_init_using_list() Erlang-driven init path is unchanged. The delay() helper stays -- still used in the reset path and by display_init_using_list(). Bytes-on-the-wire are unchanged for every controller. Signed-off-by: Davide Bettio --- ili934x_display_driver.c | 197 +---------------------------------- ili948x_display_driver.c | 174 +------------------------------ st7789_display_driver.c | 218 +-------------------------------------- 3 files changed, 6 insertions(+), 583 deletions(-) diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index 6a86bb1..c95f4d5 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -64,24 +64,6 @@ #define SPI_CLOCK_HZ 27000000 #define SPI_MODE 0 -#define ILI9341_GAMMASET 0x26 - -#define ILI9341_FRMCTR1 0xB1 -#define ILI9341_FRMCTR2 0xB2 -#define ILI9341_FRMCTR3 0xB3 -#define ILI9341_INVCTR 0xB4 -#define ILI9341_DFUNCTR 0xB6 - -#define ILI9341_PWCTR1 0xC0 -#define ILI9341_PWCTR2 0xC1 -#define ILI9341_PWCTR3 0xC2 -#define ILI9341_PWCTR4 0xC3 -#define ILI9341_PWCTR5 0xC4 -#define ILI9341_VMCTR1 0xC5 -#define ILI9341_VMCTR2 0xC7 - -#define ILI9341_GMCTRP1 0xE0 -#define ILI9341_GMCTRN1 0xE1 #include "font_data.h" @@ -105,8 +87,6 @@ struct DCSLCDDriver static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); -static void display_init_9342c(struct DCSLCDDriver *driver); -static void display_init_9341(struct DCSLCDDriver *driver); static void do_update(Context *ctx, term display_list) { @@ -304,9 +284,9 @@ static void display_init(Context *ctx, term opts) vTaskDelay(5 / portTICK_PERIOD_MS); if (enable_ili93442c) { - display_init_9342c(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9342c); } else { - display_init_9341(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9341); } spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); @@ -329,176 +309,3 @@ static void display_init(Context *ctx, term opts) xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_9341(struct DCSLCDDriver *driver) -{ - spi_dc_write_command(&driver->bus, 0xEF); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x80); - spi_dc_write_data(&driver->bus, 0x02); - - spi_dc_write_command(&driver->bus, 0xCF); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0xC1); - spi_dc_write_data(&driver->bus, 0x30); - - spi_dc_write_command(&driver->bus, 0xED); - spi_dc_write_data(&driver->bus, 0x64); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x12); - spi_dc_write_data(&driver->bus, 0x81); - - spi_dc_write_command(&driver->bus, 0xE8); - spi_dc_write_data(&driver->bus, 0x85); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x78); - - spi_dc_write_command(&driver->bus, 0xCB); - spi_dc_write_data(&driver->bus, 0x39); - spi_dc_write_data(&driver->bus, 0x2C); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x34); - spi_dc_write_data(&driver->bus, 0x02); - - spi_dc_write_command(&driver->bus, 0xF7); - spi_dc_write_data(&driver->bus, 0x20); - - spi_dc_write_command(&driver->bus, 0xEA); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI9341_PWCTR1); - spi_dc_write_data(&driver->bus, 0x23); - - spi_dc_write_command(&driver->bus, ILI9341_PWCTR2); - spi_dc_write_data(&driver->bus, 0x10); - - spi_dc_write_command(&driver->bus, ILI9341_VMCTR1); - spi_dc_write_data(&driver->bus, 0x3E); - spi_dc_write_data(&driver->bus, 0x28); - - spi_dc_write_command(&driver->bus, ILI9341_VMCTR2); - spi_dc_write_data(&driver->bus, 0x86); - - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, 0x08); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x55); - - spi_dc_write_command(&driver->bus, ILI9341_FRMCTR1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x13); - - spi_dc_write_command(&driver->bus, ILI9341_DFUNCTR); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0xA2); - spi_dc_write_data(&driver->bus, 0x27); - - spi_dc_write_command(&driver->bus, 0xF2); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI9341_GAMMASET); - spi_dc_write_data(&driver->bus, 0x01); - - spi_dc_write_command(&driver->bus, ILI9341_GMCTRP1); - spi_dc_write_data(&driver->bus, 0x0F); - spi_dc_write_data(&driver->bus, 0x31); - spi_dc_write_data(&driver->bus, 0x2B); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x4E); - spi_dc_write_data(&driver->bus, 0xF1); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x10); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI9341_GMCTRN1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x14); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x31); - spi_dc_write_data(&driver->bus, 0xC1); - spi_dc_write_data(&driver->bus, 0x48); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x0F); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x31); - spi_dc_write_data(&driver->bus, 0x36); - spi_dc_write_data(&driver->bus, 0x0F); -} - -static void display_init_9342c(struct DCSLCDDriver *driver) -{ - spi_dc_write_command(&driver->bus, 0xC8); - spi_dc_write_data(&driver->bus, 0xFF); - spi_dc_write_data(&driver->bus, 0x93); - spi_dc_write_data(&driver->bus, 0x42); - - spi_dc_write_command(&driver->bus, ILI9341_PWCTR1); - spi_dc_write_data(&driver->bus, 0x12); - spi_dc_write_data(&driver->bus, 0x12); - - spi_dc_write_command(&driver->bus, ILI9341_PWCTR2); - spi_dc_write_data(&driver->bus, 0x03); - - spi_dc_write_command(&driver->bus, 0xB0); - spi_dc_write_data(&driver->bus, 0xE0); - - spi_dc_write_command(&driver->bus, 0xF6); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0x01); - - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x55); - - spi_dc_write_command(&driver->bus, ILI9341_DFUNCTR); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x82); - spi_dc_write_data(&driver->bus, 0x27); - - spi_dc_write_command(&driver->bus, ILI9341_GMCTRP1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x04); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x89); - spi_dc_write_data(&driver->bus, 0x4C); - spi_dc_write_data(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x2E); - spi_dc_write_data(&driver->bus, 0x34); - spi_dc_write_data(&driver->bus, 0x0F); - - spi_dc_write_command(&driver->bus, ILI9341_GMCTRN1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x0B); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x05); - spi_dc_write_data(&driver->bus, 0x13); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_data(&driver->bus, 0x67); - spi_dc_write_data(&driver->bus, 0x48); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x0B); - spi_dc_write_data(&driver->bus, 0x2E); - spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_data(&driver->bus, 0x0F); -} diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index cd85cc2..576b48d 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -67,22 +67,6 @@ #define SPI_CLOCK_HZ 27000000 #define SPI_MODE 0 -#define ILI948X_IFMODE 0xB0 -#define ILI948X_FRMCTR1 0xB1 -#define ILI948X_INVCTR 0xB4 -#define ILI948X_DFUNCTR 0xB6 -#define ILI948X_ETMOD 0xB7 -#define ILI948X_PWRCTR1 0xC0 -#define ILI948X_PWRCTR2 0xC1 -#define ILI948X_PWRCTR3 0xC2 -#define ILI948X_VMCTR1 0xC5 -#define ILI948X_HS_LANES_CTRL 0xBE -#define ILI948X_IMAGE_FUNCTION 0xE9 -#define ILI948X_PGAMCTRL 0xE0 -#define ILI948X_NGAMCTRL 0xE1 -#define ILI948X_DGAMCTRL 0xE2 -#define ILI948X_ADJCTRL3 0xF7 - #define ILI948X_TFTWIDTH 320 #define ILI948X_TFTHEIGHT 480 @@ -132,9 +116,6 @@ static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *s static void display_init(Context *ctx, term opts); -static void display_init_9486(struct DCSLCDDriver *driver); -static void display_init_9488(struct DCSLCDDriver *driver); - static void do_update(Context *ctx, term display_list) { int proper; @@ -405,9 +386,9 @@ static void display_init(Context *ctx, term opts) vTaskDelay(5 / portTICK_PERIOD_MS); if (driver->is_ili9488) { - display_init_9488(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9488); } else { - display_init_9486(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9486); } spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); @@ -432,154 +413,3 @@ static void display_init(Context *ctx, term opts) xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_9486(struct DCSLCDDriver *driver) -{ - spi_dc_write_command(&driver->bus, ILI948X_IFMODE); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x55); - - spi_dc_write_command(&driver->bus, ILI948X_PWRCTR3); - spi_dc_write_data(&driver->bus, 0x44); - - spi_dc_write_command(&driver->bus, ILI948X_VMCTR1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI948X_PGAMCTRL); - spi_dc_write_data(&driver->bus, 0x0f); - spi_dc_write_data(&driver->bus, 0x1f); - spi_dc_write_data(&driver->bus, 0x1c); - spi_dc_write_data(&driver->bus, 0x0c); - spi_dc_write_data(&driver->bus, 0x0f); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x48); - spi_dc_write_data(&driver->bus, 0x98); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x0a); - spi_dc_write_data(&driver->bus, 0x13); - spi_dc_write_data(&driver->bus, 0x04); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x0d); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI948X_NGAMCTRL); - spi_dc_write_data(&driver->bus, 0x0f); - spi_dc_write_data(&driver->bus, 0x32); - spi_dc_write_data(&driver->bus, 0x2e); - spi_dc_write_data(&driver->bus, 0x0b); - spi_dc_write_data(&driver->bus, 0x0d); - spi_dc_write_data(&driver->bus, 0x05); - spi_dc_write_data(&driver->bus, 0x47); - spi_dc_write_data(&driver->bus, 0x75); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0x10); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x24); - spi_dc_write_data(&driver->bus, 0x20); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI948X_DGAMCTRL); - spi_dc_write_data(&driver->bus, 0x0f); - spi_dc_write_data(&driver->bus, 0x32); - spi_dc_write_data(&driver->bus, 0x2e); - spi_dc_write_data(&driver->bus, 0x0b); - spi_dc_write_data(&driver->bus, 0x0d); - spi_dc_write_data(&driver->bus, 0x05); - spi_dc_write_data(&driver->bus, 0x47); - spi_dc_write_data(&driver->bus, 0x75); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0x10); - spi_dc_write_data(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x24); - spi_dc_write_data(&driver->bus, 0x20); - spi_dc_write_data(&driver->bus, 0x00); -} - -static void display_init_9488(struct DCSLCDDriver *driver) -{ - // ILI9488: RGB666 over SPI (3 bytes/pixel). - spi_dc_write_command(&driver->bus, ILI948X_IFMODE); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, ILI948X_ADJCTRL3); - spi_dc_write_data(&driver->bus, 0xA9); - spi_dc_write_data(&driver->bus, 0x51); - spi_dc_write_data(&driver->bus, 0x2C); - spi_dc_write_data(&driver->bus, 0x82); - - spi_dc_write_command(&driver->bus, ILI948X_PWRCTR1); - spi_dc_write_data(&driver->bus, 0x11); - spi_dc_write_data(&driver->bus, 0x09); - - spi_dc_write_command(&driver->bus, ILI948X_PWRCTR2); - spi_dc_write_data(&driver->bus, 0x41); - - spi_dc_write_command(&driver->bus, ILI948X_VMCTR1); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x80); - - spi_dc_write_command(&driver->bus, ILI948X_FRMCTR1); - spi_dc_write_data(&driver->bus, 0xB0); - spi_dc_write_data(&driver->bus, 0x11); - - spi_dc_write_command(&driver->bus, ILI948X_INVCTR); - spi_dc_write_data(&driver->bus, 0x02); - - spi_dc_write_command(&driver->bus, ILI948X_DFUNCTR); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x02); - - spi_dc_write_command(&driver->bus, ILI948X_ETMOD); - spi_dc_write_data(&driver->bus, 0xC6); - - spi_dc_write_command(&driver->bus, ILI948X_HS_LANES_CTRL); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x04); - - spi_dc_write_command(&driver->bus, ILI948X_IMAGE_FUNCTION); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x66); - - spi_dc_write_command(&driver->bus, ILI948X_PGAMCTRL); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x10); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x17); - spi_dc_write_data(&driver->bus, 0x0B); - spi_dc_write_data(&driver->bus, 0x41); - spi_dc_write_data(&driver->bus, 0x89); - spi_dc_write_data(&driver->bus, 0x4B); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x18); - spi_dc_write_data(&driver->bus, 0x1B); - spi_dc_write_data(&driver->bus, 0x0F); - - spi_dc_write_command(&driver->bus, ILI948X_NGAMCTRL); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x17); - spi_dc_write_data(&driver->bus, 0x1A); - spi_dc_write_data(&driver->bus, 0x04); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0x2F); - spi_dc_write_data(&driver->bus, 0x45); - spi_dc_write_data(&driver->bus, 0x43); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x32); - spi_dc_write_data(&driver->bus, 0x36); - spi_dc_write_data(&driver->bus, 0x0F); -} diff --git a/st7789_display_driver.c b/st7789_display_driver.c index f17ee7f..3b323fa 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -65,19 +65,6 @@ #define SPI_CLOCK_HZ 40000000 #define SPI_MODE 0 -#define ST7789_RAMCTRL 0xB0 -#define ST7789_PORCTRL 0xB2 -#define ST7789_GCTRL 0xB7 -#define ST7789_VCOMS 0xBB -#define ST7789_LCMCTRL 0xC0 -#define ST7789_VDVVRHEN 0xC2 -#define ST7789_VRHS 0xC3 -#define ST7789_VDVSET 0xC4 -#define ST7789_FRCTR2 0xC6 -#define ST7789_PWCTRL1 0xD0 -#define ST7789_PVGAMCTRL 0xE0 -#define ST7789_NVGAMCTRL 0xE1 - #include "font_data.h" static const char *TAG = "st7789_display_driver"; @@ -105,8 +92,6 @@ struct DCSLCDDriver static struct DCSLCDScreen *screen; static void display_init(Context *ctx, term opts); -static void display_init_alt_gamma_2(struct DCSLCDDriver *driver); -static void display_init_std(struct DCSLCDDriver *driver); static void display_init_using_list(struct DCSLCDDriver *driver, term init_list); static void do_update(Context *ctx, term display_list) @@ -326,10 +311,10 @@ static void display_init(Context *ctx, term opts) int str_ok; char *init_seq_type_string = interop_term_to_string(init_seq_type_term, &str_ok); if (str_ok && !strcmp(init_seq_type_string, "alt_gamma_2")) { - display_init_alt_gamma_2(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_st7789_alt); free(init_seq_type_string); } else { - display_init_std(driver); + dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_st7789_std); } set_rotation(driver, driver->rotation); @@ -350,205 +335,6 @@ static void display_init(Context *ctx, term opts) xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } -static void display_init_alt_gamma_2(struct DCSLCDDriver *driver) -{ - spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); - delay(120); - - spi_dc_write_command(&driver->bus, DCS_LCD_NORON); - - // - display and color format setting - // - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x55); - delay(10); - - // - ST7789V frame rate setting - // - spi_dc_write_command(&driver->bus, ST7789_PORCTRL); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_data(&driver->bus, 0x33); - - spi_dc_write_command(&driver->bus, ST7789_GCTRL); - spi_dc_write_data(&driver->bus, 0x75); - - // - ST7789V power setting - // - spi_dc_write_command(&driver->bus, ST7789_VCOMS); - spi_dc_write_data(&driver->bus, 0x1A); - - spi_dc_write_command(&driver->bus, ST7789_LCMCTRL); - spi_dc_write_data(&driver->bus, 0x2C); - - spi_dc_write_command(&driver->bus, ST7789_VDVVRHEN); - spi_dc_write_data(&driver->bus, 0x01); - - spi_dc_write_command(&driver->bus, ST7789_VRHS); - spi_dc_write_data(&driver->bus, 0x13); - - spi_dc_write_command(&driver->bus, ST7789_VDVSET); - spi_dc_write_data(&driver->bus, 0x20); - - spi_dc_write_command(&driver->bus, ST7789_FRCTR2); - spi_dc_write_data(&driver->bus, 0x0F); - - spi_dc_write_command(&driver->bus, ST7789_PWCTRL1); - spi_dc_write_data(&driver->bus, 0xA4); - spi_dc_write_data(&driver->bus, 0xA1); - - // - ST7789V gamma setting - // - spi_dc_write_command(&driver->bus, ST7789_PVGAMCTRL); - spi_dc_write_data(&driver->bus, 0xD0); - spi_dc_write_data(&driver->bus, 0x0D); - spi_dc_write_data(&driver->bus, 0x14); - spi_dc_write_data(&driver->bus, 0x0D); - spi_dc_write_data(&driver->bus, 0x0D); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x38); - spi_dc_write_data(&driver->bus, 0x44); - spi_dc_write_data(&driver->bus, 0x4E); - spi_dc_write_data(&driver->bus, 0x3A); - spi_dc_write_data(&driver->bus, 0x17); - spi_dc_write_data(&driver->bus, 0x18); - spi_dc_write_data(&driver->bus, 0x2F); - spi_dc_write_data(&driver->bus, 0x30); - - spi_dc_write_command(&driver->bus, ST7789_NVGAMCTRL); - spi_dc_write_data(&driver->bus, 0xD0); - spi_dc_write_data(&driver->bus, 0x09); - spi_dc_write_data(&driver->bus, 0x0F); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x14); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x44); - spi_dc_write_data(&driver->bus, 0x4D); - spi_dc_write_data(&driver->bus, 0x38); - spi_dc_write_data(&driver->bus, 0x15); - spi_dc_write_data(&driver->bus, 0x16); - spi_dc_write_data(&driver->bus, 0x2C); - spi_dc_write_data(&driver->bus, 0x3E); - - spi_dc_write_command(&driver->bus, DCS_LCD_CASET); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0xEF); // 239 - - spi_dc_write_command(&driver->bus, DCS_LCD_PASET); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0x3F); // 319 -} - -static void display_init_std(struct DCSLCDDriver *driver) -{ - spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); - delay(120); - - spi_dc_write_command(&driver->bus, DCS_LCD_NORON); - - // - display and color format setting - // - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, 0x00); - - spi_dc_write_command(&driver->bus, 0xB6); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x82); - - spi_dc_write_command(&driver->bus, ST7789_RAMCTRL); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0xE0); - - spi_dc_write_command(&driver->bus, DCS_LCD_COLMOD); - spi_dc_write_data(&driver->bus, 0x55); - delay(10); - - // - ST7789V frame rate setting - // - spi_dc_write_command(&driver->bus, ST7789_PORCTRL); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x0C); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x33); - spi_dc_write_data(&driver->bus, 0x33); - - spi_dc_write_command(&driver->bus, ST7789_GCTRL); - spi_dc_write_data(&driver->bus, 0x35); - - // - ST7789V power setting - // - spi_dc_write_command(&driver->bus, ST7789_VCOMS); - spi_dc_write_data(&driver->bus, 0x28); - - spi_dc_write_command(&driver->bus, ST7789_LCMCTRL); - spi_dc_write_data(&driver->bus, 0x0C); - - spi_dc_write_command(&driver->bus, ST7789_VDVVRHEN); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xFF); - - spi_dc_write_command(&driver->bus, ST7789_VRHS); - spi_dc_write_data(&driver->bus, 0x10); - - spi_dc_write_command(&driver->bus, ST7789_VDVSET); - spi_dc_write_data(&driver->bus, 0x20); - - spi_dc_write_command(&driver->bus, ST7789_FRCTR2); - spi_dc_write_data(&driver->bus, 0x0F); - - spi_dc_write_command(&driver->bus, ST7789_PWCTRL1); - spi_dc_write_data(&driver->bus, 0xA4); - spi_dc_write_data(&driver->bus, 0xA1); - - // - ST7789V gamma setting - // - spi_dc_write_command(&driver->bus, ST7789_PVGAMCTRL); - spi_dc_write_data(&driver->bus, 0xD0); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x28); - spi_dc_write_data(&driver->bus, 0x32); - spi_dc_write_data(&driver->bus, 0x44); - spi_dc_write_data(&driver->bus, 0x42); - spi_dc_write_data(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x12); - spi_dc_write_data(&driver->bus, 0x14); - spi_dc_write_data(&driver->bus, 0x17); - - spi_dc_write_command(&driver->bus, ST7789_NVGAMCTRL); - spi_dc_write_data(&driver->bus, 0xD0); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x07); - spi_dc_write_data(&driver->bus, 0x0A); - spi_dc_write_data(&driver->bus, 0x28); - spi_dc_write_data(&driver->bus, 0x31); - spi_dc_write_data(&driver->bus, 0x54); - spi_dc_write_data(&driver->bus, 0x47); - spi_dc_write_data(&driver->bus, 0x0E); - spi_dc_write_data(&driver->bus, 0x1C); - spi_dc_write_data(&driver->bus, 0x17); - spi_dc_write_data(&driver->bus, 0x1B); - spi_dc_write_data(&driver->bus, 0x1E); - - spi_dc_write_command(&driver->bus, DCS_LCD_CASET); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0xEF); // 239 - - spi_dc_write_command(&driver->bus, DCS_LCD_PASET); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0x3F); // 319 -} - static void display_init_using_list(struct DCSLCDDriver *driver, term init_list) { term t = init_list; From ffc674220e9d02807386992393cd880086596a59 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 09:24:34 +0000 Subject: [PATCH 31/52] Add DCS LCD controller descriptors Define struct DCSLCDDesc and populate descriptors for the 6 compatible strings served by the DCS LCD drivers: ILI9341, ILI9342C, ILI9486, ILI9488, ST7789, ST7796. Each descriptor captures native resolution, SPI clock, pixel format, per-rotation MADCTL values, default color order, and the built-in init sequence pointer. Rotations not validated on the existing hardware are marked 0xFF. Signed-off-by: Davide Bettio --- dcs_lcd_commands.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++ dcs_lcd_commands.h | 31 ++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/dcs_lcd_commands.c b/dcs_lcd_commands.c index f9ac1e6..7f41cab 100644 --- a/dcs_lcd_commands.c +++ b/dcs_lcd_commands.c @@ -290,3 +290,92 @@ _Static_assert(sizeof(dcs_lcd_init_seq_st7789_alt) == 89, "st7789 alt gamma 2 init sequence size mismatch"); // clang-format on + +// --- Per-controller descriptors --- +// +// madctl[4] values come from the current set_rotation() behavior: +// 0xFF marks a rotation that the existing driver did not validate. +// ILI948x has all 4 rotations (madctl_bgr ORed in at runtime from driver +// state, so the table stores the base value without BGR). +// ILI934x / ST7789 only support rotation 0 (init-default MADCTL) and +// rotation 1 (overridden by set_rotation). + +const struct DCSLCDDesc dcs_lcd_desc_ili9341 = { + .name = "ILI9341", + .native_width = 240, + .native_height = 320, + .spi_clock_hz = 27000000, + .pixel_bytes = 2, + .colmod_value = 0x55, + .madctl = { 0x08, 0xA8, 0xFF, 0xFF }, + .default_bgr = true, + .default_init_seq = dcs_lcd_init_seq_ili9341, + .init_fn = NULL, +}; + +const struct DCSLCDDesc dcs_lcd_desc_ili9342c = { + .name = "ILI9342C", + .native_width = 320, + .native_height = 240, + .spi_clock_hz = 27000000, + .pixel_bytes = 2, + .colmod_value = 0x55, + .madctl = { 0xA0, 0xA8, 0xFF, 0xFF }, + .default_bgr = false, + .default_init_seq = dcs_lcd_init_seq_ili9342c, + .init_fn = NULL, +}; + +const struct DCSLCDDesc dcs_lcd_desc_ili9486 = { + .name = "ILI9486", + .native_width = 320, + .native_height = 480, + .spi_clock_hz = 27000000, + .pixel_bytes = 2, + .colmod_value = 0x55, + .madctl = { 0x40, 0x20, 0x80, 0xE0 }, + .default_bgr = true, + .default_init_seq = dcs_lcd_init_seq_ili9486, + .init_fn = NULL, +}; + +const struct DCSLCDDesc dcs_lcd_desc_ili9488 = { + .name = "ILI9488", + .native_width = 320, + .native_height = 480, + .spi_clock_hz = 27000000, + .pixel_bytes = 3, + .colmod_value = 0x66, + .madctl = { 0x40, 0x20, 0x80, 0xE0 }, + .default_bgr = true, + .default_init_seq = dcs_lcd_init_seq_ili9488, + .init_fn = NULL, +}; + +const struct DCSLCDDesc dcs_lcd_desc_st7789 = { + .name = "ST7789", + .native_width = 240, + .native_height = 320, + .spi_clock_hz = 40000000, + .pixel_bytes = 2, + .colmod_value = 0x55, + .madctl = { 0x00, 0x60, 0xFF, 0xFF }, + .default_bgr = false, + .default_init_seq = dcs_lcd_init_seq_st7789_std, + .init_fn = NULL, +}; + +// ST7796 currently routes through the ST7789 driver and uses the same +// init sequence. It may diverge in a future session. +const struct DCSLCDDesc dcs_lcd_desc_st7796 = { + .name = "ST7796", + .native_width = 320, + .native_height = 480, + .spi_clock_hz = 40000000, + .pixel_bytes = 2, + .colmod_value = 0x55, + .madctl = { 0x00, 0x60, 0xFF, 0xFF }, + .default_bgr = false, + .default_init_seq = dcs_lcd_init_seq_st7789_std, + .init_fn = NULL, +}; diff --git a/dcs_lcd_commands.h b/dcs_lcd_commands.h index c926f7b..fbee5b1 100644 --- a/dcs_lcd_commands.h +++ b/dcs_lcd_commands.h @@ -80,4 +80,35 @@ extern const uint8_t dcs_lcd_init_seq_ili9488[]; extern const uint8_t dcs_lcd_init_seq_st7789_std[]; extern const uint8_t dcs_lcd_init_seq_st7789_alt[]; +// --- Per-controller descriptor --- +// +// Describes a DCS LCD controller variant so a unified driver can dispatch +// on compatible string. madctl[4] holds per-rotation MADCTL values (0xFF +// for rotations not validated on the current hardware). init_fn is a +// fallback when a byte-array init sequence is insufficient; NULL for all +// current controllers. + +struct DCSLCDDriver; // forward declaration for init_fn + +struct DCSLCDDesc +{ + const char *name; + int native_width; + int native_height; + int spi_clock_hz; + uint8_t pixel_bytes; + uint8_t colmod_value; + uint8_t madctl[4]; + bool default_bgr; + const uint8_t *default_init_seq; + void (*init_fn)(struct DCSLCDDriver *); +}; + +extern const struct DCSLCDDesc dcs_lcd_desc_ili9341; +extern const struct DCSLCDDesc dcs_lcd_desc_ili9342c; +extern const struct DCSLCDDesc dcs_lcd_desc_ili9486; +extern const struct DCSLCDDesc dcs_lcd_desc_ili9488; +extern const struct DCSLCDDesc dcs_lcd_desc_st7789; +extern const struct DCSLCDDesc dcs_lcd_desc_st7796; + #endif From 0d84c320cf927060c91ca7ce126dc3abba12774d Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 10:38:15 +0000 Subject: [PATCH 32/52] spi_display: Add clock_speed_hz opt Expose the SPI clock rate as an Erlang opt so applications can override the descriptor default. Signed-off-by: Davide Bettio --- spi_display.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/spi_display.c b/spi_display.c index f3b4a61..8487146 100644 --- a/spi_display.c +++ b/spi_display.c @@ -88,8 +88,20 @@ bool spi_display_parse_config(struct SPIDisplayConfig *spi_config, term opts, Gl term spi_port = interop_proplist_get_value(opts, spi_host_atom); ok = spi_driver_get_peripheral(spi_port, &spi_config->host_dev, global); + if (!ok) { + return false; + } - return ok; + term clock_speed_hz = interop_kv_get_value_default( + opts, ATOM_STR("\xE", "clock_speed_hz"), term_nil(), global); + if (clock_speed_hz != term_nil()) { + if (!term_is_integer(clock_speed_hz)) { + return false; + } + spi_config->clock_speed_hz = term_to_int(clock_speed_hz); + } + + return true; } bool spi_display_init(struct SPIDisplay *spi_disp, struct SPIDisplayConfig *spi_config) From 5002fc84eb545030d175776204515ca8acf94396 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 11:04:59 +0000 Subject: [PATCH 33/52] dcs_lcd: Move reset and power-on into init seqs Move the SWRESET, SLPOUT and DISPON commands (with their delays) into the per-controller init sequence tables. Each driver previously issued these manually around dcs_lcd_execute_init_seq; folding them into the tables lets a single shared driver execute the entire bring-up sequence with no controller-specific C code. ILI controllers gain a DISPON+120ms tail (matching st7789). ST7789 without a reset GPIO drops the explicit SWRESET+100ms in favour of SWRESET+5ms+SLPOUT+120ms -- both datasheet-compliant. Signed-off-by: Davide Bettio --- dcs_lcd_commands.c | 49 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/dcs_lcd_commands.c b/dcs_lcd_commands.c index 7f41cab..c4cefef 100644 --- a/dcs_lcd_commands.c +++ b/dcs_lcd_commands.c @@ -143,8 +143,12 @@ void dcs_lcd_execute_init_seq(struct SPIDCBus *bus, const uint8_t *seq) // clang-format off // ILI9341 — from display_init_9341() in ili934x_display_driver.c -// 19 command groups, no delays. +// 21 command groups, 3 delays (SWRESET 5ms, SLPOUT 120ms, DISPON 120ms). +// SLPOUT intentionally follows the vendor power/voltage commands: the +// internal booster must wake up with the configured VMCTR/PWCTR values, +// otherwise many ILI9341 modules never produce usable output. const uint8_t dcs_lcd_init_seq_ili9341[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0xEF, 3, 0x03, 0x80, 0x02, // (3) 0xCF, 3, 0x00, 0xC1, 0x30, // Power ctrl B (3) 0xED, 4, 0x64, 0x03, 0x12, 0x81, // Power on seq (4) @@ -166,14 +170,18 @@ const uint8_t dcs_lcd_init_seq_ili9341[] = { 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, 0xE1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, // GMCTRN1 (15) 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_ili9341) == 104, +_Static_assert(sizeof(dcs_lcd_init_seq_ili9341) == 113, "ili9341 init sequence size mismatch"); // ILI9342C — from display_init_9342c() in ili934x_display_driver.c -// 10 command groups, no delays. +// 12 command groups, 3 delays (SWRESET 5ms, SLPOUT 120ms, DISPON 120ms). +// SLPOUT follows the vendor power commands; see ILI9341 seq for details. const uint8_t dcs_lcd_init_seq_ili9342c[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0xC8, 3, 0xFF, 0x93, 0x42, // Vendor enable (3) 0xC0, 2, 0x12, 0x12, // PWCTR1 (2) 0xC1, 1, 0x03, // PWCTR2 (1) @@ -186,14 +194,18 @@ const uint8_t dcs_lcd_init_seq_ili9342c[] = { 0x4C, 0x06, 0x0C, 0x0A, 0x2E, 0x34, 0x0F, 0xE1, 15, 0x00, 0x0B, 0x11, 0x05, 0x13, 0x09, 0x33, 0x67, // GMCTRN1 (15) 0x48, 0x07, 0x0E, 0x0B, 0x2E, 0x33, 0x0F, + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_ili9342c) == 66, +_Static_assert(sizeof(dcs_lcd_init_seq_ili9342c) == 75, "ili9342c init sequence size mismatch"); // ILI9486 — from display_init_9486() in ili948x_display_driver.c -// 7 command groups, no delays. +// 9 command groups, 3 delays (SWRESET 5ms, SLPOUT 120ms, DISPON 120ms). +// SLPOUT follows the vendor power commands; see ILI9341 seq for details. const uint8_t dcs_lcd_init_seq_ili9486[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0xB0, 1, 0x00, // IFMODE (1) 0x3A, 1, 0x55, // COLMOD (1) 0xC2, 1, 0x44, // PWRCTR3 (1) @@ -204,14 +216,19 @@ const uint8_t dcs_lcd_init_seq_ili9486[] = { 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, 0xE2, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, // DGAMCTRL (15) 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00, + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_ili9486) == 67, +_Static_assert(sizeof(dcs_lcd_init_seq_ili9486) == 76, "ili9486 init sequence size mismatch"); // ILI9488 — from display_init_9488() in ili948x_display_driver.c -// 14 command groups, no delays. RGB666 (COLMOD 0x66, 3 bytes/pixel). +// 16 command groups, 3 delays (SWRESET 5ms, SLPOUT 120ms, DISPON 120ms). +// RGB666 (COLMOD 0x66, 3 bytes/pixel). +// SLPOUT follows the vendor power commands; see ILI9341 seq for details. const uint8_t dcs_lcd_init_seq_ili9488[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0xB0, 1, 0x00, // IFMODE (1) 0xF7, 4, 0xA9, 0x51, 0x2C, 0x82, // ADJCTRL3 (4) 0xC0, 2, 0x11, 0x09, // PWRCTR1 (2) @@ -228,14 +245,18 @@ const uint8_t dcs_lcd_init_seq_ili9488[] = { 0x4B, 0x0A, 0x0C, 0x0E, 0x18, 0x1B, 0x0F, 0xE1, 15, 0x00, 0x17, 0x1A, 0x04, 0x0E, 0x06, 0x2F, 0x45, // NGAMCTRL (15) 0x43, 0x02, 0x0A, 0x09, 0x32, 0x36, 0x0F, + 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_ili9488) == 80, +_Static_assert(sizeof(dcs_lcd_init_seq_ili9488) == 89, "ili9488 init sequence size mismatch"); // ST7789 standard — from display_init_std() in st7789_display_driver.c -// 19 command groups, 2 delays (SLPOUT 120ms, COLMOD 10ms). +// 21 command groups, 4 delays (SWRESET 5ms, SLPOUT 120ms, COLMOD 10ms, +// DISPON 120ms). const uint8_t dcs_lcd_init_seq_st7789_std[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) 0x13, 0, // NORON (0) 0x36, 1, 0x00, // MADCTL (1) @@ -257,14 +278,17 @@ const uint8_t dcs_lcd_init_seq_st7789_std[] = { 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E, 0x2A, 4, 0x00, 0x00, 0x00, 0xEF, // CASET 0-239 (4) 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, // PASET 0-319 (4) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_st7789_std) == 98, +_Static_assert(sizeof(dcs_lcd_init_seq_st7789_std) == 104, "st7789 std init sequence size mismatch"); // ST7789 alt gamma 2 — from display_init_alt_gamma_2() in st7789_display_driver.c -// 17 command groups, 2 delays (SLPOUT 120ms, COLMOD 10ms). +// 19 command groups, 4 delays (SWRESET 5ms, SLPOUT 120ms, COLMOD 10ms, +// DISPON 120ms). const uint8_t dcs_lcd_init_seq_st7789_alt[] = { + 0x01, DCS_LCD_INIT_SEQ_DELAY | 0, 5, // SWRESET +5ms (0) 0x11, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // SLPOUT +120ms (0) 0x13, 0, // NORON (0) 0x36, 1, 0x00, // MADCTL (1) @@ -284,9 +308,10 @@ const uint8_t dcs_lcd_init_seq_st7789_alt[] = { 0x4D, 0x38, 0x15, 0x16, 0x2C, 0x3E, 0x2A, 4, 0x00, 0x00, 0x00, 0xEF, // CASET 0-239 (4) 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, // PASET 0-319 (4) + 0x29, DCS_LCD_INIT_SEQ_DELAY | 0, 120, // DISPON +120ms (0) DCS_LCD_INIT_SEQ_END }; -_Static_assert(sizeof(dcs_lcd_init_seq_st7789_alt) == 89, +_Static_assert(sizeof(dcs_lcd_init_seq_st7789_alt) == 95, "st7789 alt gamma 2 init sequence size mismatch"); // clang-format on From ac3f09af284dbc8f340cff738f4b66d3fac5af3a Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 11:12:37 +0000 Subject: [PATCH 34/52] DCS LCD: Embed DCSLCDScreen in driver Move DCSLCDScreen from a file-static pointer into the DCSLCDDriver struct across all three DCS LCD drivers (ili934x, ili948x, st7789). Removes one global, one heap allocation, and one pointer indirection per driver. Calloc replaces malloc so the embedded screen's unused fields (offsets, bytes for non-9488) are zero-initialised. Also drop the explicit SWRESET, SLPOUT and DISPON commands each driver issued manually; they are now part of the controller's init sequence table. For st7789 this also drops the no-reset-GPIO SWRESET branch. Signed-off-by: Davide Bettio --- ili934x_display_driver.c | 45 +++++++++++----------------- ili948x_display_driver.c | 65 ++++++++++++++++------------------------ st7789_display_driver.c | 46 +++++++++++----------------- 3 files changed, 60 insertions(+), 96 deletions(-) diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c index c95f4d5..fd724bb 100644 --- a/ili934x_display_driver.c +++ b/ili934x_display_driver.c @@ -76,6 +76,8 @@ struct DCSLCDDriver avm_int_t rotation; + struct DCSLCDScreen screen; + Context *ctx; struct DisplayTaskArgs display_args; @@ -84,8 +86,6 @@ struct DCSLCDDriver #define DCS_LCD_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) -static struct DCSLCDScreen *screen; - static void display_init(Context *ctx, term opts); static void do_update(Context *ctx, term display_list) @@ -101,11 +101,11 @@ static void do_update(Context *ctx, term display_list) t = term_get_list_tail(t); } - int screen_width = screen->w; - int screen_height = screen->h; struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; - dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + dcs_lcd_set_paint_area(&driver->bus, &driver->screen, 0, 0, screen_width, screen_height); spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); @@ -114,7 +114,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(&driver->screen, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -126,10 +126,10 @@ static void do_update(Context *ctx, term display_list) } //NEW CODE - void *tmp = screen->pixels; - screen->pixels = screen->pixels_out; - screen->pixels_out = tmp; - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + void *tmp = driver->screen.pixels; + driver->screen.pixels = driver->screen.pixels_out; + driver->screen.pixels_out = tmp; + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); transaction_in_progress = true; } @@ -175,7 +175,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&driver->bus, screen, 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, &driver->screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -217,14 +217,13 @@ Context *ili934x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = calloc(1, sizeof(struct DCSLCDScreen)); - // FIXME: hardcoded width and height - screen->w = 320; - screen->h = 240; - screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); + struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); - struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); + // FIXME: hardcoded width and height + driver->screen.w = 320; + driver->screen.h = 240; + driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); + driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); driver->display_args.process_message_fn = process_message; @@ -279,22 +278,12 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); - - vTaskDelay(5 / portTICK_PERIOD_MS); - if (enable_ili93442c) { dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9342c); } else { dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9341); } - spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); - - vTaskDelay(120 / portTICK_PERIOD_MS); - - spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); - if (enable_tft_invon) { spi_dc_write_command(&driver->bus, DCS_LCD_INVON); } diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c index 576b48d..1db2d80 100644 --- a/ili948x_display_driver.c +++ b/ili948x_display_driver.c @@ -84,6 +84,8 @@ struct DCSLCDDriver bool madctl_bgr; + struct DCSLCDScreen screen; + Context *ctx; struct DisplayTaskArgs display_args; @@ -92,8 +94,6 @@ struct DCSLCDDriver #define DCS_LCD_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) -static struct DCSLCDScreen *screen; - // ILI9488 scanline conversion: RGB565 -> RGB888 bytes. static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) { @@ -129,11 +129,11 @@ static void do_update(Context *ctx, term display_list) t = term_get_list_tail(t); } - int screen_width = screen->w; - int screen_height = screen->h; struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; - dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + dcs_lcd_set_paint_area(&driver->bus, &driver->screen, 0, 0, screen_width, screen_height); spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); @@ -142,7 +142,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(&driver->screen, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -152,19 +152,19 @@ static void do_update(Context *ctx, term display_list) } // Swap scanline buffers. - void *tmp = screen->pixels; - screen->pixels = screen->pixels_out; - screen->pixels_out = tmp; + void *tmp = driver->screen.pixels; + driver->screen.pixels = driver->screen.pixels_out; + driver->screen.pixels_out = tmp; if (!driver->is_ili9488) { - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); } else { - void *tmpb = screen->bytes; - screen->bytes = screen->bytes_out; - screen->bytes_out = tmpb; + void *tmpb = driver->screen.bytes; + driver->screen.bytes = driver->screen.bytes_out; + driver->screen.bytes_out = tmpb; - rgb565_swapped_line_to_rgb888(screen->bytes_out, screen->pixels_out, screen_width); - spi_display_dma_write(&driver->bus.spi_disp, screen_width * 3, screen->bytes_out); + rgb565_swapped_line_to_rgb888(driver->screen.bytes_out, driver->screen.pixels_out, screen_width); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * 3, driver->screen.bytes_out); } transaction_in_progress = true; @@ -212,7 +212,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&driver->bus, screen, driver->is_ili9488 ? 3 : 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, &driver->screen, driver->is_ili9488 ? 3 : 2, x, y, width, height, data); // draw_buffer is fire-and-forget. return; @@ -277,9 +277,7 @@ Context *ili948x_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = calloc(1, sizeof(struct DCSLCDScreen)); - - struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); + struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); driver->display_args.process_message_fn = process_message; @@ -351,22 +349,19 @@ static void display_init(Context *ctx, term opts) // Swap w/h for 90/270. if (driver->rotation & 1) { - screen->w = ILI948X_TFTHEIGHT; - screen->h = ILI948X_TFTWIDTH; + driver->screen.w = ILI948X_TFTHEIGHT; + driver->screen.h = ILI948X_TFTWIDTH; } else { - screen->w = ILI948X_TFTWIDTH; - screen->h = ILI948X_TFTHEIGHT; + driver->screen.w = ILI948X_TFTWIDTH; + driver->screen.h = ILI948X_TFTHEIGHT; } - screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); + driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); + driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); if (driver->is_ili9488) { - screen->bytes = heap_caps_malloc(screen->w * 3, MALLOC_CAP_DMA); - screen->bytes_out = heap_caps_malloc(screen->w * 3, MALLOC_CAP_DMA); - } else { - screen->bytes = NULL; - screen->bytes_out = NULL; + driver->screen.bytes = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); + driver->screen.bytes_out = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); } // Reset. @@ -381,22 +376,12 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); - - vTaskDelay(5 / portTICK_PERIOD_MS); - if (driver->is_ili9488) { dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9488); } else { dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9486); } - spi_dc_write_command(&driver->bus, DCS_LCD_SLPOUT); - - vTaskDelay(120 / portTICK_PERIOD_MS); - - spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); - if (enable_tft_invon) { spi_dc_write_command(&driver->bus, DCS_LCD_INVON); } else { diff --git a/st7789_display_driver.c b/st7789_display_driver.c index 3b323fa..68c2966 100644 --- a/st7789_display_driver.c +++ b/st7789_display_driver.c @@ -81,6 +81,8 @@ struct DCSLCDDriver avm_int_t rotation; + struct DCSLCDScreen screen; + Context *ctx; struct DisplayTaskArgs display_args; @@ -89,8 +91,6 @@ struct DCSLCDDriver #define DCS_LCD_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) -static struct DCSLCDScreen *screen; - static void display_init(Context *ctx, term opts); static void display_init_using_list(struct DCSLCDDriver *driver, term init_list); @@ -107,11 +107,11 @@ static void do_update(Context *ctx, term display_list) t = term_get_list_tail(t); } - int screen_width = screen->w; - int screen_height = screen->h; struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; - dcs_lcd_set_paint_area(&driver->bus, screen, 0, 0, screen_width, screen_height); + dcs_lcd_set_paint_area(&driver->bus, &driver->screen, 0, 0, screen_width, screen_height); spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); @@ -120,7 +120,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = dcs_lcd_draw_x(screen, xpos, ypos, items, len); + int drawn_pixels = dcs_lcd_draw_x(&driver->screen, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -132,10 +132,10 @@ static void do_update(Context *ctx, term display_list) } // NEW CODE - void *tmp = screen->pixels; - screen->pixels = screen->pixels_out; - screen->pixels_out = tmp; - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), screen->pixels_out); + void *tmp = driver->screen.pixels; + driver->screen.pixels = driver->screen.pixels_out; + driver->screen.pixels_out = tmp; + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); transaction_in_progress = true; } @@ -181,7 +181,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&driver->bus, screen, 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, &driver->screen, 2, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -228,13 +228,11 @@ static void display_init(Context *ctx, term opts) term height_term = interop_kv_get_value_default( opts, ATOM_STR("\x6", "height"), term_from_int(240), ctx->global); - screen = calloc(1, sizeof(struct DCSLCDScreen)); - screen->w = term_to_int(width_term); - screen->h = term_to_int(height_term); - screen->pixels = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - screen->pixels_out = heap_caps_malloc(screen->w * sizeof(uint16_t), MALLOC_CAP_DMA); - - struct DCSLCDDriver *driver = malloc(sizeof(struct DCSLCDDriver)); + struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); + driver->screen.w = term_to_int(width_term); + driver->screen.h = term_to_int(height_term); + driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); + driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); driver->display_args.process_message_fn = process_message; @@ -272,8 +270,8 @@ static void display_init(Context *ctx, term opts) opts, ATOM_STR("\x8", "y_offset"), term_from_int(0), ctx->global); if (term_is_integer(x_off_term) && term_is_integer(y_off_term)) { - screen->x_offset = (int16_t) term_to_int(x_off_term); - screen->y_offset = (int16_t) term_to_int(y_off_term); + driver->screen.x_offset = (int16_t) term_to_int(x_off_term); + driver->screen.y_offset = (int16_t) term_to_int(y_off_term); } else { ok = false; } @@ -297,11 +295,6 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - if (!reset_configured) { - spi_dc_write_command(&driver->bus, DCS_LCD_SWRESET); - delay(100); - } - term maybe_init_list = interop_kv_get_value_default(opts, ATOM_STR("\x9", "init_list"), term_nil(), ctx->global); if (maybe_init_list != term_nil()) { @@ -324,9 +317,6 @@ static void display_init(Context *ctx, term opts) } } - spi_dc_write_command(&driver->bus, DCS_LCD_DISPON); - delay(120); - struct BacklightGPIOConfig backlight_config; backlight_gpio_init_config(&backlight_config); backlight_gpio_parse_config(&backlight_config, opts, ctx->global); From 40364db1babb3d0dbfb422875b35bae38446a471 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 11:24:36 +0000 Subject: [PATCH 35/52] Rename st7789 driver to unified DCS LCD name git mv st7789_display_driver.c -> dcs_lcd_display_driver.c; rename the create_port entry point and the log TAG accordingly. Pure rename -- sitronix,st7789 and sitronix,st7796 dispatch through the new function; ILI compatibles still route through their legacy drivers. Signed-off-by: Davide Bettio --- CMakeLists.txt | 2 +- st7789_display_driver.c => dcs_lcd_display_driver.c | 4 ++-- display_driver.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename st7789_display_driver.c => dcs_lcd_display_driver.c (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53b027c..48ebf0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ endif() idf_component_register(SRCS "dcs_lcd_commands.c" + "dcs_lcd_display_driver.c" "dcs_lcd_draw.c" "display_common.c" "display_driver.c" @@ -44,7 +45,6 @@ idf_component_register(SRCS "ili948x_display_driver.c" "memory_display_driver.c" "ssd1306_display_driver.c" - "st7789_display_driver.c" "spi_dc_driver.c" "spi_display.c" "ufontlib.c" diff --git a/st7789_display_driver.c b/dcs_lcd_display_driver.c similarity index 99% rename from st7789_display_driver.c rename to dcs_lcd_display_driver.c index 68c2966..1273591 100644 --- a/st7789_display_driver.c +++ b/dcs_lcd_display_driver.c @@ -67,7 +67,7 @@ #include "font_data.h" -static const char *TAG = "st7789_display_driver"; +static const char *TAG = "dcs_lcd_display_driver"; static inline void delay(int ms) { @@ -213,7 +213,7 @@ static void set_rotation(struct DCSLCDDriver *driver, int rotation) } } -Context *st7789_display_create_port(GlobalContext *global, term opts) +Context *dcs_lcd_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); ctx->native_handler = display_task_consume_mailbox; diff --git a/display_driver.c b/display_driver.c index 7b2936a..e82e5cd 100644 --- a/display_driver.c +++ b/display_driver.c @@ -31,11 +31,11 @@ static const char *TAG = "display_driver"; Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts); Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts); +Context *dcs_lcd_display_create_port(GlobalContext *global, term opts); Context *ili934x_display_create_port(GlobalContext *global, term opts); Context *ili948x_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); Context *ssd1306_display_create_port(GlobalContext *global, term opts); -Context *st7789_display_create_port(GlobalContext *global, term opts); Context *display_create_port(GlobalContext *global, term opts) { @@ -76,7 +76,7 @@ Context *display_create_port(GlobalContext *global, term opts) ctx = ssd1306_display_create_port(global, opts); } else if (!strcmp(compat_string, "sitronix,st7789") || !strcmp(compat_string, "sitronix,st7796")) { - ctx = st7789_display_create_port(global, opts); + ctx = dcs_lcd_display_create_port(global, opts); } else { ESP_LOGE(TAG, "No matching display driver for given `comptaible`: `%s`.", compat_string); } From 1b6e524c72ff6a0650462eea627df9a5aeb10a40 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 11:30:27 +0000 Subject: [PATCH 36/52] Refactor DCS LCD driver to use descriptor table Generalise the driver into a single descriptor-driven implementation that can run any of the six DCS LCD controllers covered by the DCSLCDDesc table. A compatible-string lookup picks the descriptor; init sequence, MADCTL bytes, default geometry, default colour order, SPI clock and pixel size come from it. Hardware-touching code becomes uniform across controllers: MADCTL slots feed set_rotation (with 0xFF rejecting an unsupported rotation), pixel_bytes selects between RGB565 and RGB888 scanline paths, and the boot flow always emits an explicit INVON/INVOFF. init_list, init_seq_type, width/height, x_offset/y_offset, and color_order now apply to every controller (silently fixing the hardcoded-geometry FIXME the ILI934x driver carried). Signed-off-by: Davide Bettio --- dcs_lcd_display_driver.c | 164 ++++++++++++++++++++++++++++++++------- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/dcs_lcd_display_driver.c b/dcs_lcd_display_driver.c index 1273591..482350d 100644 --- a/dcs_lcd_display_driver.c +++ b/dcs_lcd_display_driver.c @@ -61,8 +61,6 @@ #include "spi_dc_driver.h" #include "spi_display.h" -// if needed it can be lowered to 27000000, while maximum is 62.5 Mhz -#define SPI_CLOCK_HZ 40000000 #define SPI_MODE 0 #include "font_data.h" @@ -80,6 +78,9 @@ struct DCSLCDDriver int reset_gpio; avm_int_t rotation; + bool madctl_bgr; + + const struct DCSLCDDesc *desc; struct DCSLCDScreen screen; @@ -88,6 +89,48 @@ struct DCSLCDDriver struct DisplayTaskArgs display_args; }; +static const struct { + const char *compat; + const struct DCSLCDDesc *desc; +} dcs_lcd_compat_table[] = { + { "ilitek,ili9341", &dcs_lcd_desc_ili9341 }, + { "ilitek,ili9342c", &dcs_lcd_desc_ili9342c }, + { "ilitek,ili9486", &dcs_lcd_desc_ili9486 }, + { "ilitek,ili9488", &dcs_lcd_desc_ili9488 }, + { "sitronix,st7789", &dcs_lcd_desc_st7789 }, + { "sitronix,st7796", &dcs_lcd_desc_st7796 }, +}; + +static const struct DCSLCDDesc *dcs_lcd_desc_for_compatible(const char *compat) +{ + for (size_t i = 0; i < sizeof(dcs_lcd_compat_table) / sizeof(dcs_lcd_compat_table[0]); i++) { + if (!strcmp(compat, dcs_lcd_compat_table[i].compat)) { + return dcs_lcd_compat_table[i].desc; + } + } + return NULL; +} + +// ILI9488 scanline conversion: RGB565 -> RGB888 bytes. +static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) +{ + for (int i = 0; i < n_pixels; i++) { + uint16_t px = (uint16_t) SPI_SWAP_DATA_TX(src_swapped[i], 16); + + uint8_t r5 = (px >> 11) & 0x1F; + uint8_t g6 = (px >> 5) & 0x3F; + uint8_t b5 = (px >> 0) & 0x1F; + + uint8_t r8 = (r5 << 3) | (r5 >> 2); + uint8_t g8 = (g6 << 2) | (g6 >> 4); + uint8_t b8 = (b5 << 3) | (b5 >> 2); + + dst[i * 3 + 0] = r8; + dst[i * 3 + 1] = g8; + dst[i * 3 + 2] = b8; + } +} + #define DCS_LCD_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) @@ -131,11 +174,22 @@ static void do_update(Context *ctx, term display_list) spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - // NEW CODE + // Swap scanline buffers. void *tmp = driver->screen.pixels; driver->screen.pixels = driver->screen.pixels_out; driver->screen.pixels_out = tmp; - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); + + if (driver->desc->pixel_bytes == 2) { + spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); + } else { + void *tmpb = driver->screen.bytes; + driver->screen.bytes = driver->screen.bytes_out; + driver->screen.bytes_out = tmpb; + + rgb565_swapped_line_to_rgb888(driver->screen.bytes_out, driver->screen.pixels_out, screen_width); + spi_display_dma_write(&driver->bus.spi_disp, screen_width * 3, driver->screen.bytes_out); + } + transaction_in_progress = true; } @@ -181,7 +235,7 @@ static void process_message(Message *message, Context *ctx) const void *data = (const void *) ((addr_low | (addr_high << 16))); - dcs_lcd_draw_buffer(&driver->bus, &driver->screen, 2, x, y, width, height, data); + dcs_lcd_draw_buffer(&driver->bus, &driver->screen, driver->desc->pixel_bytes, x, y, width, height, data); // draw_buffer is a kind of cast, no need to reply return; @@ -207,10 +261,12 @@ static void process_message(Message *message, Context *ctx) static void set_rotation(struct DCSLCDDriver *driver, int rotation) { - if (rotation == 1) { - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, DCS_LCD_MAD_MX | DCS_LCD_MAD_MV); + uint8_t madctl = driver->desc->madctl[rotation & 3]; + if (driver->madctl_bgr) { + madctl |= DCS_LCD_MAD_BGR; } + spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); + spi_dc_write_data(&driver->bus, madctl); } Context *dcs_lcd_display_create_port(GlobalContext *global, term opts) @@ -223,16 +279,48 @@ Context *dcs_lcd_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { + // Resolve compatible string -> per-controller descriptor. + term compat_term = interop_kv_get_value_default( + opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); + int str_ok; + char *compat_string = interop_term_to_string(compat_term, &str_ok); + const struct DCSLCDDesc *desc = NULL; + if (str_ok && compat_string) { + desc = dcs_lcd_desc_for_compatible(compat_string); + } + if (!desc) { + ESP_LOGE(TAG, "Failed init: unknown or missing compatible '%s'.", + compat_string ? compat_string : "(null)"); + free(compat_string); + return; + } + free(compat_string); + + term rotation_term = interop_kv_get_value_default( + opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); + bool ok = term_is_integer(rotation_term); + avm_int_t rotation = ok ? term_to_int(rotation_term) : 0; + + // Default geometry from descriptor, swapped on odd rotation, overrideable. + int default_w = (rotation & 1) ? desc->native_height : desc->native_width; + int default_h = (rotation & 1) ? desc->native_width : desc->native_height; term width_term = interop_kv_get_value_default( - opts, ATOM_STR("\x5", "width"), term_from_int(320), ctx->global); + opts, ATOM_STR("\x5", "width"), term_from_int(default_w), ctx->global); term height_term = interop_kv_get_value_default( - opts, ATOM_STR("\x6", "height"), term_from_int(240), ctx->global); + opts, ATOM_STR("\x6", "height"), term_from_int(default_h), ctx->global); struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); + driver->desc = desc; + driver->rotation = rotation; + driver->madctl_bgr = desc->default_bgr; driver->screen.w = term_to_int(width_term); driver->screen.h = term_to_int(height_term); driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); + if (desc->pixel_bytes == 3) { + driver->screen.bytes = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); + driver->screen.bytes_out = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); + } driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); driver->display_args.process_message_fn = process_message; @@ -244,11 +332,11 @@ static void display_init(Context *ctx, term opts) struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); spi_config.mode = SPI_MODE; - spi_config.clock_speed_hz = SPI_CLOCK_HZ; + spi_config.clock_speed_hz = desc->spi_clock_hz; spi_display_parse_config(&spi_config, opts, ctx->global); spi_display_init(&driver->bus.spi_disp, &spi_config); - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); + ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); bool reset_configured = true; if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global)) { @@ -256,14 +344,22 @@ static void display_init(Context *ctx, term opts) reset_configured = false; } - term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); - ok = ok && term_is_integer(rotation); - driver->rotation = term_to_int(rotation); - term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); bool enable_tft_invon = (invon == TRUE_ATOM); + // color_order: rgb|bgr (default: per-descriptor) + term color_order_term = interop_kv_get_value_default(opts, ATOM_STR("\xB", "color_order"), term_nil(), ctx->global); + if (color_order_term != term_nil()) { + if (color_order_term == context_make_atom(ctx, "\x3" "rgb")) { + driver->madctl_bgr = false; + } else if (color_order_term == context_make_atom(ctx, "\x3" "bgr")) { + driver->madctl_bgr = true; + } else { + ok = false; + } + } + term x_off_term = interop_kv_get_value_default( opts, ATOM_STR("\x8", "x_offset"), term_from_int(0), ctx->global); term y_off_term = interop_kv_get_value_default( @@ -281,6 +377,12 @@ static void display_init(Context *ctx, term opts) return; } + if (desc->madctl[driver->rotation & 3] == 0xFF) { + ESP_LOGE(TAG, "Failed init: rotation %d not supported by controller %s.", + (int) driver->rotation, desc->name); + return; + } + // Reset if (reset_configured) { spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); @@ -295,27 +397,31 @@ static void display_init(Context *ctx, term opts) gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); + // Init sequence: init_list opt overrides everything; otherwise descriptor + // default, with init_seq_type "alt_gamma_2" selecting the ST7789 alt seq. term maybe_init_list = interop_kv_get_value_default(opts, ATOM_STR("\x9", "init_list"), term_nil(), ctx->global); if (maybe_init_list != term_nil()) { display_init_using_list(driver, maybe_init_list); } else { - term init_seq_type_term = interop_kv_get_value_default(opts, ATOM_STR("\xD", "init_seq_type"), term_nil(), ctx->global); - int str_ok; - char *init_seq_type_string = interop_term_to_string(init_seq_type_term, &str_ok); - if (str_ok && !strcmp(init_seq_type_string, "alt_gamma_2")) { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_st7789_alt); - free(init_seq_type_string); - } else { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_st7789_std); + const uint8_t *init_seq = desc->default_init_seq; + term init_seq_type_term = interop_kv_get_value_default( + opts, ATOM_STR("\xD", "init_seq_type"), term_nil(), ctx->global); + if (init_seq_type_term != term_nil()) { + int type_ok; + char *type_str = interop_term_to_string(init_seq_type_term, &type_ok); + if (type_ok && !strcmp(type_str, "alt_gamma_2") + && (desc == &dcs_lcd_desc_st7789 || desc == &dcs_lcd_desc_st7796)) { + init_seq = dcs_lcd_init_seq_st7789_alt; + } + free(type_str); } + dcs_lcd_execute_init_seq(&driver->bus, init_seq); + } - set_rotation(driver, driver->rotation); + set_rotation(driver, driver->rotation); - if (enable_tft_invon) { - spi_dc_write_command(&driver->bus, DCS_LCD_INVON); - } - } + spi_dc_write_command(&driver->bus, enable_tft_invon ? DCS_LCD_INVON : DCS_LCD_INVOFF); struct BacklightGPIOConfig backlight_config; backlight_gpio_init_config(&backlight_config); From a0666bc6ec686cc19a3229f416b939230c89dbe8 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 11:40:21 +0000 Subject: [PATCH 37/52] Remove legacy DCS LCD drivers Route the four ILI compatibles through the unified dcs_lcd_display_create_port and drop the now-orphaned ili934x and ili948x driver files. All six DCS LCD compatibles (ilitek,ili9341/ili9342c/ili9486/ili9488 and sitronix,st7789/st7796) are served by a single descriptor-driven driver, with x_offset/y_offset, color_order, init_list, width/height and an optional reset GPIO available uniformly. Signed-off-by: Davide Bettio --- CMakeLists.txt | 2 - display_driver.c | 20 +- ili934x_display_driver.c | 300 ----------------------------- ili948x_display_driver.c | 400 --------------------------------------- 4 files changed, 7 insertions(+), 715 deletions(-) delete mode 100644 ili934x_display_driver.c delete mode 100644 ili948x_display_driver.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 48ebf0d..f7fad0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,6 @@ idf_component_register(SRCS "5in65_acep_7c_display_driver.c" "gdep073e01_display_driver.c" "font_data.c" - "ili934x_display_driver.c" - "ili948x_display_driver.c" "memory_display_driver.c" "ssd1306_display_driver.c" "spi_dc_driver.c" diff --git a/display_driver.c b/display_driver.c index e82e5cd..eab6511 100644 --- a/display_driver.c +++ b/display_driver.c @@ -32,8 +32,6 @@ static const char *TAG = "display_driver"; Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts); Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts); Context *dcs_lcd_display_create_port(GlobalContext *global, term opts); -Context *ili934x_display_create_port(GlobalContext *global, term opts); -Context *ili948x_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); Context *ssd1306_display_create_port(GlobalContext *global, term opts); @@ -60,23 +58,19 @@ Context *display_create_port(GlobalContext *global, term opts) ctx = gdep073e01_display_driver_create_port(global, opts); } else if (!strcmp(compat_string, "sharp,memory-lcd")) { ctx = memory_lcd_display_create_port(global, opts); - } else if (!strcmp(compat_string, "ilitek,ili9341")) { - ctx = ili934x_display_create_port(global, opts); - } else if (!strcmp(compat_string, "ilitek,ili9342c")) { - ctx = ili934x_display_create_port(global, opts); - } else if (!strcmp(compat_string, "ilitek,ili9486")) { - ctx = ili948x_display_create_port(global, opts); - } else if (!strcmp(compat_string, "ilitek,ili9488")) { - ctx = ili948x_display_create_port(global, opts); + } else if (!strcmp(compat_string, "ilitek,ili9341") + || !strcmp(compat_string, "ilitek,ili9342c") + || !strcmp(compat_string, "ilitek,ili9486") + || !strcmp(compat_string, "ilitek,ili9488") + || !strcmp(compat_string, "sitronix,st7789") + || !strcmp(compat_string, "sitronix,st7796")) { + ctx = dcs_lcd_display_create_port(global, opts); } else if (!strcmp(compat_string, "solomon-systech,ssd1306")) { ctx = ssd1306_display_create_port(global, opts); } else if (!strcmp(compat_string, "solomon-systech,ssd1315")) { ctx = ssd1306_display_create_port(global, opts); } else if (!strcmp(compat_string, "sino-wealth,sh1106")) { ctx = ssd1306_display_create_port(global, opts); - } else if (!strcmp(compat_string, "sitronix,st7789") - || !strcmp(compat_string, "sitronix,st7796")) { - ctx = dcs_lcd_display_create_port(global, opts); } else { ESP_LOGE(TAG, "No matching display driver for given `comptaible`: `%s`.", compat_string); } diff --git a/ili934x_display_driver.c b/ili934x_display_driver.c deleted file mode 100644 index fd724bb..0000000 --- a/ili934x_display_driver.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2020-2022 Davide Bettio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "display_driver.h" - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "backlight_gpio.h" -#include "dcs_lcd_color.h" -#include "dcs_lcd_commands.h" -#include "dcs_lcd_draw.h" -#include "dcs_lcd_screen.h" -#include "display_common.h" -#include "display_items.h" -#include "display_message.h" -#include "display_task.h" -#include "image_helpers.h" -#include "spi_dc_driver.h" -#include "spi_display.h" - -#define SPI_CLOCK_HZ 27000000 -#define SPI_MODE 0 - - -#include "font_data.h" - -static const char *TAG = "ili934x_display_driver"; - -struct DCSLCDDriver -{ - struct SPIDCBus bus; - int reset_gpio; - - avm_int_t rotation; - - struct DCSLCDScreen screen; - - Context *ctx; - - struct DisplayTaskArgs display_args; -}; - -#define DCS_LCD_DRIVER_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) - -static void display_init(Context *ctx, term opts); - -static void do_update(Context *ctx, term display_list) -{ - int proper; - int len = term_list_length(display_list, &proper); - - BaseDisplayItem *items = malloc(sizeof(BaseDisplayItem) * len); - - term t = display_list; - for (int i = 0; i < len; i++) { - display_items_init_item(&items[i], term_get_list_head(t), ctx); - t = term_get_list_tail(t); - } - - struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - int screen_width = driver->screen.w; - int screen_height = driver->screen.h; - - dcs_lcd_set_paint_area(&driver->bus, &driver->screen, 0, 0, screen_width, screen_height); - spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - - bool transaction_in_progress = false; - - for (int ypos = 0; ypos < screen_height; ypos++) { - int xpos = 0; - while (xpos < screen_width) { - int drawn_pixels = dcs_lcd_draw_x(&driver->screen, xpos, ypos, items, len); - xpos += drawn_pixels; - } - - if (transaction_in_progress) { - spi_transaction_t *trans; - // I did a quick measurement, and most of the time is spent waiting for DMA transaction - // eg. 23 us spent in draw_x, 188 us spent in spi_device_get_trans_result - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - //NEW CODE - void *tmp = driver->screen.pixels; - driver->screen.pixels = driver->screen.pixels_out; - driver->screen.pixels_out = tmp; - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); - transaction_in_progress = true; - } - - if (transaction_in_progress) { - spi_transaction_t *trans; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - spi_device_release_bus(driver->bus.spi_disp.handle); - - display_items_delete(items, len); -} - -static void process_message(Message *message, Context *ctx) -{ - GenMessage gen_message; - if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { - fprintf(stderr, "Received invalid message."); - AVM_ABORT(); - } - - term req = gen_message.req; - if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { - AVM_ABORT(); - } - term cmd = term_get_tuple_element(req, 0); - - struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - - if (cmd == context_make_atom(ctx, "\x6" - "update")) { - term display_list = term_get_tuple_element(req, 1); - do_update(ctx, display_list); - - } else if (cmd == context_make_atom(ctx, "\xB" - "draw_buffer")) { - int x = term_to_int(term_get_tuple_element(req, 1)); - int y = term_to_int(term_get_tuple_element(req, 2)); - int width = term_to_int(term_get_tuple_element(req, 3)); - int height = term_to_int(term_get_tuple_element(req, 4)); - unsigned long addr_low = term_to_int(term_get_tuple_element(req, 5)); - unsigned long addr_high = term_to_int(term_get_tuple_element(req, 6)); - - const void *data = (const void *) ((addr_low | (addr_high << 16))); - - dcs_lcd_draw_buffer(&driver->bus, &driver->screen, 2, x, y, width, height, data); - - // draw_buffer is a kind of cast, no need to reply - return; - - } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" "load_image")) { - handle_load_image(req, gen_message.ref, gen_message.pid, ctx); - return; - - } else { - fprintf(stderr, "display: "); - term_display(stderr, req, ctx); - fprintf(stderr, "\n"); - } - - BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); - term return_tuple = term_alloc_tuple(2, &heap); - term_put_tuple_element(return_tuple, 0, gen_message.ref); - term_put_tuple_element(return_tuple, 1, OK_ATOM); - - display_message_send(gen_message.pid, return_tuple, ctx->global); - END_WITH_STACK_HEAP(heap, ctx->global); -} - -static void set_rotation(struct DCSLCDDriver *driver, int rotation) -{ - if (rotation == 1) { - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, DCS_LCD_MAD_BGR | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV); - } -} - -Context *ili934x_display_create_port(GlobalContext *global, term opts) -{ - Context *ctx = context_new(global); - ctx->native_handler = display_task_consume_mailbox; - display_init(ctx, opts); - return ctx; -} - -static void display_init(Context *ctx, term opts) -{ - struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); - - // FIXME: hardcoded width and height - driver->screen.w = 320; - driver->screen.h = 240; - driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); - driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); - - driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - driver->display_args.process_message_fn = process_message; - driver->display_args.ctx = ctx; - ctx->platform_data = &driver->display_args; - - driver->ctx = ctx; - - struct SPIDisplayConfig spi_config; - spi_display_init_config(&spi_config); - spi_config.mode = SPI_MODE; - spi_config.clock_speed_hz = SPI_CLOCK_HZ; - spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&driver->bus.spi_disp, &spi_config); - - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); - - term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); - int str_ok; - char *compat_string = interop_term_to_string(compat_value_term, &str_ok); - bool enable_ili93442c = false; - if (str_ok && compat_string) { - enable_ili93442c = !strcmp(compat_string, "ilitek,ili9342c"); - free(compat_string); - } else { - ok = false; - } - - term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); - ok = ok && term_is_integer(rotation); - driver->rotation = term_to_int(rotation); - - term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); - ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); - bool enable_tft_invon = (invon == TRUE_ATOM); - - if (UNLIKELY(!ok)) { - ESP_LOGE(TAG, "Failed init: invalid display parameters."); - return; - } - - // Reset - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(driver->reset_gpio, 1); - vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(driver->reset_gpio, 0); - vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(driver->reset_gpio, 1); - spi_device_release_bus(driver->bus.spi_disp.handle); - - gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - - if (enable_ili93442c) { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9342c); - } else { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9341); - } - - if (enable_tft_invon) { - spi_dc_write_command(&driver->bus, DCS_LCD_INVON); - } - - set_rotation(driver, driver->rotation); - - struct BacklightGPIOConfig backlight_config; - backlight_gpio_init_config(&backlight_config); - backlight_gpio_parse_config(&backlight_config, opts, ctx->global); - backlight_gpio_init(&backlight_config); - - xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); -} - diff --git a/ili948x_display_driver.c b/ili948x_display_driver.c deleted file mode 100644 index 1db2d80..0000000 --- a/ili948x_display_driver.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2020-2022 Davide Bettio - * Copyright 2025 Masatoshi Nishiguchi - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/* Based on ili934x_display_driver.c. */ - -#include "display_driver.h" - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "backlight_gpio.h" -#include "dcs_lcd_color.h" -#include "dcs_lcd_commands.h" -#include "dcs_lcd_draw.h" -#include "dcs_lcd_screen.h" -#include "display_common.h" -#include "display_items.h" -#include "display_message.h" -#include "display_task.h" -#include "image_helpers.h" -#include "spi_dc_driver.h" -#include "spi_display.h" - -#define SPI_CLOCK_HZ 27000000 -#define SPI_MODE 0 - -#define ILI948X_TFTWIDTH 320 -#define ILI948X_TFTHEIGHT 480 - -#include "font_data.h" - -static const char *TAG = "ili948x_display_driver"; - -struct DCSLCDDriver -{ - struct SPIDCBus bus; - int reset_gpio; - - avm_int_t rotation; - bool is_ili9488; - - bool madctl_bgr; - - struct DCSLCDScreen screen; - - Context *ctx; - - struct DisplayTaskArgs display_args; -}; - -#define DCS_LCD_DRIVER_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct DCSLCDDriver, display_args) - -// ILI9488 scanline conversion: RGB565 -> RGB888 bytes. -static inline void rgb565_swapped_line_to_rgb888(uint8_t *dst, const uint16_t *src_swapped, int n_pixels) -{ - for (int i = 0; i < n_pixels; i++) { - uint16_t px = (uint16_t) SPI_SWAP_DATA_TX(src_swapped[i], 16); - - uint8_t r5 = (px >> 11) & 0x1F; - uint8_t g6 = (px >> 5) & 0x3F; - uint8_t b5 = (px >> 0) & 0x1F; - - uint8_t r8 = (r5 << 3) | (r5 >> 2); - uint8_t g8 = (g6 << 2) | (g6 >> 4); - uint8_t b8 = (b5 << 3) | (b5 >> 2); - - dst[i * 3 + 0] = r8; - dst[i * 3 + 1] = g8; - dst[i * 3 + 2] = b8; - } -} - -static void display_init(Context *ctx, term opts); - -static void do_update(Context *ctx, term display_list) -{ - int proper; - int len = term_list_length(display_list, &proper); - - BaseDisplayItem *items = malloc(sizeof(BaseDisplayItem) * len); - - term t = display_list; - for (int i = 0; i < len; i++) { - display_items_init_item(&items[i], term_get_list_head(t), ctx); - t = term_get_list_tail(t); - } - - struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - int screen_width = driver->screen.w; - int screen_height = driver->screen.h; - - dcs_lcd_set_paint_area(&driver->bus, &driver->screen, 0, 0, screen_width, screen_height); - spi_dc_write_command(&driver->bus, DCS_LCD_RAMWR); - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - - bool transaction_in_progress = false; - - for (int ypos = 0; ypos < screen_height; ypos++) { - int xpos = 0; - while (xpos < screen_width) { - int drawn_pixels = dcs_lcd_draw_x(&driver->screen, xpos, ypos, items, len); - xpos += drawn_pixels; - } - - if (transaction_in_progress) { - spi_transaction_t *trans; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - // Swap scanline buffers. - void *tmp = driver->screen.pixels; - driver->screen.pixels = driver->screen.pixels_out; - driver->screen.pixels_out = tmp; - - if (!driver->is_ili9488) { - spi_display_dma_write(&driver->bus.spi_disp, screen_width * sizeof(uint16_t), driver->screen.pixels_out); - } else { - void *tmpb = driver->screen.bytes; - driver->screen.bytes = driver->screen.bytes_out; - driver->screen.bytes_out = tmpb; - - rgb565_swapped_line_to_rgb888(driver->screen.bytes_out, driver->screen.pixels_out, screen_width); - spi_display_dma_write(&driver->bus.spi_disp, screen_width * 3, driver->screen.bytes_out); - } - - transaction_in_progress = true; - } - - if (transaction_in_progress) { - spi_transaction_t *trans; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - spi_device_release_bus(driver->bus.spi_disp.handle); - - display_items_delete(items, len); -} - -static void process_message(Message *message, Context *ctx) -{ - GenMessage gen_message; - if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { - fprintf(stderr, "Received invalid message."); - AVM_ABORT(); - } - - term req = gen_message.req; - if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { - AVM_ABORT(); - } - term cmd = term_get_tuple_element(req, 0); - - struct DCSLCDDriver *driver = DCS_LCD_DRIVER_FROM_CTX(ctx); - - if (cmd == context_make_atom(ctx, "\x6" - "update")) { - term display_list = term_get_tuple_element(req, 1); - do_update(ctx, display_list); - - } else if (cmd == context_make_atom(ctx, "\xB" - "draw_buffer")) { - int x = term_to_int(term_get_tuple_element(req, 1)); - int y = term_to_int(term_get_tuple_element(req, 2)); - int width = term_to_int(term_get_tuple_element(req, 3)); - int height = term_to_int(term_get_tuple_element(req, 4)); - unsigned long addr_low = term_to_int(term_get_tuple_element(req, 5)); - unsigned long addr_high = term_to_int(term_get_tuple_element(req, 6)); - - const void *data = (const void *) ((addr_low | (addr_high << 16))); - - dcs_lcd_draw_buffer(&driver->bus, &driver->screen, driver->is_ili9488 ? 3 : 2, x, y, width, height, data); - - // draw_buffer is fire-and-forget. - return; - - } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" - "load_image")) { - handle_load_image(req, gen_message.ref, gen_message.pid, ctx); - return; - - } else { - fprintf(stderr, "display: "); - term_display(stderr, req, ctx); - fprintf(stderr, "\n"); - } - - BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); - term return_tuple = term_alloc_tuple(2, &heap); - term_put_tuple_element(return_tuple, 0, gen_message.ref); - term_put_tuple_element(return_tuple, 1, OK_ATOM); - - display_message_send(gen_message.pid, return_tuple, ctx->global); - END_WITH_STACK_HEAP(heap, ctx->global); -} - -static void set_rotation(struct DCSLCDDriver *driver, int rotation) -{ - uint8_t madctl = 0; - - if (driver->madctl_bgr) { - madctl |= DCS_LCD_MAD_BGR; - } - - switch (rotation & 3) { - case 0: - madctl |= DCS_LCD_MAD_MX; - break; - - case 1: - madctl |= DCS_LCD_MAD_MV; - break; - - case 2: - madctl |= DCS_LCD_MAD_MY; - break; - - case 3: - madctl |= DCS_LCD_MAD_MX | DCS_LCD_MAD_MY | DCS_LCD_MAD_MV; - break; - } - - spi_dc_write_command(&driver->bus, DCS_LCD_MADCTL); - spi_dc_write_data(&driver->bus, madctl); -} - -Context *ili948x_display_create_port(GlobalContext *global, term opts) -{ - Context *ctx = context_new(global); - ctx->native_handler = display_task_consume_mailbox; - display_init(ctx, opts); - return ctx; -} - -static void display_init(Context *ctx, term opts) -{ - struct DCSLCDDriver *driver = calloc(1, sizeof(struct DCSLCDDriver)); - - driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - driver->display_args.process_message_fn = process_message; - driver->display_args.ctx = ctx; - ctx->platform_data = &driver->display_args; - - driver->ctx = ctx; - - struct SPIDisplayConfig spi_config; - spi_display_init_config(&spi_config); - spi_config.mode = SPI_MODE; - spi_config.clock_speed_hz = SPI_CLOCK_HZ; - spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&driver->bus.spi_disp, &spi_config); - - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); - - term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); - int str_ok; - char *compat_string = interop_term_to_string(compat_value_term, &str_ok); - - bool is_ili9486 = false; - bool is_ili9488 = false; - if (str_ok && compat_string) { - is_ili9486 = !strcmp(compat_string, "ilitek,ili9486"); - is_ili9488 = !strcmp(compat_string, "ilitek,ili9488"); - free(compat_string); - } else { - ok = false; - } - - if (!is_ili9486 && !is_ili9488) { - ok = false; - } - driver->is_ili9488 = is_ili9488; - - // color_order: rgb|bgr (default: bgr) - term color_order_term = interop_kv_get_value_default(opts, ATOM_STR("\xB", "color_order"), term_nil(), ctx->global); - - if (term_is_nil(color_order_term)) { - driver->madctl_bgr = true; - } else if (term_is_atom(color_order_term)) { - if (color_order_term == context_make_atom(ctx, "\x3" - "rgb")) { - driver->madctl_bgr = false; - } else if (color_order_term == context_make_atom(ctx, "\x3" - "bgr")) { - driver->madctl_bgr = true; - } else { - ok = false; - } - } else { - ok = false; - } - - term rotation = interop_kv_get_value_default(opts, ATOM_STR("\x8", "rotation"), term_from_int(0), ctx->global); - ok = ok && term_is_integer(rotation); - driver->rotation = term_to_int(rotation); - - term invon = interop_kv_get_value_default(opts, ATOM_STR("\x10", "enable_tft_invon"), FALSE_ATOM, ctx->global); - ok = ok && ((invon == TRUE_ATOM) || (invon == FALSE_ATOM)); - bool enable_tft_invon = (invon == TRUE_ATOM); - - if (UNLIKELY(!ok)) { - ESP_LOGE(TAG, "Failed init: invalid display parameters."); - return; - } - - // Swap w/h for 90/270. - if (driver->rotation & 1) { - driver->screen.w = ILI948X_TFTHEIGHT; - driver->screen.h = ILI948X_TFTWIDTH; - } else { - driver->screen.w = ILI948X_TFTWIDTH; - driver->screen.h = ILI948X_TFTHEIGHT; - } - - driver->screen.pixels = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); - driver->screen.pixels_out = heap_caps_malloc(driver->screen.w * sizeof(uint16_t), MALLOC_CAP_DMA); - - if (driver->is_ili9488) { - driver->screen.bytes = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); - driver->screen.bytes_out = heap_caps_malloc(driver->screen.w * 3, MALLOC_CAP_DMA); - } - - // Reset. - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(driver->reset_gpio, 1); - vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(driver->reset_gpio, 0); - vTaskDelay(50 / portTICK_PERIOD_MS); - gpio_set_level(driver->reset_gpio, 1); - spi_device_release_bus(driver->bus.spi_disp.handle); - - gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - - if (driver->is_ili9488) { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9488); - } else { - dcs_lcd_execute_init_seq(&driver->bus, dcs_lcd_init_seq_ili9486); - } - - if (enable_tft_invon) { - spi_dc_write_command(&driver->bus, DCS_LCD_INVON); - } else { - spi_dc_write_command(&driver->bus, DCS_LCD_INVOFF); - } - - set_rotation(driver, driver->rotation); - - struct BacklightGPIOConfig backlight_config; - backlight_gpio_init_config(&backlight_config); - backlight_gpio_parse_config(&backlight_config, opts, ctx->global); - backlight_gpio_init(&backlight_config); - - xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); -} - From 5fe657b8cbcd49c048ae22e571699134e0fdcf4d Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 18:02:48 +0000 Subject: [PATCH 38/52] Add e-paper init sequence infrastructure The two e-paper drivers currently open-code their init sequences as long chains of spi_dc_write_command/data calls, with BUSY-polling (gdep073e01) or an embedded delay (acep 5.65") interleaved between commands. Each additional panel would add another ~30-60 lines in the same shape. Extract the sequences to declarative byte arrays in the format [CMD][FLAGS_LEN][DATA...][optional DELAY_MS], paired with a size_t length constant. The interpreter takes an extra wait_busy_between_cmds bool to cover the gdep073e01 convention without bloating the per-row format. _Static_assert on array sizes catches miscounted data bytes at build time. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + epaper_commands.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++ epaper_commands.h | 63 ++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 epaper_commands.c create mode 100644 epaper_commands.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f7fad0d..93c4d2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ idf_component_register(SRCS "display_message.c" "display_task.c" "epaper_color.c" + "epaper_commands.c" "epaper_draw.c" "mono_draw.c" "5in65_acep_7c_display_driver.c" diff --git a/epaper_commands.c b/epaper_commands.c new file mode 100644 index 0000000..be836f1 --- /dev/null +++ b/epaper_commands.c @@ -0,0 +1,112 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "epaper_commands.h" + +#include +#include + +#include + +static void wait_busy_high(int busy_gpio) +{ + while (gpio_get_level(busy_gpio) != 1) { + vTaskDelay(100); + } +} + +void epaper_execute_init_seq(struct SPIDCBus *bus, int busy_gpio, + const uint8_t *seq, size_t seq_len, bool wait_busy_between_cmds) +{ + const uint8_t *end = seq + seq_len; + while (seq < end) { + uint8_t cmd = *seq++; + uint8_t flags_len = *seq++; + uint8_t len = flags_len & 0x7F; + + spi_dc_write_cmd_data(bus, cmd, seq, len); + seq += len; + + if (flags_len & EPAPER_INIT_SEQ_DELAY) { + vTaskDelay(*seq++ / portTICK_PERIOD_MS); + } + + if (wait_busy_between_cmds) { + wait_busy_high(busy_gpio); + } + } +} + +// --- Built-in init sequences --- +// +// Transcribed mechanically from the original display_spi_init() +// bodies in 5in65_acep_7c_display_driver.c and +// gdep073e01_display_driver.c. _Static_assert on sizeof guards +// against miscounted data bytes. + +// clang-format off + +// Waveshare 5.65" ACeP 7-color. No BUSY polling between commands; a +// single 100 ms delay between VDCS (0x82) and the second CDI (0x50) +// replicates the vTaskDelay(10) at tick rate 100 Hz in the original +// driver. +const uint8_t epaper_init_seq_acep7c[] = { + 0x00, 2, 0xEF, 0x08, // PSR + 0x01, 4, 0x37, 0x00, 0x23, 0x23, // PWRR + 0x03, 1, 0x00, // POFS + 0x06, 3, 0xC7, 0xC7, 0x1D, // BTST + 0x30, 1, 0x3C, // PLL + 0x40, 1, 0x00, // TSE + 0x50, 1, 0x3F, // CDI + 0x60, 1, 0x22, // TCON + 0x61, 4, 0x02, 0x58, 0x01, 0xC0, // TRES (600x448) + 0xE3, 1, 0xAA, // PWS + 0x82, EPAPER_INIT_SEQ_DELAY | 1, 0x80, 100, // VDCS + 100 ms + 0x50, 1, 0x37, // CDI (second) +}; +_Static_assert(sizeof(epaper_init_seq_acep7c) == 46, + "epaper_init_seq_acep7c: miscounted bytes"); +const size_t epaper_init_seq_acep7c_len = sizeof(epaper_init_seq_acep7c); + +// Good Display GDEP073E01 7.3" 7-color. BUSY polling interleaved +// after each command by the executor (wait_busy_between_cmds = true); +// no per-command delay bytes are required. The leading 0xAA entry is +// an undocumented vendor preamble preserved verbatim. +const uint8_t epaper_init_seq_gdep073e01[] = { + 0xAA, 6, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18, // vendor preamble + 0x01, 1, 0x3F, // PWRR + 0x00, 2, 0x5F, 0x69, // PSR + 0x03, 4, 0x00, 0x54, 0x00, 0x44, // POFS + 0x05, 4, 0x40, 0x1F, 0x1F, 0x2C, // BTST1 + 0x06, 4, 0x6F, 0x1F, 0x17, 0x49, // BTST2 + 0x08, 4, 0x6F, 0x1F, 0x1F, 0x22, // BTST3 + 0x30, 1, 0x00, // PLL + 0x50, 1, 0x3F, // CDI + 0x60, 2, 0x02, 0x00, // TCON + 0x61, 4, 0x03, 0x20, 0x01, 0xE0, // TRES (800x480) + 0x84, 1, 0x01, // T_VDCS + 0xE3, 1, 0x2F, // PWS + 0x04, 0, // PON +}; +_Static_assert(sizeof(epaper_init_seq_gdep073e01) == 63, + "epaper_init_seq_gdep073e01: miscounted bytes"); +const size_t epaper_init_seq_gdep073e01_len = sizeof(epaper_init_seq_gdep073e01); + +// clang-format on diff --git a/epaper_commands.h b/epaper_commands.h new file mode 100644 index 0000000..2b6f321 --- /dev/null +++ b/epaper_commands.h @@ -0,0 +1,63 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _EPAPER_COMMANDS_H_ +#define _EPAPER_COMMANDS_H_ + +#include +#include +#include + +#include "spi_dc_driver.h" + +// --- Init sequence byte-array format --- +// +// Each entry: [CMD] [FLAGS_LEN] [DATA_0 ... DATA_N] [DELAY_MS] +// CMD: command byte (any value, including 0x00) +// FLAGS_LEN: bits 6:0 = data byte count (0-127) +// bit 7 = delay flag (DELAY_MS byte follows data) +// DELAY_MS: delay in milliseconds (0-255), present only if flag set +// +// The sequence is length-bounded: callers pass the array length as +// seq_len and the executor walks the buffer until that count is +// exhausted. Length framing (rather than a sentinel byte) lets any +// command byte appear in an init sequence unambiguously — notably +// 0x00, which is PSR on most Waveshare / GoodDisplay controllers. + +#define EPAPER_INIT_SEQ_DELAY 0x80 + +// Execute an init sequence over an SPI+DC bus. seq/seq_len describe +// a byte array in the format documented above. When +// wait_busy_between_cmds is true, a busy-high poll is inserted after +// each command (mirroring the Good Display / Waveshare convention of +// "command completes when BUSY rises"). busy_gpio is ignored when +// the flag is false. +void epaper_execute_init_seq(struct SPIDCBus *bus, int busy_gpio, + const uint8_t *seq, size_t seq_len, bool wait_busy_between_cmds); + +// Built-in init sequences. Each array is paired with a size_t +// constant giving its length; callers pass both to +// epaper_execute_init_seq(). +extern const uint8_t epaper_init_seq_acep7c[]; +extern const size_t epaper_init_seq_acep7c_len; +extern const uint8_t epaper_init_seq_gdep073e01[]; +extern const size_t epaper_init_seq_gdep073e01_len; + +#endif From b79866fe4f0aa49bd504be61deca82d796e372da Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 21:43:25 +0000 Subject: [PATCH 39/52] E-paper: Use init sequence tables Replace the per-panel chains of spi_dc_write_command/write_data calls (acep: 35 lines with an inline vTaskDelay; gdep073e01: 14 steps each followed by wait_busy_level) with a single call to epaper_execute_init_seq for each panel. The byte-array tables come from epaper_commands.c. For gdep073e01 the executor polls BUSY between commands via wait_busy_between_cmds=true. For acep the 100 ms delay between VDCS (0x82) and the second CDI (0x50) is encoded in the table via the delay flag. Bytes-on-the-wire are unchanged. Drop the now-unused gdep073e01 command-byte defines (PSR, PWRR, POF, POFS, PON, BTST1/2/3, DTM, DRF, PLL, CDI, TCON, TRES, REV, VDCS, T_VDCS, PWS, DSLP); the frame-protocol sites use raw hex to match the acep driver. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 39 ++------------- gdep073e01_display_driver.c | 91 ++-------------------------------- 2 files changed, 6 insertions(+), 124 deletions(-) diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 25dd27c..709d123 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -45,6 +45,7 @@ #include "display_task.h" #include "display_common.h" #include "epaper_color.h" +#include "epaper_commands.h" #include "epaper_draw.h" #include "epaper_screen.h" #include "image_helpers.h" @@ -332,42 +333,8 @@ static void display_spi_init(Context *ctx, term opts) wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0xEF); - spi_dc_write_data(&driver->bus, 0x08); - spi_dc_write_command(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0x37); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_data(&driver->bus, 0x23); //datasheet says: 0x05 - spi_dc_write_data(&driver->bus, 0x23); //datasheet says: 0x05 - spi_dc_write_command(&driver->bus, 0x03); - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_command(&driver->bus, 0x06); - spi_dc_write_data(&driver->bus, 0xC7); - spi_dc_write_data(&driver->bus, 0xC7); - spi_dc_write_data(&driver->bus, 0x1D); - spi_dc_write_command(&driver->bus, 0x30); - spi_dc_write_data(&driver->bus, 0x3C); - spi_dc_write_command(&driver->bus, 0x40); //datasheet says: 0x41 - spi_dc_write_data(&driver->bus, 0x00); - spi_dc_write_command(&driver->bus, 0x50); - spi_dc_write_data(&driver->bus, 0x3F); //datasheet says: 0x37 - spi_dc_write_command(&driver->bus, 0x60); - spi_dc_write_data(&driver->bus, 0x22); - spi_dc_write_command(&driver->bus, 0x61); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x58); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xC0); - spi_dc_write_command(&driver->bus, 0xE3); - spi_dc_write_data(&driver->bus, 0xAA); - spi_dc_write_command(&driver->bus, 0x82); - spi_dc_write_data(&driver->bus, 0x80); - - vTaskDelay(10); - - spi_dc_write_command(&driver->bus, 0x50); - spi_dc_write_data(&driver->bus, 0x37); + epaper_execute_init_seq(&driver->bus, driver->busy_gpio, + epaper_init_seq_acep7c, epaper_init_seq_acep7c_len, false); ctx->platform_data = &driver->display_args; diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index 79abbb6..17363dd 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -44,32 +44,13 @@ #include "display_task.h" #include "display_common.h" #include "epaper_color.h" +#include "epaper_commands.h" #include "epaper_draw.h" #include "epaper_screen.h" #include "image_helpers.h" #include "spi_dc_driver.h" #include "spi_display.h" -#define PSR 0x00 -#define PWRR 0x01 -#define POF 0x02 -#define POFS 0x03 -#define PON 0x04 -#define BTST1 0x05 -#define BTST2 0x06 -#define DSLP 0x07 -#define BTST3 0x08 -#define DTM 0x10 -#define DRF 0x12 -#define PLL 0x30 -#define CDI 0x50 -#define TCON 0x60 -#define TRES 0x61 -#define REV 0x70 -#define VDCS 0x82 -#define T_VDCS 0x84 -#define PWS 0xE3 - #define REPORT_UNEXPECTED_MSGS 0 #define SELF_TEST 0 @@ -359,74 +340,8 @@ static void display_spi_init(Context *ctx, term opts) wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0xAA); - uint8_t psr1_data[] = {0x49, 0x55, 0x20, 0x08, 0x09, 0x18}; - spi_dc_write_data_n(&driver->bus, psr1_data, sizeof(psr1_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, PWRR); - uint8_t pwrr_data[] = {0x3F}; - spi_dc_write_data_n(&driver->bus, pwrr_data, sizeof(pwrr_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, PSR); - uint8_t psr_data[] = {0x5F, 0x69}; - spi_dc_write_data_n(&driver->bus, psr_data, sizeof(psr_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, POFS); - uint8_t pofs_data[] = {0x00, 0x54, 0x00, 0x44}; - spi_dc_write_data_n(&driver->bus, pofs_data, sizeof(pofs_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, BTST1); - uint8_t btst1_data[] = {0x40, 0x1F, 0x1F, 0x2C}; - spi_dc_write_data_n(&driver->bus, btst1_data, sizeof(btst1_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, BTST2); - uint8_t btst2_data[] = {0x6F, 0x1F, 0x17, 0x49}; - spi_dc_write_data_n(&driver->bus, btst2_data, sizeof(btst2_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, BTST3); - uint8_t btst3_data[] = {0x6F, 0x1F, 0x1F, 0x22}; - spi_dc_write_data_n(&driver->bus, btst3_data, sizeof(btst3_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, PLL); - uint8_t pll_data[] = {0x00}; - spi_dc_write_data_n(&driver->bus, pll_data, sizeof(pll_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, CDI); - uint8_t cdi_data[] = {0x3F}; - spi_dc_write_data_n(&driver->bus, cdi_data, sizeof(cdi_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, TCON); - uint8_t tcon_data[] = {0x02, 0x00}; - spi_dc_write_data_n(&driver->bus, tcon_data, sizeof(tcon_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, TRES); - uint8_t tres_data[] = {0x03, 0x20, 0x01, 0xe0}; - spi_dc_write_data_n(&driver->bus, tres_data, sizeof(tres_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, T_VDCS); - uint8_t vdcs_data[] = {0x01}; - spi_dc_write_data_n(&driver->bus, vdcs_data, sizeof(vdcs_data)); - wait_busy_level(driver, 1); - - spi_dc_write_command(&driver->bus, PWS); - uint8_t pws_data[] = {0x2F}; - spi_dc_write_data_n(&driver->bus, pws_data, sizeof(pws_data)); - wait_busy_level(driver, 1); - - // PON - spi_dc_write_command(&driver->bus, 0x04); - wait_busy_level(driver, 1); + epaper_execute_init_seq(&driver->bus, driver->busy_gpio, + epaper_init_seq_gdep073e01, epaper_init_seq_gdep073e01_len, true); ctx->platform_data = &driver->display_args; From b1519a9af70fe5b64e045f194b73163b01634796 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 21:49:28 +0000 Subject: [PATCH 40/52] Add e-paper controller descriptors Introduce struct EPaperDesc with everything a unified driver needs to dispatch on compatible string: panel name, native dimensions, SPI clock, palette pointer + size, init sequence, optional per-frame preamble, and the post-frame refresh flags (DRF data byte, POF busy-wait level, periodic white-out interval). Instantiate two descriptors -- epaper_desc_acep7c and epaper_desc_gdep073e01 -- populated from the constants currently baked into the two driver files. The ACeP descriptor also ships a frame_preamble_seq encoding the 0x61 resolution command that today is open-coded in the driver's do_update and clear_screen paths. Signed-off-by: Davide Bettio --- epaper_commands.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ epaper_commands.h | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/epaper_commands.c b/epaper_commands.c index be836f1..9a67aa2 100644 --- a/epaper_commands.c +++ b/epaper_commands.c @@ -25,6 +25,8 @@ #include +#include "epaper_color.h" + static void wait_busy_high(int busy_gpio) { while (gpio_get_level(busy_gpio) != 1) { @@ -109,4 +111,64 @@ _Static_assert(sizeof(epaper_init_seq_gdep073e01) == 63, "epaper_init_seq_gdep073e01: miscounted bytes"); const size_t epaper_init_seq_gdep073e01_len = sizeof(epaper_init_seq_gdep073e01); +// --- Per-frame preambles --- +// +// ACeP 5.65" retransmits the resolution command (0x61 + 600x448) +// before every DTM; GoodDisplay's GDEP073E01 does not. The preamble +// uses the same byte-array format as init_seq. + +static const uint8_t epaper_preamble_acep7c[] = { + 0x61, 4, 0x02, 0x58, 0x01, 0xC0, // TRES (600x448) +}; +_Static_assert(sizeof(epaper_preamble_acep7c) == 6, + "epaper_preamble_acep7c: miscounted bytes"); + +// --- Per-panel descriptors --- + +const struct EPaperDesc epaper_desc_acep7c = { + .name = "Waveshare 5.65\" ACeP 7-color", + .native_width = 600, + .native_height = 448, + .spi_clock_hz = 1000000, + + .palette = epaper_acep_palette, + .palette_size = 7, + + .init_seq = epaper_init_seq_acep7c, + .init_seq_len = sizeof(epaper_init_seq_acep7c), + .init_wait_busy_between_cmds = false, + + .frame_preamble_seq = epaper_preamble_acep7c, + .frame_preamble_seq_len = sizeof(epaper_preamble_acep7c), + + .refresh_has_data = false, + .refresh_data_byte = 0x00, + .post_power_off_busy_level = 0, + + .periodic_refresh_interval = 5, +}; + +const struct EPaperDesc epaper_desc_gdep073e01 = { + .name = "Good Display GDEP073E01 7.3\" 7-color", + .native_width = 800, + .native_height = 480, + .spi_clock_hz = 4000000, + + .palette = epaper_gdep073e01_palette, + .palette_size = 7, + + .init_seq = epaper_init_seq_gdep073e01, + .init_seq_len = sizeof(epaper_init_seq_gdep073e01), + .init_wait_busy_between_cmds = true, + + .frame_preamble_seq = NULL, + .frame_preamble_seq_len = 0, + + .refresh_has_data = true, + .refresh_data_byte = 0x00, + .post_power_off_busy_level = 1, + + .periodic_refresh_interval = 0, +}; + // clang-format on diff --git a/epaper_commands.h b/epaper_commands.h index 2b6f321..c90faf0 100644 --- a/epaper_commands.h +++ b/epaper_commands.h @@ -60,4 +60,50 @@ extern const size_t epaper_init_seq_acep7c_len; extern const uint8_t epaper_init_seq_gdep073e01[]; extern const size_t epaper_init_seq_gdep073e01_len; +// --- Per-panel descriptor --- +// +// Captures every panel-specific knob so a single unified driver can +// drive multiple controllers by compatible-string dispatch. The +// struct carries no function pointers: the current variation across +// ACeP 5.65" and GDEP073E01 is entirely data. + +struct EPaperDesc +{ + const char *name; + int native_width; + int native_height; + int spi_clock_hz; + + // Color palette (RGB triplets) and its entry count. + const uint8_t (*palette)[3]; + int palette_size; + + // One-time init sequence (format documented above). + const uint8_t *init_seq; + size_t init_seq_len; + bool init_wait_busy_between_cmds; + + // Optional per-frame preamble sent before DTM (0x10) on every + // do_update and clear_screen. NULL when unused. Uses the same + // byte-array format as init_seq and is executed without inter- + // command BUSY polling. + const uint8_t *frame_preamble_seq; + size_t frame_preamble_seq_len; + + // Post-frame refresh protocol: PON (0x04) -> wait BUSY=1; DRF + // (0x12) optionally followed by one data byte, then wait BUSY=1; + // POF (0x02) then wait BUSY=post_power_off_busy_level. + bool refresh_has_data; + uint8_t refresh_data_byte; + int post_power_off_busy_level; + + // Periodic full-screen white-out. Zero = disabled. N > 0 means + // "call clear_screen(7) once every N do_update() invocations" to + // prevent ghosting on panels that need it (ACeP 5.65"). + int periodic_refresh_interval; +}; + +extern const struct EPaperDesc epaper_desc_acep7c; +extern const struct EPaperDesc epaper_desc_gdep073e01; + #endif From 206231adbd485fb0466100033b37aad5b3ceb361 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 21:50:52 +0000 Subject: [PATCH 41/52] E-paper: Embed EpaperScreen in driver Fold EpaperScreen into struct EpaperDriver as an embedded member and drop the file-static pointer across both e-paper drivers (acep, gdep073e01). The driver allocation via malloc() already reserves space for it, removing the separate calloc() and its attendant leak-on-driver-free. A prerequisite for the unified e-paper driver: multi-instance support needs every per-screen value to live behind the driver pointer recovered by EPAPER_DRIVER_FROM_CTX, not behind a file-static reachable from a single translation unit. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 15 +++++++-------- gdep073e01_display_driver.c | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c index 709d123..6fcff0a 100644 --- a/5in65_acep_7c_display_driver.c +++ b/5in65_acep_7c_display_driver.c @@ -67,6 +67,8 @@ struct EpaperDriver int busy_gpio; int reset_gpio; + struct EpaperScreen screen; + Context *ctx; int count_to_refresh; @@ -78,8 +80,6 @@ struct EpaperDriver #define EPAPER_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) -static struct EpaperScreen *screen; - static void display_reset(struct EpaperDriver *driver) { gpio_set_level(driver->reset_gpio, 0); @@ -179,7 +179,7 @@ static void do_update(Context *ctx, term display_list) int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = epaper_draw_x(screen, buf, xpos, ypos, items, len); + int drawn_pixels = epaper_draw_x(&driver->screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -340,11 +340,10 @@ static void display_spi_init(Context *ctx, term opts) driver->ctx = ctx; - screen = calloc(1, sizeof(struct EpaperScreen)); - screen->w = DISPLAY_WIDTH; - screen->h = DISPLAY_HEIGHT; - screen->palette = epaper_acep_palette; - screen->palette_size = 7; + driver->screen.w = DISPLAY_WIDTH; + driver->screen.h = DISPLAY_HEIGHT; + driver->screen.palette = epaper_acep_palette; + driver->screen.palette_size = 7; update_last_refresh_ts(ctx); driver->count_to_refresh = 0; diff --git a/gdep073e01_display_driver.c b/gdep073e01_display_driver.c index 17363dd..b9546a8 100644 --- a/gdep073e01_display_driver.c +++ b/gdep073e01_display_driver.c @@ -65,6 +65,8 @@ struct EpaperDriver int busy_gpio; int reset_gpio; + struct EpaperScreen screen; + Context *ctx; int count_to_refresh; @@ -76,8 +78,6 @@ struct EpaperDriver #define EPAPER_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) -static struct EpaperScreen *screen; - static void display_reset(struct EpaperDriver *driver) { gpio_set_level(driver->reset_gpio, 0); @@ -180,7 +180,7 @@ static void do_update(Context *ctx, term display_list) int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = epaper_draw_x(screen, buf, xpos, ypos, items, len); + int drawn_pixels = epaper_draw_x(&driver->screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -347,11 +347,10 @@ static void display_spi_init(Context *ctx, term opts) driver->ctx = ctx; - screen = calloc(1, sizeof(struct EpaperScreen)); - screen->w = DISPLAY_WIDTH; - screen->h = DISPLAY_HEIGHT; - screen->palette = epaper_gdep073e01_palette; - screen->palette_size = 7; + driver->screen.w = DISPLAY_WIDTH; + driver->screen.h = DISPLAY_HEIGHT; + driver->screen.palette = epaper_gdep073e01_palette; + driver->screen.palette_size = 7; update_last_refresh_ts(ctx); driver->count_to_refresh = 0; From 05e3ae75d31c767c6ca9ce479d71622b48e526ad Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 21:59:28 +0000 Subject: [PATCH 42/52] Rename gdep073e01 driver to unified e-paper name git mv gdep073e01_display_driver.c -> epaper_display_driver.c; rename the create_port entry point and the log TAG accordingly. Pure rename -- the gdep compatible dispatches through the new function, and the waveshare,5in65-acep-7c compatible still calls the separate acep driver file. Signed-off-by: Davide Bettio --- CMakeLists.txt | 2 +- display_driver.c | 4 ++-- gdep073e01_display_driver.c => epaper_display_driver.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename gdep073e01_display_driver.c => epaper_display_driver.c (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93c4d2b..b52130b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,7 @@ idf_component_register(SRCS "epaper_draw.c" "mono_draw.c" "5in65_acep_7c_display_driver.c" - "gdep073e01_display_driver.c" + "epaper_display_driver.c" "font_data.c" "memory_display_driver.c" "ssd1306_display_driver.c" diff --git a/display_driver.c b/display_driver.c index eab6511..9ff05eb 100644 --- a/display_driver.c +++ b/display_driver.c @@ -30,7 +30,7 @@ static const char *TAG = "display_driver"; Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts); -Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts); +Context *epaper_display_create_port(GlobalContext *global, term opts); Context *dcs_lcd_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); Context *ssd1306_display_create_port(GlobalContext *global, term opts); @@ -55,7 +55,7 @@ Context *display_create_port(GlobalContext *global, term opts) if (!strcmp(compat_string, "waveshare,5in65-acep-7c")) { ctx = acep_5in65_7c_display_driver_create_port(global, opts); } else if (!strcmp(compat_string, "good-display/gdep073e01")) { - ctx = gdep073e01_display_driver_create_port(global, opts); + ctx = epaper_display_create_port(global, opts); } else if (!strcmp(compat_string, "sharp,memory-lcd")) { ctx = memory_lcd_display_create_port(global, opts); } else if (!strcmp(compat_string, "ilitek,ili9341") diff --git a/gdep073e01_display_driver.c b/epaper_display_driver.c similarity index 98% rename from gdep073e01_display_driver.c rename to epaper_display_driver.c index b9546a8..d57dff6 100644 --- a/gdep073e01_display_driver.c +++ b/epaper_display_driver.c @@ -54,7 +54,7 @@ #define REPORT_UNEXPECTED_MSGS 0 #define SELF_TEST 0 -static const char *TAG = "gdep073e01"; +static const char *TAG = "epaper_display_driver"; static void clear_screen(Context *ctx, int color); @@ -373,7 +373,7 @@ static void display_spi_init(Context *ctx, term opts) #endif } -Context *gdep073e01_display_driver_create_port(GlobalContext *global, term opts) +Context *epaper_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); ctx->native_handler = display_task_consume_mailbox; From 57ba1cf5533d951cb623f66cc1fba4922ae73d69 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 22:03:20 +0000 Subject: [PATCH 43/52] Refactor e-paper driver to use descriptor table Generalise epaper_display_driver.c to drive both compatibles from the per-panel EPaperDesc descriptors. A compatible-string lookup picks the descriptor; dimensions, SPI clock, palette, init sequence, per-frame preamble, periodic-refresh interval, and post-frame refresh flags all come from it. The driver also accepts an init_list Erlang-side override in the tuple format used by the DCS LCD driver, extended with {wait_busy_level, 0|1} for the BUSY-pin polling that e-paper controllers typically require mid-init. Route good-display/gdep073e01 and waveshare,5in65-acep-7c through the single epaper_display_create_port dispatch entry. Signed-off-by: Davide Bettio --- display_driver.c | 6 +- epaper_display_driver.c | 230 +++++++++++++++++++++++++++------------- 2 files changed, 156 insertions(+), 80 deletions(-) diff --git a/display_driver.c b/display_driver.c index 9ff05eb..3700a39 100644 --- a/display_driver.c +++ b/display_driver.c @@ -29,7 +29,6 @@ static const char *TAG = "display_driver"; -Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts); Context *epaper_display_create_port(GlobalContext *global, term opts); Context *dcs_lcd_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); @@ -52,9 +51,8 @@ Context *display_create_port(GlobalContext *global, term opts) } Context *ctx = NULL; - if (!strcmp(compat_string, "waveshare,5in65-acep-7c")) { - ctx = acep_5in65_7c_display_driver_create_port(global, opts); - } else if (!strcmp(compat_string, "good-display/gdep073e01")) { + if (!strcmp(compat_string, "waveshare,5in65-acep-7c") + || !strcmp(compat_string, "good-display/gdep073e01")) { ctx = epaper_display_create_port(global, opts); } else if (!strcmp(compat_string, "sharp,memory-lcd")) { ctx = memory_lcd_display_create_port(global, opts); diff --git a/epaper_display_driver.c b/epaper_display_driver.c index d57dff6..6b5b763 100644 --- a/epaper_display_driver.c +++ b/epaper_display_driver.c @@ -1,7 +1,7 @@ /* * This file is part of AtomGL. * - * Copyright 2025 Davide Bettio + * Copyright 2022-2026 Davide Bettio * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,9 +36,6 @@ #include -#define DISPLAY_WIDTH 800 -#define DISPLAY_HEIGHT 480 - #include "display_items.h" #include "display_message.h" #include "display_task.h" @@ -65,6 +62,7 @@ struct EpaperDriver int busy_gpio; int reset_gpio; + const struct EPaperDesc *desc; struct EpaperScreen screen; Context *ctx; @@ -78,6 +76,26 @@ struct EpaperDriver #define EPAPER_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) +static const struct { + const char *compat; + const struct EPaperDesc *desc; +} epaper_compat_table[] = { + { "waveshare,5in65-acep-7c", &epaper_desc_acep7c }, + { "good-display/gdep073e01", &epaper_desc_gdep073e01 }, +}; + +static const struct EPaperDesc *epaper_desc_for_compatible(const char *compat) +{ + for (size_t i = 0; i < sizeof(epaper_compat_table) / sizeof(epaper_compat_table[0]); i++) { + if (!strcmp(compat, epaper_compat_table[i].compat)) { + return epaper_compat_table[i].desc; + } + } + return NULL; +} + +static void display_init_using_list(struct EpaperDriver *driver, term init_list); + static void display_reset(struct EpaperDriver *driver) { gpio_set_level(driver->reset_gpio, 0); @@ -101,8 +119,8 @@ static void wait_some_time(Context *ctx) uint64_t now = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); uint64_t delta = now - driver->last_refresh; if (delta < 2000) { - // Wait 2 seconds before allowing a new refresh - // this is not on datasheets, but without this the screen will not update. + // Wait 2 seconds before allowing a new refresh; undocumented but + // empirically required or the panel drops updates. vTaskDelay((2000 - delta) / portTICK_PERIOD_MS); } } @@ -118,24 +136,50 @@ static void update_last_refresh_ts(Context *ctx) static void maybe_refresh(Context *ctx) { -#if 0 struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); + if (driver->desc->periodic_refresh_interval <= 0) { + return; + } driver->count_to_refresh--; if (driver->count_to_refresh <= 0) { - // 7 is the special "clear screen color" + // 7 is the panel's white entry on both current palettes. clear_screen(ctx, 7); update_last_refresh_ts(ctx); - driver->count_to_refresh = 5; + driver->count_to_refresh = driver->desc->periodic_refresh_interval; } -#endif +} + +static void send_frame_preamble(struct EpaperDriver *driver) +{ + if (driver->desc->frame_preamble_seq != NULL) { + epaper_execute_init_seq(&driver->bus, driver->busy_gpio, + driver->desc->frame_preamble_seq, + driver->desc->frame_preamble_seq_len, false); + } +} + +static void send_post_frame_refresh(struct EpaperDriver *driver) +{ + // PON + spi_dc_write_command(&driver->bus, 0x04); + wait_busy_level(driver, 1); + + // DRF (+ optional data byte) + spi_dc_write_command(&driver->bus, 0x12); + if (driver->desc->refresh_has_data) { + spi_dc_write_data_n(&driver->bus, &driver->desc->refresh_data_byte, 1); + } + wait_busy_level(driver, 1); + + // POF + spi_dc_write_command(&driver->bus, 0x02); + wait_busy_level(driver, driver->desc->post_power_off_busy_level); } static void do_update(Context *ctx, term display_list) { maybe_refresh(ctx); - // it looks like we need to wait some time - // let's use 2 seconds wait_some_time(ctx); int proper; @@ -149,24 +193,17 @@ static void do_update(Context *ctx, term display_list) t = term_get_list_tail(t); } - int screen_width = DISPLAY_WIDTH; - int screen_height = DISPLAY_HEIGHT; struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; -#if 0 - // resolution command - spi_dc_write_command(&driver->bus, 0x61); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x58); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xC0); -#endif + send_frame_preamble(driver); - // update command + // DTM — data transfer to panel memory. spi_dc_write_command(&driver->bus, 0x10); - uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - memset(buf, 0x11, DISPLAY_WIDTH / 2); + uint8_t *buf = heap_caps_malloc(screen_width / 2, MALLOC_CAP_DMA); + memset(buf, 0x11, screen_width / 2); bool transaction_in_progress = false; @@ -184,7 +221,7 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + spi_display_dma_write(&driver->bus.spi_disp, screen_width / 2, buf); transaction_in_progress = true; } @@ -197,21 +234,7 @@ static void do_update(Context *ctx, term display_list) free(buf); - // not sure if we should add 0x11, which is end of data command or not - - // power on command - spi_dc_write_command(&driver->bus, 0x04); - wait_busy_level(driver, 1); - - // refresh command - spi_dc_write_command(&driver->bus, 0x12); - uint8_t refresh_data[] = {0x00}; - spi_dc_write_data_n(&driver->bus, refresh_data, sizeof(refresh_data)); - wait_busy_level(driver, 1); - - // power off command - spi_dc_write_command(&driver->bus, 0x02); - wait_busy_level(driver, 1); + send_post_frame_refresh(driver); display_items_delete(items, len); @@ -262,31 +285,29 @@ static void process_message(Message *message, Context *ctx) static void clear_screen(Context *ctx, int color) { struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; - uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); + send_frame_preamble(driver); -#if 0 - spi_dc_write_command(&driver->bus, 0x61); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x58); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xC0); -#endif spi_dc_write_command(&driver->bus, 0x10); + uint8_t *buf = heap_caps_malloc(screen_width / 2, MALLOC_CAP_DMA); + bool transaction_in_progress = false; spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - for (int i = 0; i < DISPLAY_HEIGHT; i++) { + for (int i = 0; i < screen_height; i++) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); } - // let's ensure a memset otherwise we might generate odd artifacts - memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); + // memset inside the loop so every scanline carries fresh data, + // avoiding artefacts if a prior scanline left stale bytes. + memset(buf, color | (color << 4), screen_width / 2); + spi_display_dma_write(&driver->bus.spi_disp, screen_width / 2, buf); transaction_in_progress = true; } @@ -299,24 +320,46 @@ static void clear_screen(Context *ctx, int color) free(buf); - spi_dc_write_command(&driver->bus, 0x04); - wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0x12); - uint8_t refresh_data[] = {0x00}; - spi_dc_write_data_n(&driver->bus, refresh_data, sizeof(refresh_data)); - wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0x02); - wait_busy_level(driver, 1); + send_post_frame_refresh(driver); } static void display_spi_init(Context *ctx, term opts) { + // Resolve compatible string -> per-panel descriptor. + term compat_term = interop_kv_get_value_default( + opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); + int str_ok; + char *compat_string = interop_term_to_string(compat_term, &str_ok); + const struct EPaperDesc *desc = NULL; + if (str_ok && compat_string) { + desc = epaper_desc_for_compatible(compat_string); + } + if (!desc) { + ESP_LOGE(TAG, "Failed init: unknown or missing compatible '%s'.", + compat_string ? compat_string : "(null)"); + free(compat_string); + return; + } + free(compat_string); + struct EpaperDriver *driver = malloc(sizeof(struct EpaperDriver)); // TODO check here + driver->desc = desc; + driver->ctx = ctx; + driver->screen.w = desc->native_width; + driver->screen.h = desc->native_height; + driver->screen.palette = desc->palette; + driver->screen.palette_size = desc->palette_size; + + driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); + driver->display_args.process_message_fn = process_message; + driver->display_args.ctx = ctx; + ctx->platform_data = &driver->display_args; + struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); - spi_config.clock_speed_hz = 4000000; + spi_config.clock_speed_hz = desc->spi_clock_hz; spi_display_parse_config(&spi_config, opts, ctx->global); spi_display_init(&driver->bus.spi_disp, &spi_config); @@ -340,17 +383,16 @@ static void display_spi_init(Context *ctx, term opts) wait_busy_level(driver, 1); - epaper_execute_init_seq(&driver->bus, driver->busy_gpio, - epaper_init_seq_gdep073e01, epaper_init_seq_gdep073e01_len, true); - - ctx->platform_data = &driver->display_args; - - driver->ctx = ctx; - - driver->screen.w = DISPLAY_WIDTH; - driver->screen.h = DISPLAY_HEIGHT; - driver->screen.palette = epaper_gdep073e01_palette; - driver->screen.palette_size = 7; + // Init sequence: init_list opt overrides the descriptor default. + term init_list = interop_kv_get_value_default( + opts, ATOM_STR("\x9", "init_list"), term_nil(), ctx->global); + if (init_list != term_nil()) { + display_init_using_list(driver, init_list); + } else { + epaper_execute_init_seq(&driver->bus, driver->busy_gpio, + desc->init_seq, desc->init_seq_len, + desc->init_wait_busy_between_cmds); + } update_last_refresh_ts(ctx); driver->count_to_refresh = 0; @@ -366,9 +408,6 @@ static void display_spi_init(Context *ctx, term opts) while (1) ; #else - driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - driver->display_args.process_message_fn = process_message; - driver->display_args.ctx = ctx; xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); #endif } @@ -380,3 +419,42 @@ Context *epaper_display_create_port(GlobalContext *global, term opts) display_spi_init(ctx, opts); return ctx; } + +// Erlang-side init override: accepts a list of +// {CmdByte :: 0..255, Binary :: binary()} +// {sleep_ms, Ms :: 0..255} +// {wait_busy_level, Level :: 0 | 1} +// tuples and applies them in order. Mirrors dcs_lcd's display_init_using_list +// with an added wait_busy_level clause, since e-paper controllers typically +// require BUSY-pin polling between commands that DCS LCDs do not. +static void display_init_using_list(struct EpaperDriver *driver, term init_list) +{ + term t = init_list; + while (term_is_nonempty_list(t)) { + term head = term_get_list_head(t); + if (term_is_tuple(head) && term_get_tuple_arity(head) == 2) { + term cmd_term = term_get_tuple_element(head, 0); + term data_term = term_get_tuple_element(head, 1); + if (term_is_integer(cmd_term) && term_is_binary(data_term)) { + avm_int_t cmd = term_to_int(cmd_term); + const uint8_t *data = (const uint8_t *) term_binary_data(data_term); + spi_dc_write_cmd_data(&driver->bus, cmd, data, term_binary_size(data_term)); + } else if ((cmd_term == context_make_atom(driver->ctx, ATOM_STR("\x8", "sleep_ms"))) + && term_is_integer(data_term)) { + vTaskDelay(term_to_int(data_term) / portTICK_PERIOD_MS); + } else if ((cmd_term == context_make_atom(driver->ctx, ATOM_STR("\xF", "wait_busy_level"))) + && term_is_integer(data_term)) { + wait_busy_level(driver, term_to_int(data_term)); + } else { + break; + } + } else { + break; + } + + t = term_get_list_tail(t); + } + if (t != term_nil()) { + fprintf(stderr, "Invalid init_list!\n"); + } +} From 8680c21e8c04d310b32fb4a4838f4ac6a7b7b6f6 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Tue, 14 Apr 2026 22:32:56 +0000 Subject: [PATCH 44/52] Remove legacy 5in65 acep driver The waveshare,5in65-acep-7c compatible now dispatches through the unified epaper_display_driver.c, leaving the legacy 5in65_acep_7c_display_driver.c orphaned. Drop it and its CMakeLists.txt entry. Signed-off-by: Davide Bettio --- 5in65_acep_7c_display_driver.c | 375 --------------------------------- CMakeLists.txt | 1 - 2 files changed, 376 deletions(-) delete mode 100644 5in65_acep_7c_display_driver.c diff --git a/5in65_acep_7c_display_driver.c b/5in65_acep_7c_display_driver.c deleted file mode 100644 index 6fcff0a..0000000 --- a/5in65_acep_7c_display_driver.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * This file is part of AtomGL. - * - * Copyright 2022 Davide Bettio - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#define DISPLAY_WIDTH 600 -#define DISPLAY_HEIGHT 448 - -#include "display_items.h" -#include "display_message.h" -#include "display_task.h" -#include "display_common.h" -#include "epaper_color.h" -#include "epaper_commands.h" -#include "epaper_draw.h" -#include "epaper_screen.h" -#include "image_helpers.h" -#include "spi_dc_driver.h" -#include "spi_display.h" - -#define REPORT_UNEXPECTED_MSGS 0 -#define CHECK_OVERFLOW 1 -#define SELF_TEST 0 - -static const char *TAG = "5in65_acep_7c_display_driver"; - -static void clear_screen(Context *ctx, int color); - -struct EpaperDriver -{ - struct SPIDCBus bus; - - int busy_gpio; - int reset_gpio; - - struct EpaperScreen screen; - - Context *ctx; - - int count_to_refresh; - uint64_t last_refresh; - - struct DisplayTaskArgs display_args; -}; - -#define EPAPER_DRIVER_FROM_CTX(ctx) \ - CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct EpaperDriver, display_args) - -static void display_reset(struct EpaperDriver *driver) -{ - gpio_set_level(driver->reset_gpio, 0); - vTaskDelay(100); - gpio_set_level(driver->reset_gpio, 1); -} - -static void wait_busy_level(struct EpaperDriver *driver, int level) -{ - while (gpio_get_level(driver->busy_gpio) != level) { - vTaskDelay(100); - } -} - - -static void wait_some_time(Context *ctx) -{ - struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - - struct timeval tv; - gettimeofday(&tv, NULL); - uint64_t now = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); - uint64_t delta = now - driver->last_refresh; - if (delta < 2000) { - // Wait 2 seconds before allowing a new refresh - // this is not on datasheets, but without this the screen will not update. - vTaskDelay((2000 - delta) / portTICK_PERIOD_MS); - } -} - -static void update_last_refresh_ts(Context *ctx) -{ - struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - - struct timeval tv; - gettimeofday(&tv, NULL); - driver->last_refresh = tv.tv_sec * 1000LL + (tv.tv_usec / 1000LL); -} - -static void maybe_refresh(Context *ctx) -{ - struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - - driver->count_to_refresh--; - if (driver->count_to_refresh <= 0) { - // 7 is the special "clear screen color" - clear_screen(ctx, 7); - update_last_refresh_ts(ctx); - driver->count_to_refresh = 5; - } -} - -static void do_update(Context *ctx, term display_list) -{ - maybe_refresh(ctx); - // it looks like we need to wait some time - // let's use 2 seconds - wait_some_time(ctx); - - int proper; - int len = term_list_length(display_list, &proper); - - BaseDisplayItem *items = malloc(sizeof(BaseDisplayItem) * len); - - term t = display_list; - for (int i = 0; i < len; i++) { - display_items_init_item(&items[i], term_get_list_head(t), ctx); - t = term_get_list_tail(t); - } - - int screen_width = DISPLAY_WIDTH; - int screen_height = DISPLAY_HEIGHT; - struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - - // resolution command - spi_dc_write_command(&driver->bus, 0x61); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x58); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xC0); - - // update command - spi_dc_write_command(&driver->bus, 0x10); - - uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - memset(buf, 0x11, DISPLAY_WIDTH / 2); - - bool transaction_in_progress = false; - - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - - for (int ypos = 0; ypos < screen_height; ypos++) { - if (transaction_in_progress) { - spi_transaction_t *trans = NULL; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - int xpos = 0; - while (xpos < screen_width) { - int drawn_pixels = epaper_draw_x(&driver->screen, buf, xpos, ypos, items, len); - xpos += drawn_pixels; - } - - spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); - transaction_in_progress = true; - } - - if (transaction_in_progress) { - spi_transaction_t *trans = NULL; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - spi_device_release_bus(driver->bus.spi_disp.handle); - - free(buf); - - // not sure if we should add 0x11, which is end of data command or not - - // power on command - spi_dc_write_command(&driver->bus, 0x04); - wait_busy_level(driver, 1); - - // refresh command - spi_dc_write_command(&driver->bus, 0x12); - wait_busy_level(driver, 1); - - // power off command - spi_dc_write_command(&driver->bus, 0x02); - wait_busy_level(driver, 0); - - display_items_delete(items, len); - - update_last_refresh_ts(ctx); -} - -static void process_message(Message *message, Context *ctx) -{ - GenMessage gen_message; - if (UNLIKELY(port_parse_gen_message(message->message, &gen_message) != GenCallMessage)) { - fprintf(stderr, "Received invalid message."); - AVM_ABORT(); - } - - term req = gen_message.req; - if (UNLIKELY(!term_is_tuple(req) || term_get_tuple_arity(req) < 1)) { - AVM_ABORT(); - } - term cmd = term_get_tuple_element(req, 0); - - if (cmd == context_make_atom(ctx, "\x6" - "update")) { - - term display_list = term_get_tuple_element(req, 1); - do_update(ctx, display_list); - - } else if (cmd == globalcontext_make_atom(ctx->global, "\xA" "load_image")) { - handle_load_image(req, gen_message.ref, gen_message.pid, ctx); - return; - - } else { -#if REPORT_UNEXPECTED_MSGS - fprintf(stderr, "display: "); - term_display(stderr, req, ctx); - fprintf(stderr, "\n"); -#endif - } - - BEGIN_WITH_STACK_HEAP(TUPLE_SIZE(2) + REF_SIZE, heap); - term return_tuple = term_alloc_tuple(2, &heap); - term_put_tuple_element(return_tuple, 0, gen_message.ref); - term_put_tuple_element(return_tuple, 1, OK_ATOM); - - display_message_send(gen_message.pid, return_tuple, ctx->global); - END_WITH_STACK_HEAP(heap, ctx->global); -} - -static void clear_screen(Context *ctx, int color) -{ - struct EpaperDriver *driver = EPAPER_DRIVER_FROM_CTX(ctx); - - uint8_t *buf = heap_caps_malloc(DISPLAY_WIDTH / 2, MALLOC_CAP_DMA); - - spi_dc_write_command(&driver->bus, 0x61); - spi_dc_write_data(&driver->bus, 0x02); - spi_dc_write_data(&driver->bus, 0x58); - spi_dc_write_data(&driver->bus, 0x01); - spi_dc_write_data(&driver->bus, 0xC0); - spi_dc_write_command(&driver->bus, 0x10); - - bool transaction_in_progress = false; - - spi_device_acquire_bus(driver->bus.spi_disp.handle, portMAX_DELAY); - - for (int i = 0; i < DISPLAY_HEIGHT; i++) { - if (transaction_in_progress) { - spi_transaction_t *trans = NULL; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - // let's ensure a memset otherwise we might generate odd artifacts - memset(buf, color | (color << 4), DISPLAY_WIDTH / 2); - spi_display_dma_write(&driver->bus.spi_disp, DISPLAY_WIDTH / 2, buf); - transaction_in_progress = true; - } - - if (transaction_in_progress) { - spi_transaction_t *trans = NULL; - spi_device_get_trans_result(driver->bus.spi_disp.handle, &trans, portMAX_DELAY); - } - - spi_device_release_bus(driver->bus.spi_disp.handle); - - free(buf); - - spi_dc_write_command(&driver->bus, 0x04); - wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0x12); - wait_busy_level(driver, 1); - spi_dc_write_command(&driver->bus, 0x02); - wait_busy_level(driver, 0); -} - -static void display_spi_init(Context *ctx, term opts) -{ - struct EpaperDriver *driver = malloc(sizeof(struct EpaperDriver)); - // TODO check here - - struct SPIDisplayConfig spi_config; - spi_display_init_config(&spi_config); - spi_config.clock_speed_hz = 1000000; - spi_display_parse_config(&spi_config, opts, ctx->global); - spi_display_init(&driver->bus.spi_disp, &spi_config); - - bool ok = display_common_gpio_from_opts(opts, ATOM_STR("\x4", "busy"), &driver->busy_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x2", "dc"), &driver->bus.dc_gpio, ctx->global); - ok = ok && display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &driver->reset_gpio, ctx->global); - if (UNLIKELY(!ok)) { - ESP_LOGE(TAG, "Failed init: invalid display GPIOs."); - return; - } - - gpio_set_direction(driver->reset_gpio, GPIO_MODE_OUTPUT); - gpio_set_level(driver->reset_gpio, 1); - gpio_set_direction(driver->bus.dc_gpio, GPIO_MODE_OUTPUT); - gpio_set_pull_mode(driver->bus.dc_gpio, GPIO_PULLUP_ENABLE); - gpio_set_direction(driver->busy_gpio, GPIO_MODE_INPUT); - gpio_set_pull_mode(driver->busy_gpio, GPIO_PULLUP_ENABLE); - gpio_set_level(driver->bus.dc_gpio, 0); - - display_reset(driver); - - wait_busy_level(driver, 1); - - epaper_execute_init_seq(&driver->bus, driver->busy_gpio, - epaper_init_seq_acep7c, epaper_init_seq_acep7c_len, false); - - ctx->platform_data = &driver->display_args; - - driver->ctx = ctx; - - driver->screen.w = DISPLAY_WIDTH; - driver->screen.h = DISPLAY_HEIGHT; - driver->screen.palette = epaper_acep_palette; - driver->screen.palette_size = 7; - - update_last_refresh_ts(ctx); - driver->count_to_refresh = 0; - -#if SELF_TEST - for (int i = 0; i < 8; i++) { - fprintf(stderr, "color: %i\n", i); - clear_screen(ctx, i); - vTaskDelay(30000 / portTICK_PERIOD_MS); - } - clear_screen(ctx, 1); - - while (1) - ; -#else - driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); - driver->display_args.process_message_fn = process_message; - driver->display_args.ctx = ctx; - xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); -#endif -} - -Context *acep_5in65_7c_display_driver_create_port(GlobalContext *global, term opts) -{ - Context *ctx = context_new(global); - ctx->native_handler = display_task_consume_mailbox; - display_spi_init(ctx, opts); - return ctx; -} diff --git a/CMakeLists.txt b/CMakeLists.txt index b52130b..a188593 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,6 @@ idf_component_register(SRCS "epaper_commands.c" "epaper_draw.c" "mono_draw.c" - "5in65_acep_7c_display_driver.c" "epaper_display_driver.c" "font_data.c" "memory_display_driver.c" From fea6a9e2393872f4c3bbd8ace79d8528a24f52f1 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:02:32 +0000 Subject: [PATCH 45/52] Add OLED init sequence infrastructure Introduce oled_commands.h/.c with a length-framed init-sequence executor and two init arrays (SSD1306/SH1106 minimal, SSD1315 full). The format ([CMD][FLAGS_LEN][DATA...][DELAY_MS]) matches epaper_commands.h verbatim so the codebase converges on one init-sequence convention. Unlike an inline init transaction, the executor owns the I2C transaction boundary -- one i2c_cmd_link per step -- so that any future vTaskDelay between steps actually waits between commands rather than being a no-op inside a queued cmd_link. Current SSD13xx init steps don't use the DELAY flag. Each array carries a _Static_assert on sizeof to catch miscounted data bytes at compile time. Signed-off-by: Davide Bettio --- CMakeLists.txt | 1 + oled_commands.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ oled_commands.h | 69 +++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 oled_commands.c create mode 100644 oled_commands.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a188593..0f38ce1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ idf_component_register(SRCS "epaper_display_driver.c" "font_data.c" "memory_display_driver.c" + "oled_commands.c" "ssd1306_display_driver.c" "spi_dc_driver.c" "spi_display.c" diff --git a/oled_commands.c b/oled_commands.c new file mode 100644 index 0000000..4b93737 --- /dev/null +++ b/oled_commands.c @@ -0,0 +1,106 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "oled_commands.h" + +#include +#include + +// SSD13xx I2C control byte (Co=0, D/C#=0): all subsequent bytes in +// this transaction are interpreted as command bytes (commands and +// their inline parameters). +#define OLED_CTRL_CMD_STREAM 0x00 + +void oled_execute_init_seq(i2c_port_t i2c_num, uint8_t i2c_addr, + const uint8_t *seq, size_t seq_len) +{ + const uint8_t *end = seq + seq_len; + while (seq < end) { + uint8_t cmd_byte = *seq++; + uint8_t flags_len = *seq++; + uint8_t len = flags_len & 0x7F; + + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, OLED_CTRL_CMD_STREAM, true); + i2c_master_write_byte(cmd, cmd_byte, true); + for (uint8_t i = 0; i < len; i++) { + i2c_master_write_byte(cmd, seq[i], true); + } + i2c_master_stop(cmd); + i2c_master_cmd_begin(i2c_num, cmd, 50 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + + seq += len; + + if (flags_len & OLED_INIT_SEQ_DELAY) { + vTaskDelay(*seq++ / portTICK_PERIOD_MS); + } + } +} + +// --- Built-in init sequences --- +// +// Transcribed mechanically from the original display_init() body in +// ssd1306_display_driver.c. _Static_assert on sizeof guards against +// miscounted data bytes. + +// clang-format off + +// SSD1306 / SH1106 minimal init. The post-reset defaults are mostly +// correct for these controllers; the only adjustments needed are +// charge-pump enable and the segment-remap / COM-scan flip used by +// most modules. +const uint8_t oled_init_seq_ssd1306[] = { + 0x8D, 1, 0x14, // SET_CHARGE_PUMP + enable + 0xA1, 0, // SET_SEGMENT_REMAP (column 127 -> SEG0) + 0xC8, 0, // SET_COM_SCAN_MODE (remapped) +}; +_Static_assert(sizeof(oled_init_seq_ssd1306) == 7, + "oled_init_seq_ssd1306: miscounted bytes"); +const size_t oled_init_seq_ssd1306_len = sizeof(oled_init_seq_ssd1306); + +// SSD1315 full init. Derived from the u8g2 project (BSD-2-Clause) +// per the comment in the original driver. Standard hardware +// initialization commands defined by the Solomon Systech SSD1315 +// datasheet. +const uint8_t oled_init_seq_ssd1315[] = { + 0xAE, 0, // Display OFF + 0xD5, 1, 0x80, // Display Clock Divide Ratio (0x80 standard) + 0xA8, 1, 0x3F, // Multiplex Ratio (64 MUX) + 0xD3, 1, 0x00, // Display Offset (none) + 0x40, 0, // Display Start Line = 0 + 0x8D, 1, 0x14, // Charge Pump (enable) + 0xA1, 0, // Segment Remap + 0xC8, 0, // COM Scan Mode + 0xDA, 1, 0x12, // COM Pins Hardware Configuration (alternative) + 0x81, 1, 0xCF, // Contrast Control (high contrast per u8x8) + 0xD9, 1, 0xF1, // Pre-charge Period (required for 400 kHz stability) + 0xDB, 1, 0x40, // VCOMH Deselect Level (~0.77x VCC) + 0xA4, 0, // Resume to RAM content display + 0xA6, 0, // Normal Display (not inverted) + 0xAD, 1, 0x10, // Internal IREF Setting +}; +_Static_assert(sizeof(oled_init_seq_ssd1315) == 39, + "oled_init_seq_ssd1315: miscounted bytes"); +const size_t oled_init_seq_ssd1315_len = sizeof(oled_init_seq_ssd1315); + +// clang-format on diff --git a/oled_commands.h b/oled_commands.h new file mode 100644 index 0000000..7288841 --- /dev/null +++ b/oled_commands.h @@ -0,0 +1,69 @@ +/* + * This file is part of AtomGL. + * + * Copyright 2026 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _OLED_COMMANDS_H_ +#define _OLED_COMMANDS_H_ + +#include +#include + +#include + +// --- Init sequence byte-array format --- +// +// Each entry: [CMD] [FLAGS_LEN] [DATA_0 ... DATA_N] [DELAY_MS] +// CMD: OLED command byte +// FLAGS_LEN: bits 6:0 = data byte count (0-127) +// bit 7 = delay flag (DELAY_MS byte follows data) +// DELAY_MS: delay in milliseconds (0-255), present only if flag set +// +// The sequence is length-bounded: callers pass seq_len and the +// executor walks the buffer until exhausted. The format +// intentionally matches epaper_commands.h so the codebase converges +// on one init-sequence convention; current OLED init steps don't +// need delays, but future variants can opt in without a format +// change. +// +// Driver-controlled finalization (the optional "invert" command and +// the unconditional "display ON") is NOT encoded in init sequences; +// it stays in the driver because it depends on a runtime opt. + +#define OLED_INIT_SEQ_DELAY 0x80 + +// Execute an init sequence over an I2C bus. Owns the transaction +// boundary: emits one i2c_cmd_link transaction per init step so that +// the optional inter-step delay (vTaskDelay) actually waits between +// commands rather than being a no-op inside a queued cmd_link. Uses +// the SSD13xx command-stream control byte (Co=0, D/C#=0) so a +// command and its parameters can ride together in one transaction. +void oled_execute_init_seq(i2c_port_t i2c_num, uint8_t i2c_addr, + const uint8_t *seq, size_t seq_len); + +// Built-in init sequences. SSD1306 and SH1106 share the minimal +// 4-step charge-pump/remap init; SSD1315 has its own full reset +// sequence derived from u8g2. Each array is paired with a size_t +// constant giving its length; callers pass both to +// oled_execute_init_seq(). +extern const uint8_t oled_init_seq_ssd1306[]; +extern const size_t oled_init_seq_ssd1306_len; +extern const uint8_t oled_init_seq_ssd1315[]; +extern const size_t oled_init_seq_ssd1315_len; + +#endif From c6345726672ef9a9be76f09f6e6a305dd867b10a Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:14:23 +0000 Subject: [PATCH 46/52] ssd1306: Use init sequence tables Replace the inline i2c_master_write_byte loop and its long SSD1315/SSD1306 if/else fork with a single oled_execute_init_seq() call dispatched on driver->type. Bytes-on-the-wire are unchanged; the executor opens one i2c_cmd_handle_t per init step instead of batching the entire sequence into one transaction, which on the panel side just means extra stop/start conditions between commands. The optional invert command and the unconditional display ON now ride in a small post-executor transaction inside display_init. They depend on a runtime opt and are not panel-specific data, so they belong with the driver, not the per-variant init array. Drop CMD_SET_CHARGE_PUMP, CMD_SET_SEGMENT_REMAP, and CMD_SET_COM_SCAN_MODE: they were only referenced by the inline init that is being removed here. Signed-off-by: Davide Bettio --- ssd1306_display_driver.c | 73 +++++++++------------------------------- 1 file changed, 15 insertions(+), 58 deletions(-) diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index c02ff4e..6a665d9 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -41,6 +41,7 @@ #include "display_message.h" #include "display_task.h" #include "image_helpers.h" +#include "oled_commands.h" #define TAG "SSD1306" @@ -57,9 +58,6 @@ #define CMD_DISPLAY_INVERTED 0xA7 #define CMD_DISPLAY_ON 0xAF -#define CMD_SET_SEGMENT_REMAP 0xA1 -#define CMD_SET_COM_SCAN_MODE 0xC8 -#define CMD_SET_CHARGE_PUMP 0x8D typedef enum { @@ -273,69 +271,28 @@ static void display_init(Context *ctx, term opts) return; } driver->i2c_host = i2c_host; - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); - i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true); + const uint8_t *init_seq; + size_t init_seq_len; if (driver->type == DisplayTypeSsd1315) { - /* - * Init sequence derived from u8g2 project (BSD-2-Clause). - * Source: https://github.com/olikraus/u8g2 - * - * These values are standard hardware initialization commands - * defined by the Solomon Systech SSD1315 datasheet. - */ - - i2c_master_write_byte(cmd, 0xAE, true); // Display OFF - - i2c_master_write_byte(cmd, 0xD5, true); // Set Display Clock Divide Ratio / Oscillator Frequency - i2c_master_write_byte(cmd, 0x80, true); // 0x80 is standard/stable - - i2c_master_write_byte(cmd, 0xA8, true); // Set Multiplex Ratio - i2c_master_write_byte(cmd, 0x3F, true); // 64 MUX - - i2c_master_write_byte(cmd, 0xD3, true); // Set Display Offset - i2c_master_write_byte(cmd, 0x00, true); // No offset - - i2c_master_write_byte(cmd, 0x40, true); // Set Display Start Line to 0 - - i2c_master_write_byte(cmd, 0x8D, true); // Set Charge Pump - i2c_master_write_byte(cmd, 0x14, true); // Enable Charge Pump - - i2c_master_write_byte(cmd, 0xA1, true); // Set Segment Remap - i2c_master_write_byte(cmd, 0xC8, true); // Set COM Scan Mode - - i2c_master_write_byte(cmd, 0xDA, true); // Set COM Pins Hardware Configuration - i2c_master_write_byte(cmd, 0x12, true); // Alternative COM pin config - - i2c_master_write_byte(cmd, 0x81, true); // Set Contrast Control - i2c_master_write_byte(cmd, 0xCF, true); // Use High Contrast (0xCF) as per u8x8 - - i2c_master_write_byte(cmd, 0xD9, true); // Set Pre-charge Period - i2c_master_write_byte(cmd, 0xF1, true); // 0xF1 is required for stable 400kHz operation - - i2c_master_write_byte(cmd, 0xDB, true); // Set VCOMH Deselect Level - i2c_master_write_byte(cmd, 0x40, true); // 0x40 (approx 0.77x VCC) - - i2c_master_write_byte(cmd, 0xA4, true); // Resume to RAM content display - i2c_master_write_byte(cmd, 0xA6, true); // Normal Display (not inverted) - - i2c_master_write_byte(cmd, 0xAD, true); // Internal IREF Setting - i2c_master_write_byte(cmd, 0x10, true); // Internal Iref + init_seq = oled_init_seq_ssd1315; + init_seq_len = oled_init_seq_ssd1315_len; } else { - i2c_master_write_byte(cmd, CMD_SET_CHARGE_PUMP, true); - i2c_master_write_byte(cmd, 0x14, true); - - i2c_master_write_byte(cmd, CMD_SET_SEGMENT_REMAP, true); - i2c_master_write_byte(cmd, CMD_SET_COM_SCAN_MODE, true); + init_seq = oled_init_seq_ssd1306; + init_seq_len = oled_init_seq_ssd1306_len; } + oled_execute_init_seq(i2c_num, I2C_ADDRESS, init_seq, init_seq_len); + // Driver-controlled finalization: optional invert, then display ON. + // These depend on a runtime opt and are not panel data, so they + // stay outside the per-variant init array. + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true); if (invert) { i2c_master_write_byte(cmd, CMD_DISPLAY_INVERTED, true); } - i2c_master_write_byte(cmd, CMD_DISPLAY_ON, true); i2c_master_stop(cmd); From 11eb088a5178ff7345f77933e3085953b7fb8518 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:18:45 +0000 Subject: [PATCH 47/52] Add OLED controller descriptors Define struct OLEDDesc in oled_commands.h with the per-controller fields needed to drive SSD1306, SSD1315, and SH1106 from a single unified driver: dimensions, I2C address, init sequence pointer + length, and the two runtime-behavior flags that today require if/else branching on a display_type_t enum (column reset before each page write; data-stream prefix padding for SH1106's 132-pixel controller exposing a 128-pixel viewport). Instantiate the three descriptors. SSD1306 and SH1106 share the same minimal init array (charge-pump enable + segment / COM-scan remap); SSD1315 uses its own full init. Signed-off-by: Davide Bettio --- oled_commands.c | 42 ++++++++++++++++++++++++++++++++++++++++++ oled_commands.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/oled_commands.c b/oled_commands.c index 4b93737..a10bde6 100644 --- a/oled_commands.c +++ b/oled_commands.c @@ -104,3 +104,45 @@ _Static_assert(sizeof(oled_init_seq_ssd1315) == 39, const size_t oled_init_seq_ssd1315_len = sizeof(oled_init_seq_ssd1315); // clang-format on + +// --- Per-controller descriptors --- + +const struct OLEDDesc oled_desc_ssd1306 = { + .name = "Solomon Systech SSD1306", + .native_width = 128, + .native_height = 64, + .i2c_address = 0x3C, + + .init_seq = oled_init_seq_ssd1306, + .init_seq_len = sizeof(oled_init_seq_ssd1306), + + .column_reset_per_page = false, + .scanline_prefix_pad_bytes = 0, +}; + +const struct OLEDDesc oled_desc_ssd1315 = { + .name = "Solomon Systech SSD1315", + .native_width = 128, + .native_height = 64, + .i2c_address = 0x3C, + + .init_seq = oled_init_seq_ssd1315, + .init_seq_len = sizeof(oled_init_seq_ssd1315), + + .column_reset_per_page = true, + .scanline_prefix_pad_bytes = 0, +}; + +const struct OLEDDesc oled_desc_sh1106 = { + .name = "Sino Wealth SH1106", + .native_width = 128, + .native_height = 64, + .i2c_address = 0x3C, + + // SH1106 shares the SSD1306 minimal init. + .init_seq = oled_init_seq_ssd1306, + .init_seq_len = sizeof(oled_init_seq_ssd1306), + + .column_reset_per_page = true, + .scanline_prefix_pad_bytes = 2, +}; diff --git a/oled_commands.h b/oled_commands.h index 7288841..bb8c423 100644 --- a/oled_commands.h +++ b/oled_commands.h @@ -21,6 +21,7 @@ #ifndef _OLED_COMMANDS_H_ #define _OLED_COMMANDS_H_ +#include #include #include @@ -66,4 +67,41 @@ extern const size_t oled_init_seq_ssd1306_len; extern const uint8_t oled_init_seq_ssd1315[]; extern const size_t oled_init_seq_ssd1315_len; +// --- Per-controller descriptor --- +// +// Captures every controller-specific knob so a single unified driver +// can drive all three SSD13xx / SH1106 variants by compatible-string +// dispatch. The struct carries no function pointers: the variation +// across SSD1306, SSD1315 and SH1106 is entirely data. + +struct OLEDDesc +{ + const char *name; + int native_width; + int native_height; + uint8_t i2c_address; + + // One-time init sequence (length-framed format documented above). + const uint8_t *init_seq; + size_t init_seq_len; + + // True for controllers that require an explicit column-address + // reset (lower nibble 0x00, upper nibble 0x10) before writing + // each page of pixel data. SSD1315 and SH1106 need this; the + // bare SSD1306 retains its column pointer across pages and does + // not. + bool column_reset_per_page; + + // Number of zero bytes to write at the start of each page's + // data stream. SH1106 modules expose a 128-pixel viewport on a + // 132-pixel-wide controller, so the first two RAM columns are + // off-screen and skipped by writing 0x00 0x00 before the + // visible pixels. Zero for SSD1306 and SSD1315. + uint8_t scanline_prefix_pad_bytes; +}; + +extern const struct OLEDDesc oled_desc_ssd1306; +extern const struct OLEDDesc oled_desc_ssd1315; +extern const struct OLEDDesc oled_desc_sh1106; + #endif From 84a99ddecdf63eaea54ee7e0d0f79f391ff37e65 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:37:59 +0000 Subject: [PATCH 48/52] memory_lcd: Embed MonoScreen in driver Fold MonoScreen into struct MemoryLCDDriver as an embedded member and drop the file-static pointer + its separate calloc. The per-screen state now travels with the driver pointer recovered by MEMORY_LCD_DRIVER_FROM_CTX, preparing multi-instance support for this driver. While here, accept width/height Erlang opts (defaults 400x240 for the LS027B4DH01) and resolve the `// FIXME: hardcoded width and height`. The new opt surface mirrors what DCS LCD already accepts. struct Screen and its DMA buffers stay at file scope here; they are specific to the Sharp frame format. vcom likewise stays a file-global. Signed-off-by: Davide Bettio --- memory_display_driver.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/memory_display_driver.c b/memory_display_driver.c index fc65485..e7b76ac 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -52,6 +52,7 @@ #include "display_common.h" #include "display_task.h" +#include "mono_draw.h" #include "spi_display.h" #define DISPLAY_WIDTH 400 @@ -66,6 +67,8 @@ struct MemoryLCDDriver struct SPIDisplay spi_disp; Context *ctx; + struct MonoScreen screen; + struct DisplayTaskArgs display_args; }; @@ -75,7 +78,6 @@ struct MemoryLCDDriver #include "display_items.h" #include "display_message.h" #include "image_helpers.h" -#include "mono_draw.h" // This struct is just for compatibility reasons with the SDL display driver // so it is possible to easily copy & paste code from there. @@ -89,7 +91,6 @@ struct Screen }; static struct Screen *screen; -static struct MonoScreen *mono_screen; static void display_init(Context *ctx, term opts); @@ -139,7 +140,7 @@ static void do_update(Context *ctx, term display_list) int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = mono_draw_x(mono_screen, buf + 2, xpos, ypos, items, len); + int drawn_pixels = mono_draw_x(&driver->screen, buf + 2, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -225,14 +226,18 @@ Context *memory_lcd_display_create_port(GlobalContext *global, term opts) static void display_init(Context *ctx, term opts) { - screen = malloc(sizeof(struct Screen)); - // FIXME: hardcoded width and height - screen->w = 400; - screen->h = 240; + GlobalContext *glb = ctx->global; - mono_screen = calloc(1, sizeof(struct MonoScreen)); - mono_screen->w = DISPLAY_WIDTH; - mono_screen->h = DISPLAY_HEIGHT; + term width_term = interop_kv_get_value_default( + opts, ATOM_STR("\x5", "width"), term_from_int(DISPLAY_WIDTH), glb); + term height_term = interop_kv_get_value_default( + opts, ATOM_STR("\x6", "height"), term_from_int(DISPLAY_HEIGHT), glb); + int width = term_to_int(width_term); + int height = term_to_int(height_term); + + screen = malloc(sizeof(struct Screen)); + screen->w = width; + screen->h = height; int memsize = 2 + 400 / 8 + 2; @@ -248,8 +253,6 @@ static void display_init(Context *ctx, term opts) abort(); } - GlobalContext *glb = ctx->global; - struct MemoryLCDDriver *driver = malloc(sizeof(struct MemoryLCDDriver)); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); @@ -258,6 +261,8 @@ static void display_init(Context *ctx, term opts) ctx->platform_data = &driver->display_args; driver->ctx = ctx; + driver->screen.w = width; + driver->screen.h = height; struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); From b5e701f905922a141b1cde0985344d13cc9673b0 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:42:16 +0000 Subject: [PATCH 49/52] memory_lcd: Embed DMA buffers and VCOM in driver Move the Sharp frame-format DMA buffers (pixels, dma_out) and the VCOM toggle state from file scope into struct MemoryLCDDriver as embedded members. Drop the struct Screen wrapper entirely; w/h are carried by the embedded MonoScreen and pixels/dma_out live directly on the driver. Rewrite get_vcom() as next_vcom(driver), advancing the toggle bit on the driver pointer recovered by MEMORY_LCD_DRIVER_FROM_CTX. All file-level globals are gone; nothing in this translation unit now outlives the driver pointer. Signed-off-by: Davide Bettio --- memory_display_driver.c | 80 ++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/memory_display_driver.c b/memory_display_driver.c index e7b76ac..0688ed4 100644 --- a/memory_display_driver.c +++ b/memory_display_driver.c @@ -69,6 +69,10 @@ struct MemoryLCDDriver struct MonoScreen screen; + uint8_t *pixels; + uint8_t *dma_out; + int vcom; + struct DisplayTaskArgs display_args; }; @@ -79,31 +83,12 @@ struct MemoryLCDDriver #include "display_message.h" #include "image_helpers.h" -// This struct is just for compatibility reasons with the SDL display driver -// so it is possible to easily copy & paste code from there. -struct Screen -{ - int w; - int h; - uint8_t *pixels; - uint8_t *dma_out; - // keep double buffer disabled for now: uint16_t *pixels_out; -}; - -static struct Screen *screen; - static void display_init(Context *ctx, term opts); -int vcom = 0x0; -static inline int get_vcom() +static inline int next_vcom(struct MemoryLCDDriver *driver) { - int current_vcom = vcom; - if (!vcom) { - vcom = 0x2; - } else { - vcom = 0; - } - + int current_vcom = driver->vcom; + driver->vcom = current_vcom ? 0 : 0x2; return current_vcom; } @@ -120,18 +105,18 @@ static void do_update(Context *ctx, term display_list) t = term_get_list_tail(t); } - int screen_width = screen->w; - int screen_height = screen->h; struct MemoryLCDDriver *driver = MEMORY_LCD_DRIVER_FROM_CTX(ctx); + int screen_width = driver->screen.w; + int screen_height = driver->screen.h; int memsize = 2 + 400 / 8 + 2; - uint8_t *buf = screen->pixels; + uint8_t *buf = driver->pixels; spi_device_acquire_bus(driver->spi_disp.handle, portMAX_DELAY); bool transaction_in_progress = false; for (int ypos = 0; ypos < screen_height; ypos++) { - if (!screen->dma_out && transaction_in_progress) { + if (!driver->dma_out && transaction_in_progress) { spi_transaction_t *trans = NULL; spi_device_get_trans_result(driver->spi_disp.handle, &trans, portMAX_DELAY); } @@ -144,22 +129,22 @@ static void do_update(Context *ctx, term display_list) xpos += drawn_pixels; } - buf[0] = 0x1 | get_vcom(); + buf[0] = 0x1 | next_vcom(driver); buf[1] = ypos + 1; buf[2 + DISPLAY_WIDTH / 8] = 0; buf[2 + DISPLAY_WIDTH / 8 + 1] = 0; - if (screen->dma_out) { + if (driver->dma_out) { if (transaction_in_progress) { spi_transaction_t *trans = NULL; spi_device_get_trans_result(driver->spi_disp.handle, &trans, portMAX_DELAY); } - void *tmp = screen->pixels; - screen->pixels = screen->dma_out; - buf = screen->pixels; - screen->dma_out = tmp; + void *tmp = driver->pixels; + driver->pixels = driver->dma_out; + buf = driver->pixels; + driver->dma_out = tmp; - spi_display_dma_write(&driver->spi_disp, memsize, screen->dma_out); + spi_display_dma_write(&driver->spi_disp, memsize, driver->dma_out); } else { spi_display_dma_write(&driver->spi_disp, memsize, buf); } @@ -235,24 +220,8 @@ static void display_init(Context *ctx, term opts) int width = term_to_int(width_term); int height = term_to_int(height_term); - screen = malloc(sizeof(struct Screen)); - screen->w = width; - screen->h = height; - int memsize = 2 + 400 / 8 + 2; - screen->pixels = heap_caps_malloc(memsize, MALLOC_CAP_DMA); - if (UNLIKELY(!screen->pixels)) { - fprintf(stderr, "failed to allocate buf!\n"); - abort(); - } - - screen->dma_out = heap_caps_malloc(memsize, MALLOC_CAP_DMA); - if (UNLIKELY(!screen->dma_out)) { - fprintf(stderr, "failed to allocate buf!\n"); - abort(); - } - struct MemoryLCDDriver *driver = malloc(sizeof(struct MemoryLCDDriver)); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); @@ -263,6 +232,19 @@ static void display_init(Context *ctx, term opts) driver->ctx = ctx; driver->screen.w = width; driver->screen.h = height; + driver->vcom = 0; + + driver->pixels = heap_caps_malloc(memsize, MALLOC_CAP_DMA); + if (UNLIKELY(!driver->pixels)) { + fprintf(stderr, "failed to allocate buf!\n"); + abort(); + } + + driver->dma_out = heap_caps_malloc(memsize, MALLOC_CAP_DMA); + if (UNLIKELY(!driver->dma_out)) { + fprintf(stderr, "failed to allocate buf!\n"); + abort(); + } struct SPIDisplayConfig spi_config; spi_display_init_config(&spi_config); From b4a766ca741493db7f4c9b61037733dbedfc93e8 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 09:58:21 +0000 Subject: [PATCH 50/52] ssd1306: Embed MonoScreen in driver Fold MonoScreen into struct OLEDDriver as an embedded member and drop the file-static pointer + its separate calloc. The per-screen state now travels with the driver pointer recovered by OLED_DRIVER_FROM_CTX, a prerequisite for multi-instance use. The controller panels are fixed at 128x64 for all three variants, so the MonoScreen dimensions are initialised from the existing DISPLAY_WIDTH / DISPLAY_HEIGHT constants rather than from opts. Signed-off-by: Davide Bettio --- ssd1306_display_driver.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index 6a665d9..caf10cb 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -41,6 +41,7 @@ #include "display_message.h" #include "display_task.h" #include "image_helpers.h" +#include "mono_draw.h" #include "oled_commands.h" #define TAG "SSD1306" @@ -72,17 +73,16 @@ struct OLEDDriver display_type_t type; Context *ctx; + struct MonoScreen screen; + struct DisplayTaskArgs display_args; }; #define OLED_DRIVER_FROM_CTX(ctx) \ CONTAINER_OF((struct DisplayTaskArgs *) (ctx)->platform_data, struct OLEDDriver, display_args) -static struct MonoScreen *mono_screen; - #include "font_data.h" #include "display_items.h" -#include "mono_draw.h" static void do_update(Context *ctx, term display_list) { @@ -114,7 +114,7 @@ static void do_update(Context *ctx, term display_list) for (int ypos = 0; ypos < screen_height; ypos++) { int xpos = 0; while (xpos < screen_width) { - int drawn_pixels = mono_draw_x(mono_screen, buf, xpos, ypos, items, len); + int drawn_pixels = mono_draw_x(&driver->screen, buf, xpos, ypos, items, len); xpos += drawn_pixels; } @@ -224,10 +224,6 @@ static void display_init(Context *ctx, term opts) bool invert = interop_kv_get_value(opts, ATOM_STR("\x6", "invert"), glb) == TRUE_ATOM; - mono_screen = calloc(1, sizeof(struct MonoScreen)); - mono_screen->w = DISPLAY_WIDTH; - mono_screen->h = DISPLAY_HEIGHT; - struct OLEDDriver *driver = malloc(sizeof(struct OLEDDriver)); driver->display_args.messages_queue = xQueueCreate(32, sizeof(Message *)); @@ -236,6 +232,8 @@ static void display_init(Context *ctx, term opts) ctx->platform_data = &driver->display_args; driver->ctx = ctx; + driver->screen.w = DISPLAY_WIDTH; + driver->screen.h = DISPLAY_HEIGHT; driver->type = DisplayTypeSsd1306; // Default to SSD1306 term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); From c914c01c2731b5d4a3c921b2cd71517f36d1167a Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 10:18:03 +0000 Subject: [PATCH 51/52] Refactor SSD1306 driver to use descriptors Generalise the dispatch to drive all three supported controllers from the per-controller OLEDDesc descriptors. A compatible-string lookup replaces the display_type_t enum branching: I2C address, init sequence, per-page column-reset flag, and scanline prefix padding all come from the descriptor. Unknown or missing compatibles now log an ESP_LOGE and early- return instead of silently defaulting to SSD1306, fixing a path that quietly masked typos in the Erlang `compatible` opt. Remove display_type_t, the driver->type field, and the hardcoded I2C_ADDRESS macro -- all three subsumed by the descriptor. Signed-off-by: Davide Bettio --- ssd1306_display_driver.c | 93 +++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/ssd1306_display_driver.c b/ssd1306_display_driver.c index caf10cb..da0e4a7 100644 --- a/ssd1306_display_driver.c +++ b/ssd1306_display_driver.c @@ -51,8 +51,6 @@ #define PAGE_HEIGHT 8 #define PAGES_NUM 8 -#define I2C_ADDRESS 0x3C - #define CTRL_BYTE_CMD_SINGLE 0x80 #define CTRL_BYTE_CMD_STREAM 0x00 #define CTRL_BYTE_DATA_STREAM 0x40 @@ -60,17 +58,10 @@ #define CMD_DISPLAY_INVERTED 0xA7 #define CMD_DISPLAY_ON 0xAF -typedef enum -{ - DisplayTypeSsd1306, - DisplayTypeSsd1315, - DisplayTypeSh1106, -} display_type_t; - struct OLEDDriver { term i2c_host; - display_type_t type; + const struct OLEDDesc *desc; Context *ctx; struct MonoScreen screen; @@ -84,6 +75,25 @@ struct OLEDDriver #include "font_data.h" #include "display_items.h" +static const struct { + const char *compat; + const struct OLEDDesc *desc; +} oled_compat_table[] = { + { "solomon-systech,ssd1306", &oled_desc_ssd1306 }, + { "solomon-systech,ssd1315", &oled_desc_ssd1315 }, + { "sino-wealth,sh1106", &oled_desc_sh1106 }, +}; + +static const struct OLEDDesc *oled_desc_for_compatible(const char *compat) +{ + for (size_t i = 0; i < sizeof(oled_compat_table) / sizeof(oled_compat_table[0]); i++) { + if (!strcmp(compat, oled_compat_table[i].compat)) { + return oled_compat_table[i].desc; + } + } + return NULL; +} + static void do_update(Context *ctx, term display_list) { int proper; @@ -127,13 +137,12 @@ static void do_update(Context *ctx, term display_list) i2c_cmd_handle_t cmd; cmd = i2c_cmd_link_create(); i2c_master_start(cmd); - i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, (driver->desc->i2c_address << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0xB0 | ypos / 8, true); - if (driver->type == DisplayTypeSh1106 || driver->type == DisplayTypeSsd1315) { - // SSD1315 and SH1106 require explicit column address reset + if (driver->desc->column_reset_per_page) { i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); i2c_master_write_byte(cmd, 0x00, true); i2c_master_write_byte(cmd, CTRL_BYTE_CMD_SINGLE, true); @@ -141,11 +150,10 @@ static void do_update(Context *ctx, term display_list) } i2c_master_write_byte(cmd, CTRL_BYTE_DATA_STREAM, true); - - if (driver->type == DisplayTypeSh1106) { - // add 2 empty pages on sh1106 since it can have up to 132 pixels - // and 128 pixel screen starts at (2, 0) - i2c_master_write_byte(cmd, 0, true); + // Pad the data stream with leading zero bytes for controllers + // whose RAM is wider than the visible pixel area (SH1106 exposes + // 128 of 132 columns starting at offset 2). + for (uint8_t k = 0; k < driver->desc->scanline_prefix_pad_bytes; k++) { i2c_master_write_byte(cmd, 0, true); } @@ -153,12 +161,6 @@ static void do_update(Context *ctx, term display_list) i2c_master_write_byte(cmd, out_buf[j], true); } - // no need to send the last 2 page, the position will be set on next line again - // if (driver->type == DisplayTypeSh1106) { - // i2c_master_write_byte(cmd, 0, true); - // i2c_master_write_byte(cmd, 0, true); - // } - i2c_master_stop(cmd); i2c_master_cmd_begin(i2c_num, cmd, 100 / portTICK_PERIOD_MS); i2c_cmd_link_delete(cmd); @@ -232,27 +234,27 @@ static void display_init(Context *ctx, term opts) ctx->platform_data = &driver->display_args; driver->ctx = ctx; - driver->screen.w = DISPLAY_WIDTH; - driver->screen.h = DISPLAY_HEIGHT; - driver->type = DisplayTypeSsd1306; // Default to SSD1306 - term compat_value_term = interop_kv_get_value_default(opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); + term compat_value_term = interop_kv_get_value_default( + opts, ATOM_STR("\xA", "compatible"), term_nil(), ctx->global); int str_ok; char *compat_string = interop_term_to_string(compat_value_term, &str_ok); - - if (!(str_ok && compat_string)) { - ESP_LOGE(TAG, "No Compatible Device Found."); - return; + const struct OLEDDesc *desc = NULL; + if (str_ok && compat_string) { + desc = oled_desc_for_compatible(compat_string); } - - if (!strcmp(compat_string, "sino-wealth,sh1106")) { - driver->type = DisplayTypeSh1106; - } else if (!strcmp(compat_string, "solomon-systech,ssd1315")) { - driver->type = DisplayTypeSsd1315; + if (!desc) { + ESP_LOGE(TAG, "Failed init: unknown or missing compatible '%s'.", + compat_string ? compat_string : "(null)"); + free(compat_string); + return; } - free(compat_string); + driver->desc = desc; + driver->screen.w = desc->native_width; + driver->screen.h = desc->native_height; + int reset_gpio; if (!display_common_gpio_from_opts(opts, ATOM_STR("\x5", "reset"), &reset_gpio, glb)) { ESP_LOGI(TAG, "Reset GPIO not configured."); @@ -270,23 +272,14 @@ static void display_init(Context *ctx, term opts) } driver->i2c_host = i2c_host; - const uint8_t *init_seq; - size_t init_seq_len; - if (driver->type == DisplayTypeSsd1315) { - init_seq = oled_init_seq_ssd1315; - init_seq_len = oled_init_seq_ssd1315_len; - } else { - init_seq = oled_init_seq_ssd1306; - init_seq_len = oled_init_seq_ssd1306_len; - } - oled_execute_init_seq(i2c_num, I2C_ADDRESS, init_seq, init_seq_len); + oled_execute_init_seq(i2c_num, desc->i2c_address, desc->init_seq, desc->init_seq_len); // Driver-controlled finalization: optional invert, then display ON. // These depend on a runtime opt and are not panel data, so they // stay outside the per-variant init array. i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); - i2c_master_write_byte(cmd, (I2C_ADDRESS << 1) | I2C_MASTER_WRITE, true); + i2c_master_write_byte(cmd, (desc->i2c_address << 1) | I2C_MASTER_WRITE, true); i2c_master_write_byte(cmd, CTRL_BYTE_CMD_STREAM, true); if (invert) { i2c_master_write_byte(cmd, CMD_DISPLAY_INVERTED, true); @@ -296,7 +289,7 @@ static void display_init(Context *ctx, term opts) esp_err_t res = i2c_master_cmd_begin(i2c_num, cmd, 50 / portTICK_PERIOD_MS); if (res != ESP_OK) { - ESP_LOGE(TAG, "ssd1306/ssd1315 OLED configuration failed. error: 0x%.2X", res); + ESP_LOGE(TAG, "%s configuration failed. error: 0x%.2X", desc->name, res); } else { xTaskCreate(display_task_process_messages, "display", 10000, &driver->display_args, 1, NULL); } From a2bd5996deff7103ed1514542666ba54b2f21fc2 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 15 Apr 2026 10:26:22 +0000 Subject: [PATCH 52/52] Rename SSD1306 driver to unified OLED name git mv ssd1306_display_driver.c -> oled_display_driver.c; rename the create_port entry point and the log TAG accordingly. Pure rename -- all three OLED compatibles (solomon-systech,ssd1306, solomon-systech,ssd1315, sino-wealth,sh1106) now dispatch through oled_display_create_port. The previous filename attributed a Sino Wealth controller (SH1106) to Solomon Systech and was obsolete since the file started driving multiple variants. Signed-off-by: Davide Bettio --- CMakeLists.txt | 2 +- display_driver.c | 8 ++++---- ssd1306_display_driver.c => oled_display_driver.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename ssd1306_display_driver.c => oled_display_driver.c (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f38ce1..39707ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ idf_component_register(SRCS "font_data.c" "memory_display_driver.c" "oled_commands.c" - "ssd1306_display_driver.c" + "oled_display_driver.c" "spi_dc_driver.c" "spi_display.c" "ufontlib.c" diff --git a/display_driver.c b/display_driver.c index 3700a39..bce5df6 100644 --- a/display_driver.c +++ b/display_driver.c @@ -32,7 +32,7 @@ static const char *TAG = "display_driver"; Context *epaper_display_create_port(GlobalContext *global, term opts); Context *dcs_lcd_display_create_port(GlobalContext *global, term opts); Context *memory_lcd_display_create_port(GlobalContext *global, term opts); -Context *ssd1306_display_create_port(GlobalContext *global, term opts); +Context *oled_display_create_port(GlobalContext *global, term opts); Context *display_create_port(GlobalContext *global, term opts) { @@ -64,11 +64,11 @@ Context *display_create_port(GlobalContext *global, term opts) || !strcmp(compat_string, "sitronix,st7796")) { ctx = dcs_lcd_display_create_port(global, opts); } else if (!strcmp(compat_string, "solomon-systech,ssd1306")) { - ctx = ssd1306_display_create_port(global, opts); + ctx = oled_display_create_port(global, opts); } else if (!strcmp(compat_string, "solomon-systech,ssd1315")) { - ctx = ssd1306_display_create_port(global, opts); + ctx = oled_display_create_port(global, opts); } else if (!strcmp(compat_string, "sino-wealth,sh1106")) { - ctx = ssd1306_display_create_port(global, opts); + ctx = oled_display_create_port(global, opts); } else { ESP_LOGE(TAG, "No matching display driver for given `comptaible`: `%s`.", compat_string); } diff --git a/ssd1306_display_driver.c b/oled_display_driver.c similarity index 99% rename from ssd1306_display_driver.c rename to oled_display_driver.c index da0e4a7..e299a49 100644 --- a/ssd1306_display_driver.c +++ b/oled_display_driver.c @@ -44,7 +44,7 @@ #include "mono_draw.h" #include "oled_commands.h" -#define TAG "SSD1306" +#define TAG "oled_display" #define DISPLAY_WIDTH 128 #define DISPLAY_HEIGHT 64 @@ -298,7 +298,7 @@ static void display_init(Context *ctx, term opts) i2c_driver_release(i2c_host, glb); } -Context *ssd1306_display_create_port(GlobalContext *global, term opts) +Context *oled_display_create_port(GlobalContext *global, term opts) { Context *ctx = context_new(global); ctx->native_handler = display_task_consume_mailbox;