Go Down

Topic: Cyclic buffer for fast sampling with sdFat - How can I increase sample rate? (Read 334 times) previous topic - next topic

jacobbloodworth

Hi,

I'm trying to log data from three sensors at a high sampling rate at an SD card. However, if I increase the sampling rate above 250Hz then no data is logged at all. Any help would be greatly appreciated.

The sensors are:

- An MPU-6050
- A Pressure transducer (analogue signal)
- An inductive proximity sensor (digital signal)

The proximity sensor detects holes in a spinning disc at up to 500Hz so (by the nyquist sampling theorem) I need to sample at 1KHz.

To log data to the SD card at high frequency I'm using a cyclic buffer method - where buffer1 is filled at regular intervals (by means of an interrupt routine) whilst buffer2 up logged to the SD card and visa versa. I'm using the SdFat library to do this.

The problem I have is that if I decrease SAMPLE_INTERVAL_MUS to 4000 microSec or lower, no data is logged to the SD card at all. However, clearly my sampling interval needs to be 1000 microSec or lower.

I'm aware that high frequency sampling libraries do exist that probably solve my problem, but I couldn't get them to work when I downloaded them.

If anyone could tell me why I can't go bellow 4000 microSec, or suggest a way I could improve my code that would be great.

Thanks!

Here is my code:

Code: [Select]

#include <SPI.h>
#include "SdFat.h"
#include<Wire.h>
#include "TimerOne.h"

// Pins to collect data from
const int proximityPin = 3; // Digital pin for proximity sensor
int tranducerPin = 3;       // Analog pin for transducer
const int MPU_addr=0x68;  // I2C address of the MPU-6050

const int bufferSize = 20;
const int colNum = 8;
// Define the two buffers for the cyclic buffer
int buffer0[bufferSize][colNum];
int buffer1[bufferSize][colNum];
long logTime0[bufferSize];
long logTime1[bufferSize];
/*
buffer[0][x] is transducerVal
buffer[1:3][x] is AcX:AcZ
buffer[4:6][x] is GcX:GcZ
buffer[7][x] is priximityData
*/

// Various counters
int recInd = 0;
int logInd = 0;
int bufferID = 0;

const uint8_t chipSelect = 4;
const long SAMPLE_INTERVAL_MUS = 5000;

// Log file base name.  Must be six characters or less.
#define FILE_BASE_NAME "Data"
// File system object alias.
SdFat sd;
// Log file.
SdFile file;
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//==============================================================================
// Functions defined bellow
//------------------------------------------------------------------------------
// Write data header.
void writeHeader() {
  file.println("AcX,AcY,AcZ,GyX,GyY,GyZ,Pressure Signal,Proximity Signal,Run Time (micro sec), buffer no");
}
//------------------------------------------------------------------------------
// Log a data the CSV
void logData() {
  // Note here that we use the compliment of the buffer id as we just changed the buffer ID
  if(bufferID){
    for(int row=0; row<bufferSize; row++){
      for(int col=0; col<colNum; col++){      
        file.print(buffer0[row][col]); file.write(",");
        //Serial.print(buffer0[row][col]); Serial.print(",");
      }
      file.print(logTime0[row]);file.println(",0");
      //Serial.println(logTime0[row]);
    }
  }else{
    for(int row=0; row<bufferSize; row++){
      for(int col=0; col<colNum; col++){
        file.print(buffer1[row][col]); file.write(",");
        //Serial.print(buffer1[row][col]); Serial.print(",");
      }
      file.print(logTime1[row]);file.println(",1");
      //Serial.println(logTime1[row]);
    }
  }
}
//------------------------------------------------------------------------------
void recordData(){
      
    sei();  // Enable interupts for the I2C
    if(bufferID){
      // Use buffer1
      //Serial.println("Recording to buffer1");
      Wire.beginTransmission(MPU_addr);
      Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
      Wire.endTransmission(false);
      Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
      for(int col=0; col<=5; col++){
        buffer1[recInd][col]=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
      }
      // Get transducer signal
      buffer1[recInd][6] = analogRead(tranducerPin);
      // Get the proximity sensor signal
      buffer1[recInd][7] = digitalRead(proximityPin);
      // Record logTime
      logTime1[recInd] = micros();
    }else{
      // Use buffer0
      //Serial.println("Recording to buffer0");
      Wire.beginTransmission(MPU_addr);
      Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
      Wire.endTransmission(false);
      Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
      for(int col=0; col<=5; col++){
        buffer0[recInd][col]=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
      }
      // Get transducer signal
      buffer0[recInd][6] = analogRead(tranducerPin);
      // Get the proximity sensor signal
      buffer0[recInd][7] = digitalRead(proximityPin);
      // Record logTime
      logTime0[recInd] = micros();
    }

    // increment buffer index
    recInd++;    
  }
//------------------------------------------------------------------------------
void interuptTriggered(){
  recordData();
  if(recInd >= bufferSize){
    // Reset the recording index
    recInd = 0;
    // Change the state of the buffer to redord to
    bufferID = !bufferID;
    // Log the data
    logData();
  }
  //Serial.print(micros()); Serial.println("Triggered");  
  }
//------------------------------------------------------------------------------
void setup() {
  const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
  char fileName[13] = FILE_BASE_NAME "00.csv";

  // The input pin for the proximity sensor
  pinMode(proximityPin, INPUT);

  // Wake up the MPU-6050
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  Serial.begin(9600);
  // Wait for USB Serial
  while (!Serial) {
    SysCall::yield();
  }
  
  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(chipSelect, SD_SCK_MHZ(50))) {
    sd.initErrorHalt();
  }

  // Find an unused file name.
  if (BASE_NAME_SIZE > 6) {
    error("FILE_BASE_NAME too long");
  }
  while (sd.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
      fileName[BASE_NAME_SIZE + 1]++;
    } else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } else {
      error("Can't create file name");
    }
  }
  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
    error("file.open");
  }

  Serial.print(F("Logging to: "));
  Serial.println(fileName);
  Serial.println(F("Type any character to stop"));

  // Write data header.
  writeHeader();

  // Set a timer of lentgh SAMPLE_INTERVAL_MUS
  Timer1.initialize(SAMPLE_INTERVAL_MUS);
  Timer1.attachInterrupt(interuptTriggered); // Attach the interupt routine
}
//------------------------------------------------------------------------------
void loop() {
  // Force data to SD and update the directory entry to avoid data loss.
  if (!file.sync() || file.getWriteError()) {
    error("write error");
  }
  if (Serial.available()) {
    // Close file and stop.
    file.close();
    Timer1.detachInterrupt();
    Serial.println(F("Done"));
    SysCall::halt();
  }
}



cattledog

Quote
To log data to the SD card at high frequency I'm using a cyclic buffer method - where buffer1 is filled at regular intervals (by means of an interrupt routine) whilst buffer2 up logged to the SD card and visa versa. I'm using the SdFat library to do this.
I don't think you are doing that. Sdfat maintains its own buffer of 512 bytes, and physically writes this buffer to the card when 1) it is full  2) with file.close() or 3) with file.flush() or file.sync(). The physical writing to the card is what take time, and the filling of the library buffer with file.write() or file.print() is fast.


Code: [Select]

 // Force data to SD and update the directory entry to avoid data loss.
if (!file.sync() || file.getWriteError()) {
    error("write error");
  }


When you call this every pass through loop, I don't think you managing the filling of one buffer and the physical writing of the other. You are physically writing to the card, whatever data has been buffered at the time.


Go Up