This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

when set NRF_LOG_DEFAULT_LEVEL as 1 in sdk_config.h, OTA failed

I am using nRF52840 development software based on ble_app_uart_c, and I found that when I set NRF_LOG_DEFAULT_LEVEL as 1, if I download the software by Segger, the device worked well.  If I updated the software by OTA, the devcie cannot start.  If I set NRF_LOG_DEFAULT_LEVEL  as 3, updating the software by OTA, the device worked well.

Parents
  • When NRF_LOG_DEFAULT_LEVEL set as 1, the compiled app uploaded to the device by OTA, the device didn't get start. I don't know how to catch the logs.

  • Hi,

    The example should print out the logs over RTT. Use J-link RTT viewer to view the logs.

    regards

    Jared 

  • Hi Cynthia, 

    I'm taking over the case from Jared as it's more of a DFU case  . 

    Could you give me more information about your SDK version ? Also the board/IC version. Are you testing on a DK ? 

    If you try doing DFU with an unmodified bootloader do you see the same issue ? 

    Also could you you explain a little bit about the log, I can find that there are quite a lot of WDT feed before the actual activity in main. Have you made sure the WDT is fed when the bootloader started ? 

    From what I can see in the log, the assert happened inside app_activate()  , most likely inside image_copy() function. 

    I would suggest to use the _debug version of the bootloader and put a breakpoint inside app_activate() and step through the code. 

  • Hi Hung,

    Thanks for your reply.

    1. I'm developing bootloader on  pca10056_s140_ble_debug. I'm using SDK16 + nRF52840. PCA10056+s140. I'm testing  on our borad.

    2. I did DFU with SDK bootloader only changing NRF_DFU_BLE_ADV_NAME, I didn't see the same issue.

    3. There is a extern WDT on our board. Initialy I want to add a app timer to feed the extern WDT and control a green LED and a blue LED to flash in cycle, but it failed. So I open inner wdt , and added code in wdt_feed()  function of nrf_bootloader_wdt.c to feed  extern wdt and control the two LED flash.

    Could you give me any advice about how to add an app timer ?

    Thanks in advance.

  • Hi Cynthia, 

    At point 2, you meant if you test with unmodified bootloader example you don't see the issue and you can complete the DFU update without any issue ? 

    Which modification did you add to the bootloader besides the external WDT handling ? If you remove the external WDT handling would it work ? 

    Regarding the app timer, the bootloader has its own timer library called nrf_bootloader_dfu_timers.c You can find how the mp_wdt_feed and mp_inactivity is defined in the file. You can add your own timer in by modifying m_timers[]. Note that each timer occupy one CC register and with nRF52840 you have max 4 CC registers.

  • OriginBootloader_LOG_LEVE_1-J-Link RTT Viewer_logs.logoriginBootloader-loglevel-1-Log 2022-03-22 14_45_30.txtUSERBootloader_LOG_LEVE_1-J-Link RTT Viewer_logs.loguserbootloader-loglevel-1-Log 2022-03-22 14_48_57.txt

    Hi Hung,

    I added timer in nrf_bootloader_dfu_timers.c, after DFU, the device can't start.  The failed log file show in USERBootloader- . This test's done with my bootloader.  The successful log file show in orginBootloader-. This test's done with sdk bootloader only ADV name is changed.

  • Hi Cynthia, 
    Could you provide us the userbootloader code that we can test here ? 

    Could you please clarify: 

    - If you only change the advertising name (nothing else) the original bootloader works ? 

    - If you add a timer in nrf_bootloader_dfu_timers the bootloader stopped working ? 

    If it's the case please provide the code you added to the nrf_bootloader_dfu_timers ()

    Have you tried to step in the code in image_copy() and checked what's wrong ? 

Reply
  • Hi Cynthia, 
    Could you provide us the userbootloader code that we can test here ? 

    Could you please clarify: 

    - If you only change the advertising name (nothing else) the original bootloader works ? 

    - If you add a timer in nrf_bootloader_dfu_timers the bootloader stopped working ? 

    If it's the case please provide the code you added to the nrf_bootloader_dfu_timers ()

    Have you tried to step in the code in image_copy() and checked what's wrong ? 

