This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Handling Wake While Already Awake

My code uses a model where an ISR wakes up the mainline code that is already awake. When the mainline code goes back to sleep, it has missed the wakeup. My work-around is to use a wake pending flag to avoid going back to sleep. Is there more "MSP430"ish way of handling of handling this?

unsigned char wake_pending = 0;

void main(void)
{
  for(;;)
  {
    __bic_SR_register(GIE);  // Disable IRQs
    if(g_wake_pending)
    {
      wake_pending = 0;
      __bis_SR_register(GIE); // Enable IRQs
    }
    else
    {
      __bis_SR_register(LPM4_bits|GIE); // Enable IRQs and wait for any IRQ
    }
    // IRQs are enabled at this point.

    process_stuff();
  }
}

#pragma vector=SOME_VECTOR
__interrupt void SOME_ISR (void)
{
  handle_some_irq();

  /* Wake if CPU is asleep. Otherwise set wake pending flag. */
  if((__get_SR_register_on_exit()&CPUOFF)==0)
    wake_pending=1;
  else
    __bic_SR_register_on_exit(LPM4_bits); // Wake up mainline
}

  • Are you aware of the intrinsic function short int  __get_SR_register_on_exit(void);? With this, your ISR can determine if CPU is active or not (and other statuses) before the ISR is entered. 

  • Well, the ISR can only say 'when I'm done, main shall run in any case' or 'main shall continue doing what was doing before the interrupt'.
    While indeed the ISR can check the CPU status after return, there's nothing it can do. Waking main if it is already awake is a NOP, as well as sending it intentionally to sleep when it was already in sleep.
    A possible way is to send main into sleep  (freeze it) on an interrupt event, until another interrupt event occurs. But a better way would then be to tell main about the event by a global (volatile!) flag and let main go to sleep when the current job is done, at a safe position.
    And the common case is to wake main when an event requires further processing. However, in case main isn't sleeping already, the ISR cannot wait until it is - this would be possible for parallel tasks, using semaphores, but not for an interrupt function.

    So if this may happen, then a different kind of synchronization must be found. E.g. using a volatile global flag. In this case, main may go into sleep after checking the flag(s) and not having anything to do anymore.

    However, to avoid racing conditions. main should disable interrupts before checking the flags, and enable them with the same instruction it goes to sleep.

    Almost what you've already done. Just that with proper main code flow, the ISR don't have to check whether main is running. Just set the flag and wake main. And let main decide what to do and when to go to sleep.

  • Thanks for the comments. I was hoping for a way to do this without disabling interupts in the mainline code. Some magical MSP430 intrinsic. I guess executing a few lines of C code with interrupts disabled is probably okay.

  • Actually, __get_SR_register_on_exit(void); returns the SR value before the ISR is invoked provided that the ISR did not use __bic_SR_register_on_exit(...); or __bis_SR_register_on_exit(...); yet.

  • Occurred to me that I could use a software interrupt to centrally trigger a wakeup. Seems that MSP430s don't have SW IRQs but I assume that I could implement using an unused peripheral IRQ. Pseudo code is below. Haven't tried it yet. Not portable across MSP430s but less code, no global var, avoids disabling all interrupts...and harder to understand.

    void main(void)
    {
      __bis_SR_register(GIE); // Enable IRQs
      for(;;)
      {
        ENABLE_SW_IRQ();
        __bis_SR_register(LPM4_bits); // wait for SW ISR to wake me
        DISABLE_SW_IRQ();
     
        process_stuff();
      }
    }

    #pragma vector=SOME_VECTOR
    __interrupt void SOME_ISR (void)
    {
      handle_some_irq();
      SET_SW_IRQ();
    }

    #pragma vector=SW_VECTOR
    __interrupt void SW_ISR (void)
    {
      CLEAR_SW_IRQ();
      __bic_SR_register_on_exit(LPM4_bits); // Wake up mainline
    }



  • Norman Wong said:
    Occurred to me that I could use a software interrupt to centrally trigger a wakeup.

    The idea of (ab)using an unused peripherals interrupt as a software interrupt is a good idea. However, it can be tricky.
    When enabling the software IRQ, you don't want ti to be carried out before main is going into LPM. So you need to clear GIE before you enable the SW-IRQ's IE bit. An set it again in the same instruction that enters LPM4.
    So this sequence should be grouped into a function or (for better optimization) a macro.

  • Norman Wong said:
    When the mainline code goes back to sleep, it has missed the wakeup.

    This is really a great question and I am surprised MSP430 coders don't ask it more often.  Proper use of an OS prevents this problem altogether, but most MSP430 applications do not use an OS.

    Let's say your main() code looks like this:

    1. Has event "A" happened?
       a.  Process event "A".
    2. Has event "B" happened?
       b.  Process event "B".
    3. Go to sleep.
    4. Return to step 1.

    And let's say you have ISR_A and ISR_B that handle the raw interrupts for events A and B, and wake the processor so main() can process the events.

    Here is the problem.  What happens if ISR_A runs (and signals event A) while main() is processing event B?  When the code arrives at step 3, it will go to sleep.  Oops!  Event A still needs processing!

    Norman, like you I avoid disabling interrupts.  It is clumsy and often unnecessary with elegant design.

    For example, step 3 (above) should not put the CPU to sleep unconditionally.  However, maybe a flag is not the best solution because it requires interrupt masking to avoid race conditions.  How about like this:

    Bad Step 3:

    __bis_SR_register(LPM4_bits | GIE);

    Good Step 3:

    __bis_SR_register(allowedSleepBits | GIE);
    allowedSleepBits = LPM4_bits;

    With this approach, ISRs manipulate allowedSleepBits whenever they wake the processor.  This way you err on the side of waking up too often whenever there is an "untimely" interrupt.

    Jeff

  • Jens-Michael Gross said:

    The idea of (ab)using an unused peripherals interrupt as a software interrupt is a good idea. However, it can be tricky.
    When enabling the software IRQ, you don't want ti to be carried out before main is going into LPM. So you need to clear GIE before you enable the SW-IRQ's IE bit. An set it again in the same instruction that enters LPM4.
    So this sequence should be grouped into a function or (for better optimization) a macro.

    You're right. There is window for a missed event.

    ENABLE_SW_IRQ();
    <----WAKE HAPPENING HERE IS MISSED---->
    __bis_SR_register(LPM4_bits); // wait for SW ISR to wake me

    Disabling interrupts would remove that window but if I have to disable interrupts, the more high-level flags approach is no more inefficient and easier to understand.

  • Jeff Tenney said:

    Good Step 3:

    __bis_SR_register(allowedSleepBits | GIE);
    allowedSleepBits = LPM4_bits;

    That might be better way but I am not sure __bis_SR_register() accepts variables. Doc says it only takes literals. Never actually tried passing a variable. I suppose some nifty self modifying assembler code in RAM might make version that takes variable flag. Cringe worthy. Sounds like fun though.

    I find that OS'es have very nice mutex facilities but the OS code just hides an huge amount of high level code. Out of sight...out of mind.

  • Norman Wong said:
    I am not sure __bis_SR_register() accepts variables. Doc says it only takes literals. Never actually tried passing a variable.

    It does work with my tools (IAR).  I'm surprised that documentation would say that about literals.  Strange.

    Norman Wong said:
    I find that OS'es have very nice mutex facilities but the OS code just hides an huge amount of high level code.

    Right!  But I wasn't really talking about mutexes.  In fact, like interrupt masking, mutexes are an over-used construct to be avoided when possible by good design.  I was talking about constructs like OS_getMessage() or OS_waitOnMultipleEvents().  A good OS takes care of the synchronization issues relating to asynchronous events, and "independent" tasks allow you to wait only for events you care about.  With proper use of a good OS, you would never "miss" the optimal time to process an asynchronous event.  Without the OS, you have to think more critically to achieve the same result.

    Jeff

  • Looks like the IAR doc is wrong or left over from an earlier version. IAR seems okay with any addressing mode for "BIS.W op,SR". Pseudo code is below. May occasionally get woken with no events but that's okay as long as I don't miss any events. My project is stalled right now. No HW to test on. Call it solved for now.

    unsigned short sleep_mask = LPM4_bits;

    void main(void)
    {
      __bis_SR_register(GIE); // Enable IRQs
      for(;;)
      {
        __bis_SR_register(sleep_mask); // wait for ISR to wake me or already woke me
        sleep_mask = LPM4_bits;
     
        process_stuff();
      }
    }

    #pragma vector=SOME_VECTOR
    __interrupt void SOME_ISR (void)
    {
      handle_some_irq();
      sleep_mask = 0; // Don't let mainline go back to sleep if it is awake.
      __bic_SR_register_on_exit(LPM4_bits); // Wake up mainline if it is asleep
    }

  • Jeff Tenney said:
    Norman, like you I avoid disabling interrupts.  It is clumsy and often unnecessary with elegant design.

    Atomic operations or critical sections are an important tool. The key is not to put everything into a critical section, only where needed.

    Jeff Tenney said:
    Here is the problem.  What happens if ISR_A runs (and signals event A) while main() is processing event B?  When the code arrives at step 3, it will go to sleep.  Oops!  Event A still needs processing!

    This can be easily handled by replacing step 3 by a more sophisticated version:
    _disable_interrupts();
    if(no more events pending)
      Go to sleep
    else
      _enable_interrupts();

    It keeps interrupts disabled only for the critical IF check.

    Norman Wong said:
    You're right. There is window for a missed event.

    If you disable interrupts, before you enable the software IRQ, then right after this, when entering LPM, you enable them again, the window has closed. And it takes only a single-word single-cycle instruction to disable interrupts (you'll enable them by changing the parameter of the LPM entry, adding GIE to the LPM4_bits, so it doesn't change code size or execution time.

    Jeff Tenney said:
    A good OS takes care of the synchronization issues relating to asynchronous events,

    Sure? Well, then there are not many good OSs out there. usually, you have to learn about the limitations and consider those synchronization issues. Or you use a framework that takes care of this, at the price of being big, slow and even less flexible.

    Jeff Tenney said:
    and "independent" tasks allow you to wait only for events you care about

    You don't need an OS for this. I implemented a preemptive task scheduler that allows exactly this in below 1k for the F5438.

    Jeff Tenney said:
    With proper use of a good OS,

    The keyword is 'proper'. That's the difficult part, often more difficult than implementing things yourself.

**Attention** This is a public forum