delayMicroseconds() safe in interrupt?

I know that delay() can’t be used inside an interrupt. I had a full freeze of my core (no red flashing or auto-reset) after weeks of uptime and I am currently suspecting delayMicroseconds().
I looked at the implementation of delayMicroseconds() but it seems to be save.
Any suggestions?

Markus

@MarkusL, you will need to post your ISR code so we can help :smile:

1 Like

Absolutely, everything is better with code :smile:

        float totalCurrent = 0.;
        int rounds = 0;
        long waiting;
        unsigned long startTime = micros();        
        __disable_irq();
        for(int sweep = 1; sweep < 11; sweep++)
        {
            analogWrite(inverterPin,sweep);
            for(int i = 0; i < 40; i++)
            {
                totalCurrent += currBattAmps(analogRead(battAPin)) + currSolarAmps(analogRead(solarAPin));
                rounds++;
                waiting = 160000*rounds/400 - (micros() - startTime);
                if(waiting > 0)
                    delayMicroseconds(waiting);
            }
        }
        __enable_irq();

float currSolarAmps(float raw)
{    return max(0.,((3.3*(raw -2048.)/4095.)/.03));
}

float currBattAmps(float raw)
{    return ((3.3*(raw -2048.)/4095.)/ -.03); 
}

@MarkusL, there’s a LOT going on in that ISR!! Can you tell me what fires off that ISR and how often?

The use of __disable_irq() stops ALL interrupts including the CC3000 interrupts so it needs to be used carefully. Doing floats, function calls and for() loops within for() loops adds time to the ISR, possibly causing problems elsewhere. Often, an ISR like yours where you are calculating totalCurrent will only read raw analog values and store them in a buffer for processing in loop(). You can use a simple flag in the ISR to indicate when the buffer has been processed and when the buffer is ready for processing. That way, the ISR becomes “non-blocking” and lets the loop() code do all the work. :smiley:

3 Likes

@peekay123 , there is actually more going on outside of __disable_irq().
Using this interrupt stopper came from this thread here

The entire ISR code is only executed when the main loop is blocking as in the thread. This is rare but I had measured loop blocks of up to 20 seconds in the past.

Also the code here blocks only 160ms before enabling the interrupts again. That should be ok for the CC3000?

I cannot just sample the measurements and defer the comptation. I also need to process the results to make decisions if new events are triggering states. These events are time critical. For example I need to open the circuit if the current flow is too high.

@MarkusL, the use of __disable_irq() in the example you pointed to was used for VERY short ISR code. Blocking for 160ms can absolutely create issues with the CC3000 if data is missed or an ISR is not serviced. This is why the firmware noInterrupts() only disables user level interrupts and not critical "background" interupts.

The use of SEMI_AUTOMATIC or MANUAL system modes may resolve this blocking issue. Have you considered this?

It sounds like you don't trust loop() to run consistently for your "time critical" code. An ISR is not the right place to do this. Perhaps stepping back and looking at your requirements vis-a-vis the latest Core firmware functionality is warranted. :smile:

3 Likes

@peekay123, I can try to limit the blocking just to the user interrupts - hoping the CC3000 ISR are very short as well.
I am already using SEMI_AUTOMATIC but that does not seem to make any difference.

Yes, I dont trust loop() to run consistently. Can you elaborate a bit on what you mean by looking at the > latest Core firmware functionality ?

@MarkusL, I would have to see your overall code to help. Is system mode MANUAL possible? When you disable cloud connectivity, loop() runs much faster. It all comes down to what you are trying to do. :smile:

@peekay123, I think MANUAL will not help because the only issue is that there is no guarantee that loop() or Spark.process() finishes within a defined max interval.

@MarkusL, I would not use loop() for tight timing but without Cloud connection, it will not “lock”. It comes down to how you distribute the code between the ISR and loop(). I believe you are seeing freezes due to the design of your ISR. The trick is to decouple true “real-time” events from near real-time events. If you can explain what your timing constraints are and what you need to achieve, I may be able to make some suggestions.

@peekay123, the main loop is doing housekeeping things like:

  • listening for the serial port
  • updating the cloud variables (every 2 seconds)
  • storing data into flashram (every minute)

Then there is the critical code that needs to be serviced at least once every 20ms.
I have a watchdog timer that fires every 20ms and if the loop() has not serviced the critical code yet it will get executed as ISR.

For 99% the timing of loop() is totally fine and only writing into flashram causes a delay every minute.

@MarkusL, when you say “flashram” what exactly do you mean? Are you using a timer interrupt to trigger the ISR?

One possible approach if you are using a timer interrupt is to user a faster timer slice like 1ms or even less and run a finite state machine in your ISR to do your sampling, etc but in smaller increments, keeping the ISR service time short. You could also use integer math instead of float to make it faster. There are lots of possibilities IMO.

@peekay123, I am using sFlash to store a few bytes every second (not Flashee - frequent crashes).
I am also using SparkIntervalTimer as the watchdog. The ISR gets only triggered if the loop execution lasts longer than 20ms.
The piece of code that lasts 160ms is part of the ISR but It will not trigger itself. I am using myWatchdog.end() and .begin() around that code.

@MarkusL, so I see two possible areas where the Core can lock:

  1. sFlash due to SPI bus issues with CC3000 though I believe those were addressed in the firmware
  2. The ISR service time and the disabling of ALL interrupts for a long period of time is killing the background process or even the stack/heap.

My advice is:
a) Don’t use __disable_irq(), and instead just disable/enable the timer interrupt with interrupt_SIT(INT_DISABLE) / interrupt_SIT(INT_ENABLE). The timer runs at preemption level 10 above user interrupts but below critical system interrupts.
b) Don’t do floating point stuff in the ISR. It takes a lot of time and uses a lot of stack/heap.
c) Don’t call your math functions in the ISR. Put them inline instead.

From the looks of it, “waiting” in the ISR increments by 400us. You could use a 400us timer interrupt and, as mentioned above, convert your “for” loops to states in an FSM. The ISR would still do what it does but in short “bursts” doing the same thing, avoiding the 160ms no-interrupts approach.

2 Likes

When you saw the crashes, were you using flashee from the ISR or just from the main loop?

@mdma, the calls were done from loop() but execution took longer then 20ms so the ISR would have been called at least once somewhere in-between.

@peekay123, what do I need to include for interrupt_SIT(). I am using noInterrupts() for now. Is that equivalent?

@MarkusL, interrupt_SIT() is part of the SparkIntervalTimer library. It specifically disables the timer interrupt which is not affected by noInterrupts(). Add timer.interval_SIT(INT_DISABLE) - timer is your timer construct name - where you have your noInterrupts() call and timer.interval_SIT(INT_ENABLE) where Interrupts() is. :grinning: