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

Use WDT to enter DFU mode

Hi,

I'm working with an NRF52 Dev Kit and we want to implement OTA DFU using BLE as we have in a previous project. All our projects are worked on in Segger Embedded Studio. Recently we migrated from Nordic SDK 14.2.0 to SDK 15.3.0 and soft device 132 5.0.0 to sd 132 6.1.1 and so far everything is working well. I've searched around the Infocenter and Devzone and right now am able to make DFU .zip files from my own projects (made from modified ble_blinky) with my own private key using the nrfutil command line tool. The most helpful resources in getting this for me have been this blog post and this github readme.

Ultimately my goal will be to create projects to upload to any future devices using OTA DFU that implement a WDT in case of a hardfault, infinite loop, etc. occurring in an application. Having had a few devices stop working after completely closing them up in an enclosure with no access from the outside has made this a priority.

I've modified the "sdk_config.h" of a copy of the "secure_bootloader" example project as well as the source file "nrf_bootloader.c" thus far to try and include functionality for detecting a reset caused by a WDT. My assumption would be that if the device is reset from the WDT that the uploaded application likely has an error in it and I enter DFU mode from there instead of just jumping back into the same application.

In "sdk_config.h" the file originally includes:

#ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN
#define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 16
#endif

// </e>

// <q> NRF_BL_DFU_ENTER_METHOD_PINRESET  - Enter DFU mode on pin reset.
 

#ifndef NRF_BL_DFU_ENTER_METHOD_PINRESET
#define NRF_BL_DFU_ENTER_METHOD_PINRESET 1
#endif

// <q> NRF_BL_DFU_ENTER_METHOD_GPREGRET  - Enter DFU mode when bit 1 (0-indexed) is set in the NRF_POWER_GPREGRET register.
 

#ifndef NRF_BL_DFU_ENTER_METHOD_GPREGRET
#define NRF_BL_DFU_ENTER_METHOD_GPREGRET 1
#endif

// <q> NRF_BL_DFU_ENTER_METHOD_BUTTONLESS  - Enter DFU mode when the Boolean enter_buttonless_dfu in DFU settings is true.
 

#ifndef NRF_BL_DFU_ENTER_METHOD_BUTTONLESS
#define NRF_BL_DFU_ENTER_METHOD_BUTTONLESS 0
#endif

I have now also included:

// <q> NRF_BL_DFU_ENTER_METHOD_WDT       - Enter DFU mode on WDT reset.


#ifndef NRF_BL_DFU_ENTER_METHOD_WDT
#define NRF_BL_DFU_ENTER_METHOD_WDT 1  //NOTE: this has been added in by me 6-18-2019
#endif

In "nrf_bootloader.c" there is already a function for checking if the device should enter DFU mode:

/**@brief Function for checking whether to enter DFU mode or not.
 */
