This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF51422 won't sleep

I am trying to get my system with a hardware RTC generating periodic interrupts every 30 seconds to sleep between interrupts. I'll paste the code below, but here is the problem:

I get the interrupts signalled to my PORT event handler and, in my loop, write a message to a SEGGER RTT console and do some distinctive led flashing just so I know what's happening. I have a sw flag (powerDown) that I set that SHOULD cause the loop to sleep after each 'tic'. The LEDs flash 3 times prior to the _WFE sequence and then produce a distinctive flash on return from the _WFE sequence. What I expect is that I'd get the 3 flashes and then not get anything else for slightly less than 30 seconds. However, I get the distinctive sequence immediately, meaning that there is no sleeping going on...

I'm sure I'm neglecting something, but I've scanned these questions and looked at my code over and over and it looks like all the other cases that work (by the way, I have run the power_down examples "system-on-wakeup-on-gpio" case on a pca10028 and it works, with and without SEGGER RTT).

Update: I've modified the system-on-wakeup-on-gpio test program by adding my routines to initialize the serial flash chip attached via SPI0. I did this mainly because I tried to see how much power was drawn with just this simple case and found it drawing about 24uA when sleeping. Thinking that I needed to put the flash in Ultra power down mode, I had to bring in all the SPI gorp. Well, now it doesn't go to low power mode at all! The CPU sleeps (as it takes a button push to wake it up), but there is no power down (current is about 2.3mA). Any ideas on how to shut down the whole SPI infrastructure? I am doing a spi0_uninit()...

I am using an N5M8 module with nRF51422_xxac, using SDK V10.0.0.0. Here is my code:

while (1)
  {
    if (tic)
    {
      // reset interrupt status
      rtc_resetIF(RTC_TIM_IF | RTC_ALM_IF);
      if (tic_show)
      {
        SEGGER_RTT_printf(0,"tic... %ld, pin:%d \n",(long)(tic_time-prev_time),h_pin);
        printTime();
      }
      prev_time=tic_time;
      tic=false;
      if (powerDown)
      {
        SEGGER_RTT_printf(0,"Powering down!  See ya later... \n");
        blinkLED(3, 20,750);  // blink to signal shutting down
        
        // disable the internal RTC & SPI before sleeping
        spi0_uninit();
        nrf_drv_rtc_disable(&rtc);
        
        // Enter System ON sleep mode
        __WFE();
        // Make sure any pending events are cleared
        __SEV();
        __WFE();

        // re-enable the internal RTC
        nrf_drv_rtc_enable(&rtc);
        spi0_init();
        
        blinkLED(20, 5,50);  // blink to signal wake up
      }
    }

    checkDebug();
  }
  • Are you using a debugger (RTT printing etc.) when you say that power consumption is 2.3mA? when powered down? When measuring power consumption you should not use a debugger (this also means that you have to do a pin reset after programming before measuring), as the debugger interface consumes approximatly 1.2 mA.

  • Yes, my mistake on this part of the problem. I had been shutting down after program load and restarting with the SEGGER disconnected, but hadn't noticed any difference previously (probably because it's not sleeping in the first case), so didn't think it mattered. In this second case, I retried it and, once disconnected and restarted, the total board current is a respectable 2.5uA.

    Thank you for pointing this out. However, what I'd really like to know is why the first piece of code won't sleep.

  • Probably the problem is the way your code attempts to go to sleep. You are using this code:

        // Enter System ON sleep mode
        __WFE();
        // Make sure any pending events are cleared
        __SEV();
        __WFE();
    

    But this will probably not do what you think. If we assume that a event is pending, then the first WFE (Wait for Event) instruction will not do what you expect, as the pending event will cause the CPU to wake up again immediately. Then the SEV (Send Event) instruction will signal an event to the CPU. Again, the CPU will wake up immediatly from the next WFE as a event is pending.

    The correct order is this:

        // Make sure any pending events are cleared
        __SEV();
        __WFE();
        // Enter System ON sleep mode
        __WFE();
    

    Here the SEV will make sure that a event is set, in case it is not. The first WFE will make sure the event is cleared. Thus after those two instructions we can be confident that there are no pending events. The last WFE will put the CPU to sleep.

  • Doh! Actually, this makes total sense to me, however, throughout this forum all the examples seemed to be the sequence that I originally used. I've reordered as you've suggested and, indeed, it DOES SLEEP!!! YAY! Thank you very much!

  • I am glad to hear this fixed your problem. The thing is that if nothing else is done in the main loop, as is often the case in a purely event driven application, then the order does not matter as it will basically result in the same set of instructions (except for the relative position of the jump). However, when you do other stuff in your main loop you have to get the order right.

Related