How would you test the influence of noINterrupts()?

Hi,

to improve my knowledge about Atmel-microcontrollers does noInterrupts() affect serial receiving and millis() / micros().

No interrupts() disables the interrupts. How would you test the influence on serial receiving?
can there be done some estimating calculations about how long code needs time to execute and how long/short a noInterrupts code-section could be with serial receive and millis() still working normal?

I guess for serial receive it depends on the baudrate.

best regards Stefan

The USART issues an interrupt after receiving a complete character. If interrupts are disabled at the time, it will be held pending and serviced after interrupts are reenabled. However, only one USART interrupt can be held pending. If subsequent characters arrive while you still have interrupts disabled, some will be lost. So, the advice (as always) is to keep your ISRs short.

gfvalvo:
However, only one USART interrupt can be held pending. If subsequent characters arrive while you still have interrupts disabled, some will be lost.

the USART will buffer some number of characters. once the interrupt is enabled, it will service all the characters in the buffer. there doesn't need to be an interrupt for each characters, this would be inefficient at higher rates.

yes, characters will be lost if the interrupt is delayed too long and the buffer overflows

gcjr:
the USART will buffer some number of characters.

I believe that number is 1.

gfvalvo:
I believe that number is 1.

looks like you're correct. (see Receiver Error Flags pg 154 in ATmega328p). that's dissappointing.

looks like the (software) driver will buffer more (64)

thanks

Hi gfvalvo and gcr,

thank you very much for answering. DO you happen to know how millsi() and micros() are stopped from working with noInterrupts()?

