无名科技ESP32_SUPERMINI 开发板SD卡读写初始化不成功解决方法(Arduino环境)

在运行ESP32SUPERMINI的SD卡例程时遇到报错,显示初始化不成功,解决方法,使用软件SPI,测试有效,参考代码如下:(后有软件SPISD卡读取文件)

以下是我的解决方案,如果不正确可能是我的资料提供有误,但总体思路是使用软件SPI替代硬件SPI,可以自己去尝试。

示例代码

//调用库及宏定义方面
  //OLED相关
    #include <U8g2lib.h>
    #ifdef U8X8_HAVE_HW_SPI
    #include <SPI.h>
    #endif
    #ifdef U8X8_HAVE_HW_I2C
    #include <Wire.h>
    #endif
    U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ 3, /* data=*/ 2, /* reset=*/ U8X8_PIN_NONE);    //Low spped I2C
    
    //定义OLED屏接口
  //micro SD相关
    #include <mySD.h>
    ext::File root;
  //自设定函数
  void printDirectory(ext::File dir, int numTabs) {
  
  while(true) {
     ext::File entry =  dir.openNextFile();
     if (! entry) {
       break;
     }
     for (uint8_t i=0; i<numTabs; i++) {
       Serial.print('\t');   // we'll have a nice indentation
     }
     // Print the name
     Serial.print(entry.name());
     /*   递归目录,否则打印文件大小 */
     if (entry.isDirectory()) {
       Serial.println("/");
       printDirectory(entry, numTabs+1);
     } else {
       /*   files have sizes, directories do not */
       Serial.print("\t\t");
       Serial.println(entry.size());
     }
     entry.close();
   }
}
//全局变量设置
  //设定每行字数变量
    int Ln=0;//列数
    int col=0;//行数
    int page=0;//页数
    int num=0;//字数
    int by=32;
    int i=0;
    int j=0;
  //设定汉字字符数组尝试
    char ch[32]={};
    char sram[9600]={};
    //设置标志位以确定向后翻页还是向前翻页
    int latest=0;//确定最新记录位
//初始化设定

void setup() {
  Serial.begin(115200);//启动串口

  //oled初始化
  u8g2.begin();//u8g2库初始化
  u8g2.setFont(u8g2_font_wqy12_t_gb2312b);//设置中文字符集
  u8g2.enableUTF8Print(); //启用打印功能

  //sd卡初始化
  Serial.print("Initializing SD card...");//初始化中
  if (!SD.begin(7, 6, 5, 4)) {Serial.println("initialization failed!");return;}
  //SD卡接口初始化
  Serial.println("initialization done.");//初始化完成
  root = SD.open("/");
  if (root) {printDirectory(root, 0);root.close();} else {Serial.println("error opening file");}
  //验证能否打开文件
  root = SD.open("TEXT.txt");//打开TEXT文件
  //下一页按钮初始化 0 21
  pinMode(0,OUTPUT);
  pinMode(21,INPUT);
  digitalWrite(0,HIGH);
  //上一页按钮初始化 1 20
  pinMode(1,OUTPUT);
  pinMode(20,INPUT);
  digitalWrite(1,HIGH);
}

