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

BLE DFU WDT SDK15.2 bug?

Hi all

nrf52840 Development board,sdk is nRF5_SDK_15.2.0_9412b96,PC is windows 10 .My testing master device is cell phone(Huawei HTC and so on,android system),and using "nrf toolbox" APP.

I added watchdog  function in my application,and then when I test the ble dfu ,I found that  the watchdog feeding in the bootloader did not happen during app FW activation.

nrf_bootloader_wdt_feed_timer_start(reduced_timeout_ticks, wdt_feed_timer_handler);

wdt_feed_timer_handler is not called any time.Is this an SDK bug?

Thanks,

mijiale

The log as follosw:

  • Thank you for your reply.

    I'm sure that  nrf_bootloader_wdt_init() called in image_copy() is returning due to it already being initialized.

    I will paste my code to explain the process.

    1. I add a timer to see if the timer handler will work

    2 .Modify the nrf_bootloader_wdt_init() function,for convenience to call it in different position.

    3.In my bootloader,there is screen,so I can see when the character string is displayed.

    4.The display function is MID_DISPALY_STRING().

    Part of the code is as follows:

    /**
     * Copyright (c) 2018, 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"
    //#include "nrf_dfu_req_handler.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;
    
    
    //dfu_timer_t   m_timers[2] = {{.cc_channel = 0}, {.cc_channel = 1}}; //!< The timers used by this module.
    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 * mp_display    = &m_timers[2];                         //!< Direct pointer to the wdt feed timer, for convenience and readability.
    
    
    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   3//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(3))
    #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)
        {
            //MID_DISPALY_STRING("started...");
    			  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);
    				nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_COMPARE_2);
            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;
    	  //test
    	  //timer_stop(p_timer);
        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++)
        {
            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]);
            }
        }
    }
    
    #if 1
    void nrf_bootloader_activity_display_timer_restart(uint32_t                              timeout_ticks,
                                                     nrf_bootloader_dfu_timeout_callback_t callback)
    {
    		mp_display->repeated_timeout = timeout_ticks;
        timer_start(mp_display, timeout_ticks, callback);
    }
    #endif
    
    
    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);
    }
    
    
    uint32_t nrf_bootloader_dfu_timer_counter_get(void)
    {
        return nrf_rtc_counter_get(RTC_STRUCT) + (m_counter_loops << 24);
    }
    

     

    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;
        }
    
        // 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:
    					  //MID_DISPALY_STRING("DFU Successfully");
                bootloader_reset();
                NRF_LOG_ERROR("Should never come here: After bootloader_reset()");
                return NRF_ERROR_INTERNAL; // Should not reach this.
    
            case ACTIVATION_ERROR:
            default:
                return NRF_ERROR_INTERNAL;
        }
    
        if (dfu_enter)
        {
            nrf_bootloader_wdt_init(1);
    
            scheduler_init();
    
            // Clear all DFU stop flags.
            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("Should never come here: After looping forever.");
        }
        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("Should never come here: After nrf_bootloader_app_start()");
        }
    
        // Should not be reached.
        return NRF_ERROR_INTERNAL;
    }

    static uint32_t image_copy(uint32_t dst_addr,
                               uint32_t src_addr,
                               uint32_t size,
                               uint32_t progress_update_step)
    {
        if (src_addr == dst_addr)
        {
            NRF_LOG_DEBUG("No copy needed src_addr: 0x%x, dst_addr: 0x%x", src_addr, dst_addr);
            return NRF_SUCCESS;
        }
    
        ASSERT(src_addr >= dst_addr);
        ASSERT(progress_update_step > 0);
        ASSERT((dst_addr % CODE_PAGE_SIZE) == 0);
    
        uint32_t max_safe_progress_upd_step = (src_addr - dst_addr)/CODE_PAGE_SIZE;
        ASSERT(max_safe_progress_upd_step > 0);
    
        uint32_t ret_val = NRF_SUCCESS;
        uint32_t pages_left = CEIL_DIV(size, CODE_PAGE_SIZE);
    
        //Firmware copying is time consuming operation thus watchdog handling is started
        nrf_bootloader_wdt_init(2);
    
        progress_update_step = MIN(progress_update_step, max_safe_progress_upd_step);
    
        while (size > 0)
        {
            uint32_t pages;
            uint32_t bytes;
            if (pages_left <= progress_update_step)
            {
                pages = pages_left;
                bytes = size;
            }
            else
            {
                pages = progress_update_step;
                bytes = progress_update_step * CODE_PAGE_SIZE;
            }
            // Erase the target pages
            ret_val = nrf_dfu_flash_erase(dst_addr, pages, NULL);
            if (ret_val != NRF_SUCCESS)
            {
                return ret_val;
            }
    
            // Flash one page
            NRF_LOG_DEBUG("Copying 0x%x to 0x%x, size: 0x%x", src_addr, dst_addr, bytes);
            ret_val = nrf_dfu_flash_store(dst_addr,
                                          (uint32_t *)src_addr,
                                          ALIGN_NUM(sizeof(uint32_t), bytes),
                                          NULL);
            if (ret_val != NRF_SUCCESS)
            {
                return ret_val;
            }
    
            pages_left  -= pages;
            size        -= bytes;
            dst_addr    += bytes;
            src_addr    += bytes;
            s_dfu_settings.write_offset += bytes;
    
            //store progress in flash on every successful chunk write
            ret_val = nrf_dfu_settings_write_and_backup(NULL);
            if (ret_val != NRF_SUCCESS)
            {
                NRF_LOG_ERROR("Failed to write image copying progress to settings page.");
                return ret_val;
            }
        }
    
        return ret_val;
    }

    void nrf_bootloader_wdt_init(uint8_t timer)
    {
        static bool initialized = false;
    
        if (initialized)
        {
            return;
        }
    
        if (nrf_wdt_started())
        {
            //MID_DISPALY_STRING("started...");  
    			  uint32_t wdt_ticks = nrf_wdt_reload_value_get();
    
            NRF_LOG_INFO("WDT enabled CRV:%d ticks", wdt_ticks);
            #if 1
            //wdt_ticks must be reduced to feed the watchdog before the timeout.
            uint32_t reduced_timeout_ticks = MAX((int32_t)wdt_ticks - MAX_FLASH_OP_TIME_TICKS,
                                                 NRF_BOOTLOADER_MIN_TIMEOUT_TICKS);
    			  #endif
    			  //uint32_t reduced_timeout_ticks = NRF_BOOTLOADER_MIN_TIMEOUT_TICKS;
    
            /* initial watchdog feed */
            wdt_feed();
    			
    			
           
            NRF_LOG_INFO("Starting a timer (%d ticks) for feeding watchdog.", reduced_timeout_ticks);
    				if(2 == timer)
    				{
    						nrf_bootloader_activity_display_timer_restart(reduced_timeout_ticks, wdt_feed_timer2_handler);
    					  //MID_DISPALY_STRING("timer2...");
    				}
    				else
    				{ 
    				    nrf_bootloader_wdt_feed_timer_start(reduced_timeout_ticks, wdt_feed_timer_handler);
    				}
    			
    			  //nrf_bootloader_wdt_feed_timer_start(reduced_timeout_ticks, wdt_feed_timer_handler);
    			  MID_DISPALY_STRING("started...");
    
            NVIC_EnableIRQ(WDT_IRQn);
    
        }
        else
        {
            NRF_LOG_INFO("WDT is not enabled");
    			  //MID_DISPALY_STRING("not enabled");
    			  MID_DISPALY_STRING("Checking...");
        }
    
        initialized = true;
    }

    static void wdt_feed_timer_handler(void)
    {
        NRF_LOG_INFO("Internal feed");
        wdt_feed();
    	  MID_DISPALY_STRING("wdt_feed1");
    }
    
    static void wdt_feed_timer2_handler(void)
    {
        NRF_LOG_INFO("Internal feed");
        wdt_feed();
    	  MID_DISPALY_STRING("wdt_feed2");
    }

    So When I see"started..." in my screen explain that nrf_bootloader_wdt_init(),initialized = false and nrf_wdt_started() return true.

    And When I see "wdt_feed1" in my screen explain that nrf_bootloader_wdt_feed_timer_start(reduced_timeout_ticks, wdt_feed_timer_handler) is working.

    And I can't see "wdt_feed2" in my screen explain that

    nrf_bootloader_activity_display_timer_restart(reduced_timeout_ticks, wdt_feed_timer2_handler)   is not working.

    I also switch the positon about nrf_bootloader_wdt_init(1) and nrf_bootloader_wdt_init(2),

    I can see "started..." "wdt_feed2" but can't see  "wdt_feed1" in my screen.

    All the things explain that the watchdog feeding in the bootloader did not happen during app FW activation.

  • The "started..." will display two times, first is calling nrf_bootloader_wdt_init(1),second is calling nrf_bootloader_wdt_init(2) which is called in image_copy() function 

  • This phenomenon is not easy to see, because it occurs in the process of firmware activation. There will be a watchdog-induced reset and the firmware will continue to be activated after the restart. The upgrade will still succeed, but the reset caused by the watchdog is equivalent to the hardware reset. The watchdog is closed after restarting, so the firmware activation is actually done without the watchdog. This can lead to a period of time during an upgrade when the watchdog is closed, and an additional watchdog-induced reset process.

  • Which WDT interval have you configured in the application?

    mijiale said:
    The "started..." will display two times, first is calling nrf_bootloader_wdt_init(1),second is calling nrf_bootloader_wdt_init(2) which is called in image_copy() function 

    When entering the bootloader from the application nrf_bootloader_wdt_init() will be called in nrf_bootloader_init() and the WDT feed timer will be started if the WDT was active in the application. Then the DFU transfer will be performed and after the the firmware image has been transferred to the nRF52 device, the device will reset before activating the new firmware image.

    The WDT will not be reset during this Soft reset( NVIC_SystemReset())

    Upon entering main() after this reset, nrf_bootloader_init() will be called and nrf_bootloader_fw_activate() will be called, which in turn calls app_activate(), which calls image_copy(). So after the Soft reset you should only see that nrf_bootloader_wdt_init() is called once, i.e. in image_copy()

  • Hi

    The interval is 2 sec.There is any problem for my wdt.

    I'm sure this is a sdk bug for the problem.

    problem code as follow

    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);
    				//nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_COMPARE_2);
            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;
        }
    }
    at present,Code works properly when I get rid of judgment

    if (!nrf_clock_lf_is_running())

    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);
    				//nrf_rtc_event_clear(RTC_STRUCT, NRF_RTC_EVENT_COMPARE_2);
            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;
        }
    }

Related