Does 'noInterrupts' disable the millisecond timer?

Hello there,

Does the function call "noInterrupts()" disable the millisecond timer too so NO interrupts of any kind can run, or does it leave some chip interrupts alone?

From what i have read "interrupts()" turns them back on, so does the millisecond timer, if stopped above, start again where it left off or reset or something?

No, it does not block the timer. It does prevent the associated interrupt service routine from running. The interrupt service routine has to be allowed to run approximately every 1 ms for micros / millis to remain accurate.

In other words, the time between noInterrupts and interrupts should normally be very small and should never exceed 0.5 ms.

Hello again,

Hey thanks. So what effect does noInterrupts() have on code that follows. For example, if i had some asm code that outputs a pulse, would the pulse timing still be accurate after nointerrupts() because that pulse would be timed not with delay() or delayMicroseconds() or millis() but with asm code timed to run at 16MHz?

Also, will delay() and delayMicroseconds() and millis() again run normally after interrupts() is called later?

I guess you are saying that if the time between calls is more than 0.5ms that the millis() will not update to the next count, but will it update to the next count after interrupts() is called later?
In other words, if i knew my routine took exactly 1ms to run and so millis() does not update, if after i call interrupts() can i just add 1 next time i call millis()?

For a rough example:
noInterrupts();
CallRoutineThatTakes5ms();
interrupts();
ms=millis()+5;

and now is it true that ms will be somewhat accurate (doesnt have to be perfect) even though millis() is not anymore?

Let me see if i understand the roles the Timer vs the Interrupt for millis() is playing:

  1. The timer keeps running and resetting, and once it generates an interrupt millis() updates by 1 count (most of the time).
  2. If the interrupt is disabled, the timer keeps running and resetting but it does not update millis() so millis() keeps the old count.
  3. Once interrupts() is called, the millis() function starts to update again based on the last count and the state of the Timer (may update right away or take up to 1ms to update).
  4. If the count was 123 before noInterrupts() and after a time like 100ms when interrupts() is called the count will still be 123 but will then update to 124 after 1ms or less.

Sound right?

Thanks again.

This was intended to stop timing problems in asm code that is caused by any interrupts. The timing would be generated in asm and will be vary accurate using the 16MHz clock but if any interrupts occur it will not be accurate, so the interrupts have to be disabled and then enabled later. I dont know yet if any routine will run for over 0.5ms but i guess that is a possibility, although i dont mind 'correcting' the millis() function if necessary with an added number as above:
ms=millis()+Correction;

Does all this sound reasonable, and if not, please let me know.

I think it would be far better for you to explain why you think you need to disable interrupts while you call your mysterious function.

PaulS:
I think it would be far better for you to explain why you think you need to disable interrupts while you call your mysterious function.

Hi,

Ok sure...

I was under the impression that interrupts from a 328p chip Timer would be generated regardless of what other code is running at the time. Thus if there is a Timer interrupt in the middle of an important timing function it will jump to the interrupt routine and that will take some time which was not considered in the important timing routine, and probably can not be included in that timing routine anyway. So for example if we had a timing function that took 10us to execute and it had to be exactly 10us and we had no interrupt, it would take 10us, but if we had an interrupt that occurred right in the middle of it the timing routine might take 12us (2us in the interrupt routine and return) and that would mess up the original idea of having a very accurate timing routine.

I thought this was common knowledge.

The timing of the important timing routine will depend only on the clock frequency and the number and type of asm instructions in that timing routine if there are no interrupts. If there is an interrupt then that timing changes. Since we dont want the timing to change in that particular place, we disable the interrupts. Since millis() uses an interrupt to update the counter, that interrupt happens every so often like around every 1ms, at least that's the way i understand it so far.

Make sense?

I was then asking about the millis() function so that i could understand how disabling the interrupts affects the count. if i have to adjust the count manually that's fine, and i may not even have to do that, as long as the millis() function works normally after again enabling the interrupts, which i think it does.
I tried noInterrupts() and interrupts() and they seem to work ok but i have not been able to test the millis() function yet to see what is happening there. Any info on this would be helpful.

Make sense?

As much as any hand-waving ever does. THIS part of the forum is to discuss code, which we haven't seen.

noInterrupts();
CallRoutineThatTakes5ms();
interrupts();

I agree with @PaulS. Post the code for CallRoutineThatTakes5ms. I suspect that it is either not so constraint as to need interrupts disabled (easily determine by some math) or that it can be structured to (partially) use hardware to handle the constrained timing requirement.

Hello again,

I used "CallRoutineThatTakes5ms" as an example of what could take place. It may take longer than that though, such as say up to 100ms.

