From 5276f48b6d1803b2a9b43f4db3ba8d75e63709db Mon Sep 17 00:00:00 2001 From: cuu Date: Sun, 25 May 2025 15:40:46 +0800 Subject: [PATCH] update multi booter switch order of picomite and sd_boot to make more space for progs of multi booter bin folder name changed to firmware ui interface improvments --- Code/pico_multi_booter/CMakeLists.txt | 14 +-- Code/pico_multi_booter/README.md | 10 +- Code/pico_multi_booter/boot.c | 4 +- .../memmap_default.ld.mp.rp2040 | 2 +- .../linker_scripts/memmap_mp_rp2040.ld | 2 +- .../linker_scripts/rpi-pico.ld | 2 +- .../memmap_default_rp2040.ld | 2 +- .../memmap_default_rp2350.ld | 2 +- Code/pico_multi_booter/sd_boot/config.h | 2 +- Code/pico_multi_booter/sd_boot/main.c | 24 +++-- .../sd_boot/text_directory_ui.c | 96 ++++++++++++------- 11 files changed, 102 insertions(+), 58 deletions(-) diff --git a/Code/pico_multi_booter/CMakeLists.txt b/Code/pico_multi_booter/CMakeLists.txt index 492e286..b6bc14c 100644 --- a/Code/pico_multi_booter/CMakeLists.txt +++ b/Code/pico_multi_booter/CMakeLists.txt @@ -72,19 +72,19 @@ add_custom_target(BUILT_${BOOT} add_dependencies(BUILT_boot boot) -add_dependencies(PREPARE_picomite BUILT_boot) -add_dependencies(picomite PREPARE_picomite) -add_dependencies(BUILT_picomite picomite) - -add_dependencies(PREPARE_sd_boot BUILT_picomite) +add_dependencies(PREPARE_sd_boot BUILT_boot) add_dependencies(sd_boot PREPARE_sd_boot) add_dependencies(BUILT_sd_boot sd_boot) +add_dependencies(PREPARE_picomite BUILT_sd_boot) +add_dependencies(picomite PREPARE_picomite) +add_dependencies(BUILT_picomite picomite) + # *************************************************************************** # * Join the BOOT and all APP '.uf2' files together * # *************************************************************************** -set(UF2S boot.uf2 picomite.uf2 sd_boot.uf2) +set(UF2S boot.uf2 sd_boot.uf2 picomite.uf2) add_custom_target(JOIN COMMENT "Combine the '.uf2' files" @@ -95,7 +95,7 @@ add_custom_target(JOIN ${UF2S} ) -add_dependencies(JOIN BUILT_sd_boot) +add_dependencies(JOIN BUILT_picomite) add_custom_target(${PROJECT} ALL DEPENDS JOIN) diff --git a/Code/pico_multi_booter/README.md b/Code/pico_multi_booter/README.md index 2e8ea11..c67605e 100644 --- a/Code/pico_multi_booter/README.md +++ b/Code/pico_multi_booter/README.md @@ -3,8 +3,8 @@ Here is a bootloader for PicoCalc combined slightly modified [PicoMite](https://github.com/madcock/PicoMiteAllVersions) and [SD boot](https://github.com/adwuard/Picocalc_SD_Boot) - Pico1 -- No sdcard inserted ,PicoMite will show up. -- Sdcard inserted, SD boot menu will show up, load third pico app bin to run at FLASH TARGET OFFSET 2048k-940k +- No sdcard inserted,load default app to run from flash. +- Sdcard inserted, SD boot menu will show up, load third pico app bin to run at FLASH TARGET OFFSET 2048k-152k ## How to compile ``` @@ -29,13 +29,13 @@ configuration.h config.h ``` -#define SD_BOOT_FLASH_OFFSET (940 * 1024) +#define SD_BOOT_FLASH_OFFSET (152 * 1024) ``` ### SD Card Application Build and Deployment **Important Note:** ``` -Applications intended for SD card boot "MUST REBUILD" using a custom linker script to accommodate the program's offset(940k) address. +Applications intended for SD card boot "MUST REBUILD" using a custom linker script to accommodate the program's offset(152k) address. Applications intended for SD card boot is in **bin** format, not uf2. @@ -81,7 +81,7 @@ make ``` #### Step 3 Your Custom Application Is Ready For SD Card Boot -Once the build is complete, copy the generated `.bin` file to the `/sd` directory of the SD card. +Once the build is complete, copy the generated `.bin` file to the `/firmware` directory of the SD card. diff --git a/Code/pico_multi_booter/boot.c b/Code/pico_multi_booter/boot.c index 03aff41..45dca49 100644 --- a/Code/pico_multi_booter/boot.c +++ b/Code/pico_multi_booter/boot.c @@ -150,6 +150,7 @@ int main(void) { printf("max %d \n",max); // Choose an app to launch + /* int chosen; if(!sd_card_inserted()){ printf("No sd card\n"); @@ -159,7 +160,8 @@ int main(void) { chosen = max-1; if(chosen <0 ) chosen = 0; } - + */ + int chosen = 0; // Get start address of app addr = PEEK32( info + ((chosen * 16))); printf("Application at %08lX\n", addr); diff --git a/Code/pico_multi_booter/linker_scripts/memmap_default.ld.mp.rp2040 b/Code/pico_multi_booter/linker_scripts/memmap_default.ld.mp.rp2040 index e0c639b..25817b3 100644 --- a/Code/pico_multi_booter/linker_scripts/memmap_default.ld.mp.rp2040 +++ b/Code/pico_multi_booter/linker_scripts/memmap_default.ld.mp.rp2040 @@ -1,7 +1,7 @@ /*arduino-pico*/ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = __FLASH_LENGTH__ - 940k + FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = __FLASH_LENGTH__ - 152k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = __RAM_LENGTH__ SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/Code/pico_multi_booter/linker_scripts/memmap_mp_rp2040.ld b/Code/pico_multi_booter/linker_scripts/memmap_mp_rp2040.ld index aca0f98..c453adb 100644 --- a/Code/pico_multi_booter/linker_scripts/memmap_mp_rp2040.ld +++ b/Code/pico_multi_booter/linker_scripts/memmap_mp_rp2040.ld @@ -24,7 +24,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k + FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/Code/pico_multi_booter/linker_scripts/rpi-pico.ld b/Code/pico_multi_booter/linker_scripts/rpi-pico.ld index 21c88f4..c8da485 100644 --- a/Code/pico_multi_booter/linker_scripts/rpi-pico.ld +++ b/Code/pico_multi_booter/linker_scripts/rpi-pico.ld @@ -4,7 +4,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k + FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/Code/pico_multi_booter/memmap_default_rp2040.ld b/Code/pico_multi_booter/memmap_default_rp2040.ld index fc8f9fa..3c5ab0c 100644 --- a/Code/pico_multi_booter/memmap_default_rp2040.ld +++ b/Code/pico_multi_booter/memmap_default_rp2040.ld @@ -1,6 +1,6 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k + FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/Code/pico_multi_booter/memmap_default_rp2350.ld b/Code/pico_multi_booter/memmap_default_rp2350.ld index b998198..6f8d4c4 100644 --- a/Code/pico_multi_booter/memmap_default_rp2350.ld +++ b/Code/pico_multi_booter/memmap_default_rp2350.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 4096k - 940k + FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 4096k - 152k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k diff --git a/Code/pico_multi_booter/sd_boot/config.h b/Code/pico_multi_booter/sd_boot/config.h index 849f548..16cd24c 100644 --- a/Code/pico_multi_booter/sd_boot/config.h +++ b/Code/pico_multi_booter/sd_boot/config.h @@ -36,7 +36,7 @@ // According to the applink.map ,with combined PicoMite, here is 920k // This offset is used to ensure that the bootloader does not get overwritten // when loading a new application from the SD card -#define SD_BOOT_FLASH_OFFSET (940 * 1024) +#define SD_BOOT_FLASH_OFFSET (152 * 1024) // Maximum size of the application that can be loaded // This ensures we don't overwrite the bootloader itself diff --git a/Code/pico_multi_booter/sd_boot/main.c b/Code/pico_multi_booter/sd_boot/main.c index 4ece188..8d4fe50 100644 --- a/Code/pico_multi_booter/sd_boot/main.c +++ b/Code/pico_multi_booter/sd_boot/main.c @@ -207,15 +207,18 @@ int load_firmware_by_path(const char *path) // Attempt to load the application from the SD card // bool load_success = load_program(FIRMWARE_PATH); - bool load_success = load_program(path); + bool load_success=false; // Get the pointer to the application flash area uint32_t *app_location = (uint32_t *)(XIP_BASE + SD_BOOT_FLASH_OFFSET); - // Check if there is an already valid application in flash bool has_valid_app = is_valid_application(app_location); - + if(path == NULL) { + load_success = true; + }else{ + load_success = load_program(path); + } if (load_success || has_valid_app) { @@ -245,11 +248,21 @@ int load_firmware_by_path(const char *path) void final_selection_callback(const char *path) { + char status_message[128]; + const char *extension = ".bin"; + if(path == NULL) { + //load default app from flash + + snprintf(status_message, sizeof(status_message), "SEL: %s", "FLASH+152k"); + text_directory_ui_set_status(status_message); + sleep_ms(200); + load_firmware_by_path(path); + return; + } // Trigger firmware loading with the selected path DEBUG_PRINT("selected: %s\n", path); - char status_message[128]; - const char *extension = ".bin"; + size_t path_len = strlen(path); size_t ext_len = strlen(extension); @@ -286,7 +299,6 @@ int main() keypad_init(); lcd_init(); lcd_clear(); - text_directory_ui_init(); // Check for SD card presence DEBUG_PRINT("Checking for SD card...\n"); diff --git a/Code/pico_multi_booter/sd_boot/text_directory_ui.c b/Code/pico_multi_booter/sd_boot/text_directory_ui.c index f07c2e8..ecffa02 100644 --- a/Code/pico_multi_booter/sd_boot/text_directory_ui.c +++ b/Code/pico_multi_booter/sd_boot/text_directory_ui.c @@ -54,11 +54,14 @@ extern bool fs_init(void); // Maximum number of directory entries #define MAX_ENTRIES 128 +#define IS_DIR 1 +#define IS_FILE 0 +#define IS_LAST_APP 2 // Data structure for directory entries typedef struct { char name[256]; - int is_dir; // 1 if directory, 0 if file + int is_dir; // 1 if directory, 0 if file 2 == last run off_t file_size; // Size of the file in bytes } dir_entry_t; @@ -72,12 +75,11 @@ typedef struct #define SCROLL_DELAY_MS 300 // Global variables for UI state -static char current_path[512] = "/sd"; // Current directory path +static char current_path[512] = "/firmware"; // Current directory path static dir_entry_t entries[MAX_ENTRIES]; // Directory entries static int entry_count = 0; // Number of entries in the current directory static int selected_index = 0; // Currently selected entry index static char status_message[256] = ""; // Status message -static uint32_t status_timestamp = 0; // Timestamp for status message static final_selection_callback_t final_callback = NULL; // Callback for file selection static uint32_t last_scrolling = 0; // for text scrolling in selected entry // Forward declarations @@ -90,6 +92,7 @@ static void ui_draw_directory_list(void); static void ui_draw_directory_entry(int entry_idx, int posY, int font_height, int is_selected); static void ui_update_selected_entry(void); static void ui_draw_status_bar(void); +static void ui_draw_empty_tip(void); static void format_file_size(off_t size, int is_dir, char *buf, size_t buf_size); static void get_scrolling_text(const char *text, char *out, size_t out_size, int visible_chars); @@ -112,7 +115,7 @@ static void draw_text(int x, int y, const char *text, int foreground, int backgr */ static void format_file_size(off_t size, int is_dir, char *buf, size_t buf_size) { - if (is_dir) + if (is_dir == IS_DIR) { snprintf(buf, buf_size, "DIR"); } @@ -168,6 +171,12 @@ static void load_directory(const char *path) return; } entry_count = 0; + strncpy(entries[entry_count].name, "[Last app]", sizeof(entries[entry_count].name) - 1); + entries[entry_count].name[sizeof(entries[entry_count].name) - 1] = '\0'; + entries[0].is_dir = IS_LAST_APP; + entries[0].file_size = 0; + + entry_count = 1; struct dirent *ent; while ((ent = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) { @@ -185,7 +194,7 @@ static void load_directory(const char *path) // Determine if the entry is a directory and get file size if (ent->d_type != DT_UNKNOWN) { - entries[entry_count].is_dir = (ent->d_type == DT_DIR) ? 1 : 0; + entries[entry_count].is_dir = (ent->d_type == DT_DIR) ? IS_DIR : IS_FILE; // Get file size using stat even if we know the type from d_type struct stat statbuf; @@ -216,9 +225,6 @@ static void load_directory(const char *path) } closedir(dir); selected_index = 0; - if(entry_count == 0) { - text_directory_ui_set_status("No firmware found."); - } } @@ -229,6 +235,28 @@ static void ui_draw_title(void) draw_text(UI_X + 2, UI_Y + 2, "PicoCalc SD Firmware Loader", WHITE, BLACK); } +static void ui_draw_empty_tip(){ + + + int y = UI_Y + UI_HEIGHT/2;//center + int y_start = UI_Y + HEADER_TITLE_HEIGHT + PATH_HEADER_HEIGHT; + draw_rect_spi(UI_X, y_start, UI_X + UI_WIDTH - 1, UI_Y + UI_HEIGHT - STATUS_BAR_HEIGHT - 1, COLOR_BG); + + //draw_rect_spi(UI_X, UI_Y + HEADER_TITLE_HEIGHT+1, UI_X + UI_WIDTH - 1, UI_Y + UI_HEIGHT - 2, COLOR_BG); + + draw_text(UI_X + 2, y + 2, "No .bin files in \"firmware\" folder" , COLOR_FG, COLOR_BG); + draw_text(UI_X + 2, y + 12+2, "Please copy .bin files to the" , COLOR_FG, COLOR_BG); + draw_text(UI_X + 2, y + 24+2, "\"firmware\" folder" , COLOR_FG, COLOR_BG); + + + strncpy(entries[entry_count].name, "Load default", sizeof(entries[entry_count].name) - 1); + entries[entry_count].name[sizeof(entries[entry_count].name) - 1] = '\0'; + entries[0].is_dir = IS_LAST_APP; + entries[0].file_size = 0; + // Draw the entry using the helper function + ui_draw_directory_entry(0, y_start, 12, 1); +} + // Draw the current path header static void ui_draw_path_header(void) { @@ -260,7 +288,7 @@ static void ui_draw_directory_entry(int entry_idx, int posY, int font_height, in char full_file_name[300]; snprintf(full_file_name, sizeof(full_file_name), "%s%s", entries[entry_idx].name, - entries[entry_idx].is_dir ? "/" : ""); + (entries[entry_idx].is_dir == IS_DIR) ? "/" : ""); // Prepare display text with scrolling for selected items char display_buffer[300]; @@ -326,6 +354,7 @@ static void ui_update_selected_entry(void) // Draw the directory list static void ui_draw_directory_list(void) { + if(entry_count <=0 ) return; const int font_height = 12; const int entry_padding = 2; int y_start = UI_Y + HEADER_TITLE_HEIGHT + PATH_HEADER_HEIGHT; @@ -366,12 +395,10 @@ static void ui_refresh(void) ui_draw_path_header(); ui_draw_directory_list(); ui_draw_status_bar(); - - if (status_message[0] != '\0' && ((time_us_64() / 1000) - status_timestamp) > 3000) - { - status_message[0] = '\0'; - ui_draw_status_bar(); - } + if(entry_count == 0) { + ui_draw_empty_tip(); + } + } // Handle key events for navigation and selection @@ -383,40 +410,50 @@ static void process_key_event(int key) if (selected_index > 0) selected_index--; ui_draw_directory_list(); + text_directory_ui_set_status(""); break; case KEY_ARROW_DOWN: if (selected_index < entry_count - 1) selected_index++; ui_draw_directory_list(); + text_directory_ui_set_status(""); break; case KEY_ENTER: + if(entry_count == 0) { + //directly load app from flash + final_callback(NULL); + } if (entry_count > 0) { char new_path[512]; - if (entries[selected_index].is_dir) + if (entries[selected_index].is_dir == IS_DIR) { snprintf(new_path, sizeof(new_path), "%s/%s", current_path, entries[selected_index].name); strncpy(current_path, new_path, sizeof(current_path) - 1); load_directory(current_path); ui_draw_path_header(); ui_draw_directory_list(); - } - else if (final_callback) - { - char final_selected[512]; - snprintf(final_selected, sizeof(final_selected), "%s/%s", current_path, entries[selected_index].name); - final_callback(final_selected); + }else if(entries[selected_index].is_dir == IS_LAST_APP){ + if(final_callback){ + final_callback(NULL); + } + }else { + if (final_callback){ + char final_selected[512]; + snprintf(final_selected, sizeof(final_selected), "%s/%s", current_path, entries[selected_index].name); + final_callback(final_selected); + } } } break; case KEY_BACKSPACE: - if (strcmp(current_path, "/sd") != 0) + if (strcmp(current_path, "/firmware") != 0) { char *last_slash = strrchr(current_path, '/'); if (last_slash) *last_slash = '\0'; if (current_path[0] == '\0') - strncpy(current_path, "/sd", sizeof(current_path) - 1); + strncpy(current_path, "/firmware", sizeof(current_path) - 1); load_directory(current_path); ui_draw_path_header(); ui_draw_directory_list(); @@ -438,8 +475,9 @@ void text_directory_ui_set_final_callback(final_selection_callback_t callback) bool text_directory_ui_init(void) { draw_filled_rect(UI_X, UI_Y, UI_WIDTH, UI_HEIGHT, COLOR_BG); - strncpy(current_path, "/sd", sizeof(current_path)); + strncpy(current_path, "/firmware", sizeof(current_path)); load_directory(current_path); + text_directory_ui_set_status(""); ui_refresh(); last_scrolling = time_us_64()/1000; return true; @@ -450,7 +488,6 @@ void text_directory_ui_set_status(const char *msg) { strncpy(status_message, msg, sizeof(status_message) - 1); status_message[sizeof(status_message) - 1] = '\0'; - status_timestamp = (time_us_64() / 1000); ui_draw_status_bar(); } @@ -480,13 +517,6 @@ void text_directory_ui_run(void) last_scroll_update = current_time; } - // Clear status message after timeout - if (status_message[0] != '\0' && (current_time - status_timestamp) > 3000) - { - status_message[0] = '\0'; - ui_draw_status_bar(); - } - // Check for SD card removal during runtime if (!sd_card_inserted()) { text_directory_ui_set_status("SD card removed. Please reinsert card.");