diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index de6677fc9a..aa229e80b6 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -1349,8 +1349,11 @@ void setup() { #endif #endif - #if HAS_MEDIA && ANY(SDCARD_EEPROM_EMULATION, POWER_LOSS_RECOVERY) - SETUP_RUN(card.mount()); // Mount media with settings before first_load + #if HAS_MEDIA + SETUP_RUN(card.init()); // Prepare for media usage + #if ANY(SDCARD_EEPROM_EMULATION, POWER_LOSS_RECOVERY) + SETUP_RUN(card.mount()); // Mount media with settings before first_load + #endif #endif // Prepare some LCDs to display early diff --git a/Marlin/src/lcd/extui/anycubic_chiron/chiron_tft.cpp b/Marlin/src/lcd/extui/anycubic_chiron/chiron_tft.cpp index e6e8c88155..b4e1978283 100644 --- a/Marlin/src/lcd/extui/anycubic_chiron/chiron_tft.cpp +++ b/Marlin/src/lcd/extui/anycubic_chiron/chiron_tft.cpp @@ -740,7 +740,7 @@ void ChironTFT::panelAction(uint8_t req) { break; case 26: // A26 Refresh SD - if (card.isMounted())card.release(); + card.release(); card.mount(); safe_delay(500); filenavigator.reset(); diff --git a/Marlin/src/lcd/extui/mks_ui/tft_lvgl_configuration.cpp b/Marlin/src/lcd/extui/mks_ui/tft_lvgl_configuration.cpp index 729d8bc4b2..903afad884 100644 --- a/Marlin/src/lcd/extui/mks_ui/tft_lvgl_configuration.cpp +++ b/Marlin/src/lcd/extui/mks_ui/tft_lvgl_configuration.cpp @@ -131,32 +131,16 @@ void tft_lvgl_init() { // Init TFT first! SPI_TFT.spiInit(SPI_FULL_SPEED); SPI_TFT.lcdInit(); - hal.watchdog_refresh(); // LVGL init takes time - #if HAS_USB_FLASH_DRIVE - #if HAS_MULTI_VOLUME && !HAS_SD_HOST_DRIVE - if (card.isSDCardInserted()) - card.selectMediaSDCard(); - else - card.selectMediaFlashDrive(); - #endif - // Wait up to two seconds for USB Drive to mount - for (uint16_t usb_flash_loop = 500; --usb_flash_loop;) { - hal.watchdog_refresh(); - card.media_driver_usbFlash.idle(); - delay(4); - if (card.media_driver_usbFlash.isInserted()) break; - } - card.mount(); - #elif HAS_LOGO_IN_FLASH + #if HAS_LOGO_IN_FLASH + // Leave the boot screen visible for a moment delay(1000); - hal.watchdog_refresh(); + hal.watchdog_refresh(); // LVGL init takes time delay(1000); + hal.watchdog_refresh(); // LVGL init takes time #endif - hal.watchdog_refresh(); // LVGL init takes time - #if HAS_MEDIA UpdateAssets(); hal.watchdog_refresh(); // LVGL init takes time diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp index f6b1b60c7d..2b730581c8 100644 --- a/Marlin/src/lcd/marlinui.cpp +++ b/Marlin/src/lcd/marlinui.cpp @@ -1891,40 +1891,60 @@ uint8_t expand_u8str_P(char * const outstr, PGM_P const ptpl, const int8_t ind, #include "extui/ui_api.h" #endif - void MarlinUI::media_changed(const uint8_t old_status, const uint8_t status) { + void MarlinUI::media_changed(const MediaPresence old_status, const MediaPresence status) { TERN_(HAS_DISPLAY_SLEEP, refresh_screen_timeout()); if (old_status == status) { TERN_(EXTENSIBLE_UI, ExtUI::onMediaError()); // Failed to mount/unmount return; } - if (old_status < 2) { // Skip this section on first boot check - if (status) { // Media Mounted + if (old_status > MEDIA_BOOT) { // Skip this section on first boot check + + if (status > old_status) { // Media Mounted + #if ENABLED(EXTENSIBLE_UI) + ExtUI::onMediaMounted(); + #elif ENABLED(BROWSE_MEDIA_ON_INSERT) + clear_menu_history(); quick_feedback(); goto_screen(MEDIA_MENU_GATEWAY); + #else + if (card.isSDCardSelected()) LCD_MESSAGE(MSG_MEDIA_INSERTED_SD); else if (card.isFlashDriveSelected()) LCD_MESSAGE(MSG_MEDIA_INSERTED_USB); else LCD_MESSAGE(MSG_MEDIA_INSERTED); + #endif } else { // Media Removed + #if ENABLED(EXTENSIBLE_UI) + ExtUI::onMediaRemoved(); - #elif HAS_SD_DETECT // Q: Does "Media Removed" need to be shown for manual release too? - LCD_MESSAGE(MSG_MEDIA_REMOVED); - #if HAS_MARLINUI_MENU - if (ENABLED(HAS_WIRED_LCD) || !defer_return_to_status) return_to_status(); - #endif + + #elif HAS_SD_DETECT || HAS_USB_FLASH_DRIVE // Q: Does "Media Removed" need to be shown for manual release too? + + if ((old_status ^ status) & INSERT_SD) + LCD_MESSAGE(MSG_MEDIA_REMOVED_SD); + else if ((old_status ^ status) & INSERT_USB) + LCD_MESSAGE(MSG_MEDIA_REMOVED_USB); + else + LCD_MESSAGE(MSG_MEDIA_REMOVED); + + if (ENABLED(HAS_WIRED_LCD) || !defer_return_to_status) + return_to_status(); + #elif HAS_WIRED_LCD + return_to_status(); + #endif } } diff --git a/Marlin/src/lcd/marlinui.h b/Marlin/src/lcd/marlinui.h index 9f2ac5a69a..076fc63f2a 100644 --- a/Marlin/src/lcd/marlinui.h +++ b/Marlin/src/lcd/marlinui.h @@ -256,7 +256,7 @@ public: #if HAS_MEDIA #define MEDIA_MENU_GATEWAY TERN(PASSWORD_ON_SD_PRINT_MENU, password.media_gatekeeper, menu_media) - static void media_changed(const uint8_t old_stat, const uint8_t stat); + static void media_changed(const MediaPresence old_stat, const MediaPresence stat); #endif #if HAS_LCD_BRIGHTNESS diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index 21bc6e780a..5502561ea4 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -145,6 +145,7 @@ int16_t CardReader::nrItems = -1; #endif DiskIODriver* CardReader::driver = nullptr; + MarlinVolume CardReader::volume; MediaFile CardReader::myfile; @@ -483,11 +484,15 @@ void CardReader::mount() { nrItems = -1; if (root.isOpen()) root.close(); - if (!driver->init(SD_SPI_SPEED, SD_SS_PIN) + const bool driver_init = ( + driver->init(SD_SPI_SPEED, SD_SS_PIN) #if PIN_EXISTS(LCD_SDSS) && (LCD_SDSS_PIN != SD_SS_PIN) - && !driver->init(SD_SPI_SPEED, LCD_SDSS_PIN) + || driver->init(SD_SPI_SPEED, LCD_SDSS_PIN) #endif - ) SERIAL_ECHO_MSG(STR_SD_INIT_FAIL); + ); + + if (!driver_init) + SERIAL_ECHO_MSG(STR_SD_INIT_FAIL); else if (!volume.init(driver)) SERIAL_WARN_MSG(STR_SD_VOL_INIT_FAIL); else if (!root.openRoot(&volume)) @@ -519,69 +524,146 @@ void CardReader::mount() { #include "../module/stepper.h" #endif +// Provide a little time for drives to prepare +void CardReader::init() { + #if HAS_USB_FLASH_DRIVE + for (uint8_t i = 10; --i;) { + media_driver_usbFlash.idle(); + hal.watchdog_refresh(); + if (media_driver_usbFlash.isInserted()) break; + delay(20); + } + #endif +} + /** - * Handle SD card events + * Handle media insertion and removal events + * based on SD Card detect and/or driver.isInserted() + * + * MULTI_VOLUME: + * - Track insert/remove for both media drives. + * - If the MOUNTED media is removed call release(). + * - If media is INSERTED when NO MEDIA is mounted, select and mount it. */ void CardReader::manage_media() { - #if HAS_USB_FLASH_DRIVE // Wrap for optimal non-virtual? - driver->idle(); // Handle device tasks (e.g., USB Drive insert / remove) + /** + * Handle device tasks (e.g., USB Drive insert / remove) + * - USB Flash Drive needs to run even when not selected. + * - SD Card currently has no background tasks. + */ + //driver->idle(); + #if HAS_USB_FLASH_DRIVE + //if (!isFlashDriveSelected()) + media_driver_usbFlash.idle(); #endif - static uint8_t prev_stat = 2; // At boot we don't know if media is present or not - uint8_t stat = uint8_t(isInserted()); + // Prevent re-entry during Marlin::idle + #if HAS_MULTI_VOLUME + static bool no_reenter = false; + if (no_reenter) return; + #endif + + static MediaPresence prev_stat = MEDIA_BOOT; // At boot we don't know if media is present or not + + // Live status is based on available media flags + MediaPresence stat = MediaPresence( + #if HAS_MULTI_VOLUME + (isSDCardInserted() ? INSERT_SD : 0) // Without SD Detect it's always "inserted" + | (isFlashDriveInserted() ? INSERT_USB : 0) + #else + isInserted() ? INSERT_MEDIA : 0 // Without SD Detect it's always "inserted" + #endif + ); + if (stat == prev_stat) return; // Already checked and still no change? DEBUG_SECTION(cmm, "CardReader::manage_media()", true); DEBUG_ECHOLNPGM("Media present: ", prev_stat, " -> ", stat); - if (!ui.detected()) { - DEBUG_ECHOLNPGM("SD: No UI Detected."); - return; - } + // Without a UI there's no auto-mount or release + if (!ui.detected()) { DEBUG_ECHOLNPGM("SD: No UI Detected."); return; } - flag.workDirIsRoot = true; // Return to root on mount/release/init - - const uint8_t old_stat = prev_stat; + const MediaPresence old_stat = prev_stat, + old_real = old_stat == MEDIA_BOOT ? INSERT_NONE : old_stat; prev_stat = stat; // Change now to prevent re-entry in safe_delay - if (stat) { // Media Inserted - safe_delay(500); // Some boards need a delay to get settled + #if HAS_MULTI_VOLUME + const int8_t vdiff = (old_real ^ stat), vadd = vdiff & stat; + #endif + const bool did_insert = TERN(HAS_MULTI_VOLUME, vadd, stat) != INSERT_NONE; - // Try to mount the media (only later with SD_IGNORE_AT_STARTUP) - if (TERN1(SD_IGNORE_AT_STARTUP, old_stat != 2)) mount(); - if (!isMounted()) stat = 0; // Not mounted? + if (did_insert) { // Media Inserted + + TERN_(HAS_MULTI_VOLUME, ui.refresh()); // Refresh for insert events without messages + + // Some media is already mounted? Nothing to do. + if (TERN0(HAS_MULTI_VOLUME, isMounted())) return; + + // Prevent re-entry during the following phases + TERN_(HAS_MULTI_VOLUME, no_reenter = true); + + // Try to mount the media (but not at boot if SD_IGNORE_AT_STARTUP) + if (TERN1(SD_IGNORE_AT_STARTUP, old_stat > MEDIA_BOOT)) { + #if HAS_MULTI_VOLUME + if ((vadd & INSERT_SD) && !isSDCardSelected()) + selectMediaSDCard(); + if ((vadd & INSERT_USB) && !isFlashDriveSelected()) + selectMediaFlashDrive(); + #endif + safe_delay(500); // Time for inserted media to settle. May re-enter for multiple media? + mount(); + } + + // If the selected media isn't mounted throw an alert in ui.media_changed + if (!isMounted()) stat = old_real; TERN_(RESET_STEPPERS_ON_MEDIA_INSERT, reset_stepper_drivers()); // Workaround for Cheetah bug + + // Re-enable media detection logic + TERN_(HAS_MULTI_VOLUME, no_reenter = false); + } + else if ( + // Media was removed from the device slot + #if HAS_MULTI_VOLUME + (isSDCardSelected() && (vdiff & INSERT_SD)) + || (isFlashDriveSelected() && (vdiff & INSERT_USB)) + #else + stat // == INSERT_MEDIA + #endif + ) { + flag.workDirIsRoot = true; // Return to root on release + release(); + //TERN_(HAS_MULTI_VOLUME, prev_stat = INSERT_NONE); // HACK to try mounting any remaining media } else { - TERN_(HAS_SD_DETECT, release()); // Card is released + #if HAS_MULTI_VOLUME + stat = old_real; // Ignore un-mounted media being ejected + ui.refresh(); // Refresh for menus that show inserted unmounted media + #endif } - ui.media_changed(old_stat, stat); // Update the UI or flag an error + ui.media_changed(old_stat, stat); // Update the UI or flag an error - if (!stat) return; // Exit if no media is present - - bool do_auto = true; UNUSED(do_auto); + if (stat == INSERT_NONE) return; // Exit if no media is present // First mount on boot? Load emulated EEPROM and look for PLR file. - if (old_stat == 2) { + if (old_stat <= MEDIA_BOOT) { DEBUG_ECHOLNPGM("First mount."); // Load settings the first time media is inserted (not just during init) TERN_(SDCARD_EEPROM_EMULATION, settings.first_load()); - // Check for PLR file. Skip One-Click and auto#.g if found - TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false); + // Check for PLR file. If found skip other procedures! + if (TERN0(POWER_LOSS_RECOVERY, recovery.check())) return; } - // Find the newest file and prompt to print it. - TERN_(ONE_CLICK_PRINT, if (do_auto && one_click_check()) do_auto = false); + // Find the newest file and prompt to print it. Skip other procedures! + if (TERN0(ONE_CLICK_PRINT, one_click_check())) return; - // Also for the first mount run auto#.g for machine init. - // (Skip if PLR or One-Click Print was invoked.) - if (old_stat == 2) { + // On first mount at boot run auto#.g for machine init. + if (old_stat <= MEDIA_BOOT) { // Look for auto0.g on the next idle() - IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin()); + IF_DISABLED(NO_SD_AUTOSTART, autofile_begin()); } } @@ -590,6 +672,8 @@ void CardReader::manage_media() { * Used by M22, "Release Media", manage_media. */ void CardReader::release() { + if (!flag.mounted) return; + // Card removed while printing? Abort! if (isStillPrinting()) abortFilePrintSoon(); diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 1a8843da58..6c1f05b8af 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -78,6 +78,14 @@ typedef struct { ; } card_flags_t; +enum MediaPresence : int8_t { + MEDIA_BOOT = -1, + INSERT_NONE = 0x00, + INSERT_MEDIA = 0x01, + INSERT_SD = TERN(HAS_MULTI_VOLUME, 0x02, 0x00), + INSERT_USB = TERN(HAS_MULTI_VOLUME, 0x04, 0x00) +}; + enum ListingFlags : uint8_t { LS_LONG_FILENAME, LS_ONLY_BIN, LS_TIMESTAMP }; enum SortFlag : int8_t { AS_REV = -1, AS_OFF, AS_FWD, AS_ALSO_REV }; @@ -102,6 +110,9 @@ public: CardReader(); + // Init at startup before mounting media + static void init(); + /** * Media Selection - Only one drive may be active at a time, * so switching drives (currently) returns to the root folder.