Pages: [1]   Go Down
Author Topic: Problem when reading from I2C EEPROM inside an interrupt function  (Read 3152 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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
    }
}



Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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();
}
Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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??
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset




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


Logged

Rob Tillaart

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

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Code:
    // 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:
      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:
      Wire.requestFrom(0x50,1);
      value = int(Wire.receive());   
      Serial.println(value);   

Hopes this helpes

Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, that helps a lot. Thanks for the replies!!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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!



Logged

Rob Tillaart

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

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Global Moderator
Netherlands
Online Online
Shannon Member
*****
Karma: 223
Posts: 13903
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Rob Tillaart

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

Maryland, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 162
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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...)
Logged

Maryland, USA
Offline Offline
Full Member
***
Karma: 0
Posts: 162
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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()")
Logged

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

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.
Logged

Pages: [1]   Go Up
Jump to: