1
0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2024-11-26 13:25:54 +00:00

🧑‍💻 SD card

This commit is contained in:
Scott Lahteine 2023-12-18 19:12:10 -06:00
parent 875301521b
commit 6b65665aa8
16 changed files with 273 additions and 209 deletions

View File

@ -195,9 +195,9 @@ void EasythreedUI::printButton() {
return; // Bail out
}
card.ls(); // List all files to serial output
const uint16_t filecnt = card.countFilesInWorkDir(); // Count printable files in cwd
const int16_t filecnt = card.get_num_items(); // Count printable files in cwd
if (filecnt == 0) return; // None are printable?
card.selectFileByIndex(filecnt); // Select the last file according to current sort options
card.selectFileByIndex(filecnt); // Select the last file (without sort)
card.openAndPrintFile(card.filename); // Start printing it
} break;
case PF_PAUSE: { // Pause printing (not currently firing)

View File

@ -411,7 +411,7 @@ void Scroll_Menu(const uint8_t dir) {
}
inline uint16_t nr_sd_menu_items() {
return card.get_num_Files() + !card.flag.workDirIsRoot;
return card.get_num_items() + !card.flag.workDirIsRoot;
}
void Erase_Menu_Text(const uint8_t line) {
@ -1830,9 +1830,9 @@ void MarlinUI::refresh() { /* Nothing to see here */ }
void Init_Shift_Name() {
const bool is_subdir = !card.flag.workDirIsRoot;
const int8_t filenum = select_file.now - 1 - is_subdir; // Skip "Back" and ".."
const uint16_t fileCnt = card.get_num_Files();
const int16_t fileCnt = card.get_num_items();
if (WITHIN(filenum, 0, fileCnt - 1)) {
card.getfilename_sorted(SD_ORDER(filenum, fileCnt));
card.selectFileByIndexSorted(filenum);
char * const name = card.longest_filename();
make_name_without_ext(shift_name, name, 100);
}
@ -1857,7 +1857,7 @@ void Draw_SDItem(const uint16_t item, int16_t row=-1) {
return;
}
card.getfilename_sorted(SD_ORDER(item - is_subdir, card.get_num_Files()));
card.selectFileByIndexSorted(item - is_subdir);
char * const name = card.longest_filename();
#if ENABLED(SCROLL_LONG_FILENAMES)
@ -2223,7 +2223,7 @@ void HMI_SelectFile() {
}
else {
const uint16_t filenum = select_file.now - 1 - hasUpDir;
card.getfilename_sorted(SD_ORDER(filenum, card.get_num_Files()));
card.selectFileByIndexSorted(filenum);
// Enter that folder!
if (card.flag.filenameIsDir) {

View File

@ -808,7 +808,7 @@ void CrealityDWINClass::Draw_SD_Item(const uint8_t item, const uint8_t row) {
if (item == 0)
Draw_Menu_Item(0, ICON_Back, card.flag.workDirIsRoot ? F("Back") : F(".."));
else {
card.getfilename_sorted(SD_ORDER(item - 1, card.get_num_Files()));
card.selectFileByIndexSorted(item - 1);
char * const filename = card.longest_filename();
size_t max = MENU_CHAR_LIMIT;
size_t pos = strlen(filename), len = pos;
@ -4283,7 +4283,7 @@ void CrealityDWINClass::File_Control() {
EncoderState encoder_diffState = Encoder_ReceiveAnalyze();
if (encoder_diffState == ENCODER_DIFF_NO) {
if (selection > 0) {
card.getfilename_sorted(SD_ORDER(selection - 1, card.get_num_Files()));
card.selectFileByIndexSorted(selection - 1);
char * const filename = card.longest_filename();
size_t len = strlen(filename);
size_t pos = len;
@ -4302,7 +4302,7 @@ void CrealityDWINClass::File_Control() {
}
return;
}
if (encoder_diffState == ENCODER_DIFF_CW && selection < card.get_num_Files()) {
if (encoder_diffState == ENCODER_DIFF_CW && selection < card.get_num_items()) {
DWIN_Draw_Rectangle(1, Color_Bg_Black, 0, MBASE(selection - scrollpos) - 18, 14, MBASE(selection - scrollpos) + 33);
if (selection > 0) {
DWIN_Draw_Rectangle(1, Color_Bg_Black, LBLX, MBASE(selection - scrollpos) - 14, 271, MBASE(selection - scrollpos) + 28);
@ -4342,7 +4342,7 @@ void CrealityDWINClass::File_Control() {
}
}
else {
card.getfilename_sorted(SD_ORDER(selection - 1, card.get_num_Files()));
card.selectFileByIndexSorted(selection - 1);
if (card.flag.filenameIsDir) {
card.cd(card.filename);
Draw_SD_List();

View File

@ -873,7 +873,7 @@ void SetMediaAutoMount() {
}
inline uint16_t nr_sd_menu_items() {
return _MIN(card.get_num_Files() + !card.flag.workDirIsRoot, MENU_MAX_ITEMS);
return _MIN(card.get_num_items() + !card.flag.workDirIsRoot, MENU_MAX_ITEMS);
}
void make_name_without_ext(char *dst, char *src, size_t maxlen=MENU_CHAR_LIMIT) {
@ -920,7 +920,7 @@ void onClickSDItem() {
if (hasUpDir && CurrentMenu->selected == 1) return SDCard_Up();
else {
const uint16_t filenum = CurrentMenu->selected - 1 - hasUpDir;
card.getfilename_sorted(SD_ORDER(filenum, card.get_num_Files()));
card.selectFileByIndexSorted(filenum);
// Enter that folder!
if (card.flag.filenameIsDir) return SDCard_Folder(card.filename);
@ -963,7 +963,7 @@ void onClickSDItem() {
last_itemselected = selected;
if (selected >= 1 + hasUpDir) {
const int8_t filenum = selected - 1 - hasUpDir; // Skip "Back" and ".."
card.getfilename_sorted(SD_ORDER(filenum, card.get_num_Files()));
card.selectFileByIndexSorted(filenum);
make_name_without_ext(shift_name, card.longest_filename(), LONG_FILENAME_LENGTH);
shift_len = strlen(shift_name);
shift_amt = 0;
@ -991,7 +991,7 @@ void onDrawFileName(MenuItemClass* menuitem, int8_t line) {
}
else {
uint8_t icon;
card.getfilename_sorted(SD_ORDER(menuitem->pos - is_subdir - 1, card.get_num_Files()));
card.selectFileByIndexSorted(menuitem->pos - is_subdir - 1);
make_name_without_ext(shift_name, card.longest_filename());
icon = card.flag.filenameIsDir ? ICON_Folder : card.fileIsBinary() ? ICON_Binary : ICON_File;
Draw_Menu_Line(line, icon, shift_name);

View File

@ -325,8 +325,8 @@ void process_lcd_s_command(const char *command) {
// select a file for printing during a print, there's
// little reason not to do it this way.
char message_buffer[MAX_CURLY_COMMAND];
uint16_t file_count = card.get_num_Files();
for (uint16_t i = 0; i < file_count; i++) {
int16_t file_count = card.get_num_items();
for (int16_t i = 0; i < file_count; i++) {
card.selectFileByIndex(i);
sprintf_P(message_buffer, card.flag.filenameIsDir ? PSTR("{DIR:%s}") : PSTR("{FILE:%s}"), card.longest_filename());
write_to_lcd(message_buffer);

View File

@ -72,11 +72,11 @@ uint8_t sel_id = 0;
else
card.cdroot();
const uint16_t fileCnt = card.get_num_Files();
const int16_t fileCnt = card.get_num_items();
for (uint16_t i = 0; i < fileCnt; i++) {
for (int16_t i = 0; i < fileCnt; i++) {
if (list_file.Sd_file_cnt == list_file.Sd_file_offset) {
card.getfilename_sorted(SD_ORDER(i, fileCnt));
card.selectFileByIndexSorted(i);
list_file.IsFolder[valid_name_cnt] = card.flag.filenameIsDir;
strcpy(list_file.file_name[valid_name_cnt], list_file.curDirPath);

View File

@ -817,19 +817,26 @@ uint8_t Explore_Disk(char *path , uint8_t recu_level) {
if (!path) return 0;
const uint8_t fileCnt = card.get_num_Files();
const int16_t fileCnt = card.get_num_items();
for (uint8_t i = 0; i < fileCnt; i++) {
card.getfilename_sorted(SD_ORDER(i, fileCnt));
ZERO(tmp);
strcpy(tmp, card.filename);
MediaFile file;
MediaFile *diveDir;
for (int16_t i = 0; i < fileCnt; i++) {
card.selectFileByIndexSorted(i);
ZERO(Fstream);
strcpy(Fstream, tmp);
strcpy(Fstream, card.filename);
if (card.flag.filenameIsDir && recu_level <= 10)
strcat_P(Fstream, PSTR(".DIR"));
strcat_P(Fstream, PSTR(" 0")); // report 0 file size
if (with_longnames) {
strcat_P(Fstream, PSTR(" "));
strcat_P(Fstream, card.longest_filename());
}
strcat_P(Fstream, PSTR("\r\n"));
send_to_wifi((uint8_t*)Fstream, strlen(Fstream));
}

View File

@ -1166,12 +1166,12 @@ namespace ExtUI {
FileList::FileList() { refresh(); }
void FileList::refresh() { num_files = 0xFFFF; }
void FileList::refresh() { }
bool FileList::seek(const uint16_t pos, const bool skip_range_check) {
#if HAS_MEDIA
if (!skip_range_check && (pos + 1) > count()) return false;
card.getfilename_sorted(SD_ORDER(pos, count()));
card.selectFileByIndexSorted(pos);
return card.filename[0] != '\0';
#else
UNUSED(pos);
@ -1197,7 +1197,7 @@ namespace ExtUI {
}
uint16_t FileList::count() {
return TERN0(HAS_MEDIA, (num_files = (num_files == 0xFFFF ? card.get_num_Files() : num_files)));
return TERN0(HAS_MEDIA, card.get_num_items());
}
bool FileList::isAtRootDir() {
@ -1205,19 +1205,11 @@ namespace ExtUI {
}
void FileList::upDir() {
#if HAS_MEDIA
card.cdup();
num_files = 0xFFFF;
#endif
TERN_(HAS_MEDIA, card.cdup());
}
void FileList::changeDir(const char * const dirname) {
#if HAS_MEDIA
card.cd(dirname);
num_files = 0xFFFF;
#else
UNUSED(dirname);
#endif
TERN(HAS_MEDIA, card.cd(dirname), UNUSED(dirname));
}
} // namespace ExtUI

View File

@ -434,9 +434,6 @@ namespace ExtUI {
void resumePrint();
class FileList {
private:
uint16_t num_files;
public:
FileList();
void refresh();

View File

@ -109,10 +109,10 @@ void menu_media_filelist() {
ui.encoder_direction_menus();
#if HAS_MARLINUI_U8GLIB
static uint16_t fileCnt;
if (ui.first_page) fileCnt = card.get_num_Files();
static int16_t fileCnt;
if (ui.first_page) fileCnt = card.get_num_items();
#else
const uint16_t fileCnt = card.get_num_Files();
const int16_t fileCnt = card.get_num_items();
#endif
START_MENU();
@ -129,16 +129,18 @@ void menu_media_filelist() {
else if (card.isMounted())
ACTION_ITEM_F(F(LCD_STR_FOLDER " .."), lcd_sd_updir);
if (ui.should_draw()) for (uint16_t i = 0; i < fileCnt; i++) {
if (_menuLineNr == _thisItemNr) {
card.getfilename_sorted(SD_ORDER(i, fileCnt));
if (card.flag.filenameIsDir)
MENU_ITEM(sdfolder, MSG_MEDIA_MENU, card);
else
MENU_ITEM(sdfile, MSG_MEDIA_MENU, card);
if (ui.should_draw()) {
for (int16_t i = 0; i < fileCnt; i++) {
if (_menuLineNr != _thisItemNr)
SKIP_ITEM();
else {
card.selectFileByIndexSorted(i);
if (card.flag.filenameIsDir)
MENU_ITEM(sdfolder, MSG_MEDIA_MENU, card);
else
MENU_ITEM(sdfile, MSG_MEDIA_MENU, card);
}
}
else
SKIP_ITEM();
}
END_MENU();
}

View File

@ -40,6 +40,37 @@
#include "../MarlinCore.h"
#if DISABLED(SD_NO_DEFAULT_TIMEOUT)
#ifndef SD_INIT_TIMEOUT
#define SD_INIT_TIMEOUT 2000u // (ms) Init timeout
#elif SD_INIT_TIMEOUT < 0
#error "SD_INIT_TIMEOUT must be greater than or equal to 0."
#endif
#ifndef SD_ERASE_TIMEOUT
#define SD_ERASE_TIMEOUT 10000u // (ms) Erase timeout
#elif SD_ERASE_TIMEOUT < 0
#error "SD_ERASE_TIMEOUT must be greater than or equal to 0."
#endif
#ifndef SD_READ_TIMEOUT
#define SD_READ_TIMEOUT 300u // (ms) Read timeout
#elif SD_READ_TIMEOUT < 0
#error "SD_READ_TIMEOUT must be greater than or equal to 0."
#endif
#ifndef SD_WRITE_TIMEOUT
#define SD_WRITE_TIMEOUT 600u // (ms) Write timeout
#elif SD_WRITE_TIMEOUT < 0
#error "SD_WRITE_TIMEOUT must be greater than or equal to 0."
#endif
#endif // SD_NO_DEFAULT_TIMEOUT
#if ENABLED(SD_CHECK_AND_RETRY)
#ifndef SD_RETRY_COUNT
#define SD_RETRY_COUNT 3
#elif SD_RETRY_COUNT < 1
#error "SD_RETRY_COUNT must be greater than or equal to 1."
#endif
#endif
#if ENABLED(SD_CHECK_AND_RETRY)
static bool crcSupported = true;
@ -97,15 +128,16 @@ uint8_t DiskIODriver_SPI_SD::cardCommand(const uint8_t cmd, const uint32_t arg)
// Select card
chipSelect();
// Wait up to 300 ms if busy
waitNotBusy(SD_WRITE_TIMEOUT);
#if SD_WRITE_TIMEOUT
waitNotBusy(SD_WRITE_TIMEOUT); // Wait up to 600 ms (by default) if busy
#endif
uint8_t *pa = (uint8_t *)(&arg);
#if ENABLED(SD_CHECK_AND_RETRY)
// Form message
uint8_t d[6] = {(uint8_t) (cmd | 0x40), pa[3], pa[2], pa[1], pa[0] };
uint8_t d[6] = { uint8_t(cmd | 0x40), pa[3], pa[2], pa[1], pa[0] };
// Add crc
d[5] = CRC7(d, 5);
@ -186,33 +218,42 @@ void DiskIODriver_SPI_SD::chipSelect() {
bool DiskIODriver_SPI_SD::erase(uint32_t firstBlock, uint32_t lastBlock) {
if (ENABLED(SDCARD_READONLY)) return false;
csd_t csd;
if (!readCSD(&csd)) goto FAIL;
bool success = false;
do {
// check for single block erase
if (!csd.v1.erase_blk_en) {
// erase size mask
uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
if ((firstBlock & m) != 0 || ((lastBlock + 1) & m) != 0) {
// error card can't erase specified area
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
goto FAIL;
csd_t csd;
if (!readCSD(&csd)) break;
// check for single block erase
if (!csd.v1.erase_blk_en) {
// erase size mask
uint8_t m = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
if ((firstBlock & m) || ((lastBlock + 1) & m)) {
// error card can't erase specified area
error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
break;
}
}
}
if (type_ != SD_CARD_TYPE_SDHC) { firstBlock <<= 9; lastBlock <<= 9; }
if (cardCommand(CMD32, firstBlock) || cardCommand(CMD33, lastBlock) || cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
goto FAIL;
}
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
goto FAIL;
}
if (type_ != SD_CARD_TYPE_SDHC) { firstBlock <<= 9; lastBlock <<= 9; }
if (cardCommand(CMD32, firstBlock) || cardCommand(CMD33, lastBlock) || cardCommand(CMD38, 0)) {
error(SD_CARD_ERROR_ERASE);
break;
}
#if SD_ERASE_TIMEOUT
if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
error(SD_CARD_ERROR_ERASE_TIMEOUT);
break;
}
#else
while (spiRec() != 0xFF) {}
#endif
success = true;
} while (0);
chipDeselect();
return true;
FAIL:
chipDeselect();
return false;
return success;
}
/**
@ -245,8 +286,15 @@ bool DiskIODriver_SPI_SD::init(const uint8_t sckRateID, const pin_t chipSelectPi
errorCode_ = type_ = 0;
chipSelectPin_ = chipSelectPin;
// 16-bit init start time allows over a minute
const millis_t init_timeout = millis() + SD_INIT_TIMEOUT;
#if SD_INIT_TIMEOUT
const millis_t init_timeout = millis() + SD_INIT_TIMEOUT;
#define INIT_TIMEOUT() ELAPSED(millis(), init_timeout)
#else
#define INIT_TIMEOUT() false
#endif
uint32_t arg;
hal.watchdog_refresh(); // In case init takes too long
@ -274,7 +322,7 @@ bool DiskIODriver_SPI_SD::init(const uint8_t sckRateID, const pin_t chipSelectPi
// Command to go idle in SPI mode
while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
if (ELAPSED(millis(), init_timeout)) {
if (INIT_TIMEOUT()) {
error(SD_CARD_ERROR_CMD0);
goto FAIL;
}
@ -300,7 +348,7 @@ bool DiskIODriver_SPI_SD::init(const uint8_t sckRateID, const pin_t chipSelectPi
break;
}
if (ELAPSED(millis(), init_timeout)) {
if (INIT_TIMEOUT()) {
error(SD_CARD_ERROR_CMD8);
goto FAIL;
}
@ -312,7 +360,7 @@ bool DiskIODriver_SPI_SD::init(const uint8_t sckRateID, const pin_t chipSelectPi
arg = type() == SD_CARD_TYPE_SD2 ? 0x40000000 : 0;
while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
// Check for timeout
if (ELAPSED(millis(), init_timeout)) {
if (INIT_TIMEOUT()) {
error(SD_CARD_ERROR_ACMD41);
goto FAIL;
}
@ -355,7 +403,7 @@ bool DiskIODriver_SPI_SD::readBlock(uint32_t blockNumber, uint8_t * const dst) {
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
#if ENABLED(SD_CHECK_AND_RETRY)
uint8_t retryCnt = 3;
uint8_t retryCnt = SD_RETRY_COUNT;
for (;;) {
if (cardCommand(CMD17, blockNumber))
error(SD_CARD_ERROR_CMD17);
@ -460,9 +508,15 @@ bool DiskIODriver_SPI_SD::readData(uint8_t * const dst) {
bool DiskIODriver_SPI_SD::readData(uint8_t * const dst, const uint16_t count) {
bool success = false;
const millis_t read_timeout = millis() + SD_READ_TIMEOUT;
#if SD_READ_TIMEOUT
const millis_t read_timeout = millis() + SD_READ_TIMEOUT;
#define READ_TIMEOUT() ELAPSED(millis(), read_timeout)
#else
#define READ_TIMEOUT() false
#endif
while ((status_ = spiRec()) == 0xFF) { // Wait for start block token
if (ELAPSED(millis(), read_timeout)) {
if (READ_TIMEOUT()) {
error(SD_CARD_ERROR_READ_TIMEOUT);
goto FAIL;
}
@ -471,7 +525,7 @@ bool DiskIODriver_SPI_SD::readData(uint8_t * const dst, const uint16_t count) {
if (status_ == DATA_START_BLOCK) {
spiRead(dst, count); // Transfer data
const uint16_t recvCrc = (spiRec() << 8) | spiRec();
const uint16_t recvCrc = ((uint16_t)spiRec() << 8) | (uint16_t)spiRec();
#if ENABLED(SD_CHECK_AND_RETRY)
success = !crcSupported || recvCrc == CRC_CCITT(dst, count);
if (!success) error(SD_CARD_ERROR_READ_CRC);
@ -576,20 +630,23 @@ bool DiskIODriver_SPI_SD::writeBlock(uint32_t blockNumber, const uint8_t * const
return 0 == SDHC_CardWriteBlock(src, blockNumber);
#endif
bool success = false;
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
if (!cardCommand(CMD24, blockNumber)) {
if (writeData(DATA_START_BLOCK, src)) {
if (waitNotBusy(SD_WRITE_TIMEOUT)) { // Wait for flashing to complete
success = !(cardCommand(CMD13, 0) || spiRec()); // Response is r2 so get and check two bytes for nonzero
if (!success) error(SD_CARD_ERROR_WRITE_PROGRAMMING);
}
else
error(SD_CARD_ERROR_WRITE_TIMEOUT);
if (type() != SD_CARD_TYPE_SDHC) blockNumber <<= 9; // Use address if not SDHC card
bool success = !cardCommand(CMD24, blockNumber);
if (!success) {
error(SD_CARD_ERROR_CMD24);
}
else if (writeData(DATA_START_BLOCK, src)) {
#if SD_WRITE_TIMEOUT
success = waitNotBusy(SD_WRITE_TIMEOUT); // Wait for flashing to complete
if (!success) error(SD_CARD_ERROR_WRITE_TIMEOUT);
#else
while (spiRec() != 0xFF) {}
#endif
if (success) {
success = !(cardCommand(CMD13, 0) || spiRec()); // Response is r2 so get and check two bytes for nonzero
if (!success) error(SD_CARD_ERROR_WRITE_PROGRAMMING);
}
}
else
error(SD_CARD_ERROR_CMD24);
chipDeselect();
return success;
@ -603,13 +660,25 @@ bool DiskIODriver_SPI_SD::writeBlock(uint32_t blockNumber, const uint8_t * const
bool DiskIODriver_SPI_SD::writeData(const uint8_t * const src) {
if (ENABLED(SDCARD_READONLY)) return false;
bool success = true;
chipSelect();
// Wait for previous write to finish
if (!waitNotBusy(SD_WRITE_TIMEOUT) || !writeData(WRITE_MULTIPLE_TOKEN, src)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
success = false;
}
bool success = false;
do {
// Wait for previous write to finish
#if SD_WRITE_TIMEOUT
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_WRITE_MULTIPLE);
break;
}
#else
while (spiRec() != 0xFF) {}
#endif
success = writeData(WRITE_MULTIPLE_TOKEN, src);
} while (0);
chipDeselect();
return success;
}
@ -667,14 +736,31 @@ bool DiskIODriver_SPI_SD::writeStart(uint32_t blockNumber, const uint32_t eraseC
bool DiskIODriver_SPI_SD::writeStop() {
if (ENABLED(SDCARD_READONLY)) return false;
bool success = false;
chipSelect();
if (waitNotBusy(SD_WRITE_TIMEOUT)) {
bool success = false;
do {
#if SD_WRITE_TIMEOUT
if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
error(SD_CARD_ERROR_STOP_TRAN);
break;
}
#else
while (spiRec() != 0xFF) {}
#endif
spiSend(STOP_TRAN_TOKEN);
success = waitNotBusy(SD_WRITE_TIMEOUT);
}
else
error(SD_CARD_ERROR_STOP_TRAN);
#if SD_WRITE_TIMEOUT
if (!waitNotBusy(SD_WRITE_TIMEOUT)) break;
#else
while (spiRec() != 0xFF) {}
#endif
success = true;
} while (0);
chipDeselect();
return success;

View File

@ -39,11 +39,6 @@
#include <stdint.h>
uint16_t const SD_INIT_TIMEOUT = 2000, // (ms) Init timeout
SD_ERASE_TIMEOUT = 10000, // (ms) Erase timeout
SD_READ_TIMEOUT = 300, // (ms) Read timeout
SD_WRITE_TIMEOUT = 600; // (ms) Write timeout
// SD card errors
typedef enum : uint8_t {
SD_CARD_ERROR_CMD0 = 0x01, // Timeout error for command CMD0 (initialize card in SPI mode)

View File

@ -1463,7 +1463,7 @@ int8_t SdBaseFile::readDir(dir_t * const dir, char * const longFilename) {
// Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
if (VFAT->firstClusterLow == 0) {
const uint8_t seq = VFAT->sequenceNumber & 0x1F;
if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
if (WITHIN(seq, 1, VFAT_ENTRIES_LIMIT)) {
if (seq == 1) {
checksum = VFAT->checksum;
checksum_error = 0;
@ -1627,7 +1627,7 @@ bool SdBaseFile::remove() {
// Check if the entry has a LFN
bool lastEntry = false;
// loop back to search for any LFN entries related to this file
for (uint8_t sequenceNumber = 1; sequenceNumber <= MAX_VFAT_ENTRIES; ++sequenceNumber) {
for (uint8_t sequenceNumber = 1; sequenceNumber <= VFAT_ENTRIES_LIMIT; ++sequenceNumber) {
dirIndex_ = (dirIndex_ - 1) & 0xF;
if (dirBlock_ == 0) break;
if (dirIndex_ == 0xF) dirBlock_--;

View File

@ -109,4 +109,4 @@
#define LONG_FILENAME_CHARSIZE TERN(UTF_FILENAME_SUPPORT, 2, 1)
// Total bytes needed to store a single long filename
#define LONG_FILENAME_LENGTH (FILENAME_LENGTH * LONG_FILENAME_CHARSIZE * MAX_VFAT_ENTRIES + 1)
#define LONG_FILENAME_LENGTH (FILENAME_LENGTH * LONG_FILENAME_CHARSIZE * VFAT_ENTRIES_LIMIT + 1)

View File

@ -81,13 +81,14 @@ IF_DISABLED(NO_SD_AUTOSTART, uint8_t CardReader::autofile_index); // = 0
MediaFile CardReader::root, CardReader::workDir, CardReader::workDirParents[MAX_DIR_DEPTH];
uint8_t CardReader::workDirDepth;
int16_t CardReader::nrItems = -1;
#if ENABLED(SDCARD_SORT_ALPHA)
uint16_t CardReader::sort_count;
int16_t CardReader::sort_count;
#if ENABLED(SDSORT_GCODE)
bool CardReader::sort_alpha;
int CardReader::sort_folders;
int8_t CardReader::sort_folders;
//bool CardReader::sort_reverse;
#endif
@ -100,7 +101,6 @@ uint8_t CardReader::workDirDepth;
#if ENABLED(SDSORT_USES_RAM)
#if ENABLED(SDSORT_CACHE_NAMES)
uint16_t CardReader::nrFiles; // Cached total file count
#if ENABLED(SDSORT_DYNAMIC_RAM)
char **CardReader::sortshort, **CardReader::sortnames;
#else
@ -228,25 +228,20 @@ bool CardReader::is_visible_entity(const dir_t &p OPTARG(CUSTOM_FIRMWARE_UPLOAD,
//
// Get the number of (compliant) items in the folder
//
int CardReader::countItems(MediaFile dir) {
int16_t CardReader::countVisibleItems(MediaFile dir) {
dir_t p;
int c = 0;
while (dir.readDir(&p, longFilename) > 0)
c += is_visible_entity(p);
#if ALL(SDCARD_SORT_ALPHA, SDSORT_USES_RAM, SDSORT_CACHE_NAMES)
nrFiles = c;
#endif
int16_t c = 0;
dir.rewind();
while (dir.readDir(&p, longFilename) > 0) c += is_visible_entity(p);
return c;
}
//
// Get file/folder info for an item by index
//
void CardReader::selectByIndex(MediaFile dir, const uint8_t index) {
void CardReader::selectByIndex(MediaFile dir, const int16_t index) {
dir_t p;
for (uint8_t cnt = 0; dir.readDir(&p, longFilename) > 0;) {
for (int16_t cnt = 0; dir.readDir(&p, longFilename) > 0;) {
if (is_visible_entity(p)) {
if (cnt == index) {
createFilename(filename, p);
@ -295,7 +290,7 @@ void CardReader::printListing(MediaFile parent, const char * const prepend, cons
while (parent.readDir(&p, longFilename) > 0) {
if (DIR_IS_SUBDIR(&p)) {
size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
const size_t lenPrepend = prepend ? strlen(prepend) + 1 : 0;
// Allocate enough stack space for the full path including / separator
char path[lenPrepend + FILENAME_LENGTH];
if (prepend) { strcpy(path, prepend); path[lenPrepend - 1] = '/'; }
@ -446,6 +441,7 @@ void CardReader::printSelectedFilename() {
void CardReader::mount() {
flag.mounted = false;
nrItems = -1;
if (root.isOpen()) root.close();
if (!driver->init(SD_SPI_SPEED, SDSS)
@ -515,20 +511,25 @@ void CardReader::manage_media() {
if (!stat) return; // Exit if no media is present
if (old_stat != 2) return; // First mount?
DEBUG_ECHOLNPGM("First mount.");
// Load settings the first time media is inserted (not just during init)
TERN_(SDCARD_EEPROM_EMULATION, settings.first_load());
bool do_auto = true; UNUSED(do_auto);
// Check for PLR file.
TERN_(POWER_LOSS_RECOVERY, if (recovery.check()) do_auto = false);
// First mount on boot? Load emulated EEPROM and look for PLR file.
if (old_stat == 2) {
DEBUG_ECHOLNPGM("First mount.");
// Look for auto0.g on the next idle()
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
// 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);
}
// Also for the first mount run auto#.g for machine init.
// (Skip if PLR or One-Click Print was invoked.)
if (old_stat == 2) {
// Look for auto0.g on the next idle()
IF_DISABLED(NO_SD_AUTOSTART, if (do_auto) autofile_begin());
}
}
/**
@ -544,10 +545,10 @@ void CardReader::release() {
flag.mounted = false;
flag.workDirIsRoot = true;
#if ALL(SDCARD_SORT_ALPHA, SDSORT_USES_RAM, SDSORT_CACHE_NAMES)
nrFiles = 0;
#endif
nrItems = -1;
SERIAL_ECHO_MSG(STR_SD_CARD_RELEASED);
TERN_(NO_SD_DETECT, ui.refresh());
}
/**
@ -875,12 +876,12 @@ void CardReader::closefile(const bool store_location/*=false*/) {
//
// Get info for a file in the working directory by index
//
void CardReader::selectFileByIndex(const uint16_t nr) {
void CardReader::selectFileByIndex(const int16_t nr) {
#if ENABLED(SDSORT_CACHE_NAMES)
if (nr < sort_count) {
strcpy(filename, sortshort[nr]);
strcpy(longFilename, sortnames[nr]);
flag.filenameIsDir = IS_DIR(nr);
TERN_(HAS_FOLDER_SORTING, flag.filenameIsDir = IS_DIR(nr));
setBinFlag(strcmp_P(strrchr(filename, '.'), PSTR(".BIN")) == 0);
return;
}
@ -894,11 +895,11 @@ void CardReader::selectFileByIndex(const uint16_t nr) {
//
void CardReader::selectFileByName(const char * const match) {
#if ENABLED(SDSORT_CACHE_NAMES)
for (uint16_t nr = 0; nr < sort_count; nr++)
for (int16_t nr = 0; nr < sort_count; nr++)
if (strcasecmp(match, sortshort[nr]) == 0) {
strcpy(filename, sortshort[nr]);
strcpy(longFilename, sortnames[nr]);
flag.filenameIsDir = IS_DIR(nr);
TERN_(HAS_FOLDER_SORTING, flag.filenameIsDir = IS_DIR(nr));
setBinFlag(strcmp_P(strrchr(filename, '.'), PSTR(".BIN")) == 0);
return;
}
@ -907,11 +908,6 @@ void CardReader::selectFileByName(const char * const match) {
selectByName(workDir, match);
}
uint16_t CardReader::countFilesInWorkDir() {
workDir.rewind();
return countItems(workDir);
}
/**
* Dive to the given DOS 8.3 file path, with optional echo of the dive paths.
*
@ -965,8 +961,7 @@ const char* CardReader::diveToFile(const bool update_cwd, MediaFile* &inDirPtr,
// Isolate the next subitem name
const uint8_t len = name_end - atom_ptr;
char dosSubdirname[len + 1];
strncpy(dosSubdirname, atom_ptr, len);
dosSubdirname[len] = 0;
strlcpy(dosSubdirname, atom_ptr, len + 1);
if (echo) SERIAL_ECHOLN(dosSubdirname);
@ -1024,6 +1019,7 @@ void CardReader::cd(const char * relpath) {
flag.workDirIsRoot = false;
if (workDirDepth < MAX_DIR_DEPTH)
workDirParents[workDirDepth++] = workDir;
nrItems = -1;
TERN_(SDCARD_SORT_ALPHA, presort());
}
else
@ -1032,6 +1028,7 @@ void CardReader::cd(const char * relpath) {
int8_t CardReader::cdup() {
if (workDirDepth > 0) { // At least 1 dir has been saved
nrItems = -1;
workDir = --workDirDepth ? workDirParents[workDirDepth - 1] : root; // Use parent, or root if none
TERN_(SDCARD_SORT_ALPHA, presort());
}
@ -1043,6 +1040,7 @@ void CardReader::cdroot() {
workDir = root;
flag.workDirIsRoot = true;
workDirDepth = 0;
nrItems = -1;
TERN_(SDCARD_SORT_ALPHA, presort());
}
@ -1051,7 +1049,7 @@ void CardReader::cdroot() {
/**
* Get the name of a file in the working directory by sort-index
*/
void CardReader::getfilename_sorted(const uint16_t nr) {
void CardReader::selectFileByIndexSorted(const int16_t nr) {
selectFileByIndex(TERN1(SDSORT_GCODE, sort_alpha) && (nr < sort_count)
? sort_order[nr] : nr);
}
@ -1069,7 +1067,7 @@ void CardReader::cdroot() {
#endif
#else
// Copy filenames into the static array
#define _SET_SORTNAME(I) strncpy(sortnames[I], longest_filename(), SORTED_LONGNAME_MAXLEN)
#define _SET_SORTNAME(I) strlcpy(sortnames[I], longest_filename(), sizeof(sortnames[I]))
#if SORTED_LONGNAME_MAXLEN == LONG_FILENAME_LENGTH
// Short name sorting always use LONG_FILENAME_LENGTH with no trailing nul
#define SET_SORTNAME(I) _SET_SORTNAME(I)
@ -1094,20 +1092,20 @@ void CardReader::cdroot() {
* - Most RAM: Buffer the directory and return filenames from RAM
*/
void CardReader::presort() {
// Throw away old sort index
flush_presort();
int16_t fileCnt = get_num_items();
// Sorting may be turned off
if (TERN0(SDSORT_GCODE, !sort_alpha)) return;
// If there are files, sort up to the limit
uint16_t fileCnt = countFilesInWorkDir();
if (fileCnt > 0) {
// Never sort more than the max allowed
// If you use folders to organize, 20 may be enough
NOMORE(fileCnt, uint16_t(SDSORT_LIMIT));
NOMORE(fileCnt, int16_t(SDSORT_LIMIT));
// Sort order is always needed. May be static or dynamic.
TERN_(SDSORT_DYNAMIC_RAM, sort_order = new uint8_t[fileCnt]);
@ -1147,7 +1145,7 @@ void CardReader::cdroot() {
if (fileCnt > 1) {
// Init sort order.
for (uint16_t i = 0; i < fileCnt; i++) {
for (int16_t i = 0; i < fileCnt; i++) {
sort_order[i] = i;
// If using RAM then read all filenames now.
#if ENABLED(SDSORT_USES_RAM)
@ -1166,9 +1164,9 @@ void CardReader::cdroot() {
}
// Bubble Sort
for (uint16_t i = fileCnt; --i;) {
for (int16_t i = fileCnt; --i;) {
bool didSwap = false;
uint8_t o1 = sort_order[0];
int16_t o1 = sort_order[0];
#if DISABLED(SDSORT_USES_RAM)
selectFileByIndex(o1); // Pre-fetch the first entry and save it
strcpy(name1, longest_filename()); // so the loop only needs one fetch
@ -1177,22 +1175,17 @@ void CardReader::cdroot() {
#endif
#endif
for (uint16_t j = 0; j < i; ++j) {
const uint16_t o2 = sort_order[j + 1];
for (int16_t j = 0; j < i; ++j) {
const int16_t o2 = sort_order[j + 1];
// Compare names from the array or just the two buffered names
#if ENABLED(SDSORT_USES_RAM)
#define _SORT_CMP_NODIR() (strcasecmp(sortnames[o1], sortnames[o2]) > 0)
#else
#define _SORT_CMP_NODIR() (strcasecmp(name1, name2) > 0)
#endif
#define _SORT_CMP_FILE() (strcasecmp(TERN(SDSORT_USES_RAM, sortnames[o1], name1), TERN(SDSORT_USES_RAM, sortnames[o2], name2)) > 0)
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_USES_RAM)
// Folder sorting needs an index and bit to test for folder-ness.
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_NODIR() : IS_DIR(fs > 0 ? o1 : o2))
#define _SORT_CMP_DIR(fs) (IS_DIR(o1) == IS_DIR(o2) ? _SORT_CMP_FILE() : IS_DIR(fs > 0 ? o1 : o2))
#else
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_NODIR() : (fs > 0 ? dir1 : !dir1))
#define _SORT_CMP_DIR(fs) ((dir1 == flag.filenameIsDir) ? _SORT_CMP_FILE() : (fs > 0 ? dir1 : !dir1))
#endif
#endif
@ -1208,12 +1201,12 @@ void CardReader::cdroot() {
if (
#if HAS_FOLDER_SORTING
#if ENABLED(SDSORT_GCODE)
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_NODIR()
sort_folders ? _SORT_CMP_DIR(sort_folders) : _SORT_CMP_FILE()
#else
_SORT_CMP_DIR(SDSORT_FOLDERS)
#endif
#else
_SORT_CMP_NODIR()
_SORT_CMP_FILE()
#endif
) {
// Reorder the index, indicate that sorting happened
@ -1236,7 +1229,7 @@ void CardReader::cdroot() {
// Using RAM but not keeping names around
#if ENABLED(SDSORT_USES_RAM) && DISABLED(SDSORT_CACHE_NAMES)
#if ENABLED(SDSORT_DYNAMIC_RAM)
for (uint16_t i = 0; i < fileCnt; ++i) free(sortnames[i]);
for (int16_t i = 0; i < fileCnt; ++i) free(sortnames[i]);
TERN_(HAS_FOLDER_SORTING, delete [] isDir);
#endif
#endif
@ -1247,12 +1240,14 @@ void CardReader::cdroot() {
#if ENABLED(SDSORT_DYNAMIC_RAM)
sortnames = new char*[1];
sortshort = new char*[1];
isDir = new uint8_t[1];
#endif
selectFileByIndex(0);
SET_SORTNAME(0);
SET_SORTSHORT(0);
isDir[0] = flag.filenameIsDir;
#if ALL(HAS_FOLDER_SORTING, SDSORT_DYNAMIC_RAM)
isDir = new uint8_t[1];
isDir[0] = flag.filenameIsDir;
#endif
#endif
}
@ -1279,15 +1274,10 @@ void CardReader::cdroot() {
#endif // SDCARD_SORT_ALPHA
uint16_t CardReader::get_num_Files() {
int16_t CardReader::get_num_items() {
if (!isMounted()) return 0;
return (
#if ALL(SDCARD_SORT_ALPHA, SDSORT_USES_RAM, SDSORT_CACHE_NAMES)
nrFiles // no need to access the SD card for filenames
#else
countFilesInWorkDir()
#endif
);
if (nrItems < 0) nrItems = countVisibleItems(workDir);
return nrItems;
}
//

View File

@ -39,12 +39,6 @@ extern const char M23_STR[], M24_STR[];
#endif
#endif
#if ENABLED(SDCARD_RATHERRECENTFIRST) && DISABLED(SDCARD_SORT_ALPHA)
#define SD_ORDER(N,C) ((C) - 1 - (N))
#else
#define SD_ORDER(N,C) N
#endif
#define MAX_DIR_DEPTH 10 // Maximum folder depth
#define MAXDIRNAMELENGTH 8 // DOS folder name size
#define MAXPATHNAMELENGTH (1 + (MAXDIRNAMELENGTH + 1) * (MAX_DIR_DEPTH) + 1 + FILENAME_LENGTH) // "/" + N * ("ADIRNAME/") + "filename.ext"
@ -153,11 +147,10 @@ public:
static void cdroot();
static void cd(const char *relpath);
static int8_t cdup();
static uint16_t countFilesInWorkDir();
static uint16_t get_num_Files();
static int16_t get_num_items();
// Select a file
static void selectFileByIndex(const uint16_t nr);
static void selectFileByIndex(const int16_t nr);
static void selectFileByName(const char * const match); // (working directory only)
// Print job
@ -200,14 +193,16 @@ public:
#if ENABLED(SDCARD_SORT_ALPHA)
static void presort();
static void getfilename_sorted(const uint16_t nr);
static void selectFileByIndexSorted(const int16_t nr);
#if ENABLED(SDSORT_GCODE)
FORCE_INLINE static void setSortOn(bool b) { sort_alpha = b; presort(); }
FORCE_INLINE static void setSortFolders(int i) { sort_folders = i; presort(); }
FORCE_INLINE static void setSortFolders(const int8_t i) { sort_folders = i; presort(); }
//FORCE_INLINE static void setSortReverse(bool b) { sort_reverse = b; }
#endif
#else
FORCE_INLINE static void getfilename_sorted(const uint16_t nr) { selectFileByIndex(nr); }
FORCE_INLINE static void selectFileByIndexSorted(const int16_t nr) {
selectFileByIndex(TERN(SDCARD_RATHERRECENTFIRST, get_num_items() - 1 - nr, (nr)));
}
#endif
static void ls(const uint8_t lsflags=0);
@ -265,15 +260,16 @@ private:
//
static MediaFile root, workDir, workDirParents[MAX_DIR_DEPTH];
static uint8_t workDirDepth;
static int16_t nrItems; // Cache the total count
//
// Alphabetical file and folder sorting
//
#if ENABLED(SDCARD_SORT_ALPHA)
static uint16_t sort_count; // Count of sorted items in the current directory
static int16_t sort_count; // Count of sorted items in the current directory
#if ENABLED(SDSORT_GCODE)
static bool sort_alpha; // Flag to enable / disable the feature
static int sort_folders; // Folder sorting before/none/after
static int8_t sort_folders; // Folder sorting before/none/after
//static bool sort_reverse; // Flag to enable / disable reverse sorting
#endif
@ -297,7 +293,6 @@ private:
// If using dynamic ram for names, allocate on the heap.
#if ENABLED(SDSORT_CACHE_NAMES)
static uint16_t nrFiles; // Cache the total count
#if ENABLED(SDSORT_DYNAMIC_RAM)
static char **sortshort, **sortnames;
#else
@ -342,8 +337,8 @@ private:
// Directory items
//
static bool is_visible_entity(const dir_t &p OPTARG(CUSTOM_FIRMWARE_UPLOAD, const bool onlyBin=false));
static int countItems(MediaFile dir);
static void selectByIndex(MediaFile dir, const uint8_t index);
static int16_t countVisibleItems(MediaFile dir);
static void selectByIndex(MediaFile dir, const int16_t index);
static void selectByName(MediaFile dir, const char * const match);
static void printListing(
MediaFile parent, const char * const prepend, const uint8_t lsflags