Частота записи меньше, чем указана. Как исправить?

Савелий
Offline
Зарегистрирован: 26.10.2019
#include <SPI.h>
#include <SdFat.h>
#include <TimerFreeTone.h>
#include "GyverButton.h"
#include <TM1637.h>
#include "I2Cdev.h" //Библиотека для работы с I2C устройствами
#include "MPU6050.h" //Библиотека для работы с MPU6050
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <DS3231.h>

DS3231  rtc(SDA, SCL);

SdFat SD;

Time  t;

File ECG;
File BUT_GREEN;
File BUT_BLUE;
File MPU_6050;
File BMP_180;

MPU6050 accelgyro(0x69);

GButton buttG(2);
GButton buttB(3);

TM1637 disp(9, 10);

Adafruit_BMP085 bmp;

volatile int16_t ax, ay, az; //Переменная для хранения значений полученных с акселерометра

bool flag_menu;
bool flag_hold;

bool flag_bmp180;

unsigned long timer_bmp180;
unsigned long timer_mpu6050;
unsigned long timer_ECG;
unsigned long timer_disp;
bool disp_on;

void setup() {
  Serial.begin (2000000);

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);
#endif

  rtc.begin();

  pinMode (2, INPUT_PULLUP);
  pinMode (3, INPUT_PULLUP);
  pinMode (A3, OUTPUT);
  pinMode (5, OUTPUT);
  pinMode (6, OUTPUT);
  analogReference(EXTERNAL);

  accelgyro.initialize();

  disp.Clear();
  disp.SetBrightness(7); // яркость, 0 - 7 (минимум - максимум)

  if (!accelgyro.testConnection()) {
    disp << "Err1";
    delay (3000);
    disp.Clear ();
  }
  if (!bmp.begin(1)) {
    disp << "Err2";
    delay (3000);
    disp.Clear ();
    flag_bmp180 = false;
  } else {
    flag_bmp180 = true;
  }
  if (!SD.begin(4)) {
    disp << "noSd";
    while (1);
  }

  ECG = SD.open(("ECG_320.txt"), FILE_WRITE);
  BUT_GREEN = SD.open(("BUTTON_GREEN.txt"), FILE_WRITE);
  BUT_BLUE = SD.open(("BUTTON_BLUE.txt"), FILE_WRITE);
  MPU_6050 = SD.open (("MPU6050.txt"), FILE_WRITE);
  BMP_180 = SD.open (("BMP180.txt"), FILE_WRITE);
}
unsigned long test;
int buf_ECG;
byte buf_pos;
void loop() {
  test = millis ();
  while (millis () - test < 10000) {
    buttG.tick ();
    buttB.tick ();
    if (millis () - timer_ECG >= 10) {
      timer_ECG = millis ();
      ECG.println (analogRead (A0));
    }
    if (millis () - timer_mpu6050 >= 100) {
      timer_mpu6050 = millis ();
      accelgyro.getAcceleration(&ax, &ay, &az);
      MPU_6050.print (ax);
      MPU_6050.print (F(" "));
      MPU_6050.print (ay);
      MPU_6050.print (F(" "));
      MPU_6050.println (az);
    }
    if ((millis () - timer_bmp180 >= 50) && flag_bmp180) {
      timer_bmp180 = millis ();
      BMP_180.println (bmp.readPressure());
    }
    if (buttB.isSingle () || buttG.isSingle ()) {
      Serial.println (2);
      t = rtc.getTime();
      disp.PrintTime (t.hour, t.min);
      disp_on = true;
      timer_disp = millis ();
    }
    if (disp_on && (millis () - timer_disp >= 3000)) {
      disp_on = false;
      disp.Clear ();
    }
    if (buttB.isHold() && buttG.isHold() && !flag_hold) {
      flag_menu = !flag_menu;
      flag_hold = true;
    } else if (!(buttB.isHold() && buttG.isHold())) {
      flag_hold = false;
    }
  }
  ECG.close ();
  BUT_GREEN.close ();
  BUT_BLUE.close ();
  MPU_6050.close ();
  BMP_180.close ();
  while (1) {};
}