static bool dfu_enter_check(void)
{
    if (!app_is_valid(crc_on_valid_app_required()))
    {
        NRF_LOG_DEBUG("DFU mode because app is not valid.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_BUTTON &&
       (nrf_gpio_pin_read(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN) == 0))
    {
        NRF_LOG_DEBUG("DFU mode requested via button.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_PINRESET &&
       (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk))
    {
        NRF_LOG_DEBUG("DFU mode requested via pin-reset.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_GPREGRET &&
       (nrf_power_gpregret_get() & BOOTLOADER_DFU_START))
    {
        NRF_LOG_DEBUG("DFU mode requested via GPREGRET.");
        return true;
    }

    if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS &&
       (s_dfu_settings.enter_buttonless_dfu == 1))
    {
        NRF_LOG_DEBUG("DFU mode requested via bootloader settings.");
        return true;
    }

    return false;
}

I have also added these lines to the above function:

if(NRF_BL_DFU_ENTER_METHOD_WDT &&
      (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk)) //NOTE: this has been added in by me on 6-18-2019
    {
        NRF_LOG_DEBUG("DFU mode requested via watchdog-reset.");
        return true;
    }

I tried this method because I had already previously tested reading the register "NRF_POWER->RESETREAS" and checking the appropriate bit masks to detect why the device was reset before. The result of this is that the device does enter DFU mode after resetting from a timed out WDT and from there I am able to upload a new application successfully to it. Where I'm running into problems is I believe I need to also include somewhere in the source code for DFU a reset of the WDT bit in the "NRF_POWER->RESETREAS" register. When the device in DFU mode times out or when the application such as the buttonless DFU example project perform a software reset this register is not overwritten and the device will simply detect the same bit has been raised and reenter DFU mode over and over. Currently, if the device is reset by the WDT and a new application is uploaded to it the device will continuously reset and reenter DFU mode. I need to be able to have a device enter DFU mode if the application creates a WDT reset, upload a new application to the device, and have the device immediately run that new application like normal without needing to power cycle it (which does skip past DFU mode and cause the new application to run just fine).

A few other questions I had that hopefully can be answered more quickly.

1.) I'm still new to some of the changes from jumping between SDK 14.2.0 and SDK 15.3.0, some files I'm familiar with have been dubbed legacy and new ones with the "nrfx" prefix seem to be in their place. Will modifying the files as I've done potentially ruin any other example projects and/ or functionality for using OTA DFU bootloaders in the future? I've only recently delved into the "NRF_POWER->RESETREAS" register use and I'm still not completely sure what every part of it is used for and what registers are or aren't stored during what types of resets.

2.) What registers are or aren't stored during what types of resets? I've read the "GPREGRET" register has been used in the past for entering DFU mode from an application but there seems to be a newer way that doesn't include any reset I'm unfamiliar with. Is there a downside to using the reset reason register how I am?

3.) Originally I tried implementing a WDT directly in my copy of the secure bootloader project and I could not manage to get it working. No matter what preprocessor user included directories I created and what files I linked the error always said it could not find either "nrfx_clock.h" or "nrfx_wdt.h". Is there a different manner of including these files new to SDK 15.3.0 and/ or does the bootloader have extra requirements to include files? I managed to modify a copy of the ble blinky example project to run a WDT successfully before; I started a 10 second WDT and didn't feed it to ensure the device was actually entering DFU mode after a WDT reset.

Thanks,

