High speed data logging - info

Ciao a tutti.

Ho necessità di salvare quanti più valori possibili provenienti da un accelerometro (mpu 6050) nel minor tempo possibile.

Provando a salvare 499 letture di accelerazioni in dati grezzi (2 byte) dell'asse z ho ottenuto:

4,089 secondi su eeprom esterna i2c

3,594 secondi su eeprom interna (ATmega 328pu, Arduino UNO)

1,271 secondi su array nella SRAM (ATmega 328pu, Arduino UNO)

ora, siccome il metodo migliore è senz'altro usare la SRAM, ma così facendo la saturo molto velocemente, volevo sapere:

  • usando una SD posso arrivare a prestazioni simili alla SRAM? (non ho modo di provarlo attualmente)

  • è possibile utilizzare una SRAM esterna tipo questa? qui sembra che sull'atmega328 non si possa usare ma non ho trovato altre info

grazie :slight_smile:

La SPI è più veloce della I2C.
I dati dall'accelerometro li leggi via seriale?

Via i2c, l'unica supportata dal mpu6050

doppiozero:

  • è possibile utilizzare una SRAM esterna tipo questa? qui sembra che sull'atmega328 non si possa usare ma non ho trovato altre info

Certo che è possibile, è una SRAM con bus SPI ...
... quello che NON è possibile è mettere una normale SRAM come estensione della SRAM del ATmega328P.

Ovvio che la vedi come un supporto esterno, come potrebbe essere una SD, solo molto più veloce e NON come parte della SRAM della MCU :wink:

Guglielmo

Allora teoricamente dovresti farcela.
Tutto dipende dalla velocità della SD e da come scrivi lo sketch. :slight_smile:

p.s. Con la SRAM se spegni perdi i dati, con la SD no.
Anche se potresti aver problemi perché non hai chiuso il file.

PaoloP:
p.s. Con la SRAM se spegni perdi i dati, con la SD no.
Anche se potresti aver problemi perché non hai chiuso il file.

Se questo è il problema ... basta usare una FRAM, come, ad esempio, QUESTA ... da 32KB :smiley:

Guglielmo

doppiozero:
Provando a salvare 499 letture di accelerazioni in dati grezzi (2 byte) dell'asse z ho ottenuto:

Per scrivere un byte sulla EEPROM del 328 servono 3.4 ms in modo 0 (erase + write), oppure solo 1.8 ms in modo 2 (write only), se usi il modo 2 si presuppone che la EEEPROM sia stata preventivamente cancellata.
Facendo due conti se devi scrivere un totale di 1000 byte e usi il modo 2 ci vogliono almeno 3.4 secondi per completare l'operazione.
Tale tempo vale anche per le EEPROM I2C in quanto dovuto alla tecnologia usata, è risaputo che in scrittura le EEPROM sono lente.
Per capire il tempo minimo possibile serve sapere anche la frequenza di campionamento dei dati, 500 letture possono richiedere anche 5 secondi se ne fai 100 al secondo.
Se ti basta accumulare 500 letture non c'è problema a farlo in ram, non si rovina e non si consuma, basta che predefinisci un array composto da valori int di 500 celle, in questo modo ad ogni nuovo ciclo sovrascrivi le vecchie letture, meglio se le azzeri prima, e sei sicuro di non sforare mai la ram.
Se ti serve accumulare più di 500 letture, senza limitare la velocità di campionamento per via della lentezza della EEPROM, puoi benissimo usare una RAM SPI esterna.
La SPI è molto veloce come porta, come minimo riesci a trasferire più di 1000 letture al secondo senza interferire col campionamento, però non è semplicissima da gestire.

Ovvio che la vedi come un supporto esterno, come potrebbe essere una SD, solo molto più veloce e NON come parte della SRAM della MCU :wink:

ah ok, quindi dovrei in qualche modo dire all'atmega "scrivi sulla SRAM esterna" mentre se fosse un'estensione non avrei modifiche da fare allo sketch?

Allora teoricamente dovresti farcela.
Tutto dipende dalla velocità della SD e da come scrivi lo sketch. :slight_smile:

allora vedo di reperire una sd da sacrificare (non ho la shield quindi utilizzerei metodi grezzi :smiley: )

p.s. Con la SRAM se spegni perdi i dati, con la SD no.

si lo so ma mi servirebbe come buffer temporaneo, dopo salverei su eeprom o altro con calma

Per scrivere un byte sulla EEPROM ecc. ecc.

credo di usare il modo 0. Vi posto gli sketch che uso così magari se avete voglia li guardate.

Ho notato che scrivendo su SRAM le letture sono accoppiate a 2 a 2, cioè sembra che la velocità di lettura sia maggiore del refresh dei registri interni dell'accelerometro. Immagino che sia il limite dell'mpu

