Offline
Newbie
Karma: 0
Posts: 5
|
 |
« on: April 20, 2011, 01:17:35 am » |
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: #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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #1 on: April 20, 2011, 02:24:00 am » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 5
|
 |
« Reply #2 on: April 20, 2011, 02:39:46 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #3 on: April 20, 2011, 02:46:47 am » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 5
|
 |
« Reply #4 on: April 20, 2011, 03:27:38 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #5 on: April 20, 2011, 05:35:03 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #6 on: April 20, 2011, 06:20:51 am » |
// 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() 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 Wire.requestFrom(0x50,1); value = int(Wire.receive()); Serial.println(value);
Hopes this helpes
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 5
|
 |
« Reply #7 on: April 20, 2011, 08:50:49 am » |
Yes, that helps a lot. Thanks for the replies!!
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 5
|
 |
« Reply #8 on: April 09, 2012, 08:23:12 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #9 on: April 09, 2012, 10:43:12 am » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 5
|
 |
« Reply #10 on: April 09, 2012, 08:52:01 pm » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #11 on: April 10, 2012, 04:29:17 am » |
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
|
|
|
|
|
Maryland, USA
Offline
Full Member
Karma: 0
Posts: 162
|
 |
« Reply #12 on: April 14, 2012, 01:02:48 pm » |
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
Full Member
Karma: 0
Posts: 162
|
 |
« Reply #13 on: April 14, 2012, 01:09:16 pm » |
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
Edison Member
Karma: 28
Posts: 1079
Arduino rocks
|
 |
« Reply #14 on: April 14, 2012, 02:13:16 pm » |
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
|
|
|
|
|
|