0
votes

Using a 8-bit AVR micro, I arrived to a simple situation which might not be that easy to solve.

Consider the following snippet:

static volatile uint8_t counter;

//fires often and I need all the values of the counter.
void isr(void) {
  counter++;
}

int main (void) {

  while(1) {
    send_uart(counter);
    counter = 0;
    delay_ms(1000); //1 sec pause
  }  

  return 0;
}

1.) It can happen that send_uart is followed by an isr which increases the counter, and then the next statement zeroes it out. Therefore I'll miss one data from the counter.

2.) If I use ATOMIC_BLOCK(ATOMIC_RESTORESTATE) in the main fn, I can avoid the problems declared in (1), but it can happen that I miss an ISR because in this case INTs are disabled for a short time.

Is there a better way to pass information from the main fn to ISR?

1
It's not exactly clear what you want. I'm guessing you have an ISR that runs more often than the main loop, and you want the main loop to be able to check on the ISR, getting an accurate count of how many times the ISR has run since the last check. Can we at least assume that the ISR won't run more than 255 times between checks by the main loop? Would it be acceptable for the ISR to not report any events if it sees that the main loop has fallen behind in checking for events? Do you care about the exact latency of the ISR? - David Grayson
(By latency, I mean how long the ISR takes start running after the event that triggers it has happened. Disabling an interrupt temporarily increases the latency but allows us to more easily access multi-byte data that is being updated by the ISR.) - David Grayson
@DavidGrayson, this is just an example I posted. ISR runs more often then the main loop (I wrote now some delay in the main loop) queries the counter variable. But ISR is a HW event (not timer-based) so I fear of losing an ISR because of the ATOMIC_BLOCK. The ISR is triggered both on falling and rising edge of an input pin. I need both info to be counted. (However, between falling and rising there is a significant time compared to the atomic block). - Daniel
So my fear is that if program goes into atomic_block, and then the pin has a falling edge, I would lose it. Whenever the atomic_block returns, my ISR routine won't be called with the falling edge, only the rising, once it rises. No? - Daniel
No, it looks like ATOMIC_BLOCK from AVR Libc just disables interrupts, it does not clear any interrupt flags that got set while the block was running, so the interrupts will run after it finishes and re-enables interrupts. Using that will harm the latency of all the interrupts in your program though; there might be better options available if you answer all my questions. Also you'll only get one interrupt if two interrupt-triggering events happen during the atomic block (so make sure you keep it short). - David Grayson

1 Answers

2
votes

If the counter is sampled rather than reset, there won't be any timing issues. Increments happening while sending will be accounted for in the next iteration. The unsigned data type of the counter variables will guarantee well-defined overflow behavior.

uint8_t cs = 0;                  // counter sample at time of sending
uint8_t n = 0;                   // counter as last reported

while (1) {
  cs = counter;                  // sample the counter
  send_uart((uint8_t)(cs - n));  // report difference between sample and last time
  n = cs;                        // update last reported value
  delay_ms(1000);
}