1
$\begingroup$

Background: I'm using the L3GD20H MEMS gyroscope with an Arduino through a library (Pololu L3G) that in turn relies on interrupt-driven I2C (Wire.h); I'd like to be able to handle each new reading from the sensor to update the calculated angle in the background using the data ready line (DRDY). Currently, I poll the STATUS register's ZYXDA bit (which is what the DRDY line outputs) as needed.

General question: With some digital output sensors (I2C, SPI, etc.), their datasheets and application notes describe using a separate (out-of-band) hardware line to interrupt the microcontroller and have it handle new sets of data. But on many microcontrollers, retrieving data (let alone clearing the flag raising the interrupt line) requires using the normally interrupt-driven I2C subsystem of a microcontroller. How can new sensor data be retrieved from the ISR for the interrupt line when also using the I2C subsystem in an interrupt-driven manner?

Possible workarounds:

  1. Use nested interrupts (as @hauptmech mentioned): re-enable I2C interrupt inside of ISR. Isn't this approach discouraged?

  2. Use non-interrupt-driven I2C (polling)--supposedly a dangerous approach inside of ISRs. The sensor library used depends on the interrupt-driven Wire library.

  3. [Edit: professors' suggestion] Use a timer to interrupt set to the sample rate of the sensor (which is settable and constant, although we measure it to be e.g. 183.3Hz rather than 189.4Hz per the datasheet). Handling the I2C transaction still requires re-enabling interrupts, i.e. nested interrupts or performing I2C reads from the main program.

[Edit:] Here's a comment I found elsewhere on a similar issue that led me to believe that the hang encountered was from I2C reads failing inside an interrupt handler: https://www.sparkfun.com/tutorials/326#comment-4f4430c9ce395fc40d000000

…during the ISR (Interrupt Service Routine) I was trying to read the device to determine which bit changed. Bad idea, this chip uses the I2C communications which require interrupts, but interrupts are turned off during an ISR and everything goes kinda south.

$\endgroup$
2
$\begingroup$

I think you have a false assumption somewhere. A very quick scan through the atmel datasheet and arduino twi.c does not show any problems.

Why do you think the microcontroller can't handle the interrupt from an ISR using an interrupt-driven I2C subsystem?

$\endgroup$
  • $\begingroup$ I'll need to provide a more thorough response to your answer later, but I've included a link to a similar case where someone claims I2C reads fail inside of ISRs because the I2C is interrupt driven, if that helps. $\endgroup$ – chrstphrchvz Feb 24 '16 at 23:28
  • $\begingroup$ Hard to tell the exact details but that poster did not seem to understand that if you want to nest interrupts you need to explicitly enable it on the Atmel. However, in this case you wouldn't want to nest the interrupts directly anyway, but use a couple bit flags to communicate between the interrupt handlers. Anyway, not a problem with the microcontrollers interrupt system. $\endgroup$ – hauptmech Feb 25 '16 at 0:02
  • $\begingroup$ The problem should only be in my understanding of the sensor interrupt's theory of operation. Is the suspected faulty assumption that nested interrupts aren't possible? They are possible, but are discouraged; should I have rephrased the question as "how to use gyro in interrupt-driven manner, but without nested interrupts"? As it is, I poll the STATUS register of the sensor; does the method you suggest do the same but by polling a variable instead, or does it enable handling gyro data in the background? $\endgroup$ – chrstphrchvz Mar 26 '16 at 20:32
  • $\begingroup$ You should be able to solve your problem with either nested interrupts or a sequential interrupts. Either way this is a use case not planned for by the arduino twi authors. You will have to create your own twi that adds the functionality you need (responding to sensor interrupts). @david-carey gave some hints in his 'difficult' method. $\endgroup$ – hauptmech Mar 26 '16 at 22:43
  • $\begingroup$ Keep in mind that "in the background" is not what interrupts do. Interrupts disrupt and stop current processing to do something that is time sensitive. If your sensor data is time sensitive then an interrupt handler is the way to go. If you want to merely want to make programming and task organization easy (at the expense of performance) then you should look at using a multitasking OS. $\endgroup$ – hauptmech Mar 26 '16 at 22:50
2
$\begingroup$

I'm using the L3GD20H MEMS gyroscope with an Arduino ... How is the sensor's interrupt line intended to be used if the microcontroller can't handle the interrupt from an ISR using an interrupt-driven I2C subsystem?

I'm assuming you've already looked at the L3GD20H datasheet and the L3GD20H errata sheet, and you understand that the data-ready pin (DRDY) is sometimes called an interupt pin (INT2), not to be confused with "the" programmable interrupt pin (INT1). In particular, p. 45 of the datasheet says that reading the IG_SRC register clears the interrupt signal on INT1.

If I were you, my next step would be to look at Arduino software for the L3GD20H and see how they handled it. (If someone has already done it, it must be possible, right?)

easy method

Perhaps the easiest method is polling: connect the interrupt pin of the L3GD20H up to some general-purpose digital input pin of the Arduino. Then write a function to check that pin and do the appropriate things. Then call that function every time through the main loop().

Just because a pin is labeled "interrupt" doesn't mean it has to be handled by an interrupt routine.

currently more difficult method

Several people have requested that someone improve the Wire library to make it non-blocking ( "Wire library and blocking"; "Make Wire library non-blocking"; "I2C Wiring Library - Lockup"; etc. ).

With such a theoretical future Wire library, you could write one interrupt handler that handles a pin-change interrupt of some Arduino pin connected to the interrupt pin of the L3GD20H. On a low-to-high change, that interrupt handler would do nothing and immediately return. On a high-to-low change, that interrupt handler would call the (non-blocking) functions of the Wire library that appends the appropriate message to the end of a queue in a RAM buffer. If the I2C hardware was idle at that instant (i.e., it wasn't already in the middle of sending out a byte, possibly in the middle of a message to some other device on the I2C bus), that (non-blocking) function would pull the first byte of the next message in the queue and start sending it out the I2C hardware.

Much later, after that interrupt handler returned to the main loop(), the I2C hardware would finish transmitting a byte (the first byte of this message, or some byte in some previous message), and that I2C hardware would trigger a different interrupt handler (internal to the improved Wire library) that would pull the next byte from the queue in RAM and start sending that byte out the I2C hardware, check for and buffer up any incoming byte in the I2C hardware, etc.

$\endgroup$
  • $\begingroup$ None of those implementations do this already (I am using Pololu's L3G library), so I'll keep looking for an example where this has been done. "Non-blocking" refers to making sure the Wire library doesn't hang when certain faults occur; I have encountered that issue, but it is separate from this one. The Wire library will still be partly interrupt driven, so using it inside an ISR requires nested interrupts, otherwise it will hang, but not as described by that issue. $\endgroup$ – chrstphrchvz Mar 26 '16 at 20:43
  • $\begingroup$ I think I understand the suggestion now…basically, split apart the I2C transactions into separate interrupt handlers for each "stage", indeed doing away with the assumptions made by Wire, or by libraries depending on Wire. twi.c may have the means for this; it would be an interesting project, mainly if it can be done in a way that is (1) easily adapted to any model sensor and (2) allows other non-gyro I2C devices to be used simultaneously. $\endgroup$ – chrstphrchvz Apr 4 '16 at 5:19

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.