Длительное зависание в начале передачи SD-карты

sd

Я создаю регистратор данных акселерометра, который записывает данные на SD-карту. После какой-то "разминки" все работает отлично в течение как минимум ~ 15 минут (именно это я и тестировал до сих пор).

Однако в течение первых нескольких секунд происходит довольно длительное зависание функции File.write() (180 мс вместо 50us для обычной записи!). которые я могу идентифицировать на осциллографе (см. Рисунок ниже, низкий сигнал после запуска high указывает на фазы File.write()) и которые я также могу увидеть в данных, если включу в них метку времени milis().

Поскольку оперативная память Atmega328 ограничена, существует также жесткие ограничения на объем данных датчика, которые я могу буферизировать (я использую прерывание для передачи от датчика, где я могу хранить данные в буфере и таким образом преодолевать более короткие задержки sd-карты).

Я урезал свой код до абсолютных голых костей, просто записывая бессмысленные данные вместо данных акселерометра. В чем может быть причина этих заморозков? Скорость передачи данных остается одинаковой в течение всего прогона. Разве SD-карта не инициализируется электрически или логически после SD.begin()? Кажется, это не проблема со стиранием блоков флэш-памяти, потому что это происходит только в начале. Даже если я перезапущу приложение во второй раз (после постоянного включения электричества), оно уже не будет зависать. Принимая во внимание, что если я отключу / подключу питание, то вначале снова появятся зависания.

И самое главное: как я могу ее решить?

Конечно, я мог бы записать фиктивные данные до того, как получу кнопку "Пуск". Но сколько их? Зависит ли это от типа SD-карты? Зависит ли это от тактовой частоты процессора или spi? Мне нужно надежное решение, которое не ломается, когда я использую другой тип sd.

Код:

#include <SPI.h>
#include <SD.h>

const int chipSelect = 10; // chip select sd-карты
const int pushButton = 9; // здесь находится кнопка запуска (на выдвижном резисторе)
const int debugPin = 1; // строка, используемая для отображения бита отладки на осциллографе

const int bufferSize = 14;
byte buffer[bufferSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; // для теста просто фиктивные значения

String fileName = "accellog.raw";

void setup() 
{
  pinMode(pushButton, INPUT);
  pinMode(debugPin, OUTPUT);

  if (!SD.begin(chipSelect))
  {
    // on ошибка остановки выполнения
    for(;;);
  }
}

void loop() 
{
  // дождитесь нажатия кнопки "Пуск
  while (digitalRead(pushButton) == HIGH);
  delay(100); // debounce delay
  while (digitalRead(pushButton) == LOW);

  // удалить старый файл с SD-карты
  if (SD.exists(fileName)) SD.remove(fileName);

  File dataFile = SD.open(fileName, FILE_WRITE); 

  if (dataFile) 
  {
    // Debugging: указать начало полной передачи данных на осциллографе
    digitalWrite(debugPin, HIGH);

    int lastMillis = millis();

    for (long i=0; i<10000; i++)
    {
      // подождите, пока millis не изменится, чтобы получить запись SD на
      // целых миллисекунд (однако некоторые циклы могут быть потеряны
      // however, during lengthy SD writes)
      int tempMillis = millis();
      while (tempMillis == lastMillis) 
      {
        tempMillis = millis();
      }
      lastMillis = tempMillis;

      // Отладка: укажите время доступа к файлу, отправив 0 в DSO
      digitalWrite(debugPin, LOW);

      dataFile.write(buffer,bufferSize);

      // Отладка: указать конец доступа к файлу в DSO
      digitalWrite(debugPin, HIGH);
    }

    // Отладка: укажите конец всей передачи данных
    digitalWrite(debugPin, LOW);

    dataFile.close();
  }
  else 
  {
    // on ошибка остановки выполнения
    for (;;);
  }
}

, 👍3

Обсуждение

попробуйте открыть файл, а затем закрыть его без каких-либо записей, @jsotola

@jsotola: к сожалению, это ничего не изменило. Но я посмотрю, как далеко я зайду с записью каких-то бессмысленных данных заранее (в другом файле)., @oliver

Я не могу это воспроизвести. На моей SD-карте я получаю начальную задержку (между первой и второй записью) около 30 мс, за которой следует пакет записей, затем задержка 20 мс, затем еще один пакет и так далее. Паузы находятся между примерно 512 записываемыми байтами, которые будут буфером (в библиотеке SD), фактически записываемым на SD-карту. Я предлагаю попробовать другую карту и посмотреть, что получится. Может быть, один с классом быстрой записи (например. тот, который вы используете для видеосъемки)., @Nick Gammon

@NickGammon: да, может быть, все дело в карте. Я взял самые дешевые 16-гигабайтные карты, какие только смог достать. :-) Я также думал о потере данных на линиях SPI как о причине, но линии на моей плате намного короче, чем у вас в макете. В любом случае, теперь у меня есть обходной путь, по крайней мере. Мне нужно будет проверить, что происходит с картами, которые были написаны полностью один раз..., @oliver

Я не думаю, что потеря данных действительно приведет к этому. Данные либо поступают, либо нет. * Я взял самые дешевые 16-гигабайтные карты, которые только смог достать * - похоже, ты получил то, за что заплатил. :), @Nick Gammon


1 ответ


1

В качестве временного обходного пути я пришел к следующей процедуре:

Я вставил функцию, которая записывает определенное количество байтов в фиктивный файл, закрывает его и, наконец, удаляет, все это во время setup().

Это действительно, кажется, лечит зависания, если (в моем сценарии) я пишу по крайней мере 6 блоков (512 байт каждый, т.Е. Всего 3072 байта). Примечательно, что на моих графиках осциллографа всегда было видно ровно 6 зависаний. Я даже отдаленно не знаю, что может означать это магическое число 6 ...

Просто чтобы иметь некоторую дополнительную безопасность на случай, если я выберу другой тип SD-карты (и этот обходной путь станет постоянно временным ...), я решил записать 16 КБ в фиктивный файл. Это примерно в 5 раз больше эмпирического минимального значения (3 кБ) и все еще довольно быстро, так что я не замечаю никакой значительной задержки запуска после включения питания.

Однако этот эмпирический подход все еще неудовлетворителен и, безусловно, является частью посредственной инженерии. Поэтому я не буду отмечать свой ответ как принятый, чтобы оставить возможность кому-то другому дать более обоснованный ответ.

void writeAndEraseDummyFileToEliminateFreeze()
{
  if (SD.exists("dummy")) SD.remove("dummy");
  File dummyFile = SD.open("dummy", FILE_WRITE);
  int n = 16384/recordSize+1; // записать 16 КБ в куски recordSize (просто потому, что у нас уже есть этот буфер
  for (int i=0; i<n; i++)
  {
    // значения буфера неинициализированы, но кого это волнует, мы все равно избавимся от них
    dummyFile.write(buffer,recordSize);
  }
  dummyFile.close();
  SD.remove("dummy");
}

void setup() 
{
  // ... другие вещи, которые я уже делал...

  writeAndEraseDummyFileToEliminateFreeze();
}
,

Вы можете измерить время, необходимое для записи, и повторять ее так часто, как это необходимо, прерывая работу, когда количество или время превышает приличный предел., @the busybee