3

I would like to read a sensor connected via I2C using Timer3 and Arduino Mega. Values are read from the sensor in the interrupt service routine and timer3 is set to trigger an interrupt at 200Hz.

Step I: testing the timer

I tested the timer using a counter and incrementing its value in the ISR. The output is displayed every second. It works!

200
201
200
201

CODE

// Timer variable
volatile int cont=0;
void setup()
{
 Wire.begin();
 Serial.begin(115200); 
 setupL3G4200D(2000); // Configure L3G4200 - 250, 500 or 2000 deg/sec
 delay(1500); //wait for the sensor to be ready 
 // Initialize Timer
 cli();
 TCCR3A = 0;
 TCCR3B = 0;
 // Set compare match register to the desired timer count
 OCR3A=77; //16*10^6/(200Hz*1024)-1 = 77 -> 200 Hz 
 //OCR3A=193; //16*10^6/(80Hz*1024)-1 = 194 -> 80 Hz 
 TCCR3B |= (1 << WGM32);
 // Set CS10 and CS12 bits for 1024 prescaler:
 TCCR3B |= (1 << CS30) | (1 << CS32);
 // enable timer compare interrupt:
 TIMSK3 |= (1 << OCIE3B);
 // enable global interrupts:
 sei(); 
 k = micros();
}
void loop()
{
 delay(1000);
 Serial.println(cont);
 cont=0; 
}
ISR(TIMER3_COMPB_vect)
{ 
 cont++;
}

Step II: Reading values from the sensor in the ISR

Now I add the I2C function in the Interrupt Service Routine, but the program is blocked after the calcBias() function which reads from the sensor, measures and saves the acquisition time in the samplingTime variable.

CODE

// Timer variables
volatile float phi=0;
volatile float theta=0;
volatile float psi=0;
volatile int x = 0;
volatile int y = 0;
volatile int z = 0;
volatile int cont=0;
void setup()
{
 Wire.begin();
 Serial.begin(115200);
 setupL3G4200D(2000); // Configure L3G4200 - 250, 500 or 2000 deg/sec
 delay(1500); //wait for the sensor to be ready 
 calcBias():
 Serial.print("Readings take: (us)");
 Serial.println(samplingTime);
 // Initialize Timer
 cli();
 TCCR3A = 0;
 TCCR3B = 0;
 // Set compare match register to the desired timer count
 OCR3A=77; //16*10^6/(200Hz*1024)-1 = 77 -> 200 Hz 
 //OCR3A=193; //16*10^6/(80Hz*1024)-1 = 194 -> 80 Hz 
 TCCR3B |= (1 << WGM32);
 // Set CS10 and CS12 bits for 1024 prescaler:
 TCCR3B |= (1 << CS30) | (1 << CS32);
 // enable timer compare interrupt:
 TIMSK3 |= (1 << OCIE3B);
 Serial.println("Enabling interrupts..");
 // enable global interrupts:
 sei(); 
 k = micros();
}
void loop()
{
 delay(1000);
 Serial.println(cont);
 Serial.println(z);
 cont=0; 
}
ISR(TIMER3_COMPB_vect)
{ 
 // Update x, y, and z with new values 2.5ms 
 getGyroValues(); 
 cont++;
}
void getGyroValues()
{ 
 //starting samplingTimer
 samplingTime = micros(); 
 // Gets the angular velocity from the sensor
 byte zMSB = readRegister(L3G4200D_Address, 0x2D);
 byte zLSB = readRegister(L3G4200D_Address, 0x2C);
 z = ((zMSB << 8) | zLSB);
 samplingTime = micros()- samplingTime;
}
void calcBias()
{ 
 Serial.println("Bias");
 // Reads sensors and display three values
 getGyroValues(); 
 [...] 
 Serial.println(bx);
 Serial.println(by);
 Serial.println(bz); 
}
void writeRegister(int deviceAddress, byte address, byte val) 
{
 Wire.beginTransmission(deviceAddress); // start transmission to device 
 Wire.write(address); // send register address
 Wire.write(val); // send value to write
 Wire.endTransmission(); // end transmission
}
int readRegister(int deviceAddress, byte address)
{
 int v;
 Wire.beginTransmission(deviceAddress);
 Wire.write(address); // register to read
 Wire.endTransmission();
 Wire.requestFrom(deviceAddress, 1); // read a byte
 while(!Wire.available()) 
 {
 // waiting
 // Serial.println("No Data");
 }
 v = Wire.read();
 return v;
}

I thought that the interrupt frequency was to high and I tried with 80 Hz and 20 Hz. Same results.

Here is the output:

starting up L3G4200D
Bias
4.00
0.00
-3.00
Read
asked Mar 16, 2015 at 15:43
3
  • 1
    Try putting sei(); at the start of the ISR. Also avoid using Serial.print inside an ISR. Commented Mar 16, 2015 at 16:38
  • Grande Gerben! It works, please write your an answer. Why do I need to enable interrupts again? Commented Mar 16, 2015 at 17:00
  • 1
    By default interrupts are disabled inside ISRs. Commented Mar 16, 2015 at 19:52

2 Answers 2

5

Try putting sei(); at the start of the ISR. I think I2C needs interrupts enabled to work.

answered Mar 16, 2015 at 19:51
2
  • To explain why it's necessary to (re)enable interrupts, this is what the ATmega328P datasheet says: "When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The user software can write logic one to the I-bit to enable nested interrupts." Commented Feb 23, 2019 at 0:39
  • Also, note that Arduino defines an interrupts() "function" that can be used as a more readable alternative to sei() (on AVRs, it's defined as a macro that is directly equivalent to sei()). Commented Feb 23, 2019 at 0:44
5

I2C requires interrupts to work, however enabling interrupts inside an ISR is not recommended. For one thing you may get into a race condition, where another interrupt occurs while processing the existing one.

Since you are doing virtually nothing in your main loop, a more sensible design would be to simply test if it is time to take a reading, and when that time is up, take the reading then.

As an example, your main loop could be rewritten as:

unsigned long lastReading;
unsigned long lastDisplay;
void loop()
{
 if (millis () - lastReading >= 5) // 200 Hz
 {
 lastReading = millis ();
 // Update x, y, and z with new values 2.5ms 
 getGyroValues(); 
 cont++;
 }
 if (millis () - lastDisplay >= 1000) // one second
 {
 lastDisplay = millis ();
 Serial.println(cont);
 Serial.println(z);
 cont=0; 
 }
}

Now you don't need the timer, and you are interrupt-friendly. :)

More information about interrupts: http://www.gammon.com.au/interrupts

answered Jun 28, 2015 at 21:40
1
  • Thank you, with your code this solution is perfect. But in my case, there is a SerialRoutine in the loop() which sometimes takes a long time to send /receive data over the serial. If the getGyroValues is placed in the main loop, it will be delayed by the other running task and its reliability compromised Commented May 18, 2016 at 12:51

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.