From 6d1fe77fc9ebf3c620cd68f65774ab5a47030077 Mon Sep 17 00:00:00 2001 From: Niklas Dusenlund Date: Thu, 18 Sep 2025 10:43:16 +0200 Subject: [PATCH] ui: introduce double buffering Create a new module called "canvas" which is responsible for double buffering. Double buffering is required to enable asynchronous transfer of the frame buffer. While the "active" frame buffer is being transferred to the oled in the background, the ui will render to a "working" frame buffer. When the rendering is complete the buffers are flipped with "canvas_commit". --- src/CMakeLists.txt | 1 + src/bootloader/bootloader.c | 25 ++++---- src/bootloader/startup.c | 7 ++- src/factorysetup.c | 2 +- src/firmware.c | 2 +- src/reset.c | 7 ++- src/rust/bitbox02-rust/src/general/screen.rs | 9 ++- src/rust/bitbox02-sys/build.rs | 7 ++- src/rust/bitbox02-sys/wrapper.h | 1 + src/rust/bitbox02/src/lib.rs | 12 ++-- src/screen.c | 25 +++----- src/screen.h | 5 +- src/ui/canvas.c | 61 ++++++++++++++++++++ src/ui/canvas.h | 59 +++++++++++++++++++ src/ui/oled/oled.c | 30 ++++------ src/ui/oled/oled.h | 17 ++---- src/ui/oled/sh1107.c | 16 +++-- src/ui/oled/sh1107.h | 4 +- src/ui/oled/ssd1312.c | 16 +++-- src/ui/oled/ssd1312.h | 4 +- src/ui/screen_process.c | 6 +- src/ui/ugui/ugui.c | 12 ---- src/ui/ugui/ugui.h | 4 -- test/hardware-fakes/src/fake_oled.c | 15 +++++ test/hardware-fakes/src/fake_screen.c | 2 - 25 files changed, 228 insertions(+), 121 deletions(-) create mode 100644 src/ui/canvas.c create mode 100644 src/ui/canvas.h create mode 100644 test/hardware-fakes/src/fake_oled.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 254529ccfa..b010e31fbc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,7 @@ set(DRIVER-SOURCES ${CMAKE_SOURCE_DIR}/src/platform/driver_init.c ${CMAKE_SOURCE_DIR}/src/ui/oled/oled.c ${CMAKE_SOURCE_DIR}/src/ui/oled/oled_writer.c + ${CMAKE_SOURCE_DIR}/src/ui/canvas.c ) set(DRIVER-SOURCES ${DRIVER-SOURCES} PARENT_SCOPE) diff --git a/src/bootloader/bootloader.c b/src/bootloader/bootloader.c index 3791fe4e72..85da6ab6a4 100644 --- a/src/bootloader/bootloader.c +++ b/src/bootloader/bootloader.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -328,15 +329,14 @@ static void _render_message(const char* message, int duration) { char print[100]; snprintf(print, sizeof(print), "%s", message); - UG_ClearBuffer(); UG_PutString(0, 0, print, false); - UG_SendBuffer(); + canvas_commit(); + oled_present(); delay_ms(duration); } void bootloader_render_default_screen(void) { - UG_ClearBuffer(); _load_logo(); #if PLATFORM_BITBOX02PLUS == 1 UG_PutString(0, SCREEN_HEIGHT - 9 * 2 - 5, "See the BitBoxApp", false); @@ -354,7 +354,8 @@ void bootloader_render_default_screen(void) } UG_PutString(0, SCREEN_HEIGHT - 9, "See the BitBoxApp", false); #endif - UG_SendBuffer(); + canvas_commit(); + oled_present(); } #if PLATFORM_BITBOX02PLUS @@ -368,7 +369,6 @@ void bootloader_render_ble_confirm_screen(bool confirmed) uint32_t pairing_code_int = (*(uint32_t*)&bootloader_pairing_code_bytes[0]) % 1000000; char code_str[10] = {0}; snprintf(code_str, sizeof(code_str), "%06u", (unsigned)pairing_code_int); - UG_ClearBuffer(); uint16_t check_width = IMAGE_DEFAULT_CHECKMARK_HEIGHT + IMAGE_DEFAULT_CHECKMARK_HEIGHT / 2 - 1; if (confirmed) { UG_PutString(15, 0, "Confirm on app", false); @@ -380,13 +380,13 @@ void bootloader_render_ble_confirm_screen(bool confirmed) UG_FontSelect(&font_monogram_5X9); UG_PutString(45, SCREEN_HEIGHT / 2 - 9, code_str, false); UG_FontSelect(&font_font_a_9X9); - UG_SendBuffer(); + canvas_commit(); + oled_present(); } #endif static void _render_progress(float progress) { - UG_ClearBuffer(); _load_logo(); if (progress > 0) { char label[5] = {0}; @@ -401,7 +401,8 @@ static void _render_progress(float progress) msg = "INSTALLING"; } UG_PutString(SCREEN_WIDTH / 2 - 3, SCREEN_HEIGHT - 9 * 2, msg, false); - UG_SendBuffer(); + canvas_commit(); + oled_present(); } static void _render_hash(const char* title, const uint8_t* hash) @@ -433,7 +434,6 @@ static void _render_hash(const char* title, const uint8_t* hash) &hash_hex[48]); for (uint8_t i = 1; i <= seconds; i++) { - UG_ClearBuffer(); UG_PutString(0, 0, title, false); snprintf(timer_buf, sizeof(timer_buf), "%ds", seconds - i); @@ -449,7 +449,8 @@ static void _render_hash(const char* title, const uint8_t* hash) UG_FontSelect(f_regular); - UG_SendBuffer(); + canvas_commit(); + oled_present(); delay_ms(1000); } bootloader_render_default_screen(); @@ -1013,7 +1014,6 @@ static void _check_init(boot_data_t* data) #ifdef BOOTLOADER_DEVDEVICE static bool _devdevice_enter(secbool_u32 firmware_verified) { - UG_ClearBuffer(); UG_PutString(0, 0, " ", false); UG_PutString(0, SCREEN_HEIGHT / 2 - 11, "DEV DEVICE", false); UG_PutString(0, SCREEN_HEIGHT / 2 + 2, "NOT FOR VALUE", false); @@ -1043,7 +1043,8 @@ static bool _devdevice_enter(secbool_u32 firmware_verified) UG_DrawLine(xpos + 5, ypos, xpos, ypos + 5, C_WHITE); UG_DrawLine(xpos - 2, ypos + 3, xpos, ypos + 5, C_WHITE); } - UG_SendBuffer(); + canvas_commit(); + oled_present(); while (true) { do { qtouch_process(); diff --git a/src/bootloader/startup.c b/src/bootloader/startup.c index 52959772b0..75d935c130 100644 --- a/src/bootloader/startup.c +++ b/src/bootloader/startup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -83,7 +84,7 @@ int main(void) bootloader_init(); platform_init(); __stack_chk_guard = rand_sync_read32(&RAND_0); - screen_init(oled_set_pixel, oled_mirror, oled_clear_buffer); + screen_init(oled_set_pixel, oled_mirror); #if defined(BOOTLOADER_DEVDEVICE) || PLATFORM_BITBOX02PLUS == 1 qtouch_init(); #endif @@ -189,7 +190,6 @@ int main(void) if (qtouch_is_scroller_active(top_slider)) { bool ok; - UG_ClearBuffer(); if (qtouch_get_scroller_position(top_slider) < 127) { bootloader_render_default_screen(); ok = false; @@ -218,7 +218,8 @@ int main(void) ringbuffer_put(&uart_write_queue, tmp[i]); } bootloader_pairing_request = false; - UG_SendBuffer(); + canvas_commit(); + oled_present(); } } #endif diff --git a/src/factorysetup.c b/src/factorysetup.c index 24185c255e..ec10afc443 100644 --- a/src/factorysetup.c +++ b/src/factorysetup.c @@ -577,7 +577,7 @@ int main(void) system_init(); platform_init(); __stack_chk_guard = common_stack_chk_guard(); - screen_init(oled_set_pixel, oled_mirror, oled_clear_buffer); + screen_init(oled_set_pixel, oled_mirror); screen_splash(); common_main(); diff --git a/src/firmware.c b/src/firmware.c index 6af86f8747..bdc7d291c6 100644 --- a/src/firmware.c +++ b/src/firmware.c @@ -41,7 +41,7 @@ int main(void) system_init(); platform_init(); __stack_chk_guard = common_stack_chk_guard(); - screen_init(oled_set_pixel, oled_mirror, oled_clear_buffer); + screen_init(oled_set_pixel, oled_mirror); screen_splash(); qtouch_init(); common_main(); diff --git a/src/reset.c b/src/reset.c index f860fd4ea8..71d71f803e 100644 --- a/src/reset.c +++ b/src/reset.c @@ -23,12 +23,14 @@ #include "system.h" #include "uart.h" #include +#include #ifndef TESTING #include "securechip/securechip.h" #include #include #include +#include #include #endif @@ -41,9 +43,10 @@ static void _show_reset_label(bool status) { const char* msg = "Device reset"; component_t* comp = status_create(msg, status, NULL, NULL); - screen_clear(); + canvas_clear(); comp->f->render(comp); - UG_SendBuffer(); + canvas_commit(); + oled_present(); comp->f->cleanup(comp); delay_ms(3000); } diff --git a/src/rust/bitbox02-rust/src/general/screen.rs b/src/rust/bitbox02-rust/src/general/screen.rs index 1aedafb50c..b1e216ec90 100644 --- a/src/rust/bitbox02-rust/src/general/screen.rs +++ b/src/rust/bitbox02-rust/src/general/screen.rs @@ -14,13 +14,16 @@ use core::time::Duration; -use bitbox02::{delay, ug_clear_buffer, ug_font_select_9x9, ug_put_string, ug_send_buffer}; +use bitbox02::{ + canvas_clear, canvas_commit, delay, oled_present, ug_font_select_9x9, ug_put_string, +}; pub fn print_debug_internal(duration: Duration, msg: &str) { - ug_clear_buffer(); + canvas_clear(); ug_font_select_9x9(); ug_put_string(0, 0, msg, false); - ug_send_buffer(); + canvas_commit(); + oled_present(); delay(duration); } diff --git a/src/rust/bitbox02-sys/build.rs b/src/rust/bitbox02-sys/build.rs index 78c7597d9d..f77b3f90bb 100644 --- a/src/rust/bitbox02-sys/build.rs +++ b/src/rust/bitbox02-sys/build.rs @@ -53,14 +53,14 @@ const ALLOWLIST_TYPES: &[&str] = &[ ]; const ALLOWLIST_FNS: &[&str] = &[ - "UG_ClearBuffer", "UG_FontSelect", "UG_PutString", - "UG_SendBuffer", "bip32_derive_xpub", "bitbox02_smarteeprom_init", "bitbox_secp256k1_dleq_prove", "bitbox_secp256k1_dleq_verify", + "canvas_clear", + "canvas_commit", "confirm_create", "confirm_transaction_address_create", "confirm_transaction_fee_create", @@ -113,6 +113,7 @@ const ALLOWLIST_FNS: &[&str] = &[ "memory_get_platform", "memory_get_securechip_type", "memory_spi_get_active_ble_firmware_version", + "oled_present", "spi_mem_protected_area_write", "menu_create", "fake_memory_factoryreset", @@ -199,6 +200,7 @@ const BITBOX02_SOURCES: &[&str] = &[ "src/u2f.c", "src/u2f/u2f_app.c", "src/u2f/u2f_packet.c", + "src/ui/canvas.c", "src/ui/components/button.c", "src/ui/components/confirm_gesture.c", "src/ui/components/confirm_transaction.c", @@ -411,6 +413,7 @@ pub fn main() -> Result<(), &'static str> { "test/hardware-fakes/src/fake_component.c", "test/hardware-fakes/src/fake_diskio.c", "test/hardware-fakes/src/fake_memory.c", + "test/hardware-fakes/src/fake_oled.c", "test/hardware-fakes/src/fake_qtouch.c", "test/hardware-fakes/src/fake_screen.c", "test/hardware-fakes/src/fake_securechip.c", diff --git a/src/rust/bitbox02-sys/wrapper.h b/src/rust/bitbox02-sys/wrapper.h index af56304aa2..fe0803b1c3 100644 --- a/src/rust/bitbox02-sys/wrapper.h +++ b/src/rust/bitbox02-sys/wrapper.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs index 4e93f4732b..e8e4b09e5b 100644 --- a/src/rust/bitbox02/src/lib.rs +++ b/src/rust/bitbox02/src/lib.rs @@ -62,12 +62,16 @@ pub fn ug_put_string(x: i16, y: i16, input: &str, inverted: bool) { } } -pub fn ug_clear_buffer() { - unsafe { bitbox02_sys::UG_ClearBuffer() } +pub fn canvas_clear() { + unsafe { bitbox02_sys::canvas_clear() } } -pub fn ug_send_buffer() { - unsafe { bitbox02_sys::UG_SendBuffer() } +pub fn canvas_commit() { + unsafe { bitbox02_sys::canvas_commit() } +} + +pub fn oled_present() { + unsafe { bitbox02_sys::oled_present() } } pub fn ug_font_select_9x9() { diff --git a/src/screen.c b/src/screen.c index f0fbecda92..ebfe7e07dc 100644 --- a/src/screen.c +++ b/src/screen.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,6 @@ static UG_GUI guioled; // Global GUI structure for OLED screen static bool screen_upside_down = false; static void (*_mirror_fn)(bool); -static void (*_clear_fn)(void); UG_COLOR screen_front_color = C_WHITE; UG_COLOR screen_back_color = C_BLACK; @@ -45,10 +45,11 @@ void screen_print_debug(const char* message, int duration) { char print[100]; snprintf(print, sizeof(print), "%s", message); - screen_clear(); + canvas_clear(); UG_FontSelect(&font_font_a_9X9); UG_PutString(0, 0, print, false); - UG_SendBuffer(); + canvas_commit(); + oled_present(); #ifndef TESTING if (duration > 0) delay_ms(duration); #endif @@ -79,16 +80,14 @@ void screen_print_debug_hex(const uint8_t* bytes, size_t len, int duration) // Careful, this function is used in both the bootloader and the firmware. void screen_splash(void) { - screen_clear(); - int height = IMAGE_DEFAULT_ARROW_HEIGHT; int x = 0; int y = SCREEN_HEIGHT / 2 - height; image_arrow(x - height + 2, y, height, ARROW_RIGHT); image_arrow(SCREEN_WIDTH - x - 2, y, height, ARROW_LEFT); - UG_SendBuffer(); - screen_clear(); + canvas_commit(); + oled_present(); } void screen_rotate(void) @@ -105,18 +104,8 @@ bool screen_is_upside_down(void) return screen_upside_down; } -void screen_init( - void (*pixel_fn)(UG_S16, UG_S16, UG_COLOR), - void (*mirror_fn)(bool), - void (*clear_fn)(void)) +void screen_init(void (*pixel_fn)(UG_S16, UG_S16, UG_COLOR), void (*mirror_fn)(bool)) { _mirror_fn = mirror_fn; - _clear_fn = clear_fn; UG_Init(&guioled, pixel_fn, &font_font_a_11X10, SCREEN_WIDTH, SCREEN_HEIGHT); } - -void screen_clear(void) -{ - ASSERT(_clear_fn); - _clear_fn(); -} diff --git a/src/screen.h b/src/screen.h index a2f43883ff..4be6271d8e 100644 --- a/src/screen.h +++ b/src/screen.h @@ -34,10 +34,7 @@ extern slider_location_t bottom_slider; #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 -void screen_init( - void (*pixel_fn)(UG_S16, UG_S16, UG_COLOR), - void (*mirror_fn)(bool), - void (*clear_fn)(void)); +void screen_init(void (*pixel_fn)(UG_S16, UG_S16, UG_COLOR), void (*mirror_fn)(bool)); void screen_print_debug(const char* message, int duration); void screen_sprintf_debug(int duration, const char* fmt, ...) __attribute__((format(printf, 2, 0))); void screen_print_debug_hex(const uint8_t* bytes, size_t len, int duration); diff --git a/src/ui/canvas.c b/src/ui/canvas.c new file mode 100644 index 0000000000..597a32dd4f --- /dev/null +++ b/src/ui/canvas.c @@ -0,0 +1,61 @@ +// Copyright 2025 Shift Crypto AG +// +// 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. + +#include +#include +#include + +static uint8_t* _canvas_active = NULL; +static uint8_t* _canvas_working = NULL; + +// One working buffer and one active buffer. The buffer must be 4 byte aligned for DMA transfers. +static uint8_t _canvas_0[CANVAS_SIZE] __attribute__((aligned(4))) = {0}; +static uint8_t _canvas_1[CANVAS_SIZE] __attribute__((aligned(4))) = {0}; + +void canvas_init(void) +{ + _canvas_working = _canvas_0; + _canvas_active = _canvas_1; +} + +void canvas_fill(uint8_t color) +{ + uint8_t pixels = color ? 0xff : 0; + memset(canvas_working(), pixels, CANVAS_SIZE); +} + +void canvas_clear(void) +{ + canvas_fill(0); +} + +void canvas_commit(void) +{ + uint8_t* _canvas_tmp = _canvas_working; + _canvas_working = _canvas_active; + _canvas_active = _canvas_tmp; + canvas_clear(); +} + +uint8_t* canvas_working(void) +{ + ASSERT(_canvas_working); + return _canvas_working; +} + +uint8_t* canvas_active(void) +{ + ASSERT(_canvas_active); + return _canvas_active; +} diff --git a/src/ui/canvas.h b/src/ui/canvas.h new file mode 100644 index 0000000000..3a05181848 --- /dev/null +++ b/src/ui/canvas.h @@ -0,0 +1,59 @@ +// Copyright 2025 Shift Crypto AG +// +// 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. +#ifndef CANVAS_H +#define CANVAS_H + +#include + +// 8 pixels per byte in the canvas. +#define CANVAS_SIZE ((SCREEN_WIDTH * SCREEN_HEIGHT) / 8) + +#include + +/* + * Initialize canvas + */ + +void canvas_init(void); + +/* + * Fill the whole working canvas with one color + */ +void canvas_fill(uint8_t color); + +/* + * Clear working canvas (fill with 0) + */ +void canvas_clear(void); + +/* + * Commit the current "working" buffer to become "active" and clear the working buffer. + * + * Invalidates pointer returned from `canvas_working()`. `canvas_working()` must be called again to + * get the current working frame buffer. + */ +void canvas_commit(void); + +/* + * Get a pointer to current working canvas. This is the canvas that can be updated and isn't + * currently being displayed. + */ +uint8_t* canvas_working(void); + +/* + * Get a pointer ot the current active canvas, being sent to the display. (Should only be used by + * the screen driver.) + */ +uint8_t* canvas_active(void); +#endif diff --git a/src/ui/oled/oled.c b/src/ui/oled/oled.c index 713547e457..b25bb7073d 100644 --- a/src/ui/oled/oled.c +++ b/src/ui/oled/oled.c @@ -65,7 +65,6 @@ #include "oled.h" -#include "oled_writer.h" #include #include #include @@ -73,19 +72,18 @@ #include #include #include +#include +#include #include #include #include -static bool _frame_buffer_updated = false; -static uint8_t _frame_buffer[128 * 8]; - static volatile bool _enabled = false; struct bb02_display { - void (*configure)(uint8_t*); + void (*configure)(void); void (*set_pixel)(int16_t x, int16_t y, uint8_t c); - void (*update)(void); + void (*present)(void); void (*off)(void); void (*mirror)(bool); }; @@ -93,17 +91,19 @@ struct bb02_display { static struct bb02_display bb02_display = { .configure = sh1107_configure, .set_pixel = sh1107_set_pixel, - .update = sh1107_update, + .present = sh1107_present, .off = sh1107_off, .mirror = sh1107_mirror, }; void oled_init(void) { + canvas_init(); + if (memory_get_screen_type() == MEMORY_SCREEN_TYPE_SSD1312) { bb02_display.configure = ssd1312_configure; bb02_display.set_pixel = ssd1312_set_pixel; - bb02_display.update = ssd1312_update; + bb02_display.present = ssd1312_present; bb02_display.off = ssd1312_off; bb02_display.mirror = ssd1312_mirror; } @@ -120,9 +120,9 @@ void oled_init(void) gpio_set_pin_level(PIN_OLED_RES, 1); delay_us(5); - oled_clear_buffer(); + oled_present(); - bb02_display.configure(_frame_buffer); + bb02_display.configure(); delay_ms(100); @@ -131,14 +131,9 @@ void oled_init(void) _enabled = true; } -void oled_send_buffer(void) -{ - bb02_display.update(); -} - -void oled_clear_buffer(void) +void oled_present(void) { - memset(_frame_buffer, 0, sizeof(_frame_buffer)); + bb02_display.present(); } void oled_mirror(bool mirror) @@ -149,7 +144,6 @@ void oled_mirror(bool mirror) void oled_set_pixel(int16_t x, int16_t y, uint8_t c) { bb02_display.set_pixel(x, y, c); - _frame_buffer_updated = true; } void oled_off(void) diff --git a/src/ui/oled/oled.h b/src/ui/oled/oled.h index c73faedab8..614ef8d045 100644 --- a/src/ui/oled/oled.h +++ b/src/ui/oled/oled.h @@ -71,19 +71,14 @@ void oled_init(void); /** - * Prints the frame buffer to the screen. - */ -void oled_send_buffer(void); - -/** - * Clears the frame buffer. + * Sets displayed frames rotated by 180 degrees. */ -void oled_clear_buffer(void); +void oled_mirror(bool mirror); /** - * Sets displayed frames rotated by 180 degrees. + * Transfer active canvas to the screen */ -void oled_mirror(bool mirror); +void oled_present(void); /** * Turn off oled @@ -91,8 +86,8 @@ void oled_mirror(bool mirror); void oled_off(void); /** - * Set a screen pixel. This fills the frame buffer - * prior to it being sent to the screen by oled_send_buffer(). + * Set a pixel on the "working" canvas arcoding to the screen requirements. The working and active + * canvases are flipped with canvas_commit(); */ void oled_set_pixel(int16_t x, int16_t y, uint8_t c); diff --git a/src/ui/oled/sh1107.c b/src/ui/oled/sh1107.c index 544e672cfe..fa4ed3e48d 100644 --- a/src/ui/oled/sh1107.c +++ b/src/ui/oled/sh1107.c @@ -14,6 +14,7 @@ #include "sh1107.h" #include "oled_writer.h" +#include // Specify the column address of display RAM 0-127 #define SH1107_CMD_SET_LOW_COL(column) (0x00 | ((column) & 0x0F)) @@ -73,11 +74,8 @@ // Double byte command (0x00 to 0x7F) #define SH1107_CMD_SET_DISPLAY_START_LINE 0xDC -static uint8_t* _frame_buffer; - -void sh1107_configure(uint8_t* buf) +void sh1107_configure(void) { - _frame_buffer = buf; oled_writer_write_cmd(SH1107_CMD_SET_DISPLAY_OFF); oled_writer_write_cmd_with_param(SH1107_CMD_SET_CONTRAST_CONTROL, 0xff); oled_writer_write_cmd(SH1107_CMD_SET_VERTICAL_ADDRESSING_MODE); @@ -92,7 +90,7 @@ void sh1107_configure(uint8_t* buf) oled_writer_write_cmd_with_param(SH1107_CMD_SET_VCOMH_DESELECT_LEVEL, 0x35); oled_writer_write_cmd_with_param(0xad, 0x8a); oled_writer_write_cmd(SH1107_CMD_ENTIRE_DISPLAY_AND_GDDRAM_ON); - sh1107_update(); + sh1107_present(); oled_writer_write_cmd(SH1107_CMD_SET_DISPLAY_ON); } @@ -105,21 +103,21 @@ void sh1107_set_pixel(int16_t x, int16_t y, uint8_t c) p = y * 16; p += x / 8; if (c) { - _frame_buffer[p] |= 1 << (x % 8); + canvas_working()[p] |= 1 << (x % 8); } else { - _frame_buffer[p] &= ~(1 << (x % 8)); + canvas_working()[p] &= ~(1 << (x % 8)); } } /* The SH1107 Segment/Common driver specifies that there are 16 pages per column * In total we should be writing 64*128 pixels. 8 bits per page, 16 pages per column and 64 * columns */ -void sh1107_update(void) +void sh1107_present(void) { for (size_t i = 0; i < 64; i++) { oled_writer_write_cmd(SH1107_CMD_SET_LOW_COL(i)); oled_writer_write_cmd(SH1107_CMD_SET_HIGH_COL(i)); - oled_writer_write_data(&_frame_buffer[i * 16], 16); + oled_writer_write_data(&canvas_active()[i * 16], 16); } } diff --git a/src/ui/oled/sh1107.h b/src/ui/oled/sh1107.h index f6f0c42ea7..a6768e971d 100644 --- a/src/ui/oled/sh1107.h +++ b/src/ui/oled/sh1107.h @@ -22,10 +22,10 @@ /* * The sh1107 driver will store this pointer and later use it for "set_pixel" and "update". */ -void sh1107_configure(uint8_t* buf); +void sh1107_configure(void); void sh1107_set_pixel(int16_t x, int16_t y, uint8_t c); -void sh1107_update(void); +void sh1107_present(void); void sh1107_mirror(bool mirror); void sh1107_off(void); diff --git a/src/ui/oled/ssd1312.c b/src/ui/oled/ssd1312.c index 3d11f3ef7a..6b6a21e872 100644 --- a/src/ui/oled/ssd1312.c +++ b/src/ui/oled/ssd1312.c @@ -15,6 +15,7 @@ #include "ssd1312.h" #include "oled_writer.h" #include +#include #define SSD1312_CMD_SET_LOW_COL(column) (0x00 | ((column) & 0x0F)) #define SSD1312_CMD_SET_HIGH_COL(column) (0x10 | (((column) >> 4) & 0x07)) @@ -78,11 +79,8 @@ // Double byte command #define SSD1312_CMD_SET_CHARGE_PUMP_SETTING 0x8D -static uint8_t* _frame_buffer; - -void ssd1312_configure(uint8_t* buf) +void ssd1312_configure(void) { - _frame_buffer = buf; oled_writer_write_cmd(SSD1312_CMD_SET_LOW_COL(0)); oled_writer_write_cmd(SSD1312_CMD_SET_HIGH_COL(0)); oled_writer_write_cmd(SSD1312_CMD_SET_DISPLAY_OFF); @@ -98,7 +96,7 @@ void ssd1312_configure(uint8_t* buf) oled_writer_write_cmd_with_param(SSD1312_CMD_SET_VCOMH_SELECT_LEVEL, 0x35); oled_writer_write_cmd_with_param(SSD1312_CMD_SET_IREF, 0x40); oled_writer_write_cmd(SSD1312_CMD_ENTIRE_DISPLAY_AND_GDDRAM_ON); - ssd1312_update(); + ssd1312_present(); oled_writer_write_cmd(SSD1312_CMD_SET_DISPLAY_ON); } @@ -110,12 +108,12 @@ void ssd1312_set_pixel(int16_t x, int16_t y, uint8_t c) p = (y / 8) * 128; p += x; if (c) { - _frame_buffer[p] |= 1 << (y % 8); + canvas_working()[p] |= 1 << (y % 8); } else { - _frame_buffer[p] &= ~(1 << (y % 8)); + canvas_working()[p] &= ~(1 << (y % 8)); } } -void ssd1312_update(void) +void ssd1312_present(void) { /* The SSD1312 has one page per 8 rows. One page is 128 bytes. Every byte is 8 rows */ for (size_t i = 0; i < 64 / 8; i++) { @@ -126,7 +124,7 @@ void ssd1312_update(void) // address to be correct if all bytes arrive at the screen. oled_writer_write_cmd(SSD1312_CMD_SET_LOW_COL(0)); oled_writer_write_cmd(SSD1312_CMD_SET_HIGH_COL(0)); - oled_writer_write_data(&_frame_buffer[i * 128], 128); + oled_writer_write_data(&canvas_active()[i * 128], 128); } } diff --git a/src/ui/oled/ssd1312.h b/src/ui/oled/ssd1312.h index 2a0318ef52..b73a1ea59c 100644 --- a/src/ui/oled/ssd1312.h +++ b/src/ui/oled/ssd1312.h @@ -21,10 +21,10 @@ /* * The ssd1312 driver will store this pointer and later use it for "set_pixel" and "update". */ -void ssd1312_configure(uint8_t* buf); +void ssd1312_configure(void); void ssd1312_set_pixel(int16_t x, int16_t y, uint8_t c); -void ssd1312_update(void); +void ssd1312_present(void); void ssd1312_mirror(bool mirror); void ssd1312_off(void); diff --git a/src/ui/screen_process.c b/src/ui/screen_process.c index 9e84a09443..cb90fa7b47 100644 --- a/src/ui/screen_process.c +++ b/src/ui/screen_process.c @@ -16,7 +16,9 @@ #include "screen_stack.h" #include #include +#include #include +#include #include #include #include @@ -25,11 +27,11 @@ static uint8_t screen_frame_cnt = 0; void ui_screen_render_component(component_t* component) { - screen_clear(); component->position.left = 0; component->position.top = 0; component->f->render(component); - UG_SendBuffer(); + canvas_commit(); + oled_present(); } static component_t* _get_waiting_screen(void) diff --git a/src/ui/ugui/ugui.c b/src/ui/ugui/ugui.c index a8f3ed140a..5b6e40c827 100644 --- a/src/ui/ugui/ugui.c +++ b/src/ui/ugui/ugui.c @@ -852,15 +852,3 @@ void UG_FontSetVSpace( UG_U16 s ) gui->char_v_space = s; } } - -void UG_SendBuffer(void) { -#ifndef TESTING - oled_send_buffer(); -#endif -} - -void UG_ClearBuffer(void) { -#ifndef TESTING - oled_clear_buffer(); -#endif -} diff --git a/src/ui/ugui/ugui.h b/src/ui/ugui/ugui.h index e64074c58d..24ef847ac2 100644 --- a/src/ui/ugui/ugui.h +++ b/src/ui/ugui/ugui.h @@ -135,8 +135,4 @@ UG_S16 UG_GetYDim( void ); void UG_FontSetHSpace( UG_U16 s ); void UG_FontSetVSpace( UG_U16 s ); -/* ssd1306.h wrapper */ -void UG_SendBuffer(void); -void UG_ClearBuffer(void); - #endif diff --git a/test/hardware-fakes/src/fake_oled.c b/test/hardware-fakes/src/fake_oled.c new file mode 100644 index 0000000000..dbbc1bfb55 --- /dev/null +++ b/test/hardware-fakes/src/fake_oled.c @@ -0,0 +1,15 @@ +// Copyright 2025 Shift Crypto AG +// +// 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. + +void oled_present(void) {} diff --git a/test/hardware-fakes/src/fake_screen.c b/test/hardware-fakes/src/fake_screen.c index 60e9f409cc..db368def72 100644 --- a/test/hardware-fakes/src/fake_screen.c +++ b/test/hardware-fakes/src/fake_screen.c @@ -43,5 +43,3 @@ bool screen_is_upside_down(void) { return false; } - -void screen_clear(void) {}