Is there a possability to just inhibit a certain kind of interrupts? (enable timer-interrupts but disable IO-pin-interrupts?

best regards Stefan

Guidance for use of interrupts is don't, unless you absolutely have to.

When you do have to, don't try to use millis or Serial. Do as little as possible, set flags or increment counts and deal with them in the main body of the code.

Millis will give you a number if necessary, but it won't change until interrupts are back on.

StefanL38:
Is there a possability to just inhibit a certain kind of interrupts? (enable timer-interrupts but disable IO-pin-interrupts?

It's all in the ATmega328P datasheet.

The ATmega328p datasheet says 'two':

The receive buffer consists of a two level FIFO.

I think that means that you can occasionally keep the interrupts off for two character times without losing incoming characters. At 115200 baud that is about 18 microseconds. Just don't do it so often that the FIFO fills and overflows.

StefanL38:
Is there a possability to just inhibit a certain kind of interrupts? (enable timer-interrupts but disable IO-pin-interrupts?

Each interrupt source has an 'enable' bit in a register somewhere. Clear the enable and the interrupt will be disabled. By "I/O-pin-interrupts" do you mean External Interrupts, Pin Change Interrupts, or something else like UART Buffer Empty or Transmit Complete interrupts?

The Arduino environment keeps interrupt handling simple for beginners, that’s kind of the point of the whole project. If you want to do more subtle things with interrupts you need to read the data sheet for the processor you are using and learn how to control each separate interrupt to do what you want.

Hi John and Perry,

thannk you very much for answering. at the moment my interest is about disabling external interrupts like for measuring rpm with an IO-pin.

my actual concept to do this is
attach an interrupt to IO-pin 2 or 3 and the isr makes a snapshot of millis() for storing three things:
starttime of measuring
update a second variable each time another signal initiates an interrupt
increment a counter-variable

then inside the maincode once per second disable interrupts to prevent these variables to be changed in the middle of copying them to non-isr-variables
then enabling interrupts again

here is a demo-code that compiles but is not yet tested by me

volatile word steps;
volatile unsigned long TimeStamp;
volatile unsigned long StartOfNewPeriod;

unsigned long StepsForRpm;
unsigned long TimeStampForRpm;
unsigned long StartOfNewPeriodForRpm;

unsigned long MyTimer;

unsigned long rpm;

const int hall = 2;

void isr_step() {
  // get exact time when pulse occurred for a more precise rpm-calculation
  TimeStamp = millis(); 
  // if your code needs more time than the standard-measuring-period  
  // using the difference of the two timestamps   "TimeStamp" and "StartOfNewPeriod" 
  // will correct this
  
  if (steps == 0) { // if new period starts
    StartOfNewPeriod = TimeStamp;
  }
  steps++;
}

void setup() {
  // use a higher baudrate to make serial print faster
  // make sure the baudrate in the serial monitor matches this value
  Serial.begin(115200); 
  pinMode(hall, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(hall), isr_step, FALLING);
}

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}


void loop() {
  
  // function TimePeriodIsOver returns true only once every second
  // loop is still running at maximum speed to do other things  
  if ( TimePeriodIsOver(MyTimer,1000) ) {

    noInterrupts();               // disable interrupts to avoid missed counting or changing isr-variables   
    StepsForRpm = steps;          // quickly store copy of values
    TimeStampForRpm = TimeStamp;  
    StartOfNewPeriodForRpm = StartOfNewPeriod;
    steps = 0; // reset steps to start new measuring-period
    interrupts(); // enable interrupts

    // do the rest while isr is already counting for new period
    // multiply with 60 for minute multiply with 1000 for milliseconds
    rpm = StepsForRpm * 60 * 1000 / (TimeStampForRpm - StartOfNewPeriod);
    Serial.println(StepsForRpm);
    Serial.println(rpm);    
  }  
}

copying three 32bit variables might take longer than 18 microseconds. No idea yet how much time this needs.
another thing to find out by claculating or empiric measuring
with using the timestamps I want to increase the precission of the measuring on low rpms as the most possible exact time is used for calculating the rpm.

So instead of noInterrupts() that does what its name says disable all interrupts disabling the external
IO-pin-voltage-level-change-initiated-interrupt (oh what a long but "precise" word) would be enough in this application.

best regards Stefan

johnwasser:
At 115200 baud that is about 18 microseconds.

An 8N1 character is 10 bits long. At 115200 b/s, that's 86.8 us.

Declaring the variables as volatile should be enough as far as I know because it tells the compiler that they might change unexpectedly. How that works or how well it works I have no idea.

If you want to disable one specific interrupt then you need to study the data sheet to see how to disable individual interrupts. I would expect it to be possible and I am sure there are people here who know exactly how to do it but, while I've done it on PICs I've never done it on Atmel devices.

PerryBebbington:
Declaring the variables as volatile should be enough as far as I know because it tells the compiler that they might change unexpectedly. How that works or how well it works I have no idea.

Access to multi-byte variables is non-atomic on an 8-bit AVR. If those variables can be changed by an ISR, then you must disabled interrupts when accessing them in non-ISR code. It's known as a Critical Section.

EDIT:
You also need a Critical Section if the shared variable is changed by the non-ISR code.

You don’t need to read millis in the interrupt. Just count how many times it was called.

In loop, use blink without delay and once in a while (every second?) turn interrupts off, copy the count and zero it, turn interrupts on and then calculate RPM.

wildbill:
You don't need to read millis in the interrupt. Just count how many times it was called.

In loop, use blink without delay and once in a while (every second?) turn interrupts off, copy the count and zero it, turn interrupts on and then calculate RPM.

at low rpms the last pulse might have occured after 0,95 seconds and the next will occur after 1,1 seconds
so using 1,0000 seconds to calculate will result in a more deviating number from the real rpm.
Or at 0,9 seconds if the code is doing something else for let's say 0,4 seconds then 1,3 seconds have passed by while a the next expired time "thinks" its just 1,0001 seconds and then the calculated rpm is significantly wrong.
That is the reason for me to use two timestamps.
best regards Stefan

if the code is doing something else for let's say 0,4 seconds

!!!!!
How long???
You know better than to have code doing 'something else' for 400ms.

I wasn't suggesting that you assume the interval is 1000mS. Of course you should measure what it actually was but you don't need to do that in the interrupt routine.

wildbill:
I wasn't suggesting that you assume the interval is 1000mS. Of course you should measure what it actually was but you don't need to do that in the interrupt routine.

This does not convince my (yet). I insist on the following effect
The numbers are not calculated they shall only show the principle
millis is 1250 storing timestamp in the maincode
millis is 1260 first pulse occurs
.....
millis is 2245 last pulse (in the period) occurs
millis is 2257 more than one second has passed by store snapshot of millis in the maincode

with snapshots of the main-code the time-difference is 2257 - 1250
while the real time it took for the number of counted pulses to occur is 2245 - 1260

and at low rpms if you take only one second to measure them this time-difference becomes significant.

So maybe a complete different principle of measuring time might be better suited?

@Perry: yes any serious controll-algorithm should run through much faster than 400 milliseconds.
I'm serching for a solution that is most beginner-bugs forgiving.

best regards Stefan