SDFat and interrupts

Hi All...

I have a data logging application that is writing data, and for now syncing after each write. The board has been modified to trigger an interrupt when power goes away, with a capacitor holding the circuit up for a short time. The idea is to close the files.

Originally I thought I would set a global flag in the ISR, and poll that flag each time through my loop, closing the files when the flag is true. But, there are some places in my code where it gets into different loop for a short time, but long enough to screw this plan up.

So, I am thinking its best to close the files from inside the ISR. The problem with this approach is that the ISR might get called in the middle of a write operation, leaving the file in a funny state when the close is executed. Close calls Sync(), which does not appear to depend upon any interrupts. So it seems that it is safe to call close() from inside an ISR if I can solve the first problem.

It seems the solution is to put interrupts down in the sync() method. This way, a sync can not be interrupted. It may also be necessary to do this during the write() method, so that complete non-corrupted data is added to the buffer before the sync() happens.

I would appreciate any thoughts and suggestions.


Get a larger capacitor is my suggestion.

[quote author=Nick Gammon link=topic=89803.msg673943#msg673943 date=1328088102] Get a larger capacitor is my suggestion. [/quote]

Nah, larger caps take too long to charge. Been there... Besides, if the app is waiting for input, it could be there a while.

Like, hours?

I think you need a redesign. "Putting down" interrupts (what, are they dogs?) isn't going to be the solution. Looking at Sd2Card.cpp there are heaps of places that it turns interrupts on, so trying to stop them is going to be a lot of work. And bear in mind they are there for a reason.

You shouldn't be in an "inner loop" (like waiting on a button press) that stops everything else from happening, like checking for power outages. Even a small capacitor should keep you going for a few seconds, and after all, even digital cameras let you eject the SD card after a couple of seconds.

Or, if you are in an inner loop that might go for a long time, sync first.

There is one point at which the board can do nothing useful until it establishes communication from another piece of equipment, so it waits for that. At this point, its just the debug log that's an issue and that's a secret log, so I may just let that one go.

Syncing before entering any kind of loop does not help if you can write to a file from inside the loop. Any time there is a potential for writing, which is always in this app, there is the potential for an interrupt to fire in mid write. Its not unusual for developers to declare blocks of code as atomic for just this reason. You put the interrupts down, do the critical task, then put them back up. Look at the bright side, this is relatively easy. There are no threads!

Its not unusual for developers to declare blocks of code as atomic for just this reason. You put the interrupts down, do the critical task, then put them back up.

Fine. But these blocks of code enable interrupts…

/** Soft SPI send */
void spiSend(uint8_t data) {
  // no interrupts during byte send - about 8 us
  for (uint8_t i = 0; i < 8; i++) {
    fastDigitalWrite(SPI_SCK_PIN, LOW);

    fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);

    data <<= 1;

    fastDigitalWrite(SPI_SCK_PIN, HIGH);
  // hold SCK high for a few ns

  fastDigitalWrite(SPI_SCK_PIN, LOW);
  // enable interrupts

In principle, ISRs should do something short. Writing a file to an SD card doesn't fall into that category, no matter how much you want it to.

Ah! He is putting interrupts down (that's what cli does, it turns interrupts off glogally), writing the data, then putting them back up. So the writes are atomic. Okay so in that case, it should be safe to use the library as normal, as long as when the close() is called from the ISR, interrupts are put back up first.

Thanks for hunting that down Nick!

[quote author=Nick Gammon link=topic=89803.msg673956#msg673956 date=1328089109] Like, hours? [/quote]

With my 800mA regulator powering both my 220mA board and charging a 1.5F super cal, it takes nearly 40 minutes to fully charge the cap. That's with a 100 ohm resistor. The reason is because as the cap charges, the voltage on it changes, reducing the current as it charges and therefore slowing the charge. There are chips that are charge controllers, that will keep the current constant and charge the cap 5 times more quickly, but its an expensive part an takes up space.

it takes nearly 40 minutes to fully charge the cap.

That only matters if the board is powered on/off frequently.

How long a hold up are you getting now?


You can't close the file in an ISR. If you write data, an SD write may be in progress when the interrupt happens. The call to sync in the ISR will corrupt the file. An SD write can be required for the next byte that is written after a sync call. In fact writing a single byte can cause several SD blocks to be written if a cluster is allocated for the byte.

skyjumper: Thanks for hunting that down Nick!

I hunted down that your idea won't work. As fat16lib says, syncing data is likely to involve writing more than one byte, and what I found was that spiSend turns on interrupts after writing a single byte. So your idea of turning off interrupts and hoping for the best won't work.

skyjumper: I would appreciate any thoughts and suggestions.

You asked for opinions, and mine is that you need to restructure your code so that you detect the loss of power in a timely way. Fiddling with the way interrupts work is rarely a good idea.

Hm, okay thanks everyone, I'll think of something.