I2C crashes when used in an interrupt routine

Background: I needed to speed up changing frequency on a, Si5351 which used i2c. Changed from a polled function (works well, but too slow) to an interrupt routine triggered on changing of D7. When the I2C is called it crashed the software and does not return from setfreq function. I have tries using interrupts(); but this makes no difference. Any ideas? (Please note the Serial.println( .... ); statements are commented out, and the I2C calls are in the setfreq function calls)

// *************************************
// TX/RX functionality
// *************************************
void ISR7(){
interrupts();                          // Re-enable interrupts so I2C can work in setfreq
  RX_TX_input = digitalRead(RX_PIN);   // On D7. 1 = RX, 0 = TX
//  Serial.println( RX_TX_input );
  // ********************
  // It just gone to RX
  // ********************
  if (RX_TX_input == RX ){
    last_RX_state = RX;
    if (ritmode == 0 || ritmode == 2) {          // No offset needed RIT OFF & ITT
      setfreq(Dial_Freq, (radio_mode == CWN_MODE ? IF_Offset_Narrow : IF_Offset_Wide));
    } else
    if (ritmode == 1 || ritmode == 3) {          // RIT offsets needed in RIT & RITT
      setfreq(Dial_Freq + RIT_Freq, (radio_mode == CWN_MODE ? IF_Offset_Narrow : IF_Offset_Wide));
//Serial.println( "rx" );
  // *************************************
  // Assume that it must have gone to TX
  // *************************************
  else {
    last_RX_state = TX;
    if (ritmode == 0 || ritmode == 1) {          // No offset needed in RIT OFF & RIT
      setfreq(Dial_Freq, (radio_mode == CWN_MODE ? IF_Offset_Narrow : IF_Offset_Wide));
    if (ritmode == 2 || ritmode == 3) {          // offset needed in ITT & RITT
      setfreq(Dial_Freq + RIT_Freq, (radio_mode == CWN_MODE ? IF_Offset_Narrow : IF_Offset_Wide));
//Serial.println( "tx" );

Post your code in code tags. Read the forum guidelines to see how to properly post code.

Use the IDE autoformat tool (ctrl-t or Tools, Auto Format) to indent the code for readability before posting code.

Does anything in the ISR depend on interrupts? Interrupts are disabled in an ISR.

The ISR should be as short as possible. Simply set a flag in the ISR and do the work in loop() if the flag is set, then clear the flag.

...but explicitly enabled at the start of the ISR

I did not know that that would work. Still seems better to set a flag and run the function in loop().

Thanks for the tip groundFungus. I have edited it into a code tag.
As you can see I added an interrupts(); on entering the interrupt routine hoping it would allow I2C to work, but no. M

I added an interrupts(); on entering the interrupt routine

As soon as the I2C interrupt happens, interrupts are turned off again.

DO NOT do serial I/O in an interrupt routine. Just set a flag, exit and let the main routine do the work.

1 Like

The serial I/O are commented out. It is the I2C commands that are freezing the EVERY, when called after entering the interrupt routine.
Putting the I2C commands in the main loop is too slow, they have to happen on the changing of D7.

Rethink your code structure. You CANNOT do I2C in an ISR responsibly. An ISR can run for a few dozen microseconds or so; any I2C transfer is easily 10-100x that duration. Nested interrupts will occur, there will be drama, etc.

1 Like

The ISR routine can take all the time it likes, as this is the absolute priority, and the main loop can easily wait. Short ISR routines are only needed if the main loop really has to become of greater priority, but putting it on hold is fine. I just want the I2C to work in the ISR. Thanks for your comments, but any ideas why the I2C is freezing and how to stop it?



Sorry, but no.

You don't understand what an interrupt is, that's the problem. I can explain all I want, but until you understand the basics of interrupts in microprocessors, it'll be like talking to a tree.

You can write a code that is purely interrupt driven with zero program code in your main loop. If the application requires it, there's no need to preach about what to do or not do inside an ISR.

Using the Arduino Wire functions inside a interrupt routine is supposed to crash.
I really would like to see the setfreq() function to be able to tell what is possible.

I have doubts about the absolute priority that you want. The I2C bus is a slow serial bus. Moving the code to the loop() is only slightly slower. You have to prove to us that you really want it that fast. Perhaps with a different structure of your code, the same result can be achieved.

In the ideal world, the code in your interrupt routine could start a interrupt driven non-blocking I2C session. But the Wire library is not such a library.
A workaround would be using a software I2C library in your interrupt routine. That might conflict with other Arduino code, so you need luck. How much luck you need depends on the code of setfreq().

Are you using the Si5351 Adafruit module ? Because the Si5351 is a 3.3V chip and the Nano Every is a 5V board. That Adafruit module must be powered with 5V to VIN (for a 5V Arduino board), or else the level shifters don't work.

NOT with I2C communication inside the ISR. It will NOT work reliably.

Same here; apart from that it's really not a good idea to try and do I2C communication from within an ISR, it's also extremely doubtful it is necessary. Usually this kind of requirement stems from:
1: A timing requirement that's perceived to be more strict than it really is.
2: Poorly designed code elsewhere in the program that precludes sufficient timing accuracy if a flag is set from the ISR and handled elsewhere.

There is, because OP is heading down a dead end street. If you want to guide him there, go ahead. Send a post card when you hit the brick wall.

Things are even worse with Nano Every (ATmega 4809). The interrupt enable flag is NOT cleared when an ISR starts, but Interrupts of the same level principially cannot interrupt each other. That's why enabling interrupts in an ISR has no effekt on Nano every. By default all interrupt sources have the same level 0. Only one interrupt source can be given level 1, and only this source can interrupt other level 0 interrupts. Therefore a long running ISR will stop all (nearly . see level 1) other interrupt sources, and there is no way around this.

1 Like

MicroBahner - Thank you for your sensible and informative reply, which actually answers my question as to why using I2C in an interrupt routine does not work. Appreciated. I will work it out (always do) another way. If interested see my other comments / reply... M

OK, here we go. Your answers did not address my questions, as to 1. Why and 2. can it be made to work.
You did however seem to need to focus on my skills and ability. Did it make you feel better, more superior?
OK. The challenge. Si5351 is on a fixed I2C address so this makes it hard to have more than one on a I2C bus.
My design is dual processor, with dual I2C bus, on on each NANO.
When changing from RX to TX with full break-in CW (Thats morse code fyi), there are a few milliseconds to change both Si5351 oscillators before the signal is transmitted, so in real time both NANOs have to reprogram the frequencies, one VFO and the other TX CIO. One NANO signals this with the falling edge of D7. If the I2C is in the main loop, ither things have to finish first such as TFT display, and this is really slow. Hence the need for the interrupt to set the TX VFO frequency. When RIT is enables there is a blip as the change of frequency changes slightly AFTER the transmitter is biassed on. Not good.
Dont worry, I will find a solution, I always do.
As to your Tree parallel to my intelligence and skills with MicroProcessors, lets look at that eh?
First micro Motorola 6800 in 1975. self learned (there were no education places to learn from then) Designed from concept to implementation with one other person running on a traversing atomic gauge on a 560ft three story 4 layer paper / board machine. Real time, extremely damp, electrically noisy environment.
After that I have programmed professionally in 12 different assembler languages, including the hardware (ttl etc) design of real time systems. Then a few higher level languages. In the Paper, food processing, brewing, POS (for IBM, THORN), and communications (Philips PMR / Paging) I was doing 'C' n 1981 as a consultant in the UK and Holland on the very first release IBM PC, and grown from there as programmer, designer, consultant, trouble-shooter in 22+ countries, and contractor (mercenary).
Worked on 4 bit processor, 4k rom and 224 nibble ram (nibble = 4 bits fyi), with zero bytes of rom for program and zero ram left (The stack and heap met each other, can you believe that?) 1man year to program in assembler and over 1.7 million produces for a worldwide market and ZERO bug reports ever.
Now, I have done software where there is no main loop. On instruction, NOP (fyi = no operation), and ALL the code was in priority leveled interrupt routines. Another where there were on interrupts OR main routine, a single shot software ending in a loop, started with a RESET every second.
And I could go on, so yes, I think I do understand micros and real time, to the micro-second and below. So please do not ASSUME things and try to be clever. If you don't have an answer to a question asked, I suggest some humility and stay quiet, as your answers were in no way actually helpful.

In the loop is too slow, as this needs to happen in close to real time... Thanks. M

Haha, read my other long reply. You are nearly being rude or arrogant. A mirror may be needed for yourself to look into my friend!
Brick walls are for those who think in boxes, limited by their beliefs assumptions and rules. (Someone said to me years ago, "You don't think in boxes do you Michael?" I said "Boxes?". He said "This conversation isn't going far is it?" Me, smiles...)
As long as a system is repeatable, reliable and stable across all variable conditions (voltage, temperature etc), norms (which are in general good guidelines, I agree there, such a keep interrupt routines short and snappy) can be put aside when they get in the way of a perfectly fine (and cheap often) solution...

No worries on the voltage and hardware, that all works fine. I am not using the library, but direct code to the Si5351 using the I2C ports. It is just the I2C library that hangs when called in an interrupt routine for some reason, hence my query. Please see my other long reply for the reason for the need for speed... Thanks. M

Which is a fairly long period to begin with.

Like I said: you're patching up the lack of responsiveness in the rest of your code by trying to use an interrupt for something that you evidently found doesn't work well inside an ISR.

Exactly. Which is why I recommended to NOT use it in an ISR. Especially given this:

Assuming (!) I read this right and you have two I2C buses on each of your both Nano's, this implies that there's ample potential for problems if both happen to be(come) active at the same time. If, let's say, you're updating the LCD on one I2C bus and want to work with your VFO on the other bus. Of course I'd have to make assumptions about this in the absence of decent information on how you set up this system. Information is being released piece-by-piece if you don't like what you hear.

Based on your use of I2C communications in an ISR I 'assumed' you were not sufficiently aware of the inherent problems with this. I still don't see any sign of that assumption being wrong.

In the case you're working on now with your HAM project, let me ask you a question: are you in full control and fully aware of all (potentially) active ISRs and their exact memory requirements, as well as any potential memory conflicts (especially stack/heap conflicts)? In other words: does your previous experience actually translate reliably to this particular situation?

Anyway, good luck with your project. I've had my say; I could say more about unfortunate choices in system architecture that you already seem to have made which is a big part of the reason why you're having this problem in the first place, but since your knowledge and experience are evidently vastly superior to mine, I won't bother you with my silly ideas and assumptions.