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

nRF52840, How to do buttonless DFU with nrf_power_gpregret_set?

Hi Devzone:

Where can I find full document on how to use nrf_power_gpregret_set? 

Background: I am planing to do buttonless DFU without BLE but through usb cdc acm.

After digging around devzone I found a post suggesting putting this 2 lines of code into my application in order to enter DFU mode:

nrf_power_gpregret_set(0xB1);

NVIC_SystemReset();

The problem is there's not much info about what that value 0xB1 means.

Here is a screen shot of a document mentioning gpregre_set function:

No explanation on what value to set can be found on this page. Where can I get more info on this?

Thanks!

  • Hi Cpeng, 

    the 0xB1 value is just a "magic number" that is written to the GPREGRET register, which is then checked by the bootloader. The BOOTLOADER_DFU_START number, i.e. 0xB1 is defined in nrf_bootloader_info.h

    // from nrf_bootloader_info.h
    
    #define BOOTLOADER_DFU_GPREGRET                 (0xB0)      /**< Magic pattern written to GPREGRET register to signal between main app and DFU. The 3 lower bits are assumed to be used for signalling purposes.*/
    #define BOOTLOADER_DFU_START_BIT_MASK           (0x01)      /**< Bit mask to signal from main application to enter DFU mode using a buttonless service. */
    
    
    #define BOOTLOADER_DFU_START    (BOOTLOADER_DFU_GPREGRET | BOOTLOADER_DFU_START_BIT_MASK)      /**< Magic number to signal that bootloader should enter DFU mode because of signal from Buttonless DFU in main app.*/

    It is used when the application wants to enter the bootloader

    uint32_t ble_dfu_buttonless_bootloader_start_finalize(void)
    {
        uint32_t err_code;
    
        NRF_LOG_DEBUG("In ble_dfu_buttonless_bootloader_start_finalize\r\n");
    
        err_code = sd_power_gpregret_clr(0, 0xffffffff);
        VERIFY_SUCCESS(err_code);
    
        err_code = sd_power_gpregret_set(0, BOOTLOADER_DFU_START);
        VERIFY_SUCCESS(err_code);
    
        // Indicate that the Secure DFU bootloader will be entered
        m_dfu.evt_handler(BLE_DFU_EVT_BOOTLOADER_ENTER);
    
        // Signal that DFU mode is to be enter to the power management module
        nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_DFU);
    
        return NRF_SUCCESS;
    }
    

    A nRF52xxx device with a bootloader will always start the bootloader upon a reset. The bootloader will then call the function dfu_enter_check() to check if the nRF52xxx should stay in DFU mode or start the application in nrf_bootloader_init().

    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;
    }
    
    ret_code_t nrf_bootloader_init(nrf_dfu_observer_t observer)
    {
        NRF_LOG_DEBUG("In nrf_bootloader_init");
    
        ret_code_t                            ret_val;
        nrf_bootloader_fw_activation_result_t activation_result;
        uint32_t                              initial_timeout;
        bool                                  dfu_enter = false;
    
        m_user_observer = observer;
    
        if (NRF_BL_DFU_ENTER_METHOD_BUTTON)
        {
            dfu_enter_button_init();
        }
    
        ret_val = nrf_dfu_settings_init(false);
        if (ret_val != NRF_SUCCESS)
        {
            return NRF_ERROR_INTERNAL;
        }
    
        #if NRF_BL_DFU_ALLOW_UPDATE_FROM_APP
        // Postvalidate if DFU has signaled that update is ready.
        if (s_dfu_settings.bank_current == NRF_DFU_CURRENT_BANK_1)
        {
            postvalidate();
        }
        #endif
    
        // Check if an update needs to be activated and activate it.
        activation_result = nrf_bootloader_fw_activate();
    
        switch (activation_result)
        {
            case ACTIVATION_NONE:
                initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS);
                dfu_enter       = dfu_enter_check();
                break;
    
            case ACTIVATION_SUCCESS_EXPECT_ADDITIONAL_UPDATE:
                initial_timeout = NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_CONTINUATION_TIMEOUT_MS);
                dfu_enter       = true;
                break;
    
            case ACTIVATION_SUCCESS:
                bootloader_reset(true);
                NRF_LOG_ERROR("Unreachable");
                return NRF_ERROR_INTERNAL; // Should not reach this.
    
            case ACTIVATION_ERROR:
            default:
                return NRF_ERROR_INTERNAL;
        }
    
        if (dfu_enter)
        {
            nrf_bootloader_wdt_init();
            scheduler_init();
            dfu_enter_flags_clear();
    
            // Call user-defined init function if implemented
            ret_val = nrf_dfu_init_user();
            if (ret_val != NRF_SUCCESS)
            {
                return NRF_ERROR_INTERNAL;
            }
    
            nrf_bootloader_dfu_inactivity_timer_restart(initial_timeout, inactivity_timeout);
    
            ret_val = nrf_dfu_init(dfu_observer);
            if (ret_val != NRF_SUCCESS)
            {
                return NRF_ERROR_INTERNAL;
            }
    
            NRF_LOG_DEBUG("Enter main loop");
            loop_forever(); // This function will never return.
            NRF_LOG_ERROR("Unreachable");
        }
        else
        {
            // Erase additional data like peer data or advertisement name
            ret_val = nrf_dfu_settings_additional_erase();
            if (ret_val != NRF_SUCCESS)
            {
                return NRF_ERROR_INTERNAL;
            }
    
            m_flash_write_done = false;
            nrf_dfu_settings_backup(flash_write_callback);
            ASSERT(m_flash_write_done);
    
            nrf_bootloader_app_start();
            NRF_LOG_ERROR("Unreachable");
        }
    
        // Should not be reached.
        return NRF_ERROR_INTERNAL;
    }

    Best regards

    Bjørn

  • Thanks for the detailed explanation sir!

    So writing 0xB1 will make device enter DFU after reboot. But what values to write to cancel DFU?

  • Hi cpeng, 

    the nRF52xxx will enter the bootloader if GPREGRET is set to 0xB1 and the device is reset. No action will be taken before a reset is performed. So if you do not want to enter DFU mode, then just clear the GPREGRET register before you reset, i.e. 

    err_code = sd_power_gpregret_clr(0, 0xffffffff);

  • Thanks. What's the diffrence between 

    nrf_power_gpregret_set

    and

    sd_power_gpregret_set

    ?

Related