The code for this is too simple. It uses a port register and loads one bit with a 1 or 0 depending on if the pulse should be high or low. It then uses a series of nop's to generate delays of 62.5ns each. The number of delays adds up so the pulse width and time between pulses can be changed with a resolution of 62.5ns by simply changing the code block. That will generate timed pulses to test other things.

Note the reason i am doing it this way is because i like the 62.5ns resolution available using 'nop' timing. If i use delayMicroseconds() the resolution is about 16 times worse. That means the pulse rise and fall times can not be be placed in the pulse train as accurately as when using nop's. The pulse train can be of any complexity, from one pulse to 100 pulses placed in various places in the pulse train. The more accurately they are placed, the better the test for the external equipment.

A sample coding is as follows...

PORTB|=B00100000;

    __asm__("nop\n\t"); // 62.5ns at 16MHz
    __asm__("nop\n\t"); 
    __asm__("nop\n\t"); 
    __asm__("nop\n\t");
    __asm__("nop\n\t");

    __asm__("nop\n\t"); 
    __asm__("nop\n\t"); 
    __asm__("nop\n\t");
    __asm__("nop\n\t");
    __asm__("nop\n\t"); 

PORTB&=B11011111;


    __asm__("nop\n\t"); 
    __asm__("nop\n\t"); 
    __asm__("nop\n\t");


PORTB|=B00100000;

    __asm__("nop\n\t"); 


PORTB&=B11011111;

So you see this is very simple timing, but the resolution of the placement of the rise and fall times is 62.5ns so they can be placed more accurately than when using delayMicroseconds() for example which only has a resolution of 1us.

The above code may repeat in the void loop() section, but may be much more complicated than that too with many sections like that where they simply follow one after the other.

The code sections can then be changed on the fly or just use a button keypad to allow selecting which sections are executed, but that's a secondary thing.

The main concern here is if in one of those nop sections above such as in pseudo code:
asm nop;
asm nop;

what if the Timer for the millis() causes an interrupt in between those two nop's. That will either increase the pulse high time or increase the pulse low time, either of which is not acceptable.

If you have another idea that would be good to hear, but you see what i mean now right?

That is blocking the timer for millis, because the variable set by the timer0 ISR (timer0_millis) is the
only thing millis() depends on.

MarkT:
That is blocking the timer for millis, because the variable set by the timer0 ISR (timer0_millis) is the
only thing millis() depends on.

Hi Mark,

So the millis() counter just does not update then?
And if we enable the interrupts again, then the millis() starts to update again from where it left off?

I think maybe what he meant was that the timer itself keeps running, but the interrupt does not jump to update the millis() counter. if this is true, the timer might keep going from say 11 to 12 to 13 etc. even though millis() never gets changed. If this is true, then if the counter generated an interrupt at a count of 128 lets say (not sure what it really is) and millis() updated, then interrupts were blocked while the timer went from 0 to 127, if the interrupts were enabled again when the timer reached 128 again it would still update millis() so there would be no loss in the millis() count. It might be hard to sync the code for this though, but i dont think i need to do that anyway. And this is all if my assumptions are correct about the running of the timer.

MrAl:
Does the function call "noInterrupts()" disable the millisecond timer too so NO interrupts of any kind can run, or does it leave some chip interrupts alone?

This was a good question. As I was looking through the details about noInterrupts() a moment ago, and the docs say : "Disables interrupts (you can re-enable them with interrupts())."

Should probably say disable user interrupts or something instead...... even though we know that the board would stop working if ALL interrupts were disabled.

"even though we know that the board would stop working if ALL interrupts were disabled."

Why? I've used boards with no interrupts when I wanted a steady datastream out at 8 Mbits/sec with no millis()/micros() interrupts occurring.

CrossRoads:
"even though we know that the board would stop working if ALL interrupts were disabled."

Why? I've used boards with no interrupts when I wanted a steady datastream out at 8 Mbits/sec with no millis()/micros() interrupts occurring.

I see what you mean CR. Thanks. Looks like it really does disable all interrupts.

MrAl:
Hello there,

Does the function call "noInterrupts()" disable the millisecond timer too so NO interrupts of any kind can run, or does it leave some chip interrupts alone?

From what i have read "interrupts()" turns them back on, so does the millisecond timer, if stopped above, start again where it left off or reset or something?

All interrupt handling is deferred - the interrupts are still recognized, but won't be handled until interrupts() is
called. You would normally bracket a critical section with those calls for a short time (a few us).

If two interrupts of the same kind come along during noInterrupts, the second and subsequent will be lost
since only one pending interrupt is remembered by the hardware (I am talking only about the ATmega chips
here)

