From 8d0bca691ab8afe29c6dbc6a482891377498c7b9 Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Thu, 29 Jan 2026 15:23:03 -0500 Subject: [PATCH] hardware button support --- CMakeLists.txt | 4 + basic1.cpp | 101 ++++++++++++++++++ board_config.h | 4 +- board_configs/board_pico2_eink.h | 31 +++--- display/low_level_display_epaper.cpp | 72 ++++++++++--- display/low_level_display_epaper.h | 5 +- .../c/lib/Config/DEV_Config.c | 27 +++-- 7 files changed, 204 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 532bbf7..837fe66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ add_executable(basic1 lib/fatfs/source/ff.c lib/fatfs/source/ffsystem.c lib/fatfs/source/ffunicode.c + lib/Pico_ePaper_Code/c/lib/Config/DEV_Config.c + lib/Pico_ePaper_Code/c/lib/e-Paper/EPD_4in2_V2.c ) pico_set_program_name(basic1 "basic1") @@ -78,6 +80,8 @@ target_include_directories(basic1 PRIVATE ${CMAKE_CURRENT_LIST_DIR}/lib/ft6336u ${CMAKE_CURRENT_LIST_DIR}/lib/sd_card ${CMAKE_CURRENT_LIST_DIR}/display + ${CMAKE_CURRENT_LIST_DIR}/lib/Pico_ePaper_Code/c/lib/Config + ${CMAKE_CURRENT_LIST_DIR}/lib/Pico_ePaper_Code/c/lib/e-Paper ) # Add any user requested libraries diff --git a/basic1.cpp b/basic1.cpp index f66db34..02fd31c 100644 --- a/basic1.cpp +++ b/basic1.cpp @@ -17,6 +17,7 @@ #include "display/low_level_render.h" #include "display/low_level_gui.h" #include "display/low_level_display.h" +#include "display/low_level_display_epaper.h" #include "display/low_level_touch.h" @@ -30,6 +31,12 @@ volatile bool touch_interrupt_flag = false; volatile bool touch_event_down = false; LowLevelTouch* touch = nullptr; +// Button interrupt handling +#ifdef BUTTON_KEY0_PIN +volatile bool button_key0_pressed = false; +volatile bool button_key1_pressed = false; +#endif + /** * @brief Touch interrupt callback handler * @@ -86,6 +93,35 @@ void touch_interrupt_handler(uint gpio, uint32_t events) { } } +#ifdef BUTTON_KEY0_PIN +/** + * @brief Button interrupt callback handler + * + * Called automatically by hardware when button pins change state. + * Buttons are active LOW (pressed = 0, released = 1) with pull-ups. + * + * This runs in interrupt context, so keep it fast - just set flags. + * + * @param gpio GPIO pin number that triggered the interrupt + * @param events Event mask (GPIO_IRQ_EDGE_FALL and/or GPIO_IRQ_EDGE_RISE) + */ +void button_interrupt_handler(uint gpio, uint32_t events) { + // Only respond to falling edge (button press) + if (events & GPIO_IRQ_EDGE_FALL) { + if (gpio == BUTTON_KEY0_PIN) { + button_key0_pressed = true; + printf("Button KEY0 pressed\n"); + } +#ifdef BUTTON_KEY1_PIN + else if (gpio == BUTTON_KEY1_PIN) { + button_key1_pressed = true; + printf("Button KEY1 pressed\n"); + } +#endif + } +} +#endif + // Screen dimensions and configuration from board_config.h const int V_WIDTH = DISPLAY_WIDTH; const int V_HEIGHT = DISPLAY_HEIGHT; @@ -139,6 +175,20 @@ int main() return -1; } + // Do a full refresh with white screen first (removes ghosting on e-paper) + printf("Performing initial full refresh to white...\n"); + display->clear(true); // Clear to white + + // For e-paper, do a full refresh to ensure clean display + if (display->get_type() == DISPLAY_TYPE_EPAPER) { + LowLevelDisplayEPaper* epaper = static_cast(display); + epaper->full_refresh(); // Full refresh removes ghosting + printf("Full refresh complete\n"); + } else { + refresh_screen(bit_buffer, display); // For TFT, just refresh normally + } + + // Now clear to black for drawing display->clear(false); // Clear to black LowLevelRenderer renderer(bit_buffer, V_WIDTH, V_HEIGHT); @@ -173,6 +223,36 @@ int main() printf("Touch initialization failed or not configured\n"); } +#ifdef BUTTON_KEY0_PIN + // Initialize hardware buttons (e-ink board only) + printf("\nInitializing hardware buttons...\n"); + + // Initialize KEY0 button + gpio_init(BUTTON_KEY0_PIN); + gpio_set_dir(BUTTON_KEY0_PIN, GPIO_IN); + gpio_pull_up(BUTTON_KEY0_PIN); // Active LOW with pull-up + printf(" KEY0 initialized on GP%d (active LOW)\n", BUTTON_KEY0_PIN); + +#ifdef BUTTON_KEY1_PIN + // Initialize KEY1 button + gpio_init(BUTTON_KEY1_PIN); + gpio_set_dir(BUTTON_KEY1_PIN, GPIO_IN); + gpio_pull_up(BUTTON_KEY1_PIN); // Active LOW with pull-up + printf(" KEY1 initialized on GP%d (active LOW)\n", BUTTON_KEY1_PIN); +#endif + + // Enable interrupts on falling edge (button press) + gpio_set_irq_enabled_with_callback(BUTTON_KEY0_PIN, + GPIO_IRQ_EDGE_FALL, + true, + &button_interrupt_handler); +#ifdef BUTTON_KEY1_PIN + gpio_set_irq_enabled(BUTTON_KEY1_PIN, GPIO_IRQ_EDGE_FALL, true); +#endif + + printf("Button interrupts enabled (falling edge = press)\n"); +#endif + // Test SD card and FatFS // if (sd_card_init_with_board_config()) { // sd_card_test_fatfs(); @@ -203,6 +283,27 @@ int main() // Te(); // Wait For Event - CPU sleeps until interrupt or evenurs __wfi(); // Wait For Interrupt - CPU sleeps until any interrupt +#ifdef BUTTON_KEY0_PIN + // Handle button presses with debouncing + if (button_key0_pressed) { + button_key0_pressed = false; + sleep_ms(50); // Debounce delay + if (gpio_get(BUTTON_KEY0_PIN) == 0) { // Verify button still pressed + printf("Button KEY0 action triggered\n"); + // TODO: Add your KEY0 action here (e.g., focus next widget) + } + } + + if (button_key1_pressed) { + button_key1_pressed = false; + sleep_ms(50); // Debounce delay + if (gpio_get(BUTTON_KEY1_PIN) == 0) { // Verify button still pressed + printf("Button KEY1 action triggered\n"); + // TODO: Add your KEY1 action here (e.g., activate focused widget) + } + } +#endif + // Check if our touch interrupt flag was set if (!touch_interrupt_flag) { continue; // Woken by different interrupt, go back to sleep diff --git a/board_config.h b/board_config.h index 1d2ddd8..b725ad3 100644 --- a/board_config.h +++ b/board_config.h @@ -16,9 +16,9 @@ // ============================================================================ // ---- SELECT YOUR BOARD HERE ---- -#define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796 +// #define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796 // #define BOARD_PICO2_TFT // Pico 2 + 4.0" TFT ST7796 -// #define BOARD_PICO2_EINK // Pico 2 + E-Ink Display +#define BOARD_PICO2_EINK // Pico 2 + E-Ink Display // -------------------------------- // ============================================================================ diff --git a/board_configs/board_pico2_eink.h b/board_configs/board_pico2_eink.h index d83cf0d..2eeed13 100644 --- a/board_configs/board_pico2_eink.h +++ b/board_configs/board_pico2_eink.h @@ -20,33 +20,38 @@ #define TOUCH_INVERT_X false #define TOUCH_INVERT_Y false -// SPI pins for E-Ink display (using SPI0) -#define DISPLAY_SPI_PORT spi0 -#define DISPLAY_SCK_PIN 2 // GP2 -#define DISPLAY_MOSI_PIN 3 // GP3 -#define DISPLAY_MISO_PIN 4 // GP4 (often not used by e-paper) -#define DISPLAY_CS_PIN 5 // GP5 -#define DISPLAY_DC_PIN 6 // GP6 (Data/Command) -#define DISPLAY_RST_PIN 7 // GP7 (Reset) +// SPI pins for E-Ink display (using SPI1) +#define DISPLAY_SPI_PORT spi1 +#define DISPLAY_SCK_PIN 10 // GP10 +#define DISPLAY_MOSI_PIN 11 // GP11 +#define DISPLAY_MISO_PIN 12 // GP12 (often not used by e-paper) +#define DISPLAY_CS_PIN 9 // GP9 +#define DISPLAY_DC_PIN 8 // GP8 (Data/Command) +#define DISPLAY_RST_PIN 12 // GP12 (Reset) #define DISPLAY_BL_PIN 8 // GP8 - Not used for e-ink, but needs valid pin number -#define DISPLAY_BUSY_PIN 9 // GP9 - E-paper BUSY pin (critical!) +#define DISPLAY_BUSY_PIN 13 // GP13 - E-paper BUSY pin (critical!) -// I2C pins for touch (if using touch-enabled e-ink) +// I2C pins for touch (not used - this board has no touch) +// Dummy values to satisfy compilation, but TOUCH_TYPE_NONE means they won't be used #define TOUCH_I2C_PORT i2c0 #define TOUCH_SDA_PIN 12 // GP12 #define TOUCH_SCL_PIN 13 // GP13 #define TOUCH_INT_PIN 14 // GP14 #define TOUCH_RST_PIN 15 // GP15 -// SD card pins (separate SPI1) - optional +// SD card pins (not used - this board has no SD card slot) +// Dummy values to satisfy compilation, but won't be initialized #define SD_SPI_PORT spi1 -#define SD_CS_PIN 17 // GP17 +#define SD_CS_PIN 22 // GP22 - dummy value #define SD_MISO_PIN 16 // GP16 #define SD_MOSI_PIN 19 // GP19 #define SD_SCK_PIN 18 // GP18 +// Button pins (Waveshare e-Paper has 2 user buttons) +#define BUTTON_KEY0_PIN 15 // GP15 - User key 0 +#define BUTTON_KEY1_PIN 17 // GP17 - User key 1 + // Common configuration #define SPI_BAUDRATE (4 * 1000 * 1000) // 4 MHz for e-paper (slower than TFT) -#define I2C_BAUDRATE (400 * 1000) // 400 kHz for touch #endif // BOARD_PICO2_EINK_H diff --git a/display/low_level_display_epaper.cpp b/display/low_level_display_epaper.cpp index 878c911..4020f0c 100644 --- a/display/low_level_display_epaper.cpp +++ b/display/low_level_display_epaper.cpp @@ -3,8 +3,13 @@ #include #include -// Note: This is a placeholder implementation -// You'll need to add the actual e-paper driver code to lib/epaper/ +// Waveshare 4.2" e-Paper monochrome driver (supports partial refresh) +extern "C" { + #include "EPD_4in2_V2.h" + #include "DEV_Config.h" +} + +// Note: EPD_4IN2_V2 is monochrome (black/white) and supports fast partial refresh LowLevelDisplayEPaper::LowLevelDisplayEPaper(const epaper_config* cfg, int w, int h) : config(cfg), width(w), height(h), initialized(false), framebuffer(nullptr), dirty(false) { @@ -33,11 +38,15 @@ bool LowLevelDisplayEPaper::init() { return false; } - // TODO: Implement e-paper initialization - // epaper_init(config, width, height); - printf("E-paper display initialized: %dx%d (stub)\n", width, height); + printf("Initializing Waveshare 4.2\" e-Paper display (%dx%d)...\n", width, height); + + // Initialize Waveshare driver (handles GPIO and SPI setup) + DEV_Module_Init(); + EPD_4IN2_V2_Init(); + + printf("E-paper display initialized successfully (partial refresh enabled)\n"); initialized = true; - return false; // Return false until actual driver is implemented + return true; } void LowLevelDisplayEPaper::clear(bool white) { @@ -79,19 +88,56 @@ void LowLevelDisplayEPaper::refresh() { return; } - // TODO: Implement actual e-paper refresh - // epaper_update(framebuffer); - printf("E-paper refresh (stub)\n"); + if (!initialized) { + printf("E-paper not initialized, cannot refresh\n"); + return; + } + printf("Refreshing e-paper display (partial refresh)...\n"); + + // Use partial refresh for fast updates (~1 second instead of ~15 seconds) + // Partial refresh updates only the changed pixels + EPD_4IN2_V2_PartialDisplay(framebuffer, 0, 0, EPD_4IN2_V2_WIDTH, EPD_4IN2_V2_HEIGHT); + + printf("E-paper partial refresh complete\n"); dirty = false; } void LowLevelDisplayEPaper::clear_display() { - clear(true); // Fill with white - refresh(); + if (!initialized) { + printf("E-paper not initialized, cannot clear\n"); + return; + } + + printf("Clearing e-paper display...\n"); + EPD_4IN2_V2_Clear(); + + // Also clear framebuffer + clear(true); + dirty = false; // Already refreshed by EPD_4IN2_V2_Clear +} + +void LowLevelDisplayEPaper::full_refresh() { + if (!framebuffer || !initialized) { + printf("E-paper not initialized or no framebuffer\n"); + return; + } + + printf("Performing full refresh (removes ghosting, ~15 seconds)...\n"); + + // Full refresh uses EPD_4IN2_V2_Display for complete screen update + // This is slower but removes any ghosting from partial refreshes + EPD_4IN2_V2_Display(framebuffer); + + printf("Full refresh complete\n"); + dirty = false; } void LowLevelDisplayEPaper::sleep() { - // TODO: Implement e-paper sleep mode - // epaper_sleep(); + if (!initialized) { + return; + } + + printf("Putting e-paper display to sleep...\n"); + EPD_4IN2_V2_Sleep(); } diff --git a/display/low_level_display_epaper.h b/display/low_level_display_epaper.h index 3146eb9..f619428 100644 --- a/display/low_level_display_epaper.h +++ b/display/low_level_display_epaper.h @@ -41,8 +41,9 @@ public: bool is_color() const override { return false; } // Most e-paper is monochrome // E-paper specific - void clear_display(); - void sleep(); // Put display in low power mode + void clear_display(); // Full clear with refresh + void full_refresh(); // Force full screen refresh (slower but removes ghosting) + void sleep(); // Put display in low power mode }; #endif // LOW_LEVEL_DISPLAY_EPAPER_H diff --git a/lib/Pico_ePaper_Code/c/lib/Config/DEV_Config.c b/lib/Pico_ePaper_Code/c/lib/Config/DEV_Config.c index 9f27b93..bd904fc 100644 --- a/lib/Pico_ePaper_Code/c/lib/Config/DEV_Config.c +++ b/lib/Pico_ePaper_Code/c/lib/Config/DEV_Config.c @@ -28,8 +28,10 @@ # ******************************************************************************/ #include "DEV_Config.h" +#include "board_config.h" // Use our board configuration -#define SPI_PORT spi1 +// Use SPI port from board config +#define SPI_PORT DISPLAY_SPI_PORT /** * GPIO @@ -90,14 +92,16 @@ void DEV_Delay_ms(UDOUBLE xms) void DEV_GPIO_Init(void) { + // Use pin definitions from board_config.h + EPD_RST_PIN = DISPLAY_RST_PIN; + EPD_DC_PIN = DISPLAY_DC_PIN; + EPD_BUSY_PIN = DISPLAY_BUSY_PIN; + EPD_CS_PIN = DISPLAY_CS_PIN; + EPD_CLK_PIN = DISPLAY_SCK_PIN; + EPD_MOSI_PIN = DISPLAY_MOSI_PIN; - EPD_RST_PIN = 12; - EPD_DC_PIN = 8; - EPD_BUSY_PIN = 13; - - EPD_CS_PIN = 9; - EPD_CLK_PIN = 10; - EPD_MOSI_PIN = 11; + printf("E-Paper pins: RST=%d, DC=%d, CS=%d, BUSY=%d, CLK=%d, MOSI=%d\r\n", + EPD_RST_PIN, EPD_DC_PIN, EPD_CS_PIN, EPD_BUSY_PIN, EPD_CLK_PIN, EPD_MOSI_PIN); DEV_GPIO_Mode(EPD_RST_PIN, 1); DEV_GPIO_Mode(EPD_DC_PIN, 1); @@ -118,9 +122,12 @@ UBYTE DEV_Module_Init(void) // GPIO Config DEV_GPIO_Init(); + // Initialize SPI at 4MHz spi_init(SPI_PORT, 4000 * 1000); - gpio_set_function(EPD_CLK_PIN, GPIO_OUT); - gpio_set_function(EPD_MOSI_PIN, GPIO_OUT); + + // Configure SPI pins to use hardware SPI function + gpio_set_function(EPD_CLK_PIN, GPIO_FUNC_SPI); + gpio_set_function(EPD_MOSI_PIN, GPIO_FUNC_SPI); printf("DEV_Module_Init OK \r\n"); return 0;