Go Down

Topic: Problem when reading from I2C EEPROM inside an interrupt function (Read 3346 times) previous topic - next topic

rimongo

Hi everyone,

I have a program that reads analog values and stores them to an external I2C EEPROM (24LC256). The program is constantly recording values and overwrites the memory every 60 seconds. I have a button that triggers an interrupt function to stop the recording and read all the values recorded starting from the last recorded memory address. I am not able to read the external EEPROM from within the interrupt function. The program won't go past the Wire.beginTransmission(0x50) instruction inside the interrupt function.
Do you have any idea why is this happening or any suggestions to solve this?

This is the code:
Code: [Select]
#include <Wire.h>

volatile boolean Button_state = 0;   //variable to store the button state, after pressed, the last 60 seconds will be recorded
const int Button_pin = 2;  //the state detection button is located in digital pin 2
volatile int value;
volatile byte low = 0x00;    //low part of address sequence bit assignemts for 12C EEPROM 24LC256
volatile byte high = 0x00;   //high part of address sequence bit assignemts for 12C EEPROM 24LC256
volatile int i;

void setup()
{
  pinMode(Button_pin, INPUT);   // Configuring Button_pin as an input
  Wire.begin();   //join i2c bus
  Serial.begin(115200);
  attachInterrupt(0, read_values, RISING);
  Serial.println("Start recording values...");
}
void loop()
{
  while (Button_state == 0)
  {
    // need to divide by 4 because analog inputs are 10 bits (range from
    // 0 to 1023) and each byte of the EEPROM is 8 bits (can only hold a
    // value from 0 to 255)
    int val = analogRead(0) / 4;
    // write the value to the appropriate byte of the EEPROM.
    Wire.beginTransmission(0x50); // transmit to device address (0x50) 01010000
                                  // device address is specified in datasheet
    Wire.send(high);  // sends high address byte 
    Wire.send(low);  // sends low address byte 
    Wire.send(val);
    Wire.endTransmission();  // stop transmitting
   
    // advance to the next address.  there are 32,768 bytes in
    // the EEPROM, so go back to 0 when we hit 32,768.
    if(high == 0x7F && low == 0XFF) //if memory has reached max 32KB
    {
      high = 0x00;
      Serial.println("EEPROM full");
    }
    if(low == 0xFF) // if low byte reached max, increment high address
    high++;
    low++;        // increment value
    delayMicroseconds(1450);   // 1.8 ms => 1000ms/546 to obtain the maximum sampling rate for 32kB EEPROM which is ~546 Hz. (32kB/60sec.)
  }
}

void read_values()
{
  Button_state = 1;
  for(i = 0; i < 32767; i++)
    {
      Serial.println("Reading values..");
      Wire.beginTransmission(0x50);
      Wire.send(high);
      Wire.send(low);
      Wire.endTransmission();
      Serial.print("Reading from address high: ");
      Serial.print(high,DEC);
      Serial.print("low: ");
      Serial.println(low,DEC);
      Wire.requestFrom(0x50,1);
      Serial.println(int(Wire.receive()));
      value = int(Wire.receive());
      Serial.print("Value read: ");
      Serial.println(value);
      if(high == 0x7F && low == 0XFF) //if memory has reached max 32KB
      {
        high = 0x00;
      }
      if(low == 0xFF) // if low byte reached max, increment high address
      high++;
      low++;        // increment value
    }
}





robtillaart


interrupt fuctions should be as short as possible and your readvalues does a lot of IO which takes far too long.

What you should do is declare a
volatile int flag = false;

when the IRQ is called set it to true

in your main loop() you test the flag and do the read_values.