Это должен быть прибор, записывающий физиологические показатели на SD карту с последующим анализом моей программой на компьютере. Записываться будут значения с акселерометра, ЭКГ, дыхание (BMP180). Частота ЭКГ чем выше, тем лучше. Сейчас, как видно в коде, частота записи ЭКГ стоит раз в 10 мс (100 Гц), что совсем не много, хотелось бы герц 300. Но, записав запись длительностью 10 сек (программа выше как раз делает запись 10 секунд) я увидел, что в файл ЭКГ записалось всего ~700 строк, что соответствует частоте записи 70 Гц. Почему так мало и как это исправить? Хватит ли мощности ардуино чтобы записывать ЭКГ с частотой 320 Гц (раз в 3125 микросекунд), параллельно записывая значения с других датчиков? Если да то как это сделать? Заранее спасибо за ответы!

Kakmyc
Offline
Зарегистрирован: 15.01.2018

А если все лишнее убрать и писать только значения ?
Сдается мне узкое место тут не Ардуино , а карта памяти с ее библиотекой

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Вообще-то, чтобы не писать вслепую, есть такие функции, как milllis и micros.

...

Нет, не так.

Первую строчку своего сообщения я написал ДО того, как заглянул в исходник. А как заглянул - немного офонарел. Вы всерьез хотите на Ардуино писать одновременно в 5 файлов с частотой 100 раз в секунду?

В общем, для начала озвучте, какой именно разновидностью Ардуино Вы пользуетесь.

А по поводу исходника - в процессе логирования пишите в файл не только величину параметра, но время, (в мс или мкс), когда этот параметр измерен. Сразу будет информация к размышлению, какая операция какое время занимает. (ну а отсюда и некоторое впечатление, что на Ардуино сделать возможно, а что - нет).

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Причем судя по результатам, сама то карта вполне успевает.
Но в ряде случаев: когда совпадает по времени, опрос гироскопа, датчика давления и тд, уже не очень успевает.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

И некорректно поставлен вопрос в шапке.
По коду все нормально выдает, а именно: не "ЧАЩЕ ЧЕМ РАЗ В 10мс"

Савелий
Offline
Зарегистрирован: 26.10.2019

arduino nano

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

И что, Ардуино Нано позволяет открыть одновременно 5 файлов?

Чудеса!

Сообщите заодно отчет об использованной памяти в конце компиляции.

Ну и после того, как добавите в файлы время записи, выложите эти файлы сюда. В самом деле интересно.

SLKH
Offline
Зарегистрирован: 17.08.2015

andriano пишет:

И что, Ардуино Нано позволяет открыть одновременно 5 файлов?

Чудеса!

Сообщите заодно отчет об использованной памяти в конце компиляции.

Ну и после того, как добавите в файлы время записи, выложите эти файлы сюда. В самом деле интересно.

Эмм... а в чем скрытый смысл писания в 5 файлов? чем хуже один log, в который последовательно пишется время+код_параметра+значение_параметра? 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

SLKH пишет:

Эмм... а в чем скрытый смысл писания в 5 файлов? чем хуже один log, в который последовательно пишется время+код_параметра+значение_параметра? 

А почему Вы меня об этом спрашиваете?

Савелий
Offline
Зарегистрирован: 26.10.2019

Скрытый смысл в том что у всех датчиков разная частота опроса.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Савелий пишет:

Скрытый смысл в том что у всех датчиков разная частота опроса.

И что?

Вы сделали то, что я просил: добавить в каждую запись время и выложить на форум результат?

SLKH
Offline
Зарегистрирован: 17.08.2015

andriano пишет:

SLKH пишет:

Эмм... а в чем скрытый смысл писания в 5 файлов? чем хуже один log, в который последовательно пишется время+код_параметра+значение_параметра? 

А почему Вы меня об этом спрашиваете?

