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

DFU and Watchdog timer (WDT) reset

As many others have found, and answered questions here and here and in other posts, DFU will prematurely exit IF you have enabled WDT in your application, as the current Nordic SDK Buttonless DFU doesn't consider the WDT. I hope that Nordic recognizes this flaw and provide some flexibility in the DFU with WDT in the next SDK release.

We fixed this issue with a hack similar to this post in SDK 12.x.x and 13 and the issue came up again as we switched to SDK14 as there seems to be a new feature to put the processor to sleep in DFU mode in the absence of events, using a call to

sd_app_evt_wait() 

in

static void wait_for_event()
{
    while (true)
    {

        /* if the watchdog is enabled/running then kick it  */
        if ((bool)(NRF_WDT->RUNSTATUS) == true)
        {
          NRF_WDT->RR[0] = WDT_RR_RR_Reload;
        }

        app_sched_execute();
        if (!NRF_LOG_PROCESS())
        {
        #ifdef BLE_STACK_SUPPORT_REQD
            (void)sd_app_evt_wait();
        #else
            __WFE();
        #endif
        }
    }
}

Now, if WDT was not configured to sleep when processor is sleep, this will cause a WDT reset if there are no events for time remaining until WDT times out.

My proposed fix for this was to setup another timer to frequently wake up the processor and kick the watchdog in case there are no DFU events for a long time (i.e. longer than remaining WDT ticks)

add this at the end of timers_init() in nrf_dfu.c

app_timer_create(&nrf_dfu_wdt_kick_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                wdt_kick_timer_handler);

The timeout handler for the timer, which simply kicks the dog:

static void wdt_kick_timer_handler (void * p_context)
{
    /* if the watchdog is enabled/running then kick it  */
    if ((bool)(NRF_WDT->RUNSTATUS) == true)
    {
        NRF_WDT->RR[0] = WDT_RR_RR_Reload;
    }
}

start the timer in nrf_dfu_init()

ret_val = app_timer_start(nrf_dfu_wdt_kick_timer_id,
                          APP_TIMER_TICKS(WDT_KICK_TIMEOUT_MS),
                          NULL);

A completely different approach would be to change the WDT behavior setting to NRF_WDT_BEHAVIOUR_PAUSE_SLEEP_HALT when initing it. This is supposed to pause the WDT if the processor is in sleep or halt mode. I haven't tried this setting, as it has the caveat of forcing this setting to the WDT, e.g. if for some reason no tasks in my application run, then processor will be in sleep mode and WDT will be paused, not causing a reset, which defeats the purpose of WDT

P.S. Although I managed to fix the WDT reset issue by kicking the dog in DFU, DFU sometimes fails prematurely in the middle of package transfer. I haven't figured out the cause to that issue yet.

Any comments, suggestions would be appreciated.

Related