if (flag)
{
   flag = false; /// reset it again;
  read_values();
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

rimongo

Thanks for the reply!

Yes, I tried just reading the state of the button inside the loop every time a value is recorded and run the readvalues function if the button changes its state. This way I don't even need the interrupt.
I don't think it has anything to do with the size of the interrupt function. I've used interrupt functions which are way too long that basically they are just locking the main loop and they run properly. I think the problem should be related to the Wire library and interrupts.

robtillaart

Quote
I don't think it has anything to do with the size of the interrupt function. I've used interrupt functions which are way too long that basically they are just locking the main loop and they run properly.


If you are in the interrupt routine, printing all kind of things (which takes time) and the button/switch bounces new interrupts are generated and the interrupt routine will start over and over => the stack will blow up.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

rimongo

I am not planning to trigger the interrupt function after I triggered it once, which won't cause the problem of starting the interrupt function over and over. I guess that's why it's better to just check for the button state in every loop.
I tried just checking 1 memory address from the interrupt function but nothing happened. I guess the problem is actually related to the wire library and interrupts.
But, what happens in the case that you mention?? What if once I am in the interrupt function I never go back to the main loop and the stack actually overflows because the long interrupt routine starts over and over??

robtillaart




Quote
I am not able to read the external EEPROM from within the interrupt function. The program won't go past the Wire.beginTransmission(0x50) instruction inside the interrupt function.
Do you have any idea why is this happening or any suggestions to solve this?


- http://www.arduino.cc/en/Reference/AttachInterrupt - states :
Inside the attached function [the IRQ], delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.

In your main loop you do a Wire transmission too. Suppose this is interrupted by and in the interrupt it calls another Wire.beginTransmission, while the previous is not finished. That would break the communication


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

robtillaart


Code: [Select]
    // advance to the next address.  there are 32,768 bytes in
    // the EEPROM, so go back to 0 when we hit 32,768.
    if(high == 0x7F && low == 0XFF) //if memory has reached max 32KB
    {
      high = 0x00;
      Serial.println("EEPROM full");
    }
    if(low == 0xFF) // if low byte reached max, increment high address
    high++;
    low++;        // increment value

noted something else in the code in loop()

(high, low)
(0x07F, 0xFF)
If high == 0x7F && low == 0xFF => high = 0x00
(0x00, 0xFF)
if (low == 0xFF) => high++, low++
(0x01, 0x00)

while you would expect (0x00, 0x00) or ...


Furthermore in readValues()

Code: [Select]

      Wire.requestFrom(0x50,1);
      Serial.println(int(Wire.receive()));  << first call
      value = int(Wire.receive());  << second call


You ask for one byte and you read two, the second one will fail. should be rewritten to something like

Code: [Select]

      Wire.requestFrom(0x50,1);
      value = int(Wire.receive());   
      Serial.println(value);   


Hopes this helpes

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)


manu_sri

Hi, Robtillaart
Would you please explain. I saw a code in an Arduino project written " volatile byte flag_bit1 = LOW "  What does it mean?
Thanks,
Manu

robtillaart

Quote
written " volatile byte flag_bit1 = LOW "  What does it mean?


it is the declaration of a byte variable with the name flag_bit1. Its initial value is LOW (which is a macro definition which equals 0) and the volatile keyword means that the compiler is not allowed to optimize access to it e.g. by using registers.

Read the book of Kerningham and Ritchie C-programming to learn about the basics of C.
e.g. - http://letrongngoc.tech.officelive.com/Documents/TheCProgrammingLanguageSecondEdition.pdf -

If you like the book don't hesitate to buy it, its worth its price!



Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

manu_sri

Thank you for explanation.Very useful . I am learning Arduino and C- programming. If you don't mind, probably, I may ask you more questions in the future.
By the way, I 'm in Thailand. My English is not good.

Thanks,Manu

robtillaart

Quote
By the way, I 'm in Thailand. My English is not good.

Please put that in your profile, so people will understand directly, and by the way, your English is good enough to understand !

For other questions, please start your own topic / discussion.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

spirilis

Not sure if it was mentioned already, but Wire requires the use of several firings of the TWI (atmel's fancy name for I2C) interrupt to do its thing since it relies on the AVR processor's TWI hardware module.  I can't recall the specifics of the AVR's interrupt handling but I think interrupts are disabled when executing an interrupt handler.  That's why Wire doesn't work at all inside the interrupt handler.
I think you can categorically say it is inappropriate to do any kind of I2C, SPI, or UART communication inside an interrupt handler because they all require other hardware interrupts to fire in order to complete their work.

(Now that said, I think the hardware TWI module can be driven without the use of an interrupt, but the Wire library would have to be rewritten to poll the TWINT bit in the TWCR register...)

spirilis

Another tidbit to keep in mind, at least with Arduino 1.0 I found, is when you write over I2C only the contents of the last "Wire.send()" statement before "Wire.endTransmission()" actually gets sent... I think that's a bug but I'm not sure.  You can write multiple bytes at once by loading them up into an array first and writing that out with one "Wire.send()".  That bug doesn't apply to reading ("Wire.receive()")

fat16lib

As mentioned, Wire can't be used with interrupts disabled.

I wrote two I2C classes that complement Wire and are in a library called I2cMaster.  The TwiMaster class uses the avr hardware 2-wire Serial Interface (TWI).  It can be used in an ISR and runs about twice as fast as Wire.

The second class, SoftI2cMaster, implements software I2C and is handy if you want or must use pins other than the hardware avr I2C pins.

This library is located here http://forums.adafruit.com/viewtopic.php?f=25&t=13722 in the file i2cv2.zip.

Look at the examples and html.

Go Up