9
\$\begingroup\$

Initial question

I have a general question about the handling of interrupts in microcontrollers. I am using the MSP430, but I think the question may be extended to other uCs. I would like to know whether is or not a good practice to enable/disable interrupts frequently along the code. I mean, if I have a portion of code that is not going to be sensitive to interrupts (or even worse, must not listen to interrupts, for any reason), is it better to:

  • Disable the interrupts before and then re-enable them after the critical section.
  • Put a flag inside the respective ISR and (instead of disabling the interrupt), set the flag to false before the critical section and resetting it to true, just after. To prevent the code of the ISR from being executed.
  • Neither of the two, so suggestions are welcome!

Update: interrupts and state charts

I will provide a specific situation. Let us assume we want to implement a state chart, which is composed by 4 blocks:

  1. Transitions/Effect.
  2. Exit conditions.
  3. Entry activity.
  4. Do activity.

This is what a professor taught us at university. Probably, not the best way of doing it is by following this scheme:

while(true) {

  /* Transitions/Effects */
  //----------------------------------------------------------------------
  next_state = current_state;

  switch (current_state)
  {
    case STATE_A:
      if(EVENT1) {next_state = STATE_C}
      if(d == THRESHOLD) {next_state = STATE_D; a++}
      break;
    case STATE_B:
      // transitions and effects
      break;
    (...)
  }

  /* Exit activity -> only performed if I leave the state for a new one */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(current_state)
    {
      case STATE_A:
        // Exit activity of STATE_A
        break;
      case STATE_B:
        // Exit activity of STATE_B
        break;
        (...)
    }
  }

  /* Entry activity -> only performed the 1st time I enter a state */
  //----------------------------------------------------------------------
  if (next_state != current_state)
  {
    switch(next_state)
    {
      case STATE_A:
        // Entry activity of STATE_A
        break;
      case STATE_B:
        // Entry activity of STATE_B
        break;
      (...)
    }
  }

  current_state = next_state;

  /* Do activity */
  //----------------------------------------------------------------------
  switch (current_state)
  {
    case STATE_A:
      // Do activity of STATE_A
      break;
    case STATE_B:
      // Do activity of STATE_B
      break;
    (...)
  }
}

Let us also assume that from, say STATE_A, I want to be sensitive to a interrupt coming from a set of buttons (with debouce system, etc. etc.). When someone presses one of these buttons, an interrupt is generated and the flag related to the input port is copied into a variable buttonPressed. If the debounce is set to 200 ms in some way (watchdog timer, timer, counter, ...) we are sure that buttonPressed cannot be updated with a new value before 200 ms. This is what I am asking you (and myself :) of course)

Do I need to enable interrupt in the DO activity of STATE_A and disable before leaving?

/* Do activity */
//-------------------------------------
switch (current_state)
{
  case STATE_A:
    // Do activity of STATE_A
    Enable_ButtonsInterrupt(); // and clear flags before it
    // Do fancy stuff and ...
    // ... wait until a button is pressed (e.g. LPM3 of the MSP430)
    // Here I have my buttonPressed flag ready!
    Disable_ButtonsInterrupt();
    break;
  case STATE_B:
    // Do activity of STATE_B
    break;
  (...)
}

In a way that I am sure that the next time I execxute block 1 (transition/effects) at the next iteration I am sure that the conditions checked along the transitions are not coming from a subsequent interrupt that has overwritten the previous value of buttonPressed that I need (although it is impossible that this happens because 250 ms must elapse).

\$\endgroup\$
1
  • 3
    \$\begingroup\$ It's hard to make a recommendation without knowing more about your situation. But sometimes its necessary to disable interrupts in embedded systems. It's preferable to keep interrupts disabled for only short periods of time so that interrupts are not missed. It may be possible to disable only particular interrupts rather than all interrupts. I don't recall ever using the flag-inside-ISR technique like you described so I'm skeptical whether that's your best solution. \$\endgroup\$
    – kkrambo
    Commented Apr 10, 2015 at 17:15

5 Answers 5

17
\$\begingroup\$

The first tactic is to architect the overall firmware so that it's OK for interrupts to occur at any time. Having to turn off interrupts so that the foreground code can execute a atomic sequence should be done sparingly. There is often a architectural way around it.

However, the machine is there to serve you, not the other way around. General rules of thumb are only to keep bad programmers from writing really bad code. It is far better to understand exactly how the machine works and then architect a good way to harness those capabilities to perform the desired task.

Keep in mind that unless you really are tight on cycles or memory locations (can certainly happen), that otherwise you want to optimize for clarity and maintainability of the code. For example, if you have a 16 bit machine that updates a 32 bit counter in a clock tick interrupt, you need to make sure that when the foreground code reads the counter that the two halves of it are consistent. One way is to shut off interrupts, read the two words, and then turn interrupts back on. If interrupt latency isn't critical, then that's perfectly acceptable.

In the case where you must have low interrupt latentcy, you can, for example, read the high word, read the low word, read the high word again and repeat if it changed. This slows down the foreground code a little, but adds no interrupt latency at all. There are various little tricks. Another might be to set a flag in the interrupt routine that indicates the counter must be incremented, then do that in the main event loop in the foreground code. That works fine if the counter interrupt rate is slow enough so that the event loop will do the increment before the flag is set again.

Or, instead of a flag use a one-word counter. The foreground code keeps a separate variable that contains the last counter it has updated the system to. It does a unsigned subtract of the live counter minus the saved value to determine how many ticks at a time it has to handle. This allows the foreground code to miss up to 2N-1 events at a time, where N is the number of bits in a native word the ALU can handle atomically.

