mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2025-08-07 22:13:56 -06:00
🚸 Detect multi-volume insert / remove
This commit is contained in:
parent
38d6d61912
commit
f78aaf9562
7 changed files with 168 additions and 66 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue