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

Is it possible to add support for exiting dfu mode with a button press?

Hi,

I'm using a nRF52832 with SDK 14.0.0 and were wondering if there is a way to add support for exiting DFU mode by pressing the button for 3s. 

Parents
  • Hi,

    Yes, that should not be a problem. You need to implement it yourself though, but essentially you just have to check the button press duration (using a GPIO input and a RTC or app timer) and reset if button was pressed for long enough. If you use buttonless DFU you should also make sure that the retention register is cleared by calling nrf_power_gpregret_set(0) (SDK 14) or dfu_enter_flags_clear() (SDK 15).

  • I have tried this but havent really gotten it to work. I've tried adding a timer in main.c in the Bootloader to perform this operation but the timer handler never seems to be called after entering DFU mode. 

    So I added an override of 

    /** @brief Weak implementation of nrf_dfu_init
    *
    * @note This function must be overridden in application if
    * user-specific initialization is needed.
    */
    // uint32_t nrf_dfu_init_user(void)
    in main where I do a app_timer_init() and create and start a timer with 10 ms intervals and initiate a counter. In the handler of this timer I use nrf_gpio_pin_read() for my button to check if it is pressed for 300 cycles before calling a reset function which resets the gpregret and resetting the device. 

    But as I said, the app timer handler is never called..

  • That sounds odd (assuming the SoftDevice is enabled do we know the 32 kHz clock is running). Can you upload your modified bootloader code here so that we can look at it?

  • #include <stdint.h>
    #include "boards.h"
    #include "nrf_mbr.h"
    #include "nrf_bootloader.h"
    #include "nrf_bootloader_app_start.h"
    #include "nrf_dfu.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrf_delay.h"
    #include "app_error.h"
    #include "app_error_weak.h"
    #include "nrf_bootloader_info.h"
    
    #include "app_timer.h"
    #include "nrf_power.h"
    #include "nrf_gpio.h"
    
    #define BOOTLOADER_BUTTON                   (BSP_BUTTON_0)      /**< Button for entering DFU mode. */
    
    APP_TIMER_DEF(m_button_press_power_off_timer);
    
    static int m_button_pressed_counter;
    
    
    void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
    {
        error_info_t * error_info = (error_info_t*)info;
    
        if (id == 0x1001)
        {
            NRF_LOG_ERROR("Fatal: Mem access");
        }
        else if (id == NRF_FAULT_ID_SDK_ERROR)
        {
            NRF_LOG_ERROR("Fatal: NRF_FAULT_ID_SDK_ERROR");
        }
        else if (id == NRF_FAULT_ID_SDK_ASSERT)
        {
            NRF_LOG_ERROR("Fatal: NRF_FAULT_ID_SDK_ASSERT");
        }
    
        if (error_info->p_file_name != 0)
        {
            NRF_LOG_ERROR("Fatal: %d %s:%d", error_info->err_code, error_info->p_file_name, error_info->line_num);
            //NRF_LOG_ERROR("Fatal: %d", error_info->err_code);
        }
        else
        {
            NRF_LOG_ERROR("Fatal: %d", error_info->err_code);
        }
    
        NRF_LOG_FINAL_FLUSH();
        NRF_BREAKPOINT_COND;
        // On assert, the system can only recover with a reset.
    #ifndef DEBUG
        NRF_LOG_INFO("Hit weak handler");
        NVIC_SystemReset();
    #else
        app_error_save_and_stop(id, pc, info);
    #endif // DEBUG
    }
    
    
    
    /**@brief Function for initializing the button module. */
    static void buttons_init(void)
    {
        nrf_gpio_cfg_sense_input(BOOTLOADER_BUTTON,
                                 BUTTON_PULL,
                                 NRF_GPIO_PIN_SENSE_LOW);
    }
    
    static void button_pressed_handler(void * p_context)
    {
        NRF_LOG_INFO("button_pressed_handler");
        if (nrf_gpio_pin_read(24) == 0)
        {
            m_button_pressed_counter++;
        }
        else
        {
            m_button_pressed_counter = 0;
        }
    
        if (m_button_pressed_counter == 300)
        {
            // Power off
            nrf_gpio_pin_clear(5);
        }
    }
    
    /**@brief Implementation to use button press to enter bootloader
     */
    bool nrf_dfu_button_enter_check(void)
    {
        for (int timeout = 3000; timeout > 0; timeout--)
        {
            if (nrf_gpio_pin_read(BOOTLOADER_BUTTON) != 0)
                return false;
    
            nrf_delay_ms(1);
        } 
    
        return true;
    }
    
    /** @brief Weak implementation of nrf_dfu_init
     *
     * @note    This function must be overridden in application if
     *          user-specific initialization is needed.
     */
    uint32_t nrf_dfu_init_user(void)
    {
        uint32_t err_code;
    
    
        err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        m_button_pressed_counter = 0;
        err_code = app_timer_create(&m_button_press_power_off_timer,
                                    APP_TIMER_MODE_REPEATED,
                                    button_pressed_handler);
        APP_ERROR_CHECK(err_code);
    
        app_timer_start(m_button_press_power_off_timer, APP_TIMER_TICKS(10), NULL);
    
        return NRF_SUCCESS;
    }
    
    /**@brief Function for application main entry. */
    int main(void)
    {
        uint32_t ret_val;
    
        (void) NRF_LOG_INIT(NULL);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
        NRF_LOG_INFO("Inside main");
    
        ui_init();
        buttons_init();
    
        // MCU_HOLD_POWER
        nrf_gpio_cfg_output(5); // TODO: cleanup pin defines...
        nrf_gpio_pin_set(5);
    
        ret_val = nrf_bootloader_init();
        APP_ERROR_CHECK(ret_val);
    
    
        // Either there was no DFU functionality enabled in this project or the DFU module detected
        // no ongoing DFU operation and found a valid main application.
        // Boot the main application.
        nrf_bootloader_app_start(MAIN_APPLICATION_START_ADDR);
    
        // Should never be reached.
        NRF_LOG_INFO("After main");
    }

    Above is the modified bootloader main. 

    I've gotten it to work by having the exact same code as above in the file nrf_dfu.c instead of in main.c. 

    But I would if possible rather have it in main. Also if you could explain why one need to release the button for the code to executed, that would be much appreciated.

    Thanks!

  • Hi,

    Your implementation seems a bit over complicated and very inefficient, pollint the button like that. I also see that you use a similar mechanism, but with polling in a loop instead of a rapit app_timer, for the nrf_dfu_button_enter_check() instead of just checking if it is pressed during startup. There you could use the same mechanism instead, though it seems a bit counter intuitive to use a 3 second button press both to enter and exit DFU mode.

    Let me explain my suggestion for how you can exit DFU mode by a long button press in a bit more detail:

    1. Initialization:
      1. Configure a GPIO input pin to generate an interrupt on both rising and falling edge.
      2. Configure a 3 second single shot app timer.
    2. On button press (GPIO interrupt):
      1. Start the 3 second app timer
    3. Case 1: GPIO interrupt indicate that button is released before app timer times out
      1. Stop the app timer by calling app_timer_stop().
    4. Case 2: App timer times out. This only happens if button is still pressed, so we should exit DFU mode (start app).
      1. App timer timeout handler calls nrf_power_gpregret_set(0) (SDK 14) or dfu_enter_flags_clear() (SDK 15) and then resets the device by calling NVIC_SystemReset().

    Regarding your specific questions it should not matter where you put the code in this case. We could compare both files and see if you have forgotten anything in one, but I suggest you start over as suggested above. Which code is only executed if you release the button?

  • Thank you for your input. I think that this might have the same problem with the timer handler not working properly. This is probably because the nrf_dfu_init_user() is executed before the nrf_dfu_init() in nrf_bootloader_init() in my code. And in nrf_dfu_init() the clock i initialized which I think is why the handler was never called. 

    Regarding the release-button-code-not-executed part. The code that wasn't executed was the part that power off the device after the condition that the counter had reached its target.

    If you have tested that your above suggestion works you can mark it as the verified answer.

    Thank you again for your input!

  • Yes, the 32 kHz clock must be running for the app timer to work. Assuming you use BLE in the bootloader that will happen anyway though, and in practice it does not actually matter much if you configure the app timer before the clock is started, as the SoftDevice will anyway start soon when entering DFU mode (and if not, the bootloader should start the application so this will not be an issue). You can also start the 32 kHz clock explicitly before enabling the bootloader if you want.

Reply
  • Yes, the 32 kHz clock must be running for the app timer to work. Assuming you use BLE in the bootloader that will happen anyway though, and in practice it does not actually matter much if you configure the app timer before the clock is started, as the SoftDevice will anyway start soon when entering DFU mode (and if not, the bootloader should start the application so this will not be an issue). You can also start the 32 kHz clock explicitly before enabling the bootloader if you want.

Children
No Data
Related