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
This commit is contained in:
cuu 2025-05-25 15:40:46 +08:00
parent 3009ce75ba
commit 5276f48b6d
11 changed files with 102 additions and 58 deletions

View File

@ -72,19 +72,19 @@ add_custom_target(BUILT_${BOOT}
add_dependencies(BUILT_boot boot) add_dependencies(BUILT_boot boot)
add_dependencies(PREPARE_picomite BUILT_boot) add_dependencies(PREPARE_sd_boot BUILT_boot)
add_dependencies(picomite PREPARE_picomite)
add_dependencies(BUILT_picomite picomite)
add_dependencies(PREPARE_sd_boot BUILT_picomite)
add_dependencies(sd_boot PREPARE_sd_boot) add_dependencies(sd_boot PREPARE_sd_boot)
add_dependencies(BUILT_sd_boot 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 * # * 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 add_custom_target(JOIN
COMMENT "Combine the '.uf2' files" COMMENT "Combine the '.uf2' files"
@ -95,7 +95,7 @@ add_custom_target(JOIN
${UF2S} ${UF2S}
) )
add_dependencies(JOIN BUILT_sd_boot) add_dependencies(JOIN BUILT_picomite)
add_custom_target(${PROJECT} ALL DEPENDS JOIN) add_custom_target(${PROJECT} ALL DEPENDS JOIN)

View File

@ -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) 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 - Pico1
- No sdcard inserted ,PicoMite will show up. - 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-940k - Sdcard inserted, SD boot menu will show up, load third pico app bin to run at FLASH TARGET OFFSET 2048k-152k
## How to compile ## How to compile
``` ```
@ -29,13 +29,13 @@ configuration.h
config.h config.h
``` ```
#define SD_BOOT_FLASH_OFFSET (940 * 1024) #define SD_BOOT_FLASH_OFFSET (152 * 1024)
``` ```
### SD Card Application Build and Deployment ### SD Card Application Build and Deployment
**Important Note:** **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. 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 #### 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.

View File