Чтоб разговор поддержать... ;-)

SLKH
Offline
Зарегистрирован: 17.08.2015

Савелий пишет:

Скрытый смысл в том что у всех датчиков разная частота опроса.

Какая сексуальная разница? Опрашиваете, когда считаете нужным, в паузах между опросами пишете в лог (в один файл). 

andriano не зря спрашивает про пожирание памяти.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

SLKH пишет:

andriano не зря спрашивает про пожирание памяти.

Я не шутил, я действительно удивлен.

Если программа с пятью открытыми файлами работает на Нано, значит, библиотека использует единственный буфер для всех файлов. А это - просто катастрофический удар по производительности. В принципе - логично: лучше пусть работает чудовищно медленно, чем совсем не работает. С другой стороны, в программировании есть все таки традиция - предпочитать отказ от возможностей (например, открытия одновременно нескольких файлов) вместо чудовищно медленной реализации этих возможностей.

Но в любом случае хотелось бы запрошенной информации от ТС для ее анализа и выработки оптимальной стратегии. Хотя, вполне вероятно, что сокращение числа файлов до одного решит проблему, но все равно хотелось бы разобраться.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Что-то ТС куда-то исчез из темы. Подозреваю, что воспользовался советом писать в единственный файл и утратил интерес к предмету обсуждения.

Попытался немножечко посмотреть, как оно работает. Честно говоря - жуть.

Смотрел на примере стандартной SD. Если бы как следует разобрался, сравнил бы с SdFat, но не судьба. SD описана весьма обильно, но из рук вон плохо: даже в "родных" примерах (которых всего 6 штук) вовсю используются недокументированные вещи. С описанием SdFat все намного печальнее.

 

Итак, что удалось выяснить (для платы Arduino Uno).

- SPI работает на частоте 4 МГц. Казалось бы, частота обмена может достигать 500 кБайт/с, но не тут-то было.

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

- при указанных условиях время передачи одного сектора (512 байт) могло бы составить 1.728 мс + накладные расходы на передачу служебной информации. Но, увы, при обмене встречаются и гораздо более длительные паузы.

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

- по достижении гарнцы кластера время записи увеличивается до 17-18 мс. Очевидно, в это время, помимо прочего, входит работа с самой FAT, которая также расположена в секторах на карте. В памяти ее копии в случае с Ардуино не предусмотрено.

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

- собственно, в описании прямо предлагается  открывать файл непосредственно перед записью и закрывать сразу после. В таком режиме на запись в файл одного сектора информации уходит уже 15-24 мс. Причем, меньшим величинам (15-18 мс) соответствует случай не до конца заполненного буфера, а большим (22-24 мс) - когда буфер (512 байт) заполняется полностью. В последнем случае, очевидно, запись происходит одновременно в два сектора - конец предыдущего и начало следующего.

- на границе кластера время записи сектора с переоткрытием файла увеличивается до 37-44 мс.

 

Итого получается, что писать в темпе порядка 100 записей в секунду можно в одном единственно случае - если мы один раз открыли файл и не закрываем его до того, как не запишем в него самую последнюю запись. И то такая периодичность будет нарушаться каждый раз при пересечении границы кластера (для 2Gb карты FAT16 - это 32к). Не следует забывать, что в этом случае необходимо закрыть файл по окончании записи. Если не сделать этого, вся информация будет потеряна. Ну и, естественно, такая работа возможна при наличии единственного файла.

В режиме с переоткрытием максимальный темп записи снижаться примерно до 40 записей в секунду, и то время от времени од будет прерываться по мере пересечения границы кластера. Правда, можно одновременно работать с несколькими файлами. При этом указанные 40 записей - это суммарное количество по всем файлам. 

Ну чтобы более или менее выдерживать равномерный темп, этот темп не должен превышать примерно 20 записей в секунду. Речь, разумеется, идет о коротких записях - длиной менее сектора в любом случае. Ну и в условии пренебрежения временем на обработку записи, например, на преобразование числа в текст.