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
  • 1
    Try putting sei(); at the start of the ISR. Also avoid using Serial.print inside an ISR. – Gerben Mar 16 '15 at 16:38
  • Grande Gerben! It works, please write your an answer. Why do I need to enable interrupts again? – UserK Mar 16 '15 at 17:00
  • 1
    By default interrupts are disabled inside ISRs. – Gerben Mar 16 '15 at 19:52
5

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

  • 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." – kwc Feb 23 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()). – kwc Feb 23 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

  • 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 – UserK May 18 '16 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.