# This is a combination of 4 commits.
# The first commit's message is: SD Card Alpha Sorting First iteration of alphabetical sorting for SD cards, both slow+efficient and fast+rammy. Option for folders to sort first, last, or not at all. # This is the 2nd commit message: Expand on More RAM concept, address minor bugs # This is the 3rd commit message: Improvements, more SORT_USES_MORE_RAM With this option, always keeps the dir in RAM, doubling as a cache for getfilename. A board with only 8K of SRAM is cutting it very close. # This is the 4th commit message: Completed SORT_USES_MORE_RAM implementation For the MORE_RAM option we need to buffer both the short and long names, even though long names are sometimes redundant. Worst case, all the names are max length. We can save some RAM by not storing these. We could save more RAM by only storing the visible part of the long name.
This commit is contained in:
parent
1977b4490f
commit
de725bd408
@ -290,6 +290,7 @@
|
||||
#define SD_FINISHED_STEPPERRELEASE true //if sd support and the file is finished: disable steppers?
|
||||
#define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place.
|
||||
|
||||
#define SDCARD_SORT_ALPHA // Sort in ASCII order by pre-reading the folder and making a lookup table!
|
||||
#define SDCARD_RATHERRECENTFIRST //reverse file order of sd card menu display. Its sorted practically after the file system block order.
|
||||
// if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that.
|
||||
// using:
|
||||
|
@ -111,10 +111,12 @@ uint8_t const SOFT_SPI_SCK_PIN = 13;
|
||||
/**
|
||||
* Defines for long (vfat) filenames
|
||||
*/
|
||||
/** Number of UTF-16 characters per entry */
|
||||
#define FILENAME_LENGTH 13
|
||||
/** Number of VFAT entries used. Every entry has 13 UTF-16 characters */
|
||||
#define MAX_VFAT_ENTRIES (2)
|
||||
/** Total size of the buffer used to store the long filenames */
|
||||
#define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1)
|
||||
#define LONG_FILENAME_LENGTH (FILENAME_LENGTH*MAX_VFAT_ENTRIES+1)
|
||||
#endif // SdFatConfig_h
|
||||
|
||||
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
CardReader::CardReader()
|
||||
{
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
sort_count = 0;
|
||||
#endif
|
||||
filesize = 0;
|
||||
sdpos = 0;
|
||||
sdprinting = false;
|
||||
@ -33,19 +36,15 @@ CardReader::CardReader()
|
||||
autostart_atmillis=millis()+5000;
|
||||
}
|
||||
|
||||
char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
|
||||
char *createFilename(char *buffer, const dir_t &p) //buffer>12characters
|
||||
{
|
||||
char *pos=buffer;
|
||||
for (uint8_t i = 0; i < 11; i++)
|
||||
{
|
||||
if (p.name[i] == ' ')continue;
|
||||
if (i == 8)
|
||||
{
|
||||
*pos++='.';
|
||||
}
|
||||
*pos++=p.name[i];
|
||||
for (uint8_t i = 0; i < 11; i++) {
|
||||
if (p.name[i] == ' ') continue;
|
||||
if (i == 8) *pos++ = '.';
|
||||
*pos++ = p.name[i];
|
||||
}
|
||||
*pos++=0;
|
||||
*pos++ = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@ -53,15 +52,15 @@ char *createFilename(char *buffer,const dir_t &p) //buffer>12characters
|
||||
void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
{
|
||||
dir_t p;
|
||||
uint8_t cnt=0;
|
||||
uint8_t cnt=0;
|
||||
|
||||
while (parent.readDir(p, longFilename) > 0)
|
||||
{
|
||||
if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint
|
||||
{
|
||||
|
||||
char path[13*2];
|
||||
char lfilename[13];
|
||||
char path[FILENAME_LENGTH*2];
|
||||
char lfilename[FILENAME_LENGTH];
|
||||
createFilename(lfilename,p);
|
||||
|
||||
path[0]=0;
|
||||
@ -87,8 +86,6 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
}
|
||||
lsDive(path,dir);
|
||||
//close done automatically by destructor of SdFile
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -101,11 +98,10 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
if ( p.name[1] != '.')
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
|
||||
filenameIsDir=DIR_IS_SUBDIR(&p);
|
||||
|
||||
|
||||
|
||||
if(!filenameIsDir)
|
||||
{
|
||||
if(p.name[8]!='G') continue;
|
||||
@ -124,10 +120,8 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
}
|
||||
else if(lsAction==LS_GetFilename)
|
||||
{
|
||||
if(cnt==nrFiles)
|
||||
return;
|
||||
if (cnt == nrFiles) return;
|
||||
cnt++;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,9 +130,6 @@ void CardReader::lsDive(const char *prepend,SdFile parent)
|
||||
void CardReader::ls()
|
||||
{
|
||||
lsAction=LS_SerialPrint;
|
||||
if(lsAction==LS_Count)
|
||||
nrFiles=0;
|
||||
|
||||
root.rewind();
|
||||
lsDive("",root);
|
||||
}
|
||||
@ -177,6 +168,9 @@ void CardReader::initsd()
|
||||
}
|
||||
workDir=root;
|
||||
curDir=&root;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
/*
|
||||
if(!workDir.openRoot(&volume))
|
||||
{
|
||||
@ -193,8 +187,10 @@ void CardReader::setroot()
|
||||
SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL);
|
||||
}*/
|
||||
workDir=root;
|
||||
|
||||
curDir=&workDir;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
void CardReader::release()
|
||||
{
|
||||
@ -207,6 +203,7 @@ void CardReader::startFileprint()
|
||||
if(cardOK)
|
||||
{
|
||||
sdprinting = true;
|
||||
flush_presort();
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,7 +232,7 @@ void CardReader::getAbsFilename(char *t)
|
||||
while(*t!=0 && cnt< MAXPATHNAMELENGTH)
|
||||
{t++;cnt++;} //crawl counter forward.
|
||||
}
|
||||
if(cnt<MAXPATHNAMELENGTH-13)
|
||||
if(cnt<MAXPATHNAMELENGTH-FILENAME_LENGTH)
|
||||
file.getFilename(t);
|
||||
else
|
||||
t[0]=0;
|
||||
@ -305,7 +302,7 @@ void CardReader::openFile(char* name,bool read, bool replace_current/*=true*/)
|
||||
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name));
|
||||
if(dirname_end>0 && dirname_end>dirname_start)
|
||||
{
|
||||
char subdirname[13];
|
||||
char subdirname[FILENAME_LENGTH];
|
||||
strncpy(subdirname, dirname_start, dirname_end-dirname_start);
|
||||
subdirname[dirname_end-dirname_start]=0;
|
||||
SERIAL_ECHOLN(subdirname);
|
||||
@ -401,7 +398,7 @@ void CardReader::removeFile(char* name)
|
||||
//SERIAL_ECHO("end :");SERIAL_ECHOLN((int)(dirname_end-name));
|
||||
if(dirname_end>0 && dirname_end>dirname_start)
|
||||
{
|
||||
char subdirname[13];
|
||||
char subdirname[FILENAME_LENGTH];
|
||||
strncpy(subdirname, dirname_start, dirname_end-dirname_start);
|
||||
subdirname[dirname_end-dirname_start]=0;
|
||||
SERIAL_ECHOLN(subdirname);
|
||||
@ -439,6 +436,9 @@ void CardReader::removeFile(char* name)
|
||||
SERIAL_PROTOCOLPGM("File deleted:");
|
||||
SERIAL_PROTOCOLLN(fname);
|
||||
sdpos = 0;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -552,14 +552,21 @@ void CardReader::closefile(bool store_location)
|
||||
|
||||
}
|
||||
|
||||
void CardReader::getfilename(const uint8_t nr)
|
||||
void CardReader::getfilename(const uint16_t nr)
|
||||
{
|
||||
#if defined(SDCARD_SORT_ALPHA) && SORT_USES_RAM && SORT_USES_MORE_RAM
|
||||
if (nr < sort_count) {
|
||||
strcpy(filename, sortshort[nr]);
|
||||
strcpy(longFilename, sortnames[nr]);
|
||||
filenameIsDir = isDir[nr];
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
curDir=&workDir;
|
||||
lsAction=LS_GetFilename;
|
||||
nrFiles=nr;
|
||||
curDir->rewind();
|
||||
lsDive("",*curDir);
|
||||
|
||||
}
|
||||
|
||||
uint16_t CardReader::getnrfilenames()
|
||||
@ -577,7 +584,7 @@ void CardReader::chdir(const char * relpath)
|
||||
{
|
||||
SdFile newfile;
|
||||
SdFile *parent=&root;
|
||||
|
||||
|
||||
if(workDir.isOpen())
|
||||
parent=&workDir;
|
||||
|
||||
@ -595,21 +602,167 @@ void CardReader::chdir(const char * relpath)
|
||||
workDirParents[0]=*parent;
|
||||
}
|
||||
workDir=newfile;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CardReader::updir()
|
||||
{
|
||||
if(workDirDepth > 0)
|
||||
if (workDirDepth > 0)
|
||||
{
|
||||
--workDirDepth;
|
||||
workDir = workDirParents[0];
|
||||
int d;
|
||||
for (int d = 0; d < workDirDepth; d++)
|
||||
workDirParents[d] = workDirParents[d+1];
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
|
||||
/**
|
||||
* Get the name of a file in the current directory by sort-index
|
||||
*/
|
||||
void CardReader::getfilename_sorted(const uint16_t nr) {
|
||||
getfilename(nr < sort_count ? sort_order[nr] : nr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all the files and produce a sort key
|
||||
*
|
||||
* We can do this in 3 ways...
|
||||
* - Minimal RAM: Read two filenames at a time sorting along...
|
||||
* - Some RAM: Buffer the directory and return filenames from RAM
|
||||
* - Some RAM: Buffer the directory just for this sort
|
||||
*/
|
||||
void CardReader::presort()
|
||||
{
|
||||
flush_presort();
|
||||
|
||||
uint16_t fileCnt = getnrfilenames();
|
||||
if (fileCnt > 0) {
|
||||
|
||||
if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT;
|
||||
|
||||
#if SORT_USES_RAM
|
||||
#if SORT_USES_MORE_RAM
|
||||
sortshort = (char**)calloc(fileCnt, sizeof(char*));
|
||||
sortnames = (char**)calloc(fileCnt, sizeof(char*));
|
||||
#else
|
||||
char *sortnames[fileCnt];
|
||||
#endif
|
||||
#else
|
||||
char name1[LONG_FILENAME_LENGTH+1];
|
||||
#endif
|
||||
|
||||
#if FOLDER_SORTING != 0
|
||||
#if SORT_USES_RAM && SORT_USES_MORE_RAM
|
||||
isDir = (uint8_t*)calloc(fileCnt, sizeof(uint8_t));
|
||||
#else
|
||||
uint8_t isDir[fileCnt];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
sort_order = new uint8_t[fileCnt];
|
||||
|
||||
if (fileCnt > 1) {
|
||||
|
||||
// Init sort order. If using RAM then read all filenames now.
|
||||
for (uint16_t i=0; i<fileCnt; i++) {
|
||||
sort_order[i] = i;
|
||||
#if SORT_USES_RAM
|
||||
getfilename(i);
|
||||
sortnames[i] = strdup(longFilename[0] ? longFilename : filename);
|
||||
#if SORT_USES_MORE_RAM
|
||||
sortshort[i] = strdup(filename);
|
||||
#endif
|
||||
// char out[30];
|
||||
// sprintf_P(out, PSTR("---- %i %s %s"), i, filenameIsDir ? "D" : " ", sortnames[i]);
|
||||
// SERIAL_ECHOLN(out);
|
||||
#if FOLDER_SORTING != 0
|
||||
isDir[i] = filenameIsDir;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Bubble Sort
|
||||
for (uint16_t i=fileCnt; --i;) {
|
||||
bool cmp, didSwap = false;
|
||||
for (uint16_t j=0; j<i; ++j) {
|
||||
uint16_t s1 = j, s2 = j+1, o1 = sort_order[s1], o2 = sort_order[s2];
|
||||
#if SORT_USES_RAM
|
||||
#if FOLDER_SORTING != 0
|
||||
cmp = (isDir[o1] == isDir[o2]) ? (strcasecmp(sortnames[o1], sortnames[o2]) > 0) : isDir[FOLDER_SORTING > 0 ? o1 : o2];
|
||||
#else
|
||||
cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0;
|
||||
#endif
|
||||
#else
|
||||
getfilename(o1);
|
||||
strcpy(name1, longFilename[0] ? longFilename : filename);
|
||||
#if FOLDER_SORTING != 0
|
||||
bool dir1 = filenameIsDir;
|
||||
#endif
|
||||
getfilename(o2);
|
||||
char *name2 = longFilename[0] ? longFilename : filename;
|
||||
#if FOLDER_SORTING != 0
|
||||
cmp = (dir1 == filenameIsDir) ? (strcasecmp(name1, name2) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1);
|
||||
#else
|
||||
cmp = strcasecmp(name1, name2) > 0;
|
||||
#endif
|
||||
#endif
|
||||
if (cmp) {
|
||||
// char out[LONG_FILENAME_LENGTH*2+20];
|
||||
// sprintf_P(out, PSTR("Swap %i %s for %i %s"), o1, sortnames[o1], o2, sortnames[o2]);
|
||||
// SERIAL_ECHOLN(out);
|
||||
sort_order[s1] = o2;
|
||||
sort_order[s2] = o1;
|
||||
didSwap = true;
|
||||
}
|
||||
}
|
||||
if (!didSwap) break;
|
||||
}
|
||||
|
||||
#if SORT_USES_RAM && !SORT_USES_MORE_RAM
|
||||
for (uint16_t i=0; i<fileCnt; ++i) free(sortnames[i]);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
sort_order[0] = 0;
|
||||
#if SORT_USES_RAM && SORT_USES_MORE_RAM
|
||||
sortnames = (char**)malloc(sizeof(char*));
|
||||
sortshort = (char**)malloc(sizeof(char*));
|
||||
isDir = (uint8_t*)malloc(sizeof(uint8_t));
|
||||
getfilename(0);
|
||||
sortnames[0] = strdup(longFilename[0] ? longFilename : filename);
|
||||
sortshort[0] = strdup(filename);
|
||||
isDir[0] = filenameIsDir;
|
||||
#endif
|
||||
}
|
||||
|
||||
sort_count = fileCnt;
|
||||
}
|
||||
}
|
||||
|
||||
void CardReader::flush_presort() {
|
||||
if (sort_count > 0) {
|
||||
#if SORT_USES_RAM && SORT_USES_MORE_RAM
|
||||
for (uint8_t i=0; i<sort_count; ++i) {
|
||||
free(sortshort[i]);
|
||||
free(sortnames[i]);
|
||||
}
|
||||
free(sortshort);
|
||||
free(sortnames);
|
||||
#endif
|
||||
delete sort_order;
|
||||
sort_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SDCARD_SORT_ALPHA
|
||||
|
||||
void CardReader::printingHasFinished()
|
||||
{
|
||||
@ -633,6 +786,9 @@ void CardReader::printingHasFinished()
|
||||
enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND));
|
||||
}
|
||||
autotempShutdown();
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
presort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif //SDSUPPORT
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
#ifdef SDSUPPORT
|
||||
|
||||
#define MAX_DIR_DEPTH 10
|
||||
#define MAX_DIR_DEPTH 10 // Maximum folder depth
|
||||
#define SORT_USES_RAM false // Buffer while sorting, else re-read from SD
|
||||
#define SORT_USES_MORE_RAM false // Always keep the directory in RAM
|
||||
#define SORT_LIMIT 64 // Maximum number of sorted items
|
||||
#define FOLDER_SORTING -1 // -1=above 0=none 1=below
|
||||
|
||||
#include "SdFile.h"
|
||||
enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename};
|
||||
@ -28,7 +32,7 @@ public:
|
||||
void getStatus();
|
||||
void printingHasFinished();
|
||||
|
||||
void getfilename(const uint8_t nr);
|
||||
void getfilename(const uint16_t nr);
|
||||
uint16_t getnrfilenames();
|
||||
|
||||
void getAbsFilename(char *t);
|
||||
@ -39,6 +43,12 @@ public:
|
||||
void updir();
|
||||
void setroot();
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
void presort();
|
||||
void flush_presort();
|
||||
void getfilename_sorted(const uint16_t nr);
|
||||
#endif
|
||||
|
||||
|
||||
FORCE_INLINE bool isFileOpen() { return file.isOpen(); }
|
||||
FORCE_INLINE bool eof() { return sdpos>=filesize ;};
|
||||
@ -50,20 +60,29 @@ public:
|
||||
public:
|
||||
bool saving;
|
||||
bool logging;
|
||||
bool sdprinting ;
|
||||
bool cardOK ;
|
||||
char filename[13];
|
||||
bool sdprinting;
|
||||
bool cardOK;
|
||||
char filename[FILENAME_LENGTH];
|
||||
char longFilename[LONG_FILENAME_LENGTH];
|
||||
bool filenameIsDir;
|
||||
int lastnr; //last number of the autostart;
|
||||
private:
|
||||
SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH];
|
||||
uint16_t workDirDepth;
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
uint16_t sort_count;
|
||||
uint8_t *sort_order;
|
||||
#if SORT_USES_MORE_RAM
|
||||
char **sortshort;
|
||||
char **sortnames;
|
||||
uint8_t *isDir;
|
||||
#endif
|
||||
#endif
|
||||
Sd2Card card;
|
||||
SdVolume volume;
|
||||
SdFile file;
|
||||
#define SD_PROCEDURE_DEPTH 1
|
||||
#define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
|
||||
#define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1)
|
||||
uint8_t file_subcall_ctr;
|
||||
uint32_t filespos[SD_PROCEDURE_DEPTH];
|
||||
char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH];
|
||||
@ -75,7 +94,7 @@ private:
|
||||
bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware.
|
||||
|
||||
LsAction lsAction; //stored for recursion.
|
||||
int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
|
||||
uint16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory.
|
||||
char* diveDirName;
|
||||
void lsDive(const char *prepend,SdFile parent);
|
||||
};
|
||||
|
@ -993,9 +993,9 @@ void lcd_sdcard_menu()
|
||||
card.getWorkDirName();
|
||||
if(card.filename[0]=='/')
|
||||
{
|
||||
#if SDCARDDETECT == -1
|
||||
#if SDCARDDETECT == -1
|
||||
MENU_ITEM(function, LCD_STR_REFRESH MSG_REFRESH, lcd_sd_refresh);
|
||||
#endif
|
||||
#endif
|
||||
}else{
|
||||
MENU_ITEM(function, LCD_STR_FOLDER "..", lcd_sd_updir);
|
||||
}
|
||||
@ -1004,16 +1004,23 @@ void lcd_sdcard_menu()
|
||||
{
|
||||
if (_menuItemNr == _lineNr)
|
||||
{
|
||||
#ifndef SDCARD_RATHERRECENTFIRST
|
||||
card.getfilename(i);
|
||||
#if defined(SDCARD_RATHERRECENTFIRST) && !defined(SDCARD_SORT_ALPHA)
|
||||
int nr = fileCnt-1-i;
|
||||
#else
|
||||
card.getfilename(fileCnt-1-i);
|
||||
int nr = i;
|
||||
#endif
|
||||
if (card.filenameIsDir)
|
||||
{
|
||||
MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
}else{
|
||||
MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
|
||||
#ifdef SDCARD_SORT_ALPHA
|
||||
card.getfilename_sorted(nr);
|
||||
#else
|
||||
card.getfilename(nr);
|
||||
#endif
|
||||
|
||||
if (card.filenameIsDir) {
|
||||
MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
}
|
||||
else {
|
||||
MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename);
|
||||
}
|
||||
}else{
|
||||
MENU_ITEM_DUMMY();
|
||||
@ -1219,7 +1226,7 @@ void lcd_init()
|
||||
#endif // SR_LCD_2W_NL
|
||||
#endif//!NEWPANEL
|
||||
|
||||
#if defined (SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
|
||||
#if defined(SDSUPPORT) && defined(SDCARDDETECT) && (SDCARDDETECT > 0)
|
||||
pinMode(SDCARDDETECT,INPUT);
|
||||
WRITE(SDCARDDETECT, HIGH);
|
||||
lcd_oldcardstatus = IS_SD_INSERTED;
|
||||
|
Loading…
Reference in New Issue
Block a user