-5
\$\begingroup\$

This has become one of the most confusing part for me in embedded development. Recently i have written a software for measuring the time period of pulses using input capture and that variable say period_u16 i am using in main for further calculations using multiplication factors. Now this value comes correctly for some 10 seconds and suddenly kind of dip goes to some invalid value and comes back. This cycle keeps repeating. I suspected that the variable is being shared by both interrupt and main task and moved all the calculations into the interrupt. Then it is ok. Is it correct way? I have several other interrupts similar to this. What shall i do? I don't want to do all the calculations in interrupts.

\$\endgroup\$

closed as unclear what you're asking by Andrew, Voltage Spike, ThreePhaseEel, uint128_t, Wesley Lee Feb 12 '17 at 20:44

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ It's hard to say without seeing your actual code, but it sounds like you weren't handling the rollover (overflow) of a counter correctly. \$\endgroup\$ – Dave Tweed Feb 6 '17 at 16:58
  • \$\begingroup\$ But still it is always possible that a variable can be shared by both interrupt and main task at the same time. A kind of deadlock. \$\endgroup\$ – rajesh Feb 6 '17 at 17:00
  • \$\begingroup\$ Sorry accessed at the same time. \$\endgroup\$ – rajesh Feb 6 '17 at 17:00
  • \$\begingroup\$ When in an interrupt the main task is stopped so no risk of them both accessing it at the same time. Please post code. \$\endgroup\$ – Andrew Feb 6 '17 at 17:10
  • 1
    \$\begingroup\$ Understanding the concept is good, the aim of this site is to give as general answer as possible to the problem so that it's useful to others. But we can't tell you what the problem is to start with if we can't see what you've done. All we can do is make random guesses and suggest you avoid writing code with bugs in in the future. If you don't post code soon this question will probably get closed. \$\endgroup\$ – Andrew Feb 6 '17 at 17:24
1
\$\begingroup\$

Two possibilities:

  1. As Dave indicated it could be a roll over issue, the regular nature of it means it's probably either that or some beat frequency between the interrupt rate and the main code loop rate.
  2. The value is changing when you don't expect it to.

A rollover issue may be in the main value or it could be in some calculation you are doing on it.

The second issue is if you have something like this:

// if you haven't marked this as volatile then you're lucky it ever worked
volatile uint16_t period_u16;

onInterrupt () {
  period_u16 = new_value;
}

main() {
  while(true) {
    uint16_t my_number = doSomeCalculation(period_u16);
    // Invalid results if interrupt triggers here
    result = my_number - period_u16;
    ... some other code ...
  }
}

i.e. You make some calculation based on period_u16 and then reference period_u16 a second time making the assumption that it hasn't changed in between. If the interrupt happens to fire in just the wrong place then period_u16 will have changed in between those lines and the results will be wrong.

To prevent this either disable interrupts during the sensitive portion or make a local copy. If the interrupt is setting two different values (or a single variable that is larger than the processors width) then you have to do both, disable interrupts, make a copy and then re-enable them. If you don't you risk getting inconsistent results.
If the calculated value is needed within the interrupt next time it fires then you need to keep interrupts disabled until the calculations are done.

\$\endgroup\$
  • \$\begingroup\$ Finally can you answer me this question so that all my doubts gets cleared. Interrupt { period_u16= ICBUF; } In the main task int main(void){ while(1){local_period_u16 = period_u16;} so i have let us one line of code where i am copying the interrupt variable data into local buffer. If interrupt happens is it possible that msb and lsb can be different if it is 16 bit controller. \$\endgroup\$ – rajesh Feb 6 '17 at 17:39
  • \$\begingroup\$ Already answered above. Is the variable wider than the processors internal width? \$\endgroup\$ – Andrew Feb 7 '17 at 9:00
1
\$\begingroup\$

Yes, if your main task is going to read-modify-write some variable, and an interrupt handler might also modify that variable, you have a potential problem. If it's a 16-bit variable and an 8-bit processor (for example), you could even end up with one byte of the variable being the one written by the interrupt and the other byte being the one written by the main task.

One solution is for the main task to block interrupts while doing the read-modify-write.

Another solution is for the interrupt to not modify the variable, but instead to leave a message telling the main task to modify it when it is safe to do so.

\$\endgroup\$
  • \$\begingroup\$ Suppose i am just reading the variable and interrupt happens but interrupt modifies the data. What do i read? \$\endgroup\$ – rajesh Feb 6 '17 at 17:42
  • \$\begingroup\$ I am not sure i just wanted to know. \$\endgroup\$ – rajesh Feb 6 '17 at 17:43
  • \$\begingroup\$ If the variable is a single byte (or single word), you might get the old value or might get the new value, depending whether the interrupt happens before or after the read. If the variable size is greater than the processor word size (32 bit variable on 8-bit uC, for example), it's possible you get some bytes from the old value and some bytes from the new value. \$\endgroup\$ – The Photon Feb 6 '17 at 17:49
0
\$\begingroup\$

do not work (modify) with the same variable used in ISR instead use a copy of it in the main routine, and use flags to indicate new value is available

ISR modify the variable and set the flag, main routing checking the flag copy the variable to local variable and reset the flag, main routine works with the local copy.

Also check the variable type used, like holding 16bit values on 8bit variable, which truncate the data

\$\endgroup\$
  • \$\begingroup\$ But still the flag is common to both main task and interrupt. \$\endgroup\$ – rajesh Feb 6 '17 at 17:41
  • \$\begingroup\$ the idea is how quick you work with shared data, you need to complete the main task before new data arrives, more over bit wise operation are single cycle so it will be safe compared to working with variables, \$\endgroup\$ – Raj Feb 6 '17 at 17:45
  • \$\begingroup\$ But still there is some chance of occurrence. \$\endgroup\$ – rajesh Feb 6 '17 at 17:47
  • \$\begingroup\$ it depends how you manage task in main routine, there is no randomness in micro controllers code execution, it will be much predictable and you can easily modify the ISR/main code accordingly \$\endgroup\$ – Raj Feb 6 '17 at 17:53
  • \$\begingroup\$ @rajesh, it depends. Suppose the ISR always writes "1" to the flag. Main code checks the flag. If it sees 0, it just moves on to other tasks...no consequences. If it sees 1, it does its job with new data from the ISR, then writes 0 to the flag. If the ISR happens again before this is done, you have a bigger problem than just this conflict---you're getting new inputs faster than your main loop can process them, regardless of the read-modify-write conflict. \$\endgroup\$ – The Photon Feb 6 '17 at 17:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.