Andy

  • Ok in further investigation this morning I found a function in "nrf_bootloader.c" that I believe is close to doing what I want if modified. I'm going to try changing the function: 

    /**@brief Function for clearing all DFU enter flags that
     *        preserve state during reset.
     *
     * @details This is used to make sure that each of these flags
     *          is checked only once after reset.
     */
    static void dfu_enter_flags_clear(void)
    {
        if (NRF_BL_DFU_ENTER_METHOD_PINRESET &&
           (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk))
        {
            // Clear RESETPIN flag.
            NRF_POWER->RESETREAS |= POWER_RESETREAS_RESETPIN_Msk;
        }
    
        if (NRF_BL_DFU_ENTER_METHOD_GPREGRET &&
           ((nrf_power_gpregret_get() & BOOTLOADER_DFU_GPREGRET_MASK) == BOOTLOADER_DFU_GPREGRET)
                && (nrf_power_gpregret_get() & BOOTLOADER_DFU_START_BIT_MASK))
        {
            // Clear DFU mark in GPREGRET register.
            nrf_power_gpregret_set(nrf_power_gpregret_get() & ~BOOTLOADER_DFU_START);
        }
    
        if (NRF_BL_DFU_ENTER_METHOD_BUTTONLESS &&
           (s_dfu_settings.enter_buttonless_dfu == 1))
        {
            // Clear DFU flag in flash settings.
            s_dfu_settings.enter_buttonless_dfu = 0;
            APP_ERROR_CHECK(nrf_dfu_settings_write(NULL));
        }
    }

    to include the lines: 

        if (NRF_BL_DFU_ENTER_METHOD_WDT &&
           (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk))  //NOTE: this has been added in by me 6-21-2019
        {
            // Clear DOG flag.
            NRF_POWER->RESETREAS |= POWER_RESETREAS_DOG_Msk;
        }

    I'm concerned with the comments over the function since it seems like this will lower the WDT bit after entering DFU mode the first time. If that means what I think it does then the device should:

    1.) Reset in the application by a WDT (ie. error of some kind; enter DFU mode to upload a new application).

    2.) Detect the reset was caused by a WDT and enter DFU mode.

    3.) Clear the WDT bit and assume the problem has been handled.

    4.) If there's an inactivity timeout for DFU reset the device and run the old (possibly faulty) application again since the WDT bit is cleared.

    I'm not sure if this is the best course of action or if it would be preferential to continuously reset into DFU mode from inactivity timeouts instead of jumping back into the application. Is there anyway I could set the bootloader up such that it only resets the WDT bit after successfully uploading a new application through OTA DFU? Either way I'll work on testing this code change to make sure it does work how I think it does thus far.

  • Hi Andy,

    if you suspect that the application is fault if , then you can invalidate bank 0 in dfu_enter_check() using nrf_dfu_bank_invalidate(&s_dfu_settings.bank_0) from nrf_dfu_utils.c after you have cleared RESETREAS, which will ensure that the bootloader will always enter DFU mode upon boot as there is no valid application to boot. 

    Best regards

    Bjørn

  • Hi Bjørn,

    I updated my project to include that function you suggested like so:

    static bool dfu_enter_check(void)
    {
        ...
    
        if(NRF_BL_DFU_ENTER_METHOD_WDT &&
          (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk)) //NOTE: this has been added in by me 6-18-2019
        {
            NRF_LOG_DEBUG("DFU mode requested via watchdog-reset.");
            nrf_dfu_bank_invalidate(&s_dfu_settings.bank_0);  //Device application no longer valid
            return true;
        }
    
        ...
    }

    I am also resetting the RESETREAS register just as before, clearing the WDT bit, reset pin bit, etc. but it still isn't working correctly. Currently I run an application that just enables a WDT and lets it timeout to reset the device and it enters into DFU mode but once the DFU inactivity timeout is hit the device resets and resumes the application just like before. I'm unfamiliar with "nrf_dfu_utils.c" and a lot of the specifics of how the DFU stores and checks its applications so I'll read up more on these soon.

    If it matters, the process I use to update my Dev kit is erasing it in nRFgo Studio, uploading the bootloader (and I assume SoftDevice as well) in Segger Embedded Studio, and then uploading an application using the nRF Connect phone app.

    Thanks,

    Andy

    EDIT: I got it working! It took also adding the function "nrf_dfu_settings_write_and_backup(NULL);" immediately after calling the bank invalidation function. I'm pretty sure what was happening was the device would reset from the WDT and enter DFU mode and invalidate the current memory for bank 0 which doesn't do much since A.) we're already past the check and in DFU mode and B.) this is just updating the temporary memory that gets erased on a reset. I tested around with also invalidating the other bank options (ie. &s_dfu_settings.bank_1 and &s_dfu_settings.bank_current) to no avail so I searched "s_dfu_settings" on the Infocenter and found the "nrf_dfu_settings.c" source file. These functions get called in "nrf_bootloader.c" and given the context I'm pretty sure they are used for storing the DFU settings for resets, power cycles, etc.

    I've modified my DFU check function again so now it is:

    static bool dfu_enter_check(void)
    {
        ...
    
        if(NRF_BL_DFU_ENTER_METHOD_WDT &&
          (NRF_POWER->RESETREAS & POWER_RESETREAS_DOG_Msk)) //NOTE: this has been added in by me 6-18-2019
        {
            NRF_LOG_DEBUG("DFU mode requested via watchdog-reset.");
            if(NRF_BL_DFU_ENTER_WDT_INVALIDATE_APP) //NOTE: this has been added in by me 6-26-2019
            {
                nrf_dfu_bank_invalidate(&s_dfu_settings.bank_0);  //Device application no longer valid
                nrf_dfu_settings_write_and_backup(NULL);
            }
            return true;
        }
    
        ...
    }
    and I made it an optional inclusion by also including in "sdk_config.h" right beside the other WDT inclusion:

    // <q> NRF_BL_DFU_ENTER_WDT_INVALIDATE_APP  - If entering DFU mode because of a WDT reset, invalidate the Application.
    
    
    #ifndef NRF_BL_DFU_ENTER_WDT_INVALIDATE_APP
    #define NRF_BL_DFU_ENTER_WDT_INVALIDATE_APP 1 //NOTE: this has been added in by me 6-26-2019
    #endif

    Thank you very much for pointing me in the right direction Bjørn!

    Sincerely,

    Andy

Related