Power Management blocks main loop

Hi,

I am discovering the nrf52 software with the Segger Embedded Studio environment.
I have developed an application derived of the ble_app_uart example.

I have many events that run well, but I wanted to add some tests in my main loop (detect a button change for example) or trigger an action once the new BLE data has been received (I am more used to the Arduino environment where I write non-blocking functions in my main loop).

My issue is that the code doesn't seem to loop inside the main loop. After some investigation, I discovered that the nrf_pwr_mgmt_run() was the cause of my problems even if I don't really understand why.

So my questions are :

- Why does the nrf_pwr_mgmt_run() blocks the main loop ?

- What am I doing wrong ?

Thanks a lot to anyone who can help me !

PS : I have added the code that I want to run inside my main loop. To give a broad idea of what it does, I have a RV3028 RTC connected through I2C to my nrf52832. I set an alarm through the bluetooth and the device is supposed to go to sleep and wake up at the given date and time.

int main(void)
{
    bool erase_bonds;

    uint32_t button_reset_previous = 0;
    uint32_t reed_previous = 0;

    // Initialize.
    log_init();
    NRF_LOG_INFO("Main function started");
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    services_init();
    advertising_init();
    conn_params_init();
    TWI_Init();
    pwm_init();
   
    RV3028_configuration_init();
    // Start execution.
    NRF_LOG_INFO("Main function started");

    advertising_start();

    bool alarmSet = false;
    bool wakeFromAlarm = false;
    uint8_t Status = 0;


    RV3028_GetFlags(&RTC, &Status);

    if(Status & RV3028_FLAG_ALARM)
    {
        RV3028_ClearFlags(&RTC, RV3028_FLAG_ALARM);
        wakeFromAlarm = true;
    }

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();

        if(resetRTC)
        {
          RV3028_configuration_init(false);
          RV3028_ClearFlags(&RTC, RV3028_FLAG_ALARM);
          resetRTC = false;
        }

        if(wakeFromAlarm)
        {
          NRF_LOG_INFO("Waking Up from alarm");
          nrf_gpio_pin_set(PIN_DRV_SLEEP); // put driver out of sleep mode
          nrf_gpio_pin_set(PIN_DRV_DIR); // backward mode
          nrf_delay_ms(10);

          app_pwm_enable(&PWM1);
          while (app_pwm_channel_duty_set(&PWM1, 0, 50) == NRF_ERROR_BUSY);
          nrf_delay_ms(3000);
          while (app_pwm_channel_duty_set(&PWM1, 0, 0) == NRF_ERROR_BUSY);
          nrf_delay_ms(500);

          nrf_gpio_pin_clear(PIN_DRV_SLEEP); // put driver in sleep mode
          nrf_gpio_pin_clear(PIN_DRV_DIR);
          app_pwm_disable(&PWM1);
          wakeFromAlarm = false;
        }

        if(writeEeprom)
        {
          uint8_t data = 3;
          ErrorCode = RV3028_SetEEPROM(&RTC, 2, data);
          if(ErrorCode != RV3028_NO_ERROR)
          {
              NRF_LOG_INFO("Error writing Eeprom. Error: %u", ErrorCode);
          }
          else
          {
              NRF_LOG_INFO("Eeprom write done");
          }
          writeEeprom = false;
        }

        if(readEeprom)
        {
          uint8_t data = 0;
          ErrorCode = RV3028_GetEEPROM(&RTC, 2, &data);
          if(ErrorCode != RV3028_NO_ERROR)
          {
              NRF_LOG_INFO("Error reading Eeprom. Error: %u", ErrorCode);
          }
          else
          {
              NRF_LOG_INFO("Eeprom data: %u", data);
          }
          readEeprom = false;
        }


        if(alarmSet)
        {
          NRF_LOG_INFO("Going into sleep mode");
          NRF_LOG_PROCESS();
          sleep_mode_enter();
        }

        if(CurrentTime.tm_mon != 0)
        {
          ErrorCode = RV3028_SetTime(&RTC, &CurrentTime);
          if(ErrorCode != RV3028_NO_ERROR)
          {
              NRF_LOG_INFO("Error writing current time. Error: %u", ErrorCode);
          }
          CurrentTime.tm_mon = 0;
        }

        if (printRTCTime)
        {
          printRTCTime = false;
          struct tm tmpTime;
          RV3028_GetTime(&RTC, &tmpTime);
          NRF_LOG_INFO("RTC time: %u-%u-20%u %u:%u:%u", tmpTime.tm_mday, tmpTime.tm_mon, tmpTime.tm_year, tmpTime.tm_hour, tmpTime.tm_min, tmpTime.tm_sec);
        }

        if(RTC_Alarm.Day != 0)
        {
          NRF_LOG_INFO("setting Alarm %u %u %u", RTC_Alarm.Day, RTC_Alarm.Hours, RTC_Alarm.Minutes);
          ErrorCode = RV3028_EnableAlarm(&RTC, &RTC_Alarm);
          if(ErrorCode != RV3028_NO_ERROR)
          {
              NRF_LOG_INFO("Error writing Alarm. Error: %u", ErrorCode);
          }
          else
          {
            nrf_gpio_pin_set(PIN_DRV_SLEEP); // put driver out of sleep mode
            nrf_gpio_pin_clear(PIN_DRV_DIR); // forward mode
            nrf_delay_ms(10);
          
            app_pwm_enable(&PWM1);
            while (app_pwm_channel_duty_set(&PWM1, 0, 50) == NRF_ERROR_BUSY);
            nrf_delay_ms(3000);
            while (app_pwm_channel_duty_set(&PWM1, 0, 0) == NRF_ERROR_BUSY);
            nrf_delay_ms(500);

            app_pwm_disable(&PWM1);
            
            nrf_gpio_pin_clear(PIN_DRV_SLEEP); // put driver in sleep mode
            nrf_delay_ms(500);
            NRF_LOG_FLUSH();
            alarmSet = true;
          }
          RTC_Alarm.Day = 0;
        }
        
        if(getRtcFlag)
        {
          ErrorCode = RV3028_GetFlags(&RTC, &Status);
          if(ErrorCode != RV3028_NO_ERROR)
          {
              NRF_LOG_INFO("Error writing Alarm. Error: %u", ErrorCode);
          }

          if(Status & RV3028_FLAG_ALARM)
          {
              NRF_LOG_INFO(" Alarm ");
              RV3028_ClearFlags(&RTC, RV3028_FLAG_ALARM);
          }

          getRtcFlag = false;
        }

        if(button_reset_previous != nrf_gpio_pin_read(PIN_BTN_RST))
        {
          nrf_delay_ms(50);
          button_reset_previous = nrf_gpio_pin_read(PIN_BTN_RST);
          NRF_LOG_INFO("BTN_RST Toggle");
        }

        if(reed_previous != nrf_gpio_pin_read(PIN_REED_VAL))
        {
          nrf_delay_ms(50);
          reed_previous = nrf_gpio_pin_read(PIN_REED_VAL);
          NRF_LOG_INFO("REED Toggle");
          nrf_gpio_pin_toggle(BLE_STATUS_LED);
        }

    }
}

  • Hello,

    nrf_pwr_mgmt_run() will make the device enter sleep (i.e. System ON mode), and the program will not return from this function until next time the CPU is woken up by an interrupt.

    The program should iterate through the loop again after servicing the interrupt, even if you enter sleep with nrf_pwr_mgmt_run(). So, Are your state flags (wakeFromAlarm, etc) updated from interrupts? 

    Best regards,

    Vidar

  • Hi Vidar,

    Thanks a lot for your quick reply !

    If I understand correctly, the main loop is looped through after an interrupt is called by any of the services ?

    In my case, the flags are indeed updated by bluetooth communication, so I imagine the loop is called afterwards ? (this part of the code was actually working, so this makes sense).

    The only part of the code that was having trouble was the last part where I try to catch GPIO input actions. I guess that I should manage the GPIO through interrupts as well ?

    Related question : what would be the consequence of not putting the device in sleep when operating ? Just a higher power consumption ?

    In my case, the device is functioning for a few minutes during which the user configures the device, then, once the alarm is set, the device enters the deep sleep mode. Not using the nrf_pwr_mgmt_run() would only impact the times the board isn't deep sleep right ?

    Thanks again for your precious help !

  • No problem!

    If I understand correctly, the main loop is looped through after an interrupt is called by any of the services ?

    Yes, your understanding is correct.

    In my case, the flags are indeed updated by bluetooth communication, so I imagine the loop is called afterwards ? (this part of the code was actually working, so this makes sense).

    Correct. Bluetooth events are notified by the Softdevice to the application through the softdevice interrupt.

    The only part of the code that was having trouble was the last part where I try to catch GPIO input actions. I guess that I should manage the GPIO through interrupts as well ?

    Yes, sounds like this will be the solution for you.

    Related question : what would be the consequence of not putting the device in sleep when operating ? Just a higher power consumption ?

    Yes, the consequence is increased power consumption.

    In my case, the device is functioning for a few minutes during which the user configures the device, then, once the alarm is set, the device enters the deep sleep mode. Not using the nrf_pwr_mgmt_run() would only impact the times the board isn't deep sleep right ?

    Yes, it will not affect the device when it is in deep sleep (aka System OFF mode).

Related