Missed samples during interrupt, SD write

I am trying a accomplish a fairly simple task of using the UNO (rev.3) to continuously log data from an accelerometer at a sampling rate of 1kHz.

Once I apply power, the UNO reads the data and begins writing the sampled data to the SDHC card.

The problem I am running into is the UNO is missing 2-5 samples ever ~143 ms with an analog input at A0. I am monitoring the sampling rate by strobing pin 8 (PCINT0).

It seems that while the transfer of data to the SD card is happening, data collection is not. Is there any way to avoid this so I do not miss any samples?

I can't figure out why I am missing samples. Any info is gladly appreciated as I am a beginner to the programming world.

//
// file: accel_log -- Arduino sketch to sample A/D on pin 8 and log
//
// SD card attached to SPI bus as follows:
// MOSI - pin 11
// MISO - pin 12
// CLK - pin 13
// CS - pin 10
//

#include <SD.h>

int AD1pin = A0;      // input pin
int AD2pin = A2;      // input pin
int AD1val = 0;       // value read
int AD2val = 0;       // value read
int in1;              // input pin 1
int in2;              // input pin 2

char buf[24];         // output buffer
char buf1[24];        // temp buffer
char fn[24];          // filename
int cnt = 0;          // used to decimate serial output
File fd;              // file descriptor for log file

const int chipSelect = 10;

void setup(){
        int fileNum = 0; 
 
        Serial.begin(9600);      // open the serial port

 
        pinMode(10, OUTPUT);        // SD card chip select
        pinMode(8, OUTPUT);         // strobe line for debug
        pinMode(6, INPUT);          // DIO 1
        pinMode(7, INPUT);          // DIO 2

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

        if (!SD.begin(chipSelect)) {
                Serial.println("Card failed, or not present");
                // don't do anything more:
                return;
        }
        Serial.println("card initialized.");
 
        do {
                sprintf(fn, "log_%03d.dat", fileNum++);
        } while (SD.exists(fn));

        fd = SD.open(fn, FILE_WRITE);
        if (!fd) {
                sprintf(buf1, "data file: %s opened failed\n", fn);
                Serial.print(buf1);
                return;
         }
                          
        sprintf(buf1, "data file: %s opened\n", fn);
        Serial.print(buf1);
        
        // 
        // configure timer 1 for 1kHz
        //
        cli();        // disable interrupts

        //set timer0 interrupt at 1kHz
        TCCR0A = 0;                                 // set entire TCCR2A register to 0
        TCCR0B = 0;                                 // same for TCCR2B
        TCNT0  = 0;                                 // initialize counter value to 0
        
        // set compare match register for 1khz increments
        OCR0A = 248;// = (16*10^6) / (1000*64) - 1 (must be <256)  
        
        TCCR0A |= (1 << WGM01);                     // turn on CTC mode      
        TCCR0B |= (1 << CS11) | (1 << CS10);        // Set CS11 and CS10 bits for 64 prescaler     
        TIMSK0 |= (1 << OCIE0A);                    // enable timer compare interrupt
        
        sei();        //enable interrupts
       
        delay(2000);
 }

ISR(TIMER0_COMPA_vect){    //timer0 interrupt routine
        digitalWrite(8,HIGH);           // test strobe high
        
        AD1val = analogRead(AD1pin);    
        
        sprintf(buf, "%d\n", AD1val);
        
        if (fd) {
                fd.print(buf);
        }

#undef SERIAL_OUT
#ifdef SERIAL_OUT
        if (cnt % 250 == 0) {        // only output @ 4 Hz
               Serial.print(buf);
               }
        }
#endif
        if (cnt++ % 5000 == 0) {
               if (fd) {
                      fd.flush();
                }
        }
        digitalWrite(8,LOW);          // test strobe low
}


void loop(){
}

It looks like you are writing each item of data to the SD card during the interrupt. Perhaps that consumes too much time.

You might be better to save several bytes (128?) in an array and write that to the SD card OUTSIDE the interrupt when the array is full. You could have two arrays filling one while writing the other.

...R

Once I apply power, the UNO reads the data and begins writing the sampled data to the SDHC card.

Actually, it doesn't. It writes data to a buffer. When the buffer is full, it is written to the SD card. Most of the time, there is room in the buffer. But, when the buffer is full, it needs to be committed to the file, and that takes time, during which you can't log more data.

PaulS:
When the buffer is full, it is written to the SD card.

Where does it say that?

...R

Robin2:
It looks like you are writing each item of data to the SD card during the interrupt. Perhaps that consumes too much time.