basta usare una FRAM

non la conoscevo :slight_smile:

accelerometro_array_raw_values.ino (3.05 KB)

accelerometro_eeprom_raw_values.ino (3.16 KB)

accelerometro_external_eeprom_raw_values.ino (3.91 KB)

Ricordati che le SD vanno a 3V3, non a 5V. :slight_smile:

Ricordati che le SD vanno a 3V3, non a 5V

si si grazie :slight_smile: comunque ho tagliato la testa al toro prendendo una scheda per le SD su ebay.

Nel frattempo che aspetto la consegna ho approfondito questo

3.4 ms in modo 0 (erase + write), oppure solo 1.8 ms in modo 2 (write only),

e ho modificato lo sketch che usavo per la eeprom interna.

Potreste darci un'occhiata? Non ho mai fatto prima modifiche ai registri e vorrei evitare danni. In pratica nel setup prima cancello la eeprom poi modifico il registro per scrivere solamente.

Indicazioni registro a pag 21 del datasheet

#include<Wire.h>
#include<EEPROM.h>
byte flag = LOW; // utilizzato per uscire dai cicli
int address = 0; // indirizzo della eeprom esterna

unsigned long int temposcrittura; // contiene il valore del tempo necessario a scrivere tot celle di eeprom

/////////////////// ACCELEROMETRO //////////////////////
const int MPU = 0x68; // I2C address of the MPU-6050
int AcZ;  // contiene valori raw dell'accelerazione

void setup() {
  Serial.begin(9600);
  delay(1000);
  Wire.begin();
  
  ///////////////////// EEPROM CLEAR ///////////////////////
  Serial.println("eeprom clear - start");
  for (int i = 0; i < 1024; i++) {
    EEPROM.write(i, 0);
  }
  Serial.println("eeprom clear - stop");
  
  ///////////////////// EEPROM WRITE ONLY  ///////////////////////

  EECR |= (1<<EEPM1);


  /////////////////// ACCELEROMETRO //////////////////////
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.println("Inizio campionamento");
}

void loop() {
  temposcrittura = millis();
  if (flag == LOW) {
    /////////////////// ACCELEROMETRO //////////////////////
    Wire.beginTransmission(MPU); //Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the write() function and transmit them by calling endTransmission().
    Wire.write(0x3F);  // starting with register 0x3F (ACCEL_ZOUT_H) /// Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a master to slave device (in-between calls to beginTransmission() and endTransmission()).
    Wire.endTransmission(false); //Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes that were queued by write(). If true, endTransmission() sends a stop message after transmission, releasing the I2C bus.If false, endTransmission() sends a restart message after transmission. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control.The default value is true.
    Wire.requestFrom(MPU, 2, true); // request a total of 2 registers // Used by the master to request bytes from a slave device. The bytes may then be retrieved with the available() and read() functions.
    AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    Wire.endTransmission(true);
    // Serial.print(" | AcZ = "); Serial.println(AcZ);
  }

  //////////////////// EEPROM INTERNA - SCRITTURA /////////////////////
  if (address <= 996 && flag == LOW ) {
    EEPROM.put(address, AcZ );
    address += sizeof(int);
  }
  //////////////////// EEPROM INTERNA - LETTURA /////////////////////
  if (address > 996 && flag == LOW ) {
    Serial.print("Tempo Scrittura  =  "); Serial.println(temposcrittura); // verifico tempo per scrivere tot valori sulla eeprom
    delay(2000);
    Serial.print("Valori campionati  =  "); Serial.println(address / 2);
    delay(2000);
    Serial.println("Inizio Lettura");
    flag = HIGH;
    address = 0;
    delay(2000);
  }
  if (address <= 996 && flag == HIGH) {
    EEPROM.get(address, AcZ);
    Serial.print(address); Serial.print("  =  "); Serial.println(AcZ);
    address += sizeof(int);
    delay(100);

  }

}

grazie :slight_smile:

Secondo me tenendo separati i due dati è di un nano secondo più veloce.

#include<Wire.h>
#include<EEPROM.h>

byte flag = LOW; // utilizzato per uscire dai cicli
int address = 0; // indirizzo della eeprom esterna
unsigned long int temposcrittura; // contiene il valore del tempo necessario a scrivere tot celle di eeprom

/////////////////// ACCELEROMETRO //////////////////////
const int MPU = 0x68; // I2C address of the MPU-6050
byte AcZH;
byte AcZL;
// int AcZ;  // contiene valori raw dell'accelerazione

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

  ///////////////////// EEPROM CLEAR ///////////////////////
  Serial.println("eeprom clear - start");
  for (int i = 0; i < 1024; i++) {
    EEPROM.write(i, 0);
  }
  Serial.println("eeprom clear - stop");

  ///////////////////// EEPROM WRITE ONLY  ///////////////////////
  EECR |= (1 << EEPM1);

  /////////////////// ACCELEROMETRO //////////////////////
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.println("Inizio campionamento");
}