Children
  • /**
     * Copyright (c) 2016 - 2019, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "nrf_bootloader.h"
    
    #include "compiler_abstraction.h"
    #include "nrf.h"
    #include "boards.h"
    #include "sdk_config.h"
    #include "nrf_power.h"
    #include "nrf_delay.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_dfu.h"
    #include "nrf_error.h"
    #include "nrf_dfu_settings.h"
    #include "nrf_dfu_utils.h"
    #include "nrf_bootloader_wdt.h"
    #include "nrf_bootloader_info.h"
    #include "nrf_bootloader_app_start.h"
    #include "nrf_bootloader_fw_activation.h"
    #include "nrf_bootloader_dfu_timers.h"
    #include "app_scheduler.h"
    #include "nrf_dfu_validation.h"
    
    
    #define EXTERN_WATCHDOG_PIN   NRF_GPIO_PIN_MAP(0,29)
    #define BLUE_LED    NRF_GPIO_PIN_MAP(0,9)      //LED_PERSON
    #define GREEN_LED   NRF_GPIO_PIN_MAP(0,10)
    #define USER_TIMER_TIMEOUT_MS   100  //1200
    extern void nrf_user_timer_start(uint32_t timeout_ticks, nrf_bootloader_dfu_timeout_callback_t callback);
    
    void led_extWatchDog_pin_init()
    {
        ret_code_t err_code;
        // Extern WatchDog pin init
    
        nrf_gpio_cfg_output(EXTERN_WATCHDOG_PIN); 
        nrf_gpio_pin_write(EXTERN_WATCHDOG_PIN,0);
    
        // Blue led init
        nrf_gpio_cfg_output(BLUE_LED); 
        nrf_gpio_pin_write(BLUE_LED,0);
     
        // Green led init
        nrf_gpio_cfg_output(GREEN_LED); 
        nrf_gpio_pin_write(GREEN_LED,1);
        
    }
    
    static void user_timer_handler(void)
    {
        static uint8_t counter =0;
    
        nrf_gpio_pin_toggle(EXTERN_WATCHDOG_PIN);
    
        counter++;
        if(counter >= 10)
        {
            nrf_gpio_pin_toggle(BLUE_LED);  
        	nrf_gpio_pin_toggle(GREEN_LED);  
            counter = 0;
        }
    }
    
    
    static nrf_dfu_observer_t m_user_observer; //<! Observer callback set by the user.
    static volatile bool m_flash_write_done;
    
    #define SCHED_QUEUE_SIZE      32          /**< Maximum number of events in the scheduler queue. */
    #define SCHED_EVENT_DATA_SIZE NRF_DFU_SCHED_EVENT_DATA_SIZE /**< Maximum app_scheduler event size. */
    
    #if !(defined(NRF_BL_DFU_ENTER_METHOD_BUTTON)    && \
          defined(NRF_BL_DFU_ENTER_METHOD_PINRESET)  && \
          defined(NRF_BL_DFU_ENTER_METHOD_GPREGRET)  && \
          defined(NRF_BL_DFU_ENTER_METHOD_BUTTONLESS)&& \
          defined(NRF_BL_RESET_DELAY_MS)             && \
          defined(NRF_BL_DEBUG_PORT_DISABLE))
        #error Configuration file is missing flags. Update sdk_config.h.
    #endif
    
    STATIC_ASSERT((NRF_BL_DFU_INACTIVITY_TIMEOUT_MS >= 100) || (NRF_BL_DFU_INACTIVITY_TIMEOUT_MS == 0),
                 "NRF_BL_DFU_INACTIVITY_TIMEOUT_MS must be 100 ms or more, or 0 to indicate that it is disabled.");
    
    #if defined(NRF_LOG_BACKEND_FLASH_START_PAGE)
    STATIC_ASSERT(NRF_LOG_BACKEND_FLASH_START_PAGE != 0,
        "If nrf_log flash backend is used it cannot use space after code because it would collide with settings page.");
    #endif
    
    /**@brief Weak implemenation of nrf_dfu_init
     *
     * @note   This function will be overridden if nrf_dfu.c is
     *         compiled and linked with the project
     */
     #if (__LINT__ != 1)
    __WEAK uint32_t nrf_dfu_init(nrf_dfu_observer_t observer)
    {
        NRF_LOG_DEBUG("in weak nrf_dfu_init");
        return NRF_SUCCESS;
    }
    #endif
    
    
    /**@brief Weak implementation of nrf_dfu_init
     *
     * @note    This function must be overridden in application if
     *          user-specific initialization is needed.
     */
    __WEAK uint32_t nrf_dfu_init_user(void)
    {
        NRF_LOG_DEBUG("in weak nrf_dfu_init_user");
        return NRF_SUCCESS;
    }
    
    
    static void flash_write_callback(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        m_flash_write_done = true;
    }
    
    
    static void do_reset(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
    
        NRF_LOG_FINAL_FLUSH();
    
        nrf_delay_ms(NRF_BL_RESET_DELAY_MS);
    
        NVIC_SystemReset();
    }
    
    
    static void bootloader_reset(bool do_backup)
    {
        NRF_LOG_DEBUG("Resetting bootloader.");
    
        if (do_backup)
        {
            m_flash_write_done = false;
            nrf_dfu_settings_backup(do_reset);
        }
        else
        {
            do_reset(NULL);
        }
    }
    
    
    static void inactivity_timeout(void)
    {
        NRF_LOG_INFO("Inactivity timeout.");
        bootloader_reset(true);
    }
    
    
    /**@brief Function for handling DFU events.
     */
    static void dfu_observer(nrf_dfu_evt_type_t evt_type)
    {
        switch (evt_type)
        {
            case NRF_DFU_EVT_DFU_STARTED:
            case NRF_DFU_EVT_OBJECT_RECEIVED:
                nrf_bootloader_dfu_inactivity_timer_restart(
                            NRF_BOOTLOADER_MS_TO_TICKS(NRF_BL_DFU_INACTIVITY_TIMEOUT_MS),
                            inactivity_timeout);
                break;
            case NRF_DFU_EVT_DFU_COMPLETED:
            case NRF_DFU_EVT_DFU_ABORTED:
                bootloader_reset(true);
                break;
            case NRF_DFU_EVT_TRANSPORT_DEACTIVATED:
                // Reset the internal state of the DFU settings to the last stored state.
                nrf_dfu_settings_reinit();
                break;
            default:
                break;
        }
    
        if (m_user_observer)
        {
            m_user_observer(evt_type);
        }
    }
    
    
    /**@brief Function for initializing the event scheduler.
     */
    static void scheduler_init(void)
    {
        APP_SCHED_INIT(SCHED_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
    }
    
    
    /**@brief Suspend the CPU until an interrupt occurs.
     */
    static void wait_for_event(void)
    {
    #if defined(BLE_STACK_SUPPORT_REQD) || defined(ANT_STACK_SUPPORT_REQD)
        (void)sd_app_evt_wait();
    #else
        // Wait for an event.
        __WFE();
        // Clear the internal event register.
        __SEV();
        __WFE();
    #endif
    }
    
    
    /**@brief Continually sleep and process tasks whenever woken.
     */
    static void loop_forever(void)
    {
        while (true)
        {
            //feed the watchdog if enabled.
            nrf_bootloader_wdt_feed();
    
            app_sched_execute();
    
            if (!NRF_LOG_PROCESS())
            {
                wait_for_event();
            }
        }
    }
    
    /**@brief Function for initializing button used to enter DFU mode.
     */
    static void dfu_enter_button_init(void)
    {
        nrf_gpio_cfg_sense_input(NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN,
                                 BUTTON_PULL,
                                 NRF_GPIO_PIN_SENSE_LOW);
    }
    
    
    static bool crc_on_valid_app_required(void)
    {
        bool ret = true;
        if (NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET &&
            (nrf_power_resetreas_get() & NRF_POWER_RESETREAS_OFF_MASK))
        {
            nrf_power_resetreas_clear(NRF_POWER_RESETREAS_OFF_MASK);
            ret = false;
        }
        else if (NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2 &&
                ((nrf_power_gpregret2_get() & BOOTLOADER_DFU_GPREGRET2_MASK) == BOOTLOADER_DFU_GPREGRET2)
                && (nrf_power_gpregret2_get() & BOOTLOADER_DFU_SKIP_CRC_BIT_MASK))
        {
            nrf_power_gpregret2_set(nrf_power_gpregret2_get() & ~BOOTLOADER_DFU_SKIP_CRC);
            ret = false;
        }
        else
        {
        }
    
        return ret;
    }
    
    
    
    static bool boot_validate(boot_validation_t const * p_validation, uint32_t data_addr, uint32_t data_len, bool do_crc)
    {
        if (!do_crc && (p_validation->type == VALIDATE_CRC))
        {
            return true;
        }
        return nrf_dfu_validation_boot_validate(p_validation, data_addr, data_len);
    }
    
    
    /** @brief Function for checking if the main application is valid.
     *
     * @details     This function checks if there is a valid application
     *              located at Bank 0.
     *
     * @param[in]   do_crc Perform CRC check on application. Only CRC checks
                           can be skipped. For other boot validation types,
                           this parameter is ignored.
     *
     * @retval  true  If a valid application has been detected.
     * @retval  false If there is no valid application.
     */
    static bool app_is_valid(bool do_crc)
    {
        if (s_dfu_settings.bank_0.bank_code != NRF_DFU_BANK_VALID_APP)
        {
            NRF_LOG_INFO("Boot validation failed. No valid app to boot.");
            return false;
        }
        else if (NRF_BL_APP_SIGNATURE_CHECK_REQUIRED &&
            (s_dfu_settings.boot_validation_app.type != VALIDATE_ECDSA_P256_SHA256))
        {
            NRF_LOG_WARNING("Boot validation failed. The boot validation of the app must be a signature check.");
            return false;
        }
        else if (SD_PRESENT && !boot_validate(&s_dfu_settings.boot_validation_softdevice, MBR_SIZE, s_dfu_settings.sd_size, do_crc))
        {
            NRF_LOG_WARNING("Boot validation failed. SoftDevice is present but invalid.");
            return false;
        }
        else if (!boot_validate(&s_dfu_settings.boot_validation_app, nrf_dfu_bank0_start_addr(), s_dfu_settings.bank_0.image_size, do_crc))
        {
            NRF_LOG_WARNING("Boot validation failed. App is invalid.");
            return false;
        }
        // The bootloader itself is not checked, since a self-check of this kind gives little to no benefit
        // compared to the cost incurred on each bootup.
    
        NRF_LOG_DEBUG("App is valid");
        return true;
    }
    
    
    
    /**@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));
        }
    }
    
    
    /**@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;
    }
    
    
    #if NRF_BL_DFU_ALLOW_UPDATE_FROM_APP
    static void postvalidate(void)
    {
        NRF_LOG_INFO("Postvalidating update after reset.");
        nrf_dfu_validation_init();
    
        if (nrf_dfu_validation_init_cmd_present())
        {
            uint32_t firmware_start_addr;
            uint32_t firmware_size;
    
            // Execute a previously received init packed. Subsequent executes will have no effect.
            if (nrf_dfu_validation_init_cmd_execute(&firmware_start_addr, &firmware_size) == NRF_DFU_RES_CODE_SUCCESS)
            {
                if (nrf_dfu_validation_prevalidate() == NRF_DFU_RES_CODE_SUCCESS)
                {
                    if (nrf_dfu_validation_activation_prepare(firmware_start_addr, firmware_size) == NRF_DFU_RES_CODE_SUCCESS)
                    {
                        NRF_LOG_INFO("Postvalidation successful.");
                    }
                }
            }
        }
    
        s_dfu_settings.bank_current = NRF_DFU_CURRENT_BANK_0;
        UNUSED_RETURN_VALUE(nrf_dfu_settings_write_and_backup(flash_write_callback));
    }
    #endif
    
    
    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_DEBUG_PORT_DISABLE)
        {
            nrf_bootloader_debug_port_disable();
        }
    
        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();
    #if 1       // Cynthia added
    
        led_extWatchDog_pin_init();
        nrf_user_timer_start(NRF_BOOTLOADER_MS_TO_TICKS(USER_TIMER_TIMEOUT_MS),user_timer_handler);
    
    #endif
            // 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;
    }
    
    /**
     * Copyright (c) 2018 - 2019, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include "nrf_bootloader_dfu_timers.h"
    
    #include <stdint.h>
    #include <stdbool.h>
    #include <nrfx.h>
    #include "nrf_clock.h"
    #include "nrf_rtc.h"
    #include "nrf_delay.h"
    #include "nrf_log.h"
    
    
    #define RTC_PRESCALER       (0)              //!< The value provided to the RTC as the prescaler. 0 corresponds to one tick per clock cycle of the LFCLK (32768 ticks/s).
    #define RTC_WRAP_TICKS      ((1 << 24) - 1)  //!< The largest possible value in the RTC counter register.
    #define MAX_TIMEOUT_TICKS   (RTC_WRAP_TICKS) //!< The longest fire timeout allowed. Longer timeouts are handled by multiple firings.
    
    typedef struct
    {
        nrf_bootloader_dfu_timeout_callback_t callback;         //!< Callback that is called when this timer times out.
        uint32_t                              timeout;          //!< The number of ticks from the next firing until the actual timeout. This value will be different from 0 if the original timeout was longer than MAX_TIMEOUT_TICKS, so multiple firings were needed.
        uint32_t                              repeated_timeout; //!< If different from 0, this timer will be reactivated with this value after timing out.
        uint8_t                               cc_channel;       //!< Which CC register this timer uses.
    } dfu_timer_t;
    
    // Cynthia modified
    dfu_timer_t   m_timers[3] = {{.cc_channel = 0}, {.cc_channel = 1},{.cc_channel = 2}}; //!< The timers used by this module.
    dfu_timer_t * mp_inactivity = &m_timers[0];                         //!< Direct pointer to the inactivity timer, for convenience and readability.
    dfu_timer_t * mp_wdt_feed   = &m_timers[1];                         //!< Direct pointer to the wdt feed timer, for convenience and readability.
    dfu_timer_t * user_led_extWDT_feed = &m_timers[2];        // Direct pointer to the led and ext WDT feed timer.  Cynthia added
    uint32_t      m_counter_loops = 0;                                  //!< The number of times the RTC counter register has overflowed (wrapped around) since the RTC was started.
    
    #if RTC_COUNT > 2
    #define RTC_INSTANCE   2
    #define RTC_STRUCT     NRF_RTC2
    #define RTC_IRQHandler RTC2_IRQHandler
    #define RTC_IRQn       RTC2_IRQn
    #define RTC_CC_COUNT   NRF_RTC_CC_CHANNEL_COUNT(2))
    #elif RTC_COUNT > 1
    #define RTC_INSTANCE   1
    #define RTC_STRUCT     NRF_RTC1
    #define RTC_IRQHandler RTC1_IRQHandler
    #define RTC_IRQn       RTC1_IRQn
    #define RTC_CC_COUNT   NRF_RTC_CC_CHANNEL_COUNT(1))
    #else
    #error Not enough RTC instances.
    #endif
    
    /**@brief Function for initializing the timer if it is not already initialized.
     */
    static void timer_init(void)
    {
        static bool m_timer_initialized;
    
        if (!m_timer_initialized)
        {
            if (!nrf_clock_lf_is_running())
            {
                nrf_clock_task_trigger(NRF_CLOCK_TASK_LFCLKSTART);
            }
    
            nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_TICK);
            nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_COMPARE_0);
            nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_COMPARE_1);
            NRFX_IRQ_PRIORITY_SET(RTC_IRQn, 5);
            NRFX_IRQ_ENABLE(RTC_IRQn);
            nrf_rtc_prescaler_set(RTC_STRUCT, RTC_PRESCALER);
            nrf_rtc_task_trigger(RTC_STRUCT, NRF_RTC_TASK_CLEAR);
            nrf_rtc_task_trigger(RTC_STRUCT, NRF_RTC_TASK_START);
            nrf_rtc_int_enable(RTC_STRUCT, RTC_INTENSET_OVRFLW_Msk);
    
            m_timer_initialized = true;
        }
    }
    
    /**@brief Function for scheduling an RTC compare event.
     *
     * @param[in] cc_channel  Which of the RTC compare registers to use.
     * @param[in] cc_value    The ticks value at which to receive the event.
     */
    static void rtc_update(uint32_t cc_channel, uint32_t cc_value)
    {
        ASSERT(cc_channel < NRF_RTC_CC_CHANNEL_COUNT(RTC_INSTANCE));
    
        nrf_rtc_cc_set(RTC_STRUCT, cc_channel, cc_value);
        nrf_delay_us(31);
        nrf_rtc_event_clear(RTC_STRUCT, RTC_CHANNEL_EVENT_ADDR(cc_channel));
        nrf_rtc_int_enable(RTC_STRUCT, RTC_CHANNEL_INT_MASK(cc_channel));
    }
    
    
    /**@brief Function for activating a timer, so that it will be fired.
     *
     * This can happen multiple times before the actual timeout happens if the timeout is longer than
     * @ref MAX_TIMEOUT_TICKS.
     *
     * @param[in] p_timer        The timer to activate.
     * @param[in] timeout_ticks  The number of ticks until the timeout.
     *
     * @retval true   If the timer was activated.
     * @retval false  If the timer is already active.
     */
    static void timer_activate(dfu_timer_t * p_timer, uint32_t timeout_ticks)
    {
        NRF_LOG_DEBUG("timer_activate (0x%x)", p_timer);
    
        ASSERT(timeout_ticks <= MAX_TIMEOUT_TICKS);
        ASSERT(timeout_ticks >= NRF_BOOTLOADER_MIN_TIMEOUT_TICKS);
        uint32_t next_timeout_ticks = MIN(timeout_ticks, MAX_TIMEOUT_TICKS);
        uint32_t cc_value = RTC_WRAP(next_timeout_ticks + nrf_rtc_counter_get(RTC_STRUCT));
        p_timer->timeout  = timeout_ticks - next_timeout_ticks;
    
        if ((p_timer->timeout > 0) && (p_timer->timeout < NRF_BOOTLOADER_MIN_TIMEOUT_TICKS))
        {
            p_timer->timeout += NRF_BOOTLOADER_MIN_TIMEOUT_TICKS;
            cc_value         -= NRF_BOOTLOADER_MIN_TIMEOUT_TICKS;
        }
    
        rtc_update(p_timer->cc_channel, cc_value);
    }
    
    
    /**@brief Function for deactivating a timer, so that it will not fire.
     *
     * @param[in] p_timer  The timer to deactivate.
     *
     * @retval true   If the timer was deactivated.
     * @retval false  If the timer is already inactive.
     */
    static void timer_stop(dfu_timer_t * p_timer)
    {
        NRF_LOG_DEBUG("timer_stop (0x%x)", p_timer);
        nrf_rtc_int_disable(RTC_STRUCT, RTC_CHANNEL_INT_MASK(p_timer->cc_channel));
    }
    
    
    /**@brief Function for firing a timer.
     *
     * This can happen multiple times before the actual timeout happens if the timeout is longer than
     * @ref MAX_TIMEOUT_TICKS.
     * This function reactivates the timer if the timer is repeating, or if the timer has not yet
     * timed out. It then calls the callback if the timer (repeating or not) has timed out.
     *
     * @param[in] p_timer  The timer to fire.
     */
    static void timer_fire(dfu_timer_t * p_timer)
    {
        NRF_LOG_DEBUG("timer_fire (0x%x)", p_timer);
    
        if (p_timer->timeout != 0)
        {
            // The timer has not yet timed out.
            timer_activate(p_timer, p_timer->timeout);
            return;
        }
    
        if (p_timer->repeated_timeout != 0)
        {
            timer_activate(p_timer, p_timer->repeated_timeout);
        }
    
        if (p_timer->callback != NULL)
        {
            p_timer->callback();
        }
    }
    
    
    /**@brief Function for requesting a timeout.
     *
     * The timer will time out @p timeout_ticks ticks from now. When it times out, @p callback
     * will be called, and if @p p_timer->repeated_timeout is not 0, a new timeout will be scheduled.
     *
     * @param[in] p_timer        The timer to start.
     * @param[in] timeout_ticks  The number of ticks until the timeout.
     * @param[in] callback       The callback to call when the timer times out.
     */
    static void timer_start(dfu_timer_t *                         p_timer,
                            uint32_t                              timeout_ticks,
                            nrf_bootloader_dfu_timeout_callback_t callback)
    {
        timer_init(); // Initialize if needed.
        p_timer->callback = callback;
        timer_activate(p_timer, timeout_ticks);
    }
    
    
    /**@brief Interrupt handler for the RTC (Real Time Clock) used for the DFU timers.
     */
    void RTC_IRQHandler(void)
    {
        if (nrf_rtc_event_pending(RTC_STRUCT, NRF_RTC_EVENT_OVERFLOW))
        {
            m_counter_loops++;
            nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_OVERFLOW);
        }
    
        for (uint32_t channel = 0; channel < 3; channel++)      // Cynthia modified  for (uint32_t channel = 0; channel < 2; channel++)
        {
            if (nrf_rtc_event_pending(RTC_STRUCT, RTC_CHANNEL_EVENT_ADDR(channel)))
            {
                nrf_rtc_event_clear(RTC_STRUCT, RTC_CHANNEL_EVENT_ADDR(channel));
                timer_stop(&m_timers[channel]);
                timer_fire(&m_timers[channel]);
            }
        }
    }
    
    
    void nrf_bootloader_dfu_inactivity_timer_restart(uint32_t                              timeout_ticks,
                                                     nrf_bootloader_dfu_timeout_callback_t callback)
    {
        timer_stop(mp_inactivity);
        if (timeout_ticks != 0)
        {
            timer_start(mp_inactivity, timeout_ticks, callback);
        }
    }
    
    
    void nrf_bootloader_wdt_feed_timer_start(uint32_t                              timeout_ticks,
                                             nrf_bootloader_dfu_timeout_callback_t callback)
    {
        mp_wdt_feed->repeated_timeout = timeout_ticks;
        timer_start(mp_wdt_feed, timeout_ticks, callback);
    }
    
    void nrf_user_timer_start(uint32_t timeout_ticks, nrf_bootloader_dfu_timeout_callback_t callback)
    {
        user_led_extWDT_feed->repeated_timeout = timeout_ticks;
        timer_start(user_led_extWDT_feed, timeout_ticks, callback);
    }
    
    uint32_t nrf_bootloader_dfu_timer_counter_get(void)
    {
        return nrf_rtc_counter_get(RTC_STRUCT) + (m_counter_loops << 24);
    }
    

    Hi Hung,

    - I changed orginal bootloader's advertising name and NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED's value as 0 , It works .

    -  I added app timer based on original bootloader. I  changed nrf_bootloader.c and nrf_bootloader_dfu_timers.c as shown in attachment. 

    If I remove the external watchdog from the board, I dfu the app with user bootloader,It works. It seems also  have to do something with the external watchdog.

  • Hi Cynthia,

    I noticed that the WDT timeout is 100ms (?). This might be too short because the time to erase one page is 85ms. You may want to do a WDT feed right before the image_copy() is called. 

    Could you do a test by only doing LED blinking in your user timer handler ? So there is no WDT related activity and simply adding one extra timer. If this also crash, I can try to test here to reproduce and figure out what's wrong. 

    Have you tried to use the internal WDT ? Is the external WDT an obligation for your application ? 

  • Hi Cynthia, 

    Please let us know if you still have issue with the external WDT. 

Related