Проблема с пониманием метки времени, сгенерированной с помощью функции millis()

Я загрузил этот код на Arduino Uno, который подключен к гироскопу и сохраняет данные на SD-карте. Код взят от производителя.

Функция delay() по умолчанию была установлена на 500, но это значение было изменено на 10, чтобы получить выходную частоту 100 Гц.

Была добавлена функция millis() для наблюдения за тем, поддерживается ли частота 100 Гц.

#include <Wire.h>
#include <SPI.h>
#include <SD.h>
File myFile;

#define CTRL_REG1 0x20
#define CTRL_REG2 0x21
#define CTRL_REG3 0x22
#define CTRL_REG4 0x23
int Addr = 105; // I2C-адрес гироскопа
int x, y, z;

void setup(){
 Wire.begin();
Serial.begin(9600);

Serial.print("Initializing SD card...");

  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    while (1);
  }
  Serial.println("initialization done.");




 writeI2C(CTRL_REG1, 0x1F); // Включить все оси, отключить питание
 writeI2C(CTRL_REG3, 0x08); // Включить сигнал готовности к управлению
 writeI2C(CTRL_REG4, 0x80); // Установить масштаб (500 град/сек)
 delay(100); // Дождитесь синхронизации
}
void loop(){
 myFile = SD.open("test.txt", FILE_WRITE);

  
 getGyroValues(); // Получить новые значения
 // При последующем Делении на 114 уменьшается шум
 Serial.print(millis());
 Serial.print(" Raw X:"); Serial.print(x / 114);
 Serial.print(" Raw Y:"); Serial.print(y / 114);
 Serial.print(" Raw Z:"); Serial.println(z / 114);

 myFile.print(millis());
 myFile.print(" Raw X:"); myFile.print(x / 114);
 myFile.print(" Raw Y:"); myFile.print(y / 114);
 myFile.print(" Raw Z:"); myFile.println(z / 114);

myFile.close();
 
 delay(10); // Короткая задержка между чтениями
}
void getGyroValues () {
 byte MSB, LSB;
 MSB = readI2C(0x29);
 LSB = readI2C(0x28);
 x = ((MSB << 8) | LSB);
 MSB = readI2C(0x2B);
 LSB = readI2C(0x2A);
 y = ((MSB << 8) | LSB);
 MSB = readI2C(0x2D);
 LSB = readI2C(0x2C);
 z = ((MSB << 8) | LSB);
}
int readI2C (byte regAddr) {
 Wire.beginTransmission(Addr);
 Wire.write(regAddr); // Зарегистрировать адрес для чтения
 Wire.endTransmission(); // Завершить запрос
 Wire.requestFrom(Addr, 1); // Считывать байт
 while(!Wire.available()) { }; // Ждать возврата квитанции
 return(Wire.read()); // Получить результат
}
void writeI2C (byte regAddr, byte val) {
 Wire.beginTransmission(Addr);
 Wire.write(regAddr);
 Wire.write(val);
 Wire.endTransmission();
}

Однако это первые несколько значений, которые были сохранены в "test.txt " досье: Raw gyroscope values

Это показывает изменение во времени приблизительно на 0,03 с между значениями и, следовательно, частоту 33,3 Гц. Кто-нибудь знает, почему это так?

, 👍0

Обсуждение

выполнение каждой команды в программе требует времени ... некоторые дольше, чем другие, @jsotola

Вы открываете и закрываете файл на каждой итерации цикла. Это выглядит как плохая идея..., @Edgar Bonet

29 символов со скоростью 9600 бод - это примерно "0,030 с /цикл ()`., @Dave X


2 ответа


1

Выполнение вашей функции loop() занимает много миллисекунд, потому что чтение gyroscope, открытие файла на SD и запись на serial являются медленными и блокирующими операциями. Вдобавок ко всему, вы добавляете задержку (10), так что ваша конечная частота всегда будет ниже 100 Гц.

Вы должны использовать шаблон while-millis, стандартное и простое решение для решения этой проблемы. Вот полное объяснение по этому поводу.

,

2

Задержка просто добавляет время к общему времени выполнения функции loop(). Таким образом, у вас будет частота 100 Гц только в том случае, если весь остальной код займет нулевое время. И то, что вы там делаете, действительно занимает значительное количество времени (общение по I2C и SPI, запись на SD-карту). Таким образом, ваше общее время выполнения одной итерации loop() значительно больше. Исходя из ваших данных, мы можем даже сказать, сколько времени это заняло. Вы измерили интервал в 30 мс между выборками. 10 мс из этого - задержка. Таким образом, выполнение остальной части кода заняло 20 мс. Это также означает, что с вашим текущим кодом вы можете получить только до 50 Гц (убрав задержку ()).

Сначала, чтобы объяснить, как работать с подобной ситуацией синхронизации, давайте предположим, что целевая частота возможна с вашим кодом:

У вас есть 2 варианта выбора времени:

  • Уменьшите задержку(). С помощью trail и error найдите значение, которое приводит к частоте дискретизации 100 Гц.
  • Используйте millis() непосредственно для определения времени. Таким образом, не важно, сколько времени потребовалось для выполнения вашего кода (главное, чтобы он был короче вашего интервала выборки). Для этого вы можете в основном скопировать код из примера BlinkWithoutDelay, который использует millis() для мигания светодиода. Вместо этого вы можете использовать его для сбора образцов данных и записи их на SD-карту.

Второй вариант является превосходным, а также очень важной концепцией для понимания при программировании для ардуино (или любого микроконтроллера).


Теперь давайте посмотрим на проблему со 100 Гц. Как было объяснено выше, выполнение вашего текущего кода занимает слишком много времени, поэтому вы не можете достичь частоты дискретизации 100 Гц. Вы должны использовать millis(), чтобы также проверить, какая часть кода занимает больше всего времени.

Я подозреваю, что это запись файла. Один из способов ускорить это - открывать и закрывать файл только один раз, а не на каждой итерации loop() (как упоминал Эдгар Бонет в комментариях). После этого я не думаю, что вы сможете ускорить процесс дальше. Метод, который часто используется в такой ситуации, заключается в том, чтобы сначала собрать определенное количество выборок с требуемой частотой, временно сохраняя их в массиве, а затем остановить выборку для записи на SD-карту. Это может дать вам короткие всплески более быстрой выборки за счет необходимости ждать записи после этого. Это зависит от вашего приложения, подходит ли вам это.

Для I2C вы могли бы немного ускорить его. Если ваш гироскоп поддерживает это, вы можете установить тактовый сигнал I2C на более высокую частоту, например 400 кГц вместо 100 кГц, с помощью Wire.setClock(). Обратите внимание, что с помощью этой функции допускаются только определенные значения, и если значение работает, зависит от вашего микроконтроллера. Я думаю, что Uno поддерживает частоту до 400 кГц.

Вам нужно самостоятельно проверить, можете ли вы получить время выполнения ниже 10 мс на итерацию loop().

,