void loop() {
  temposcrittura = millis();
  if (flag == LOW) {
    /////////////////// ACCELEROMETRO //////////////////////
    Wire.beginTransmission(MPU); //Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the write() function and transmit them by calling endTransmission().
    Wire.write(0x3F);  // starting with register 0x3F (ACCEL_ZOUT_H) /// Writes data from a slave device in response to a request from a master, or queues bytes for transmission from a master to slave device (in-between calls to beginTransmission() and endTransmission()).
    Wire.endTransmission(false); //Ends a transmission to a slave device that was begun by beginTransmission() and transmits the bytes that were queued by write(). If true, endTransmission() sends a stop message after transmission, releasing the I2C bus.If false, endTransmission() sends a restart message after transmission. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control.The default value is true.
    Wire.requestFrom(MPU, 2, true); // request a total of 2 registers // Used by the master to request bytes from a slave device. The bytes may then be retrieved with the available() and read() functions.
    //    AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    AcZH = Wire.read();
    AcZL = Wire.read();
    Wire.endTransmission(true);
    // Serial.print(" | AcZ = "); Serial.println(AcZ);
  }

  //////////////////// EEPROM INTERNA - SCRITTURA /////////////////////
  if (address <= 996 && flag == LOW ) {
    EEPROM.write(address, AcZH);
    EEPROM.write(address + 1, AcZL);
    // EEPROM.put(address, AcZ );
    // address += sizeof(int);
    address += 2;
  }
  //////////////////// EEPROM INTERNA - LETTURA /////////////////////
  if (address > 996 && flag == LOW ) {
    Serial.print("Tempo Scrittura  =  "); Serial.println(temposcrittura); // verifico tempo per scrivere tot valori sulla eeprom
    delay(2000);
    Serial.print("Valori campionati  =  "); Serial.println(address / 2);
    delay(2000);
    Serial.println("Inizio Lettura");
    flag = HIGH;
    address = 0;
    delay(2000);
  }

  if (address <= 996 && flag == HIGH) {
    AcZH = EEPROM.read(address);
    AcZL = EEPROM.read(address + 1);
    // EEPROM.get(address, AcZ);
    Serial.print(address); Serial.print("  =  "); // Serial.println(AcZ);
    Serial.println(AcZH << 8 | AcZL);
    // address += sizeof(int);
    address += 2;
    delay(100);
  }
} // End Loop

ho provato (togliendo il clear e la modifica al registro) ed è quasi 1 secondo più lento.. strano.. ::slight_smile:

edit: mi sono accorto che nei tempi riportati nel primo post è incluso il tempo di setup, che somaro..

edit 2: i valori aggiornati

3,089 secondi su eeprom esterna i2c

2,594 secondi su eeprom interna (ATmega 328pu, Arduino UNO)

0,271 secondi su array nella SRAM (ATmega 328pu, Arduino UNO)

Per queste cose ci sono le Flash in SPI, ma non so quanto si possa tirare il collo alla SPI su Arduino.

Una Winbond da 0,0 lire arriva anche a 80 MHz di bus. Probabilmente Arduino, almeno a 10 MHz ci arriva.

Inoltre, programmando da zero i registri della periferica SPI, si leva un bel pò di Overhead, aumentando discretamente la velocità.

Boh fa prima a prendere un microcontrollore più veloce, cosa ci deve fare con freq così elevate di campionamento?

voglio fare un po di misure su uno di questi :grin: il mio sarà mooolto più spartano.

comunque 500 letture** in 2 decimi di secondo mi bastano e avanzano, il mio problema era avere abbastanza memoria e abbastanza veloce da salvare dati per almeno 4 secondi.

Al di là del progetto che non è nulla di serio ero curioso di vedere il limite dell'atmega328 e di esercitarmi un po' su eeprom e memorie varie

** è il limite dell'mpu6050, non so se posso aumentarle, devo guardare meglio il datasheet

Un conto è salvarle un altro elaborarle per farci qualcosa in realtime, che direi è più interessante

intanto volevo salvarle, un passo per volta :wink: certo sarebbe fico fare un sistema di telemetria real time che magari trasmetta direttamente su pc ma intanto vado con calma :slight_smile:

@BaBBuino darò un'occhiata anche alle Winbond, grazie :slight_smile:

flz47655:
Boh fa prima a prendere un microcontrollore più veloce, cosa ci deve fare con freq così elevate di campionamento?

In effetti...