Оптимизация выбора файлов Arduino с SD-карты

следующий код отобразит файлы на SD-карте в Arduino и позволит пользователю открыть файл по своему выбору. Вот код с описанием того, что он делает.

В состоянии Init я вызываю библиотеки, необходимые для выполнения кода

// Example use of lfnOpenNext and open by index.
#include<SPI.h>
#include "SdFat.h"
#include "FreeStack.h"

// SD card chip select pin.
const uint8_t SD_CS_PIN = 10;

SdFat sd;
SdFile dirFile;
SdFile file;

// Number of files found.
uint16_t numberOfFiles = 0;

Это область реальной проблемы, вызов SdFile parIndex[50]; Занимает большое количество динамической памяти. Для количества файлов на SD-карте это довольно маленькое число, так что мне нужно что-то получше.

// Position of file's directory entry.
uint16_t dirIndex[50];
SdFile parIndex[50];
//------------------------------------------------------------------------------

На этапе Setup мы убеждаемся, что SD-карта подключена, и, если она подключена, вызываем функцию для передачи файлов на SD-карту.

void setup() 
{
  Serial.begin(38400);
  while (!Serial) {}

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) 
  {
    sd.initErrorHalt();
  }
  if (dirFile.open("/", O_READ)) 
  {
  printDirectory(dirFile, 0);
  }
}

Функция printDirectory (Эта часть наиболее открыта для редактирования, если есть способ улучшить производительность здесь, пожалуйста, дайте мне знать).

void printDirectory (SdFile CFile, int numTabs)
{
  SdFile file;
  while (file.openNext(&CFile, O_READ))
  {
  if (file.isHidden()||false)
    {
      //file hidden, skip
    }
    else
    {
      for (uint8_t i = 0; i < numTabs; i++) 
      {
        //create tabs for spacing
        Serial.print('\t');
      }
      if (file.isSubDir())
      {
        SdFile SubDirFile;
        printDirectory(SubDirFile, numTabs+1);
      }
      else 
      {
        // Save dirIndex of file in directory.
        dirIndex[numberOfFiles] = file.dirIndex();
        parIndex[numberOfFiles] = CFile;
        // Print the file number and name.
        Serial.print(numberOfFiles);
        Serial.write(' ');
        file.printName(&Serial);
        Serial.println();
        numberOfFiles++;
      }
    }
  file.close();
  }
}

Это часть пользовательского интерфейса, она была сделана в основном в качестве доказательства концепции для вышеупомянутого кода. В том виде, в котором он написан, код принимает только значения индексов 0-9, но моя реальная программа может вызывать любое значение индекса.

void loop() {
  int c;

  // Read any existing Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);
  Serial.print(F("\r\nEnter File Number: "));

  while (!Serial.available()) {
    SysCall::yield();
  }
  c = Serial.read();
  uint8_t i = c - '0';
  if (!isdigit(c) || i >= numberOfFiles) {
    Serial.println(F("Invald number"));
    return;
  }

Убедившись, что входное значение существует, программа вызывает массивы, содержащие объект SdFile родительского каталога и индекс нужного файла.

  Serial.println(i);
  if (!file.open(&parIndex[i], dirIndex[i], O_READ)) {
    sd.errorHalt(F("open"));
  }
  Serial.println();

  char last = 0;

  // Copy up to 500 characters to Serial.
  for (int k = 0; k < 500 && (c = file.read()) > 0; k++)  {
    Serial.write(last = (char)c);
  }
  // Add new line if missing from last line.
  if (last != '\n') {
    Serial.println();
  }
  file.close();
  Serial.flush();
  delay(100);
}

Основная проблема заключается в том, что SdFile parIndex[50]; использует 22% моей динамической памяти, в то время как остальная часть программы использует 12%. Как я могу переписать свой код, чтобы не держать объекты SDFile в динамической памяти?

Для тех, кто не знаком, документацию по библиотеке SdFat можно скачать по этой ссылке.


Редактировать: С точки зрения использования: SD-файлы - это файлы .csv, в которых хранятся параметры скорости и времени для проведения теста двигателя. Каждый тест длится от нескольких минут до нескольких часов.

Решение

У вас есть несколько возможностей:

  • Не хранить имена всех файлов, а использовать индекс и заново перебирать все файлы. Недостаток: это занимает много времени (каждый раз, когда вам нужно имя файла).
  • Добавить SRAM в Arduino (например, 23LC1024, 23K256), в зависимости от потребностей в памяти. Недостаток: вам придется 'управлять' этой памятью самостоятельно.
  • Если вы знаете что-то об именах файлов, вы можете сгенерировать имя. Возможно, это не тот случай, но просто добавим еще один вариант. Например, если вы знаете, что файл № 10 всегда будет называться 'File10.txt', вы можете 'сгенерировать' имя.
  • Я не знаю, как именно вы хотите использовать имена файлов, но вы можете хранить только последние x используемых файлов, чтобы избежать повторного итерационного чтения SD-карты. Конечно, это поможет, только если есть большая вероятность, что последние x файлов используются чаще, чем не последние.

Обновление: Если вам нужно читать файлы, оставляя Arduino незаблокированным, вы должны включить это в цикл, но не читайте все файлы в цикле. Что-то в псевдокоде вроде:

bool readingSd = false; // Истина между чтением первого и последнего файла uint16_t lastTimeRead = 0; // Последний прочитанный файл

void loop()
{
   // Check if file read needed (initial or one hour past, I don't take
   // overflow into account
   if (lastTimeRead == 0 || (lastTimeRead + 1000 * 60 < millis()) 
   {
      checkReadFile();
   }

   // Perform your normal code... this will run between processing/reading
   // one file/dir.
   ...     
}

void ProcessSingleFile()
{
   readingSd = true; // Within reading first/last file
   "read/process ONE file or dir (without containing files)";
   if ("last file read")
   {
       readingSd = false;
       lastTimeRead = millis(); // Move to top to prevent reading time
         // between first/last to take into account
   }
}
Комментарии (6)