Pages: [1] 2   Go Down
Author Topic: problem with interrupt and writing to sd-card  (Read 3356 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi,
I have a sketch for logging rpm-meter data to a sd-card.
I have a reed switch connected to interrupt 1 counting pulses each second. in the main loop I am writing the counted value to the sd-card and reset the counter.
For some reason, the sd-card logging stops after some time between two to three hours regularly.
Can the problem be, that the interrupt interrupts the sd-card writing so that the sketch hangs?

Here's the code
Code:
#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "WString.h"


SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
int counter;
int counter1;
int tmpval;
int time;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 debug=false;
  pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
 counter=0;
 counter1=0;
 tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);
  }
  counter = 0;
  
 
}

void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
  
}

void loop(){

  file.print((millis()/1000));
  file.print(";");
  file.println(counter1/2);
  delay(30);
 
  if (file.writeByteError) {
    error1("write data");
    blinklogic=false;
    //Serial.println("blinklogic false");
    }
  if (!file.sync()){
    error1("sync");
    blinklogic=false;
    if(debug){
      Serial.println("blinklogic false");
    }  
  }
  if(blinklogic){
    if(debug){  
      Serial.println("blinklogic high");
    }
    if(blinker==false){
       digitalWrite(13, HIGH);
      if(debug){  
         Serial.println("blinker high");
      }
       blinker=true;
     }else
     if(blinker==true){
       digitalWrite(13,LOW);
       if(debug){
         Serial.println("blinker low");
       }
       blinker=false;
     }
   else if(!blinklogic)digitalWrite(13,LOW);
  }
   Serial.println(counter1/2);
  delay(970);
  
  
}

Does anybody have an idea?

Cheers,
Marcel
Logged

CT, USA
Offline Offline
Sr. Member
****
Karma: 3
Posts: 446
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Since the interrupt can come at anytime while you perform your file operations, you will need to use cli() (clear interrupt) and sei() (set interrupt) right before you open your file and right after you close your file. This way, your file operations will always be complete successfully and should solve your issue.

You'll need to include #include <avr/interrupt.h> for cli() and sei().
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi AlphaZeta,

I tried your solution. However after I disable the Interrupt with cli() it seems, that I cannot write to the file on the SD card. I suspect that for writing to the SD card interrupts need to be enabled.
It seems that the loop never makes it to the bottom, where I set the interrupt again with sei().

Any ideas?
Here's the updated code:

Code:
#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "WString.h"
#include <avr/interrupt.h>

SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
int counter;
int counter1;
int tmpval;
int time;
long timerun;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 timerun=0;
 debug=false;
 pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
 counter=0;
 counter1=0;
 tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);
  }
  counter = 0;
  
 
}

void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
  
}

void loop(){

  if(millis()-timerun>1000){
    //uint8_t SaveSREG = SREG;   // save interrupt flag
    cli(); //clear Interrupt
    if(debug)Serial.println("Interrupt off");
    file.print((millis()/1000));
    if(debug)Serial.println("Interrupt off1");    
    file.print(";");
    if(debug)Serial.println("Interrupt off2");
    file.println(counter1/2);
    if(debug)Serial.println("Interrupt off3");
    delay(30);
    if(debug)Serial.println("Daten geschrieben4.");
    if (file.writeByteError) {
      error1("write data");
      blinklogic=false;
      //Serial.println("blinklogic false");
      }
    if (!file.sync()){
      error1("sync");
      blinklogic=false;
      if(debug){
        Serial.println("blinklogic false");
      }  
    }
   sei(); // again Interrupt again
    //SREG = SaveSREG;   // restore the interrupt flag
    Serial.println("Interrupt ein");

    if(blinklogic){
      if(debug){
        Serial.println("blinklogic high");
      }
      if(blinker==false){
         digitalWrite(13, HIGH);
        if(debug){
           Serial.println("blinker high");
        }
         blinker=true;
       }else if(blinker==true){
         digitalWrite(13, LOW);
         if(debug){
           Serial.println("blinker low");
         }
        blinker=false;
       }
     else if(!blinklogic)digitalWrite(13, LOW);
    }
     Serial.println(counter1/2);
     timerun = millis();
    
  }
}
Logged

CT, USA
Offline Offline
Sr. Member
****
Karma: 3
Posts: 446
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

If I remember correctly, SPI probably wasn't the problem. Serial.print routines may be the culprit here. Did your code reach this line?
Code:
if(debug)Serial.println("Interrupt off");

My guess is probably not. Try again by commenting out all the Serial.print stuff.

Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Can the problem be, that the interrupt interrupts the sd-card writing so that the sketch hangs?
Why would this suddenly be a problem when it apparently works for 3 hours without issues?

The SD libraries use a lot of RAM and this would be my primary concern. Typically two buffers of 512 bytes each, 50% of the AtMega328 RAM capacity, is needed when writing to the card. This is in addition to your sketch and additional libraries RAM requirements.

Fort starters I would eliminate the Wstring library (using dynamic memory allocation is asking for problems) from the sketch and also move all string literals to flash (e.g. Serial.print("...move string to flash...").
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'll try that, but I have a similar sketch running without problems, with all the Serial.println commands. One question: how do I move Serial.print statements to flash?

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That did not help.
The problem seems to be somewhere else.
The arduino stops writing to the sd card always after a little more than two hours...
The strange thing is that the light of the led on pin 13 that keeps blinking while writing to the sd-card is becoming dimmer and dimmer over time...  At first I thought, that the problem might be the battery, but that is not the case, as the same behavior occurs when connected to a USB cable...
I tried different arduinos, but found the same behavior... Does anyone have an idea, what might be the problem?
Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I see you are printing inside your interrupt function. This is generally not good since print is a slow function that will block other interrupts. You should try to rewrite your code so that the interrupt routine is as fast as possible (move printing to the main loop).

Code:
void count(){
 //increase counter
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);
 }
 tmpval = counter1/2;
}


Also you need to prefix global variables that are changed within an interrupt handler with the "volatile" keyword. If not, changes to these variables may not be picked up by code outside of the interrupt function.

There is also a concurrency issue with accessing variables changed by interrupt handlers. Acess to variables beyond a single byte need to be guarded with interrupt enable/disable statements. This is missing from your code.

Logged

0
Offline Offline
Edison Member
*
Karma: 0
Posts: 1103
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
.... the "volatile" keyword. If not, changes to these variables may not be picked up by code outside of the interrupt
Even then, this is a weak protection for bytes only, not for multi-byte datatypes...
« Last Edit: September 09, 2010, 04:41:58 pm by mpeuser » Logged

Offline Offline
Edison Member
*
Karma: 3
Posts: 1001
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Even then, this is a weak protection for bytes only
This has nothing to do with protection at all.

Volatile is a compiler directive that turns off access optimization (register caching) for the marked variables (single or multi-byte). This is to make certain that each C-source reference to the variables will be read from RAM prior to use.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry, none of the tips helped.
I modified the code according to the above recommendations.
Code:
#include <FlexiTimer2.h>
#include "Fat16.h"
#include "SdCard.h"
#include "MemoryFree.h"
#include <avr/interrupt.h>


SdCard card;
Fat16 file;

boolean blinker;
boolean blinklogic;
volatile int counter;
volatile int counter1;
volatile int tmpval;
int time;
//Digital Pin 3 (= Interrupt 1)
// 10k auf GRND
//Digital Pin 3 auf 10K
// Reed an 10K
//andere Seite Reed an 5V
boolean debug;

void setup(){
 debug=true;
  pinMode(11, INPUT);
 pinMode(13, OUTPUT);
 Serial.begin(9600);
 
 blinker = false;
 blinklogic = true;
  counter=0;
  counter1=0;
  tmpval = 0;
 time = millis();
 attachInterrupt(1,count,RISING);

 FlexiTimer2::set(1000,report);
 FlexiTimer2::start();
 
   if (!card.init()) error1("card.init");
  
   // initialize a FAT16 volume
   if (!Fat16::init(card)) error1("Fat16::init");
  
   // create a new file
   char name[] = "WIND0.00";
   for (uint8_t i = 0; i < 100; i++) {
     name[6] = i/10 + '0';
     name[7] = i%10 + '0';
     if (file.create(name)) break;
   }
   if (!file.isOpen()) error1("file.create");
   if(debug){
     Serial.print("Logging to: ");
     Serial.println(name);
   }
   //Header im File schreiben
   file.println("Umrundungen Windturbine");
  
    if (file.writeByteError || !file.sync()) {
      error1("write header");
    }
}

void error1(char *str){
  Serial.print("error ");
  Serial.println(str);
}

void report(){
  if(debug){
    /*Serial.print("Drehzahl: ");
    Serial.print(counter/2);
    Serial.print(", ");
    Serial.println(counter1/2);*/
  }
  counter = 0;
  
 
}

void count(){
 //increase counter
 cli();
 counter++;
 counter1++;
 if((counter1/2)!=tmpval){
   /*Serial.print("tmpval: ");
   Serial.print(tmpval);
   Serial.print(", ");*/
   Serial.println(counter1/2);

 }
 tmpval = counter1/2;
 sei();
}

void loop(){

  file.print((millis()/1000));
  file.print(';');
  file.print(counter1/2);
  file.print(';');
  file.println(freeMemory());  
  delay(20);
 
  if (file.writeByteError) {
    error1("write data");
    blinklogic=false;
    Serial.println("blinklogic false");
    }
  if (!file.sync()){
    error1("sync");
    blinklogic=false;
    if(debug){
      Serial.println("blinklogic false");
    }  
  }
  if(blinklogic){
    if(debug){  
      Serial.println("blinklogic high");
    }
    if(blinker==false){
       digitalWrite(13, HIGH);
      if(debug){  
         Serial.println("blinker high");
      }
       blinker=true;
     }else
     if(blinker==true){
       digitalWrite(13,LOW);
       if(debug){
         Serial.println("blinker low");
       }
       blinker=false;
     }
   else if(!blinklogic)digitalWrite(13,LOW);
  }
   Serial.println(counter1/2);
  delay(970);
  
  
}
What I noticed is that the LED (see in loop())on Pin 13 flashes weaker and weaker while writing to the sd-card over time. After approx. 130 minutes, the flash is almost not visible anymore. That is about the time, when arduino stops writing to the sd-card.

The problem is not the power source, as the same behavior occurs when sourcing power from a battery or from USB.

There is also no memory problem, as the free memory is always >1000bytes

I am desparate. Does anybody have a clue what the problem might be?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there anybody, who at least can confirm, that writing to a sd-card has worked for longer than 2 hours continuously?
Logged

CT, USA
Offline Offline
Sr. Member
****
Karma: 3
Posts: 446
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Fritz,

Yes, I used one as a temperature logger and it ran for at least a few days at a time and I didn't run into any issues.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 6
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Did the issue ever get resolved? I'm trying to build an arduino/datalogger/accel/rpm reading device, and I'm worried about doing SD card work with interrupts going on at the same time.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 24
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I still have the same problem. I am writing the rpm info on my windrotor, however, after about two hours writing stops....
Logged

Pages: [1] 2   Go Up
Jump to: