I2C disturbed by other interrupts?

Dear all!
I am currently facing the following problem:

I regularly use Arduino Nanos to do basic tasks and have them controlled via a Raspberry Pi over a I2C bus. The Pi thereby is the master an the attached Nanos work as slaves. This so far has worked well in the past.
Now I have an Arduino that is used as a motion contoller for 2 DC motors. This means it has 2 harware interrupts wich are used to count the pulses from the rotary encoders on the motors.

Suddenly my I2C connection gets disturbed after a short while. To be more precise (I am using the Wire library): on_receive is not called anymore.

I broke the problem down so far, that I could say that it seems to have to do with the interrupts coming from the encoder disks (in a high frequency). It looks as if these interrrupts disturb the I2C data transfer.
(If I disconnect the power from the motor -> no rotation -> no encoder pulses -> I2C communication works fine.)

As far as I understood Nick Gammons interrupt tutorial, I2C also uses an interrupt vector and this one has a very low priority.

My question to the community is: Is it feasible that the interrupt disturbance is the source of the problems or am I on a completely wrong track?
And is there any solution?

Thank you for your help!
R.

How many pulses per revolution do your encoders produce? For speed control a single pulse per revolution should be sufficient.

Reading encoder pulses uses up a lot of CPU cycles so I can understand that it might interfere with I2C.

Perhaps you could suspend the encoder interrupts for a short period during which you make the I2C communication - but that would mean the Arduino would have to be the I2C master.

...R

The encoder gives ~700 pulses per rev. At ~200 RPM respectively 3 RPS in average this gives about ~2000 pulses per second. As I control 2 motors we can expect ~4000 calls per second.

This hight resolution by the way is necesarry for a exact an fast control. 1 Pulse per Rev would be way to slow.

Shutting off the harware interrupts during the phase where I'm inside on_request might be a workaround. However I was hoping that there was another solution because I will be polling the Data from the slave also at a high frequency and I cannot afford to lose the speed measurement all the time.

in a recent application running on a Uno acquiring data from a BMP280 the I2C functions were called from a Timer interrupt routine every 20msec
the I2C interrupt priority is lower than the timer therefore tinterrupts has to be enabled on entry to the timer interrupt routine by calling sei(), e.g.

