0

I have this code uploaded to an Arduino Uno which is connected to a gyroscope and storing the data on an SD card. The code is from the OEM.

The delay() function was set to 500 by default but this was changed to 10 in order to get an output frequency of 100Hz.

The millis() function was added to observe whether the 100Hz frequency was being maintained.

#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 address of gyro
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); // Turn on all axes, disable power down
 writeI2C(CTRL_REG3, 0x08); // Enable control ready signal
 writeI2C(CTRL_REG4, 0x80); // Set scale (500 deg/sec)
 delay(100); // Wait to synchronize
}
void loop(){
 myFile = SD.open("test.txt", FILE_WRITE);

  
 getGyroValues(); // Get new values
 // In following Dividing by 114 reduces noise
 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); // Short delay between reads
}
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); // Register address to read
 Wire.endTransmission(); // Terminate request
 Wire.requestFrom(Addr, 1); // Read a byte
 while(!Wire.available()) { }; // Wait for receipt
 return(Wire.read()); // Get result
}
void writeI2C (byte regAddr, byte val) {
 Wire.beginTransmission(Addr);
 Wire.write(regAddr);
 Wire.write(val);
 Wire.endTransmission();
}

However, these are the first few values that were saved onto the "test.txt" file: Raw gyroscope values

This shows a change in time of approximately 0.03s between values and therefore a frequency of 33.3Hz. Does anyone know why this is the case?

3
  • 1
    every command in the program takes time to execute ... some longer than others
    – jsotola
    Mar 20, 2022 at 18:08
  • 2
    You are opening and closing the file on every loop iteration. That looks like a bad idea... Mar 20, 2022 at 19:28
  • 29 characters at 9600 baud is about 0.030s/loop().
    – Dave X
    Mar 21, 2022 at 17:21

2 Answers 2

2

The delay just adds time to the overall runtime of the loop() function. So you would have a frequency of 100Hz only if all the other code would take zero time. And what you are doing there does take a significant amount of time (communicating over I2C and SPI, writing to the SD card). Thus your overall runtime of one loop() iteration is considerably longer. From your data we can even say how much longer it took. You measured a 30ms interval between the samples. 10ms of that is the delay. So the rest of the code took 20ms to execute. That also means, that with your current code you can get only up to 50Hz (by removing the delay()).

First to explain how to go with a timing situation like this lets assume the targeted frequency is possible with your code:

You have 2 options to go with timing:

  • Make the delay() smaller. With trail and error find a value that results in a 100Hz sampling frequency.
  • Use millis() directly for the timing. That way it is not important how long your code took to execute (as long as it was shorter than your sampling interval). For this you can basically copying the code from the BlinkWithoutDelay example, which uses millis() to blink an LED. You can use it to instead collect data samples and write them to the SD card.

The second option is superior and also a very important concept to understand in programming for Arduinos (or any microcontroller).


Now lets have a look at the problem with 100Hz. As explained above your current code takes too long to execute, so that you cannot reach a sample rate of 100Hz. You should use millis() to also check, which part of the code does take the most time.

I suspect that its the file writing. One way to speed this up is to only open and close the file once, instead of in every loop() iteration (as Edgar Bonet mentioned in the comments). After that I don't think you can speed this up further. A technique, that is often used in such situation, is to first collect a defined number of samples at the wanted frequency - temporarily saving them in an array - and then stop the sampling for writing to the SD card. That can give you short bursts of faster sampling at the cost of needing to wait for writing after that. It depends on your application if that is OK for you.

For the I2C you might be able to speed it up a bit. If your gyroscope is supporting that you can set the I2C clock signal to a higher frequency, for example 400kHz instead of 100kHz, via Wire.setClock(). Note that only specific values are allowed with this function and if a value works depends on your microcontroller. I think the Uno supports up to 400kHz.

You need to test by yourself if you can get the execution time below 10ms per loop() iteration.

1

Your loop() function takes many milliseconds to execute because reading gyroscope, opening file on SD, and writing on serial are slow and blocking operations. On top of this, you add delay(10), so your final frequency will always be lower than 100Hz.

You should use the while-millis pattern, a standard and simple solution to overcome this issue. Here a complete explanation about it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.