1. The *noInterrupts()*function has no argument; so, none of the 25 available masksable interrupts of the ATmega328 is supposed to be associated with this function. (Reset interrupt is a nonmaskable interrupt.)

2. * noInterrupts()* function, when executed, places LL at the I-bit (Global Interrupt Enable Bit) of SREG-register. Therefore, the noInterrupts() function prevents the MCU from responding to any kind of interrupts except reset.

3. The delay(arg) function contains the built-in interrupt sub routine ISR(TIMER0_OVF_vect) like the Serial.begin(arg1, arg2)() function of UART, which contains built-in ISR(USART_RX_vect) interrupt sub routine. There is no way that a user/programmer can get access to the codes of the delay() function and the ISR(TIMER_OVF_vect) routine.

The MCU executes the ISR(TIMER0_OVF_vect) routine in response to TOV0 (T0 Overflow) interrupt. The TOV0 interrupt may occur at every 1 ms interval or at some other value. Let us assume that the TOV0 will occur at 1 ms interval and its clocking frequency is set at 250 kHz clkTC0 ( 16 MHz / 64 = 250 kHz). As a result, the pre-set value for TC0 becomes 0x06 (0x100 - 250 = 0x100 - 0xFA). The Conceptual Structure of delay(1000) function stands as:

delay(1000)
{

    Initialize To as normal up counter
    //Set clkTCO at 250 kHz
    Load preset count of 0x06 to make a rollover (TOV0 flag set) at 1 ms interval
    Initialize milliSecondCounter = 0x00
    Start T0 at 250 kHz clkTC0

    WAIT: wait for TOV0 interrupt

    if (millisecondCounter ! = 1000)
        goto WAIT
    Return to the calling (Mother) Program  

}

ISR(TIMER0_OVF_vect)
{
    Re-load preset value into TC0
    milliSeconCounter = milliSecondCounter + 1
    Return to calling program from ISR
}

4. If we place the noInterrupts() function before the delay(1000) function, the MCU will still enter into the delay() function; Timer-0 will be running but looping from pre-set count ----> rollover count ----> 0x00 ----> 0xFF ----> 0x00 ---. It is so; because, the ISR(TIMER0_OV0_vect) function is not being executed, no re-load of preset value, and no increment of milliSecondCounter. The MCU will be locked in an infinite loop; waiting and waiting for T0 overflow interrupt; but, it never occurs! (Interrupt logic is disabled!)

5. The consecutive placement of noInterrupts() and interrupts() functions before the delay() function has no affect on the functioning of the delay() function.

6. The 25 maskable interrupts have their own trigger sources and interrupt vectors. No two or more interrupts are of same kind.

7. When two interrupts arrive at the same, then the interrupt having lower numerical value for the interrupt vector is given the priority.

** **8.** **
If a higher priority interrupt arrives while a lower priority is in service, the higher priority one will be remembered if it is a latched type interrupt like TOV1/TOV2, and it will be serviced once the current ISR is completed and re-enables the interrupt logic. denied. This is due to the fact that the interrupt logic became disabled when the MCU vectored at lower priority ISR. There is no scope for deferred service.

9. When roll-over occurs at the end of 1 ms time period, the TOV0 flag assumes LH-state. This flag can generate interrupt signal for the MCU provided the interrupt logic is made active via Global Interrupt Enable Bit and Timer-0 Overflow Interrupt Enable Bit.

  1. If a higher priority interrupt arrives while a lower priority is in service, the higher priority one will be denied. This is due to the fact that the interrupt logic became disabled when the MCU vectored at lower priority ISR. There is no scope for deferred service.

I don't think that's right. The second interrupt can't interrupt the first but it still gets queued and serviced after the first interrupt exits.

You can even use interrupts() inside an ISR and then any other pending interrupts will interrupt your ISR and be called. This is one way to make a "low priority" interrupt that can be interrupted by other more important jobs.

@MorganS

1. I agree with you about the nested interrupts which are common schemes for all kinds of microprocessors and microcontrollers.

2. What's about pending interrupts?
(a) As far as I know that only the latched interrupts are remembered like edge-triggered INT0 and INT1 interrupts. The INTF0 and INTF1 flags will immediately generate interrupt signals as the current ISR comes to an end and re-enables the Global Interrupt Enable bit by the execution of the reti instruction.

(b) TOV1 and TOV2 flags are also latched; so, they will generate interrupt signals as soon as the interrupt logic becomes enabled. You are correct in your saying that The second interrupt can't interrupt the first but it still gets queued and serviced after the first interrupt exits.. I go back to my post #14 and adjust/strikethrough the quoted point(
** **8.** **
).

Thanks for the meticulous observation.