Go Down

Topic: Aquisition rate with SD Card (Read 3319 times) previous topic - next topic

cattledog

Code: [Select]
if(currentMicros - previousMicros >= 3000)//this is unaffected by rollover
{//do something}


Quote
Because while testing I didn't get the right acquisitions as in the code I am using now
Are you certain that what you were seeing was a result of the standard formulation of timing an event?

You could also drive your acquisitions with a timer interrupt which will be more accurate than using micros().

diogotec

@cattledog,
Doesn't that also fail in the case I told in my previous post?

For example for 3 seconds I should get 1001 acquisitions. I think I got 999

cattledog

#47
Jul 27, 2017, 06:27 pm Last Edit: Jul 27, 2017, 06:28 pm by cattledog
I assume that you are dealing with "time" which is self referenced within the Arduino and not trying to adjust the Arduino's internal time to some external time reference. The resonator for the Arduino clock lives in its own world.

Quote
For example for 3 seconds I should get 1001 acquisitions.
Yes, if counts are zero referenced they will always be +1 for the interval.

Quote
Doesn't that also fail in the case I told in my previous post?
Are you referring to driving the acquisition with a hardware timer interrupt? There may be some fine tuning to deal with the interrupt latency but there are no overflow concerns.

Quote
I think I got 999
You may have, but I 'm not sure that the issue was how you managed the interval timing. I

Can you post some simple test code which only grabs 999 values in 3 seconds when using a millis() timer with a 3000 microsecond interval. How you manage the start and stop is clearly of concern, but timing an interval should be straightforward. Micros() only has a 4 microsecond resolution.
 
if(micros() - lastAcquisition) >= 3000)
{ lastAcquisitiion += 3000;
//acquire values
}

If you want to explore using a hardware timer, I would recommend that you start by trying the TimerOne
library available through the library manager. The timer set up is easy.

Code: [Select]
#include <TimerOne.h>
void setup(void)
{
  Timer1.initialize(3000);//takes microsecond argument, timer will be .0625 microsecond resolution
  Timer1.attachInterrupt(acquireData);
}

void loop(void)
{
  //manage the start and stop by elapsed time or acquisitions
Timer1.start();
Timer1.stop();
}

void acquireData(void)//function called every 3000 microseconds, no rollover issues
{
  //acquire the Data
  //count acquisitiions
}


diogotec

@cattledog, I was refering to your subtraction method.
What I am saying, is that that method also fails when you have micros() just before overflow (imagine 4294967280) and next_acq = 2000. micros() - next_acq >= 3000 and it acquires, but it shouldn't!

About the hardware time, looks an alternative, but first I wanna try this method which is really close to be suceeded! But thanks :D

Anyway, I ran the following code and got 999 acquisitions instead of 1001:

Code: [Select]
//aquisition variables
const int EMG_pin = A0;  // Analog input pin that the EMG signal is attached to
unsigned int EMG_value;
unsigned int PZT_value;
unsigned int ACL_value;
unsigned long acquisitions = 0;
unsigned long init_time;
bool          acquireSensorActive = false;
unsigned long last_acquisition;

//SD Card libraries & variables
#include <SPI.h>
#include "SdFat.h"
SdFat sd;
File myFile;

//SD Card to-save-variavles
byte EMGByte1;
byte EMGByte2;
byte PZTByte1;
byte PZTByte2;
byte ACLByte1;
byte ACLByte2;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

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

  if (!sd.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  myFile = sd.open("teste.dat", O_CREAT | O_WRITE | O_TRUNC);
  acquireSensorActive = true;
  acquisitions = 0;
  init_time = micros();
  last_acquisition = micros();
}

void loop() {
  if (acquireSensorActive) {
    if(micros() - last_acquisition >= 3000) {
      last_acquisition += 3000;
      EMG_value = analogRead(EMG_pin);
      PZT_value = analogRead(EMG_pin);
      ACL_value = analogRead(EMG_pin);
      EMGByte1 = EMG_value; //saves first 8 bit
      EMGByte2 = EMG_value >> 8;//shifts 8 bit to right, left bits will be zero
      PZTByte1 = PZT_value;
      PZTByte2 = PZT_value >> 8;
      ACLByte1 = ACL_value;
      ACLByte2 = ACL_value >> 8;
      byte writeBinDataBlock[6] {EMGByte1, EMGByte2, PZTByte1, PZTByte2, ACLByte1, ACLByte2};
      myFile.write(writeBinDataBlock, sizeof(writeBinDataBlock));
      acquisitions++;
    }
    if (micros() - init_time >= 3000000) {
      Serial.println(acquisitions);
      myFile.close();
      acquireSensorActive = false;
    }
  }
}

diogotec

#49
Jul 27, 2017, 11:32 pm Last Edit: Jul 28, 2017, 11:38 am by diogotec
@DrDoom, with your method I also tested the if statement and it fixes that case!
Only the case of micros() at near overflow and next_acq already overflowed left to fix :|

I have written the following code to fix the latest problem, I think I got the right acquisitions for 75 minutes, but not sure if I got them right, but with an abnormal acquisition time as it happened before the fix. Testing for 2 overflows went wrong.

Code: [Select]

void loop() {
  if (acquireSensorActive) {
    if (micros() >= acquireSensorDataNextMicros || micros() < acquireSensorDataNextMicros - acquireSensorDataDelayMicros) {
      if ((acquireSensorDataNextMicros < acquireSensorDataDelayMicros) && (micros() > acquireSensorDataDelayMicros) && (acquisitions != 0)) { //if next acquisition has value after rollover (between 0 and 2999 max) and micros() is not overflowed yet (not in the interval of 0 and 3000, equivalent to being higher than 3000), then skip acquisition
        dummy++;
        return;
      }
      acquireSensorDataNextMicros += acquireSensorDataDelayMicros;
      EMG_value = analogRead(EMG_pin);
      PZT_value = analogRead(EMG_pin);
      ACL_value = analogRead(EMG_pin);
      EMGByte1 = EMG_value; //saves first 8 bit
      EMGByte2 = EMG_value >> 8;//shifts 8 bit to right, left bits will be zero
      PZTByte1 = PZT_value;
      PZTByte2 = PZT_value >> 8;
      ACLByte1 = ACL_value;
      ACLByte2 = ACL_value >> 8;
      byte writeBinDataBlock[6] {EMGByte1, EMGByte2, PZTByte1, PZTByte2, ACLByte1, ACLByte2};
      myFile.write(writeBinDataBlock, sizeof(writeBinDataBlock));
      acquisitions++;
    }
    if (millis() - comeco >= 8940000) {
      Serial.println(acquisitions);
      Serial.println(dummy);
      myFile.close();
      acquireSensorActive = false;
    }
  }
}

cattledog

Quote
Anyway, I ran the following code and got 999 acquisitions instead of 1001:
When I slightly modify your code to zero reference the readings and start the recording period from the first reading, I see the expected 1001 readings in 3 seconds, 2001 readings in 6 seconds and 3001 readings in 9 seconds.

This code is unaffected to rollover
Code: [Select]
if (micros() - last_acquisition >= 3000)

Code: [Select]
//aquisition variables
const int EMG_pin = A0;  // Analog input pin that the EMG signal is attached to
unsigned int EMG_value;
unsigned int PZT_value;
unsigned int ACL_value;
unsigned long acquisitions = 0;
unsigned long init_time;
bool acquireSensorActive = false;
unsigned long last_acquisition;
boolean firstReading = true;


//SD Card libraries & variables
#include <SPI.h>
#include "SdFat.h"
SdFat sd;
File myFile;

//SD Card to-save-variavles
byte EMGByte1;
byte EMGByte2;
byte PZTByte1;
byte PZTByte2;
byte ACLByte1;
byte ACLByte2;


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

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

  if (!sd.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  myFile = sd.open("teste.dat", O_CREAT | O_WRITE | O_TRUNC);
  acquireSensorActive = true;
  acquisitions = 0;
  init_time = micros();
  last_acquisition = micros();
}

