Interrupt / Atomic blocking question

I've been reading up on the ATOMIC_BLOCK avr function and I've got a general question about interrupts and when they're active and inactive.

I've got a code which uses all 3 timer interrupts (on Atmega328P): 0 and 1 simply increment a counter at a desired frequency 2 does significantly more processing and uses the variables in interrupt 0 and 1 to access arrays

The timing of 0 and 1 are critical because they need to count at a precise frequency. The timing of 2 is less critical, but still requires to be in an interrupt as the timing of the loop() is too variable and also too slow.

My question is this: I've read that an interrupt routine disables all other interrupts at the start and reenables at the end.

Surely the time it takes to execute the code in interrupt 2 will effect the accuracy of 0 and 1 at counting at a desired frequency. In fact surely they all effect each other?!? :confused:

I am aware of the "you must keep interrupts short" argument. However even if all 3 interrupts were only 1 line of code, they'd still effect each other (because 2 is using the variables in 0 and 1).

I'd love to know if anyone has done any tests on this and has any opinions.

Paul

Super awesome tutorial : http://www.gammon.com.au/interrupts An interrupt takes 5us to start with, plus running the code inside the interrupt routine.

A new interrupt does not get executed until the current interrupt routine has finished. Using arrays is fast, but using long integers or float, that takes time. Why is your loop() too slow ? Something wrong with it ?

Could you show us your sketch ?

Hi, Yeah Nick's tutorials are really good. I use his site all the time! Thanks for finding timing values.

However this actually throws up even more questions! If we ignore my initial example and take a project that just uses millis(). If it takes 5uS just to execute the interrupt, then surely even millis() is slightly inaccurate? The interupt that increments the millis() counter will be out by >5uS every time. Then if every time you call millis() you risk missing increments because it temporarily disables interrupts.

Then if you add more short interrupts, millis() will become even more inaccurate! Which takes us back towards the realm of the original question.

Can't upload any code at the mo, will try and sort something later today. Sketch is to big to just copy and paste.

P.S. Reason for not using loop() is that I'm processing at audio frequency in interrupt 2. Loop() contains slow things like reading buttons, pots, lighting LEDs. Def too slow to generate audio.

paulsoulsby: If it takes 5uS just to execute the interrupt, then surely even millis() is slightly inaccurate? The interupt that increments the millis() counter will be out by >5uS every time.

It's much worse than that. The Timer0 interrupt happens every 1.024 milliseconds (on a 16 MHz Arduino). That mens the millisecond time is 24 microseconds slow every clock tick. Every 42 interrupts it adds another millisecond, which still leaves it 8 microseconds slow. Every 5250 interrupts (125*42) it adds yet another millisecond. 5250 interrupts is exactly 5376 milliseconds. Unless you are measuring an interval of exactly 5376 milliseconds your timing can be off by as much as 992 microseconds. On an 8 MHz Arduino it's always counting milliseconds by 2 and adding extra milliseconds twice as often.

The millis() is not missed because interrupts are disabled for a short moment. Interrupts can be delayed by other interrupts. It can be missed when an interrupt takes more than a millissecond (25 floating point operations).

Here is the millis() : https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring.c

Do you know this ? https://github.com/TMRh20 Can you make something like the TMRpcm ? I can't.

Arduino synthesizer : http://sensorium.github.io/Mozzi/ Listen to the examples on that page. That is the actual sound an simple 16MHz Arduino can generate.

Hmm interesting thanks. Reading up on this now. I know Mozzi well.

The situation I'm thinking of is actually for a synth I make. It's for a duophonic version of the software. Code is here.

On the main tab of code you can see the 2 timers (0 and 1) counting up and generating the 2 oscillators. Then Timer 2 mixes the 2 oscillators together and outputs it.

I'm finding at higher notes the freq of the osciallators becomes unstable. Clearly what is happening is that it's spending too much time in interrupt 2 and not incrementing interrupts 0 and 1.

I may try re-enabling interrupts at the top of interrupt 2 and see if that helps (/makes things worse!)

Timer2 is not that long. And many of the arrays and indexes are already 8 bits.

Do you have an oscilloscope or some capture device ? An output pin can be changed (high to low or vice versa) very fast. The ATmega328P got a new feature: when a pin is set as output, the state can be changed by writing to the input register. The idea is to do that as first and as last line in the interrupt routine.

setup:
  pinMode( 13, OUTPUT);

ISR:
  bitSet( PINB, PORTB5);   // toggle pin 13
  ...
  bitSet( PINB, PORTB5);   // toggle pin 13

There appears to be no attempt to streamline your ISR code.

  master_tick = m;
  master_frac = f;

?

    switch (master_prescale){
        case 1:
            sbi(TCCR0B,CS00);
            cbi(TCCR0B,CS01);
            cbi(TCCR0B,CS02);
            break;
        case 2:
            cbi(TCCR0B,CS00);
            sbi(TCCR0B,CS01);
            cbi(TCCR0B,CS02);
            break;
        case 3:
            sbi(TCCR0B,CS00);
            sbi(TCCR0B,CS01);
            cbi(TCCR0B,CS02);
            break;
        case 4:
            cbi(TCCR0B,CS00);
            cbi(TCCR0B,CS01);
            sbi(TCCR0B,CS02);
        break;
    }

??

If it takes 5uS just to execute the interrupt, then surely even millis() is slightly inaccurate?

millis() is slightly inaccurate as described here however the inaccuracy is not cumulative over a long time.

bitSet( PINB, PORTB5); // toggle pin 13

I think you will find it faster to do:

PINB = bit (PORTB5);

The read/or/write sequence is not required here.

I see, the toggle command doesn’t have to keep the other bits as they are. The other bits can stay zero, just the one bit that needs to be toggled can be set.

Writing the PINB registers takes 1 clock cycle, 62.5ns (on a 16MHz ATmega).

The compiler will compile all of these into the “sbi” and “cbi” instructions which take 2 clock cycles .

bitSet( PINB, PORTB5);
PINB |= bit (PORTB5);
PINB |= _BV(PORTB5);
PINB |= (1<<5);

Thanks Nick, I will try to remember it.

Thanks for all the info everyone.

Nick - Will use that new faster method of setting PIN registers from now :slight_smile:

JimEli - I have actually optimised those lines in the project I’m working on now. I must update that project too.

I think the next step is for me to create a test on an Arduino that has a compare interrupt on interrupt 2. I want to see how the accuracy of millis() is effected by:

  1. Using low compare value, so interrupt fires at a high frequency and hence chance is high of being in that interrupt when millis counter should be incrementing (in interrupt 0 code in wiring lib)
  2. Using high compare value, but code in interrupt chews up a low of time (e.g. long for loop), so again the chance of being in that interrupt is when millis counter should be incrementing is high.

Then do something like toggle the LED every second (by reading millis) and see if it really is toggling every second, or is it slower.