void loop() {
  
  
 
    if(digitalRead(21)==1){nextpage();}
    if(digitalRead(20)==1){lastpage();}
    if(num==1500){while(sram[i]){sram[i]=0;i++;}i=0;latest=0;num=0;}Serial.println(num);
    //if(digitalRead(21==1)&&digitalRead(20)==1){delay(50);if(digitalRead(21==1)&&digitalRead(20)==1)j=100;}
    //if(digitalRead(21==1)&&digitalRead(20)==1){delay(50);if(digitalRead(21==1)&&digitalRead(20)==1)while(j<100){nextpage();j++;if(num==1500){while(sram[i]){sram[i]=0;i++;}i=0;latest=0;num=0;}}j=0;}
  u8g2.sendBuffer();
}
//下一页函数
void nextpage()
{
  page++;
 u8g2.clearBuffer(); 

  //先判断是否到达最新进度
  if(num>=latest)
    { //第一行
    
  u8g2.setCursor(0, 12);
  if(root){while(Ln<by){  sram[num]=root.read(); ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}}
  Ln=0;
  //第二行
  u8g2.setCursor(0, 24);
  if(root){while(Ln<by){   sram[num]=root.read(); ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}}
  Ln=0;
  //第三行
  u8g2.setCursor(0, 36);
  if(root){while(Ln<by){  sram[num]=root.read(); ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}}
  Ln=0;
  //第四行
  u8g2.setCursor(0, 48);
  if(root){while(Ln<by){  sram[num]=root.read(); ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}}
  Ln=0;
  //第五行
  u8g2.setCursor(0, 60);
  if(root){while(Ln<by-10){   sram[num]=root.read(); ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}}
  Ln=0;
  u8g2.setCursor(110, 60);
  u8g2.print(page);
  }
  //如果进度不是最新的
  if(num<latest)
  {u8g2.setCursor(0, 12);
  while(Ln<by){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第二行
  u8g2.setCursor(0, 24);
  while(Ln<by){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第三行
  u8g2.setCursor(0, 36);
  while(Ln<by){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第四行
  u8g2.setCursor(0, 48);
  while(Ln<by){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第五行
  u8g2.setCursor(0, 60);
  while(Ln<by-10){   ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  }
    u8g2.setCursor(110, 60);
  u8g2.print(page);
  Ln=0;if(num>=latest){latest=num;} u8g2.sendBuffer();
}
//上一页函数
void lastpage()
{
  page--;
  u8g2.clearBuffer(); 
 
  if( num>=316 )
    {
      num=num-316;
    }
     //第一行
  u8g2.setCursor(0, 12);
  while(Ln<32){   ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第二行
  u8g2.setCursor(0, 24);
  while(Ln<32){   ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Serial.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第三行
  u8g2.setCursor(0, 36);
  while(Ln<32){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Serial.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第四行
  u8g2.setCursor(0, 48);
  while(Ln<32){  ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Serial.print(ch[Ln]);Ln++;num++;}
  Ln=0;
  //第五行
  u8g2.setCursor(0, 60);
  while(Ln<22){   ch[Ln] =sram[num] ;u8g2.print(ch[Ln]);Serial.print(ch[Ln]);Ln++;num++;}
  Ln=0;
    u8g2.setCursor(110, 60);
  u8g2.print(page);
   u8g2.sendBuffer();
}

mySD.cpp

/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics


 This library provides four key benefits:

   * Including `SD.h` automatically creates a global
     `SD` object which can be interacted with in a similar
     manner to other standard global objects like `Serial` and `Ethernet`.

   * Boilerplate initialisation code is contained in one method named 
     `begin` and no further objects need to be created in order to access
     the SD card.

   * Calls to `open` can supply a full path name including parent 
     directories which simplifies interacting with files in subdirectories.

   * Utility methods are provided to determine whether a file exists
     and to create a directory heirarchy.


  Note however that not all functionality provided by the underlying
  sdfatlib library is exposed.

 */

/*

  Implementation Notes

  In order to handle multi-directory path traversal, functionality that 
  requires this ability is implemented as callback functions.

  Individual methods call the `walkPath` function which performs the actual
  directory traversal (swapping between two different directory/file handles
  along the way) and at each level calls the supplied callback function.

  Some types of functionality will take an action at each level (e.g. exists
  or make directory) which others will only take an action at the bottom
  level (e.g. open).

 */

#include "mySD.h"

// Used by `getNextPathComponent`
#define MAX_COMPONENT_LEN 12 // What is max length?
#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1

bool getNextPathComponent(char *path, unsigned int *p_offset,
        char *buffer) {
  /*

    Parse individual path components from a path.

      e.g. after repeated calls '/foo/bar/baz' will be split
           into 'foo', 'bar', 'baz'.

    This is similar to `strtok()` but copies the component into the
    supplied buffer rather than modifying the original string.


    `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.

    `p_offset` needs to point to an integer of the offset at
    which the previous path component finished.

    Returns `true` if more components remain.

    Returns `false` if this is the last component.
      (This means path ended with 'foo' or 'foo/'.)

   */

  // TODO: Have buffer local to this function, so we know it's the
  //       correct length?

  int bufferOffset = 0;

  int offset = *p_offset;

  // Skip root or other separator
  if (path[offset] == '/') {
    offset++;
  }
  
  // Copy the next next path segment
  while (bufferOffset < MAX_COMPONENT_LEN
   && (path[offset] != '/')
   && (path[offset] != '\0')) {
    buffer[bufferOffset++] = path[offset++];
  }

  buffer[bufferOffset] = '\0';

  // Skip trailing separator so we can determine if this
  // is the last component in the path or not.
  if (path[offset] == '/') {
    offset++;
  }

  *p_offset = offset;

  return (path[offset] != '\0');
}



boolean walkPath(char *filepath, SdFile& parentDir,
     boolean (*callback)(SdFile& parentDir,
             char *filePathComponent,
             boolean isLastComponent,
             void *object),
     void *object = NULL) {
  /*
     
     When given a file path (and parent directory--normally root),
     this function traverses the directories in the path and at each
     level calls the supplied callback function while also providing
     the supplied object for context if required.

       e.g. given the path '/foo/bar/baz'
            the callback would be called at the equivalent of
      '/foo', '/foo/bar' and '/foo/bar/baz'.

     The implementation swaps between two different directory/file
     handles as it traverses the directories and does not use recursion
     in an attempt to use memory efficiently.

     If a callback wishes to stop the directory traversal it should
     return false--in this case the function will stop the traversal,
     tidy up and return false.

     If a directory path doesn't exist at some point this function will
     also return false and not subsequently call the callback.

     If a directory path specified is complete, valid and the callback
     did not indicate the traversal should be interrupted then this
     function will return true.

   */


  SdFile subfile1;
  SdFile subfile2;

  char buffer[PATH_COMPONENT_BUFFER_LEN]; 

  unsigned int offset = 0;

  SdFile *p_parent;
  SdFile *p_child;

  SdFile *p_tmp_sdfile;  
  
  p_child = &subfile1;
  
  p_parent = &parentDir;

  while (true) {

    boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);

    boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);

    if (!shouldContinue) {
      // TODO: Don't repeat this code?
      // If it's one we've created then we
      // don't need the parent handle anymore.
      if (p_parent != &parentDir) {
        (*p_parent).close();
      }
      return false;
    }
    
    if (!moreComponents) {
      break;
    }
    
    boolean exists = (*p_child).open(*p_parent, buffer, F_RDONLY);

    // If it's one we've created then we
    // don't need the parent handle anymore.
    if (p_parent != &parentDir) {
      (*p_parent).close();
    }
    
    // Handle case when it doesn't exist and we can't continue...
    if (exists) {
      // We alternate between two file handles as we go down
      // the path.
      if (p_parent == &parentDir) {
        p_parent = &subfile2;
      }

      p_tmp_sdfile = p_parent;
      p_parent = p_child;
      p_child = p_tmp_sdfile;
    } else {
      return false;
    }
  }
  
  if (p_parent != &parentDir) {
    (*p_parent).close(); // TODO: Return/ handle different?
  }

  return true;
}



/*

   The callbacks used to implement various functionality follow.

   Each callback is supplied with a parent directory handle,
   character string with the name of the current file path component,
   a flag indicating if this component is the last in the path and
   a pointer to an arbitrary object used for context.

 */

boolean callback_pathExists(SdFile& parentDir, char *filePathComponent, 
          boolean isLastComponent, void *object) {
  /*

    Callback used to determine if a file/directory exists in parent
    directory.

    Returns true if file path exists.

  */
  SdFile child;

  boolean exists = child.open(parentDir, filePathComponent, F_RDONLY);
  
  if (exists) {
     child.close(); 
  }
  
  return exists;
}



boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent, 
           boolean isLastComponent, void *object) {
  /*

    Callback used to create a directory in the parent directory if
    it does not already exist.

    Returns true if a directory was created or it already existed.

  */
  boolean result = false;
  SdFile child;
  
  result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
  if (!result) {
    result = child.makeDir(parentDir, filePathComponent);
  } 
  
  return result;
}


  /*

boolean callback_openPath(SdFile& parentDir, char *filePathComponent, 
        boolean isLastComponent, void *object) {

    Callback used to open a file specified by a filepath that may
    specify one or more directories above it.

    Expects the context object to be an instance of `SDClass` and
    will use the `file` property of the instance to open the requested
    file/directory with the associated file open mode property.

    Always returns true if the directory traversal hasn't reached the
    bottom of the directory heirarchy.

    Returns false once the file has been opened--to prevent the traversal
    from descending further. (This may be unnecessary.)

  if (isLastComponent) {
    SDClass *p_SD = static_cast<SDClass*>(object);
    p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
    if (p_SD->fileOpenMode == FILE_WRITE) {
      p_SD->file.seekSet(p_SD->file.fileSize());
    }
    // TODO: Return file open result?
    return false;
  }
  return true;
}
  */



boolean callback_remove(SdFile& parentDir, char *filePathComponent, 
      boolean isLastComponent, void *object) {
  if (isLastComponent) {
    return SdFile::remove(parentDir, filePathComponent);
  }
  return true;
}

boolean callback_rmdir(SdFile& parentDir, char *filePathComponent, 
      boolean isLastComponent, void *object) {
  if (isLastComponent) {
    SdFile f;
    if (!f.open(parentDir, filePathComponent, F_READ)) return false;
    return f.rmDir();
  }
  return true;
}



/* Implementation of class used to create `SDCard` object. */



boolean SDClass::begin(uint8_t csPin, int8_t mosi, int8_t miso, int8_t sck) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

   */
  return card.init(SPI_HALF_SPEED, csPin, mosi, miso, sck) &&
         volume.init(card) &&
         root.openRoot(volume);
}

//call this when a card is removed. It will allow you to inster and initialise a new card.
void SDClass::end()
{
  root.close();
}


// this little helper is used to traverse paths
SdFile SDClass::getParentDir(const char *filepath, int *index) {
  // get parent directory
  SdFile d1 = root; // start with the mostparent, root!
  SdFile d2;

  // we'll use the pointers to swap between the two objects
  SdFile *parent = &d1;
  SdFile *subdir = &d2;
  
  const char *origpath = filepath;

  while (strchr(filepath, '/')) {

    // get rid of leading /'s
    if (filepath[0] == '/') {
      filepath++;
      continue;
    }
    
    if (! strchr(filepath, '/')) {
      // it was in the root directory, so leave now
      break;
    }

    // extract just the name of the next subdirectory
    uint8_t idx = strchr(filepath, '/') - filepath;
    if (idx > 12)
      idx = 12;    // dont let them specify long names
    char subdirname[13];
    strncpy(subdirname, filepath, idx);
    subdirname[idx] = 0;

    // close the subdir (we reuse them) if open
    subdir->close();
    if (! subdir->open(parent, subdirname, F_READ)) {
      // failed to open one of the subdirectories
      return SdFile();
    }
    // move forward to the next subdirectory
    filepath += idx;

    // we reuse the objects, close it.
    parent->close();

    // swap the pointers
    SdFile *t = parent;
    parent = subdir;
    subdir = t;
  }

  *index = (int)(filepath - origpath);
  // parent is now the parent diretory of the file!
  return *parent;
}


ext::File SDClass::open(const char *filepath, uint8_t mode) {
  /*

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   */

  int pathidx;

  // do the interative search
  SdFile parentdir = getParentDir(filepath, &pathidx);
  // no more subdirs!

  filepath += pathidx;

  if (! filepath[0]) {
    // it was the directory itself!
    return ext::File(parentdir, "/");
  }

  // Open the file itself
  SdFile file;

  // failed to open a subdir!
  if (!parentdir.isOpen())
    return ext::File();

  // there is a special case for the Root directory since its a static dir
  if (parentdir.isRoot()) {
    if ( ! file.open(root, filepath, mode)) {
      // failed to open the file :(
      return ext::File();
    }
    // dont close the root!
  } else {
    if ( ! file.open(parentdir, filepath, mode)) {
      return ext::File();
    }
    // close the parent
    parentdir.close();
  }

  if (mode & (F_APPEND | F_WRITE)) 
    file.seekSet(file.fileSize());
  return ext::File(file, filepath);
}


/*
File SDClass::open(char *filepath, uint8_t mode) {
  //

     Open the supplied file path for reading or writing.

     The file content can be accessed via the `file` property of
     the `SDClass` object--this property is currently
     a standard `SdFile` object from `sdfatlib`.

     Defaults to read only.

     If `write` is true, default action (when `append` is true) is to
     append data to the end of the file.

     If `append` is false then the file will be truncated first.

     If the file does not exist and it is opened for writing the file
     will be created.

     An attempt to open a file for reading that does not exist is an
     error.

   //

  // TODO: Allow for read&write? (Possibly not, as it requires seek.)

  fileOpenMode = mode;
  walkPath(filepath, root, callback_openPath, this);

  return ext::File();

}
*/


//boolean SDClass::close() {
//  /*
//
//    Closes the file opened by the `open` method.
//
//   */
//  file.close();
//}


boolean SDClass::exists(char *filepath) {
  /*

     Returns true if the supplied file path exists.

   */
  return walkPath(filepath, root, callback_pathExists);
}


//boolean SDClass::exists(char *filepath, SdFile& parentDir) {
//  /*
//
//     Returns true if the supplied file path rooted at `parentDir`
//     exists.
//
//   */
//  return walkPath(filepath, parentDir, callback_pathExists);
//}


boolean SDClass::mkdir(char *filepath) {
  /*
  
    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.
  
   */
  return walkPath(filepath, root, callback_makeDirPath);
}

boolean SDClass::rmdir(char *filepath) {
  /*
  
    Makes a single directory or a heirarchy of directories.

    A rough equivalent to `mkdir -p`.
  
   */
  return walkPath(filepath, root, callback_rmdir);
}

boolean SDClass::remove(char *filepath) {
  return walkPath(filepath, root, callback_remove);
}

void SDClass::enableCRC(boolean mode) {
  card.enableCRC(mode);
}


// allows you to recurse into a directory
ext::File ext::File::openNextFile(uint8_t mode) {
  dir_t p;

  //Serial.print("\t\treading dir...");
  while (_file->readDir(&p) > 0) {

    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) {
      //Serial.println("end");
      return ext::File();
    }

    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
      //Serial.println("dots");
      continue;
    }

    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
      //Serial.println("notafile");
      continue;
    }

    // print file name with possible blank fill
    SdFile f;
    char name[13];
    _file->dirName(p, name);
    //Serial.print("try to open file ");
    //Serial.println(name);

    if (f.open(_file, name, mode)) {
      //Serial.println("OK!");
      return ext::File(f, name);    
    } else {
      //Serial.println("ugh");
      return ext::File();
    }
  }

  //Serial.println("nothing");
  return ext::File();
}

void ext::File::rewindDirectory(void) {  
  if (isDirectory())
    _file->rewind();
}

SDClass SD;

mySD.h

/*

 SD - a slightly more friendly wrapper for sdfatlib

 This library aims to expose a subset of SD card functionality
 in the form of a higher level "wrapper" object.

 License: GNU General Public License V3
          (Because sdfatlib is licensed with this.)

 (C) Copyright 2010 SparkFun Electronics

 */

#ifndef __SD_H__
#define __SD_H__

#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include <SPI.h>
#include <utility/SdFat.h>
#include <utility/SdFatUtil.h>
#include <utility/Sd2Card.h>

#define FILE_READ F_READ
#define FILE_WRITE (F_READ | F_WRITE | F_CREAT)

namespace ext{
  class File : public Stream {
   private:
    char _name[13]; // our name
    SdFile *_file;  // underlying file pointer

  public:
    File(SdFile f, const char *name);     // wraps an underlying SdFile
    File(void);      // 'empty' constructor
    ~File(void);     // destructor
  #if ARDUINO >= 100
    virtual size_t write(uint8_t);
    virtual size_t write(const uint8_t *buf, size_t size);
  #else
    virtual void write(uint8_t);
    virtual void write(const uint8_t *buf, size_t size);
  #endif
    virtual int read();
    virtual int peek();
    virtual int available();
    virtual void flush();
    int read(void *buf, uint16_t nbyte);
    boolean seek(uint32_t pos);
    uint32_t position();
    uint32_t size();
    void close();
    operator bool();
    char * name();

    boolean isDirectory(void);
    File openNextFile(uint8_t mode = F_RDONLY);
    void rewindDirectory(void);
    
    using Print::write;
  };
}

class SDClass {

private:
  // These are required for initialisation and use of sdfatlib
  Sd2Card card;
  SdVolume volume;
  SdFile root;
  
  // my quick&dirty iterator, should be replaced
  SdFile getParentDir(const char *filepath, int *indx);
public:
  // This needs to be called to set up the connection to the SD card
  // before other methods are used.
  boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN, int8_t mosi = -1, int8_t miso = -1, int8_t sck = -1);
  
  //call this when a card is removed. It will allow you to inster and initialise a new card.
  void end(); 
  
  // Open the specified file/directory with the supplied mode (e.g. read or
  // write, etc). Returns a File object for interacting with the file.
  // Note that currently only one file can be open at a time.
  ext::File open(const char *filename, uint8_t mode = FILE_READ);

  // Methods to determine if the requested file path exists.
  boolean exists(char *filepath);

  // Create the requested directory heirarchy--if intermediate directories
  // do not exist they will be created.
  boolean mkdir(char *filepath);
  
  // Delete the file.
  boolean remove(char *filepath);
  
  boolean rmdir(char *filepath);
  
  void enableCRC(boolean mode);

private:

  // This is used to determine the mode used to open a file
  // it's here because it's the easiest place to pass the 
  // information through the directory walking function. But
  // it's probably not the best place for it.
  // It shouldn't be set directly--it is set via the parameters to `open`.
  int fileOpenMode;
  
  friend class File;
  friend boolean callback_openPath(SdFile&, char *, boolean, void *); 
};

extern SDClass SD;

#endif

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值