void loop() {

  if (acquireSensorActive && firstReading == true)//take zero referenced first reading
  {
    firstReading = false;
    readWriteBlock();
    acquisitions++;
    last_acquisition = micros();
    init_time = micros();
  }

  if (acquireSensorActive && !firstReading)
  {
    if (micros() - last_acquisition >= 3000)
    {
      last_acquisition += 3000;
      readWriteBlock();
      acquisitions++;
    }

    if (micros() - init_time >= 3000000) {
      Serial.println(acquisitions);
      myFile.close();
      acquireSensorActive = false;
    }
  }
}

void readWriteBlock()
{
  EMG_value = analogRead(EMG_pin);
  PZT_value = analogRead(EMG_pin);
  ACL_value = analogRead(EMG_pin);
  EMGByte1 = EMG_value; //saves first 8 bit
  EMGByte2 = EMG_value >> 8;//shifts 8 bit to right, left bits will be zero
  PZTByte1 = PZT_value;
  PZTByte2 = PZT_value >> 8;
  ACLByte1 = ACL_value;
  ACLByte2 = ACL_value >> 8;
  byte writeBinDataBlock[6] {EMGByte1, EMGByte2, PZTByte1, PZTByte2, ACLByte1, ACLByte2};
  myFile.write(writeBinDataBlock, sizeof(writeBinDataBlock));
}


diogotec

Quote
if (micros() - last_acquisition >= 3000)
Can you explain why then?

cattledog

Quote
Can you explain why then?
The magic of unsigned values and two's complement math. 

There are many references to be found with a google search of "handling millis() rollover Arduino".

Nick Gammon's tutorial on millis() demonstrates the method
http://www.gammon.com.au/millis

diogotec

@cattledog,
Tell me if that works im the following case:
You have micros() near overflow, a really big number, and you have added 3000 to that number, in calculating next_acq time, so that it overflowed and it is a small number.
Subtracting micros() with that small number will be way higher than 3000 and so an acquisition will happen, when it shouldnt, since micros() should first overflow and get to the next_acq number. This causes an irregular acquisition

christop

@cattledog,
Tell me if that works im the following case:
You have micros() near overflow, a really big number, and you have added 3000 to that number, in calculating next_acq time, so that it overflowed and it is a small number.
Subtracting micros() with that small number will be way higher than 3000 and so an acquisition will happen, when it shouldnt, since micros() should first overflow and get to the next_acq number. This causes an irregular acquisition
You don't add 3000 to micros() to calculate next_acq. You don't calculate next_acq at all with this technique. You only keep track of the last acquisition time, so that you can subtract the last acquisition time from micros() to get the elapsed time.

cattledog

To be certain of the roll over immunity, you are best using an explicit type cast in the if bloc.

Code: [Select]
if (unsigned long (micros() - last_acquisition) >= 3000)

I'm not sure if the compiler or ide actually takes care of this.

See this reference https://www.baldengineer.com/arduino-how-do-you-reset-millis.html

You can observe the following code deal with rollover. The typecast was required in this constructed case using byte variables.

Code: [Select]
byte second;//will roll at 255 to 0
byte lastSecond;
byte previousTime;
byte elapsedInterval;

void setup() {
  Serial.begin(115200);
  Serial.println("rollover test");
}

void loop() {
  second = millis() / 100;//"second" = 100ms speed up 10x
  if (second != lastSecond)
  {
    lastSecond += 1;
    Serial.print(second);
    Serial.print('\t');
    elapsedInterval = (second - previousTime);
    Serial.println(elapsedInterval);
  }
 if (byte(second - previousTime) >= 5)//byte caste is needed here
  {
    Serial.print("scheduled event at ");
    Serial.print(second);
    Serial.print(" elapsed interval = ");
    Serial.println(elapsedInterval);
    previousTime += 5; 
    Serial.print("updated previousTime = ");
    Serial.println(previousTime); 
    Serial.print("next scheduled event at seconds =  ");
    //add 5 for calculation and display, then correct back
    Serial.println(previousTime +=5);
    previousTime -=5;
  }
}




diogotec

Oh! Sorry for my mistake! You are right, it works for rollover, sorry for my suspicion, just wanna make sure everything works 100% fine and why :)

Thanks @cattledog and @christop for your help.
And a VERY big thanks to @DrDooom for his help all along!

Go Up