Each method has its own set of advantages and disadvantages. There is no single right answer. Again, understand how the machine works, then you won't need rules of thumb.

\$\endgroup\$
0
7
\$\begingroup\$

If you need a critical section, you must make sure, that the operation guarding your critical section is atomic and cannot be interrupted.

Thus disabling the interrupts, which is most commonly handled by a single processor instruction (and called using a compiler intrinsic function), is one of the safest bets you can take.

Depending on your system, there might be some issues with that, like an interrupt might get missed. Some microcontrollers set the flags regardless of the state of the global interrupt enable, and after leaving the critical section, the interrupts get executed and are just delayed. But if you have an interrupt which occurs at a high rate, you can miss a second time the interrupt happened if you block the interrupts for a too long time.

If your critical section requires only one interrupt not to be executed, but other should be executed, the other approach seems viable.

I find myself programming the interrupt service routines as short as possible. So they just set a flag which is then checked during the normal program routines. But if you do that, beware of race conditions while waiting for that flag to be set.

There are a lot of options and surely not a single correct answer to this, this is a topic which requires careful design and deserves a bit more of thought than other things.

\$\endgroup\$
5
\$\begingroup\$

If you've determined that a section of code must run uninterrupted then, except under unusual circumstances, you should disable interrupts for the minimum duration possible to complete the task, and re-enable them after.

Put a flag inside the respective ISR and (instead of disabling the interrupt), set the flag to false before the critical section and resetting it to true, just after. To prevent the code of the ISR from being executed.

This would still allow an interrupt to occur, a code jump, a check, then a return. If your code can handle this much interruption, then you should probably simply design the ISR to set a flag rather than performing the check - it would be shorter - and handle the flag in your normal code routine. This sounds like someone put too much code into the interrupt, and they are using the interrupt to perform longer actions that should take place in the regular code.

If you are dealing with code where the interrupts are long, a flag as you suggest might resolve the issue, but it will still be better to simply disable interrupts if you can't re-factor the code to eliminate the excessive code in the interrupt.

The main issue with doing it the flag way is that you don't execute the interrupt at all - which may have repercussions later. Most microcontrolers will track interrupt flags even while interrupts are globally disabled, and will then execute the interrupt when you re-enable interrupts:

  • If no interrupts occurred during the critical section, none are executed after.
  • If one interrupt occurs during the critical section, one is executed after.
  • If multiple interrupts occur during the critical section, only one is executed after.

If your system is complex and has to track interrupts more completely, you'll have to design a more complicated system to track interrupts and operate accordingly.

However, if you always design your interrupts to perform the minimum work necessary to achieve their function, and delay everything else to the regular processing, then interrupts will rarely impact your other code negatively. Have the interrupt capture or release data if needed, or set/reset outputs, etc as needed, then have the main code path pay attention to flags, buffers, and variables the interrupt affects so the lengthy processing can be done in the main loop, rather than the interrupt.

This should eliminate all but a very, very few situations where you might need an uninterrupted code section.

\$\endgroup\$
3
  • \$\begingroup\$ I have updated the post to better explain the situation I am working at :) \$\endgroup\$
    – Umberto D.
    Commented Apr 10, 2015 at 21:42
  • 1
    \$\begingroup\$ In your example code, I would consider disabling the specific button interrupt when not needed, and enable it when needed. Doing this frequently isn't an issue of its by design. Leave global interrupts on so you can add other interrupts to the code later if needed. Alternately, just reset the flag when you go to state A and otherwise ignore it. If the button is pressed and the flag set, who cares? Ignore it until you get back to state A. \$\endgroup\$
    – Adam Davis
    Commented Apr 11, 2015 at 2:49
  • \$\begingroup\$ Yes! That may be a solution because in the actual design I frequently go in LPM3 (MSP430) with global interrupts enabled and I exit from LPM3, resuming the execution, as soon as an interrupt is detected. So a solution is the one you presented which I think is reported in the second part of the code: enable interrupts as soon as I start to perform the do activity of a state that needs it and disable before going to the transitions block. May another other possible solution be to disable interrupt just before leaving the "Do activity block" and re-enable sometime (when?) after? \$\endgroup\$
    – Umberto D.
    Commented Apr 11, 2015 at 8:57
1
\$\begingroup\$

Putting a flag inside the ISR as you describe probably won't work since you're basically ignoring the event that triggered the interrupt. Disabling interrupts globally is usually a better choice. As others have said, you shouldn't have to do this very often. Keep in mind that any read or write that's done via a single instruction shouldn't need to be protected since the instruction either executes or it doesn't.

A lot depends on what kind of resources you're trying to share. If you're feeding data from the ISR to the main program (or vice-versa), you could implement something like a FIFO buffer. The only atomic operation would be updating the read and write pointers, which minimizes the amount of time you spend with interrupts disabled.

\$\endgroup\$
0
\$\begingroup\$

There is a subtle difference you need to take into account. You might elect to "delay" the handling of an interrupt or "disregard" and interrupt.

Often we say in the code that we disable the interrupt. What probably will happen, due to hardware functions, is that once we enable interrupts it will trigger. This in a way is delaying the interrupt. In order for the system to work reliably we need to know the maximum time we may delay handling of these interrupts. And then ensure that all cases where interrupts are disabled will be finished in a shorter time.

Sometimes we want to disregard interrupts. The best way might be to block the interrupt at hardware level. There often is an interrupt controller or similar where we might say which inputs should generate interrupts.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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