//timer1 interrupt called every 20mSec - read BMP280 temperature copy to ring buffer
ISR(TIMER1_COMPA_vect){
      sei();                                            //allow interrupts
      testRecord.seq++;                                 // increment sequence number
      bmp280.getTemperature(testRecord.temperature);    // read current temperature
      bmp280.triggerMeasurement();                      // start next measurement

if your encoder interrupt routines are giving problems with the I2C your could try calling sei()

romanoir:
This hight resolution by the way is necesarry for a exact an fast control. 1 Pulse per Rev would be way to slow.

I'm not convinced that 1 pulse per rev would be insufficient but I am quite sure that 700 is vastly more than is needed. If testing proves that one really is inadequate then try two or four.

Shutting off the harware interrupts during the phase where I'm inside on_request might be a workaround.

I don't see how that can work because you never know when on-request will be called. Also it may be called at a very inconvenient time for the speed control system. I think you would need to turn the I2C control system around so the Arduino is the master.

...R

if your encoder interrupt routines are giving problems with the I2C your could try calling sei()

This is not good advice, and there is no good reason to collect I2C data from inside a timer interrupt routine. All other interrupts are enabled when you use sei() inside an interrupt, and you are really asking for trouble. For example, you could even have a second timer interrupt before the first one is completely serviced.

Murphy guarantees that the trouble will randomly strike at the worst possible moment.

A much more sensible approach is to set a global flag in the timer interrupt, and let the main program do the I/O.

jremington:
Murphy guarantees that the trouble will randomly strike at the worst possible moment.

And like all Irishmen, Murphy was an optimist.

...R

jremington:
For example, you could even have a second timer interrupt before the first one is completely serviced.

if the specification is to collect data every 20mSec while the main() loop to doing BLE IO calling I2C IO in a timer interrupt routine is possible so long as no complex calculations are performed - one then checks the IO timing is working using an oscilloscope
however, never tried it on an Arduino in a real application - in practice used PIC24 when one can change interrupt priorities to make I2C higher than timer

so long as no complex calculations are performed

"I can get away with it in this case" is not a valid excuse to do interrupt-driven I/O in an interrupt routine.

Furthermore, Murphy predicts that six months down the road, you will forget what you did and add in those "complex calculations" or their equivalent.

Sorry guys, disabling the interrupts didnt work as hoped.

At the moment I would be happy if I knew how to get my system back to work again if the I2c-trouble has occured. I thought of some kind of "resetting" the wire library.

This is what happens when the I2C-Connection crashes: From then on the on_receive() event is never called again, whereas on_request() still works fine. The main loop is also running as expected.
I tried to reset the I2C-Bus and re-init wire but that didn't help.

Any Ideas ?

romanoir:
Any Ideas ?

Only what I suggested in Reply #1

...R

to count one of the encoder pulses could you use the T1 external input for timer1 to replace one of your hardware interrupts?

o.k., meanwhile I got a few steps further with my problem. I still don't know the real cause of what happens, but maybe it makes sense to share my experiences as someone who faces the same situation might get some help from it.

By peeking into the wire library sourcecode I found out the cause why my on_receive() is never called again after trouble occurs:
The reason lies in the function
void TwoWire::onReceiveService(uint8_t* inBytes, int numBytes) more precisely in these lines:

if(rxBufferIndex < rxBufferLength){
    return;
  }

At this point the function always steps out and further processing is omitted.

This gives several insights:

  1. To re-vitalise everything it is simply sufficent to set rxBufferIndex and rxBufferLength back to 0. Afterwards I2C processing is working well again. ... at least until the next crash.

  2. Obviously the basic problem was that in my on_receive handler I didnt read the complete buffer. This is because the slave only reads the first byte and after analyzing it knows what to do or how much more data is to be read. This is by convention between master and slave. This has worked well until now.
    Now, if things get troublesome there suddenly seems to be more data in the pipe than should be. As workaround I do the following: At the end of my on_receive() processing I check via wire.avail() if there is still some (unexpected) data to come. If so, then I read this rubbish out of the buffer until it is emptied and at least Wire doesn't hang anymore.

  3. However, a byte by byte analysis of the mentioned "rubbish" data brought no insight from where it could come. Most of these additional bytes are 255 in value. I am very certain that it is no data which was ever sent out from the master!

  4. I don't think it is simply noise on the line, as this phenomenon very strongly correlates with the number of hardware interrupts occuring. Higher RPM-> more Interupts -> higher probability that the I2C communication crashes.

So far for my first insights. I will now be digging in deeper into twi, maybe I can find out where the unexpected data comes or how the interrupts influence this phenomenon.

Regards,
R.

How did you connect the Arduino to the RPi, as in did you use levels shifters?

Idahowalker:
How did you connect the Arduino to the RPi, as in did you use levels shifters?

Yes, there is a logic converter between the 5V and the 3,3V of the Pi

Do you have a logic analyzer you can use? If you do you can grab a few of the data xfer and see what's going on. I found an inexpensive logic analyzer is a big help when working out SPI/I2C issues.

what level shifter are you using using?
when using ISL33002IUZ I2C bus extender pullup resistors were required on both sides of the device
do you have pullups on both sides the level shifter?

I am using these LL-converters:

https://www.amazon.de/gp/product/B07N7FFY2Q/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1

Based on a TXS0108E chip.

Yet I only have a 4.8k pullup on the 5V side (arduino) for each cable. I will try to put Pullups on the Pi-Side too and let you know what happens.

if you have a oscilloscope have a look at the SDA and SCL signals
how do you power the system? could the motors be causing problems with the nano power supplies?
how long are the I2C lines?

romanoir:
4) I don't think it is simply noise on the line, as this phenomenon very strongly correlates with the number of hardware interrupts occuring. Higher RPM-> more Interupts -> higher probability that the I2C communication crashes.

So far for my first insights. I will now be digging in deeper into twi, maybe I can find out where the unexpected data comes or how the interrupts influence this phenomenon.

I can't help feeling that further digging will just make the hole deeper when what you really want is to climb out of the hole.

You have two systems that you know conflict with each other so you need to stop one of them to allow the other to happen.

...R