I agree, you are trying to do far too much inside the ISR. Just save data to an array in the ISR and write it outside the ISR, as Robin2 says. You can either use the double-buffered arrangement he suggests, or you can use a ring buffer that the ISR writes to and the main loop reads from. Like this:

const int buflen = 64;    // must be 256 or less because we are using 1-byte indexes; best to use a power of 2
volatile unsigned dbuf[buflen];
volatile byte wrIndex = 0, rdIndex = 0;

ISR(TIMER0_COMPA_vect){    //timer0 interrupt routine
  digitalWrite(8,HIGH);           // test strobe high

  byte newIndex = (wrIndex + 1) % buflen;
  if (newIndex != rdIndex)   // if buffer is not full
  {
     dbuf[wrIndex] = analogRead(AD1pin);
     wrIndex = newIndex;
  }    
  digitalWrite(8,LOW);          // test strobe low
}

// in loop():

  ...
  if (rdIndex != wrIndex)  // if buffer is not empty
  {
    if (fd)
    {
      ... write dbuf[rdIndex] to SD card and/or serial here ...
    }
    rdIndex = (rdIndex + 1) % buflen;
  }
  ...

Where does it say that?

In the code.

Try being helpful - you may find you like it. Where in the code?

...R

PaulS:

Where does it say that?

In the code.

Try being helpful - you may find you like it.

I did. I didn't.

You can look at the print() and write() methods to see what they do. When there is room in the buffer, they are fast. When there isn't, they aren't, because they need to commit the current buffer to the file.

Do you mean that this code (extracted from the OP)

if (fd) {
                fd.print(buf);
        }

doesn't actually write the buf contents to the SD card until the buf is full?

I certainly am not aware that that is how it works and it's not what I expect. However I may be wrong and if so a full explanation would be very helpful.

Thanks

...R

PaulS:

Try being helpful - you may find you like it.

I did. I didn't.

You can look at the print() and write() methods to see what they do. When there is room in the buffer, they are fast. When there isn't, they aren't, because they need to commit the current buffer to the file.

Do you mean that this code (extracted from the OP)

if (fd) {

fd.print(buf);
        }




doesn't actually write the buf contents to the SD card until the buf is full?

I certainly am not aware that that is how it works and it's not what I expect. However I may be wrong and if so a full explanation would be very helpful.

Look at the fd.print() method. Well, actually, that would be hard, since the File class doesn't actually implement the print() method.

But the Print class does, and it calls the write() method to actually do the work.

The File class DOES implement the write() method, passing the responsibility to the SdFile::write() method.

If you look at that method, you will see that it is rather complicated, but the end result is that data is stored in a buffer unless there is no room. If that is the case, the contents of the buffer are committed to the card, to make room in the buffer for the new data.

Interrupts are supposed to be fast. Writing to disk is hardly fast.

Thank you Paul - that's interesting and helpful.

However I don't think it was reasonable to expect a Newbie to have figured that from your earlier comment. More to the point, however, if one assumes (incorrectly) that the stuff is written directly to the SD card every time then there is an even stronger case for not having that code inside an ISR.

...R

PaulS:
Look at the fd.print() method. Well, actually, that would be hard, since the File class doesn't actually implement the print() method.

...snip...

Robin2:
I certainly am not aware that that is how it works and it's not what I expect. However I may be wrong and if so a full explanation would be very helpful.

You have my sympathy. The online documentation for the Arduino core API is IMO quite appalling and I can't understand why it's been left in such a sad state. Unfortunately it doesn't tell you the actual method signature, or where the signature is declared (so that we can go look it up for ourselves) or provide any details about what the functions actually do (such as whether they block, and if so under what conditions and for how long). If we need to know something in order to use the API correctly, it should be documented. Given that the goal of the Arduino project is to appeal to inexperienced developers, the lack of decent documentation is especially hard to understand.

If the documentation was any good then diving in to the code would be the last resort. As it is, almost any question about the API requires you to look at the source code for answers.

An sdcard may have writing latency up to 250msecs. It means you cannot write to the card for up to 250msecs. Therefore you need a buffer. For 1KHz sampling rate and ie. 2bytes per sample you need a 500bytes large fifo data buffer to make an sdcard happy.
There are several topics handling this, with examples.

FYI - with 1284p @16MHz I needed 135 records (8bytes per record) large fifo buffer in order to write the records with 1kHz sampling rate without a fifo overrun.. The max latency measured was 49msecs, but it could be up to 250msecs, so I needed 5x bigger buffer to be safe..

http://arduino.cc/forum/index.php/topic,153598.0.html