@ -150,6 +150,7 @@ int main(void) {
printf("max %d \n",max); printf("max %d \n",max);
// Choose an app to launch // Choose an app to launch
/*
int chosen; int chosen;
if(!sd_card_inserted()){ if(!sd_card_inserted()){
printf("No sd card\n"); printf("No sd card\n");
@ -159,7 +160,8 @@ int main(void) {
chosen = max-1; chosen = max-1;
if(chosen <0 ) chosen = 0; if(chosen <0 ) chosen = 0;
} }
*/
int chosen = 0;
// Get start address of app // Get start address of app
addr = PEEK32( info + ((chosen * 16))); addr = PEEK32( info + ((chosen * 16)));
printf("Application at %08lX\n", addr); printf("Application at %08lX\n", addr);

View File

@ -1,7 +1,7 @@
/*arduino-pico*/ /*arduino-pico*/
MEMORY 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__ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = __RAM_LENGTH__
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

View File

@ -24,7 +24,7 @@
MEMORY MEMORY
{ {
FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

View File

@ -4,7 +4,7 @@
MEMORY MEMORY
{ {
FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

View File

@ -1,6 +1,6 @@
MEMORY MEMORY
{ {
FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 2048k - 940k FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 2048k - 152k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k

View File

@ -23,7 +23,7 @@
MEMORY MEMORY
{ {
FLASH(rx) : ORIGIN = 0x10000000 + 940k, LENGTH = 4096k - 940k FLASH(rx) : ORIGIN = 0x10000000 + 152k, LENGTH = 4096k - 152k
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k
SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k

View File

@ -36,7 +36,7 @@
// According to the applink.map ,with combined PicoMite, here is 920k // According to the applink.map ,with combined PicoMite, here is 920k
// This offset is used to ensure that the bootloader does not get overwritten // This offset is used to ensure that the bootloader does not get overwritten
// when loading a new application from the SD card // 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 // Maximum size of the application that can be loaded
// This ensures we don't overwrite the bootloader itself // This ensures we don't overwrite the bootloader itself

View File

@ -207,15 +207,18 @@ int load_firmware_by_path(const char *path)
// Attempt to load the application from the SD card // Attempt to load the application from the SD card
// bool load_success = load_program(FIRMWARE_PATH); // 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 // Get the pointer to the application flash area
uint32_t *app_location = (uint32_t *)(XIP_BASE + SD_BOOT_FLASH_OFFSET); uint32_t *app_location = (uint32_t *)(XIP_BASE + SD_BOOT_FLASH_OFFSET);
// Check if there is an already valid application in flash // Check if there is an already valid application in flash
bool has_valid_app = is_valid_application(app_location); 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) 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) 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 // Trigger firmware loading with the selected path
DEBUG_PRINT("selected: %s\n", path); DEBUG_PRINT("selected: %s\n", path);
char status_message[128];
const char *extension = ".bin";
size_t path_len = strlen(path); size_t path_len = strlen(path);
size_t ext_len = strlen(extension); size_t ext_len = strlen(extension);
@ -286,7 +299,6 @@ int main()
keypad_init(); keypad_init();
lcd_init(); lcd_init();
lcd_clear(); lcd_clear();
text_directory_ui_init();
// Check for SD card presence // Check for SD card presence
DEBUG_PRINT("Checking for SD card...\n"); DEBUG_PRINT("Checking for SD card...\n");

View File

@ -54,11 +54,14 @@ extern bool fs_init(void);
// Maximum number of directory entries // Maximum number of directory entries
#define MAX_ENTRIES 128 #define MAX_ENTRIES 128
#define IS_DIR 1
#define IS_FILE 0
#define IS_LAST_APP 2
// Data structure for directory entries // Data structure for directory entries
typedef struct typedef struct
{ {
char name[256]; 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 off_t file_size; // Size of the file in bytes
} dir_entry_t; } dir_entry_t;
@ -72,12 +75,11 @@ typedef struct
#define SCROLL_DELAY_MS 300 #define SCROLL_DELAY_MS 300
// Global variables for UI state // 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 dir_entry_t entries[MAX_ENTRIES]; // Directory entries
static int entry_count = 0; // Number of entries in the current directory static int entry_count = 0; // Number of entries in the current directory
static int selected_index = 0; // Currently selected entry index static int selected_index = 0; // Currently selected entry index
static char status_message[256] = ""; // Status message 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 final_selection_callback_t final_callback = NULL; // Callback for file selection
static uint32_t last_scrolling = 0; // for text scrolling in selected entry static uint32_t last_scrolling = 0; // for text scrolling in selected entry
// Forward declarations // 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_draw_directory_entry(int entry_idx, int posY, int font_height, int is_selected);
static void ui_update_selected_entry(void); static void ui_update_selected_entry(void);
static void ui_draw_status_bar(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 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); 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) 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"); snprintf(buf, buf_size, "DIR");
} }
@ -168,6 +171,12 @@ static void load_directory(const char *path)
return; return;
} }
entry_count = 0; 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; struct dirent *ent;
while ((ent = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) 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 // Determine if the entry is a directory and get file size
if (ent->d_type != DT_UNKNOWN) 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 // Get file size using stat even if we know the type from d_type
struct stat statbuf; struct stat statbuf;
@ -216,9 +225,6 @@ static void load_directory(const char *path)
} }
closedir(dir); closedir(dir);
selected_index = 0; 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); 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 // Draw the current path header
static void ui_draw_path_header(void) 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]; char full_file_name[300];
snprintf(full_file_name, sizeof(full_file_name), "%s%s", snprintf(full_file_name, sizeof(full_file_name), "%s%s",
entries[entry_idx].name, entries[entry_idx].name,
entries[entry_idx].is_dir ? "/" : ""); (entries[entry_idx].is_dir == IS_DIR) ? "/" : "");
// Prepare display text with scrolling for selected items // Prepare display text with scrolling for selected items
char display_buffer[300]; char display_buffer[300];
@ -326,6 +354,7 @@ static void ui_update_selected_entry(void)
// Draw the directory list // Draw the directory list
static void ui_draw_directory_list(void) static void ui_draw_directory_list(void)
{ {
if(entry_count <=0 ) return;
const int font_height = 12; const int font_height = 12;
const int entry_padding = 2; const int entry_padding = 2;
int y_start = UI_Y + HEADER_TITLE_HEIGHT + PATH_HEADER_HEIGHT; 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_path_header();
ui_draw_directory_list(); ui_draw_directory_list();
ui_draw_status_bar(); ui_draw_status_bar();
if(entry_count == 0) {
ui_draw_empty_tip();
}
if (status_message[0] != '\0' && ((time_us_64() / 1000) - status_timestamp) > 3000)
{
status_message[0] = '\0';
ui_draw_status_bar();
}
} }
// Handle key events for navigation and selection // Handle key events for navigation and selection
@ -383,40 +410,50 @@ static void process_key_event(int key)
if (selected_index > 0) if (selected_index > 0)
selected_index--; selected_index--;
ui_draw_directory_list(); ui_draw_directory_list();
text_directory_ui_set_status("");
break; break;
case KEY_ARROW_DOWN: case KEY_ARROW_DOWN:
if (selected_index < entry_count - 1) if (selected_index < entry_count - 1)
selected_index++; selected_index++;
ui_draw_directory_list(); ui_draw_directory_list();
text_directory_ui_set_status("");
break; break;
case KEY_ENTER: case KEY_ENTER:
if(entry_count == 0) {
//directly load app from flash
final_callback(NULL);
}
if (entry_count > 0) if (entry_count > 0)
{ {
char new_path[512]; 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); snprintf(new_path, sizeof(new_path), "%s/%s", current_path, entries[selected_index].name);
strncpy(current_path, new_path, sizeof(current_path) - 1); strncpy(current_path, new_path, sizeof(current_path) - 1);
load_directory(current_path); load_directory(current_path);
ui_draw_path_header(); ui_draw_path_header();
ui_draw_directory_list(); ui_draw_directory_list();
} }else if(entries[selected_index].is_dir == IS_LAST_APP){
else if (final_callback) if(final_callback){
{ final_callback(NULL);
char final_selected[512]; }
snprintf(final_selected, sizeof(final_selected), "%s/%s", current_path, entries[selected_index].name); }else {
final_callback(final_selected); 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; break;
case KEY_BACKSPACE: case KEY_BACKSPACE:
if (strcmp(current_path, "/sd") != 0) if (strcmp(current_path, "/firmware") != 0)
{ {
char *last_slash = strrchr(current_path, '/'); char *last_slash = strrchr(current_path, '/');
if (last_slash) if (last_slash)
*last_slash = '\0'; *last_slash = '\0';
if (current_path[0] == '\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); load_directory(current_path);
ui_draw_path_header(); ui_draw_path_header();
ui_draw_directory_list(); 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) bool text_directory_ui_init(void)
{ {
draw_filled_rect(UI_X, UI_Y, UI_WIDTH, UI_HEIGHT, COLOR_BG); 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); load_directory(current_path);
text_directory_ui_set_status("");
ui_refresh(); ui_refresh();
last_scrolling = time_us_64()/1000; last_scrolling = time_us_64()/1000;
return true; return true;
@ -450,7 +488,6 @@ void text_directory_ui_set_status(const char *msg)
{ {
strncpy(status_message, msg, sizeof(status_message) - 1); strncpy(status_message, msg, sizeof(status_message) - 1);
status_message[sizeof(status_message) - 1] = '\0'; status_message[sizeof(status_message) - 1] = '\0';
status_timestamp = (time_us_64() / 1000);
ui_draw_status_bar(); ui_draw_status_bar();
} }
@ -480,13 +517,6 @@ void text_directory_ui_run(void)
last_scroll_update = current_time; 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 // Check for SD card removal during runtime
if (!sd_card_inserted()) { if (!sd_card_inserted()) {
text_directory_ui_set_status("SD card removed. Please reinsert card."); text_directory_ui_set_status("SD card removed. Please reinsert card.");