LFCLK rushing at startup?

Hi!

I am trying to figure out the app_timer_library but am running into some (to me) strange behaviour. For the first seconds it appears as if the LFCLK is running much faster than expected, before it starts to run as it should. I have the following snippet which when measuring pin 27 with oscilloscope produces a very rapid signal for approximately 10 seconds before going into 10Hz. If I print the time it behaves analogously. 

In the sdk_config file, I have defined APP_TIMER_CONFIG_RTC_FREQUENCY 0, which I believe (although in my opinion inconcurrent with documentation?) would mean that the RTC would count up by 1000 Hz. 

Could someone help me in the right direction of what I am doing wrong and how I should do to create a signal of 10 Hz already from start?

It is nrf52840DK.

Thank you!

#include "nrf_drv_spi.h"
#include "app_util_platform.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "boards.h"
#include "app_error.h"
#include <string.h>
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "app_timer.h"
#include "nrf_drv_clock.h"
#include "nrfx_clock.h"


#define TEST_PIN			NRF_GPIO_PIN_MAP(0,27)
//TIMER
uint32_t millis(void)
{
  return(app_timer_cnt_get()  );
}
static void lfclk_request(void)
{
    ret_code_t err_code = nrf_drv_clock_init();
    APP_ERROR_CHECK(err_code);
    nrf_drv_clock_lfclk_request(NULL);
}

int main(void)
{
    bsp_board_init(BSP_INIT_LEDS);
    APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
    NRF_LOG_DEFAULT_BACKENDS_INIT();


	nrf_gpio_cfg_output(TEST_PIN);
	
	uint32_t currentMillis; 
	uint32_t previousMillis;
	lfclk_request();
	app_timer_init();
		
	uint32_t freq;
		

    while (1)
    {		
	    //TIMER
		previousMillis=currentMillis;
		currentMillis=millis();
			
	    freq=freq+currentMillis-previousMillis;
		if (freq>(100)){
			freq=0;
			nrf_gpio_pin_toggle(TEST_PIN);
		} //10 Hz
				
		//TIMER
		//NRF_LOG_INFO("task time (ticks: %d", currentMillis-previousMillis);
		//NRF_LOG_INFO("ms: %d", freq);
        //NRF_LOG_FLUSH();
        //nrf_delay_ms(10);
				
    }
}

Parents
  • Hi,

    In the sdk_config file, I have defined APP_TIMER_CONFIG_RTC_FREQUENCY 0, which I believe (although in my opinion inconcurrent with documentation?) would mean that the RTC would count up by 1000 Hz. 

     APP_TIMER_CONFIG_RTC_FREQUENCY defines the RTC prescaler setting where 0 is the "fastest" setting and will make the RTC run at 32KHz. 

    Could someone help me in the right direction of what I am doing wrong and how I should do to create a signal of 10 Hz already from start?

     I would consider registering a timer callback instead, see snippet below. It should give a more accurate output signal as the pin will be toggled from an interrupt. I'm actually not sure I understand how the current implementation is giving you a 10Hz output signal. 

    #include "app_error.h"
    #include "app_timer.h"
    #include "app_util_platform.h"
    #include "boards.h"
    #include "nrf_delay.h"
    #include "nrf_drv_clock.h"
    #include "nrf_drv_spi.h"
    #include "nrf_gpio.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrfx_clock.h"
    #include <string.h>
    
    
    APP_TIMER_DEF(m_timer_id);
    
    
    #define TEST_PIN NRF_GPIO_PIN_MAP(0, 27)
    // TIMER
    uint32_t millis(void) { return (app_timer_cnt_get()); }
    static void lfclk_request(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    static void output_signal_update(void * p_context) 
    { 
        nrf_gpio_pin_toggle(TEST_PIN); 
    }
    
    
    int main(void)
    {
        uint32_t err_code;
    
        bsp_board_init(BSP_INIT_LEDS);
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    
        nrf_gpio_cfg_output(TEST_PIN);
    
        lfclk_request();
        app_timer_init();
        err_code = app_timer_create(&m_timer_id, APP_TIMER_MODE_REPEATED, output_signal_update);
        APP_ERROR_CHECK(err_code);
    
        err_code = app_timer_start(m_timer_id, APP_TIMER_TICKS(50), NULL); // Call output_signal_update() every 50 ms to get 10 Hz.
        APP_ERROR_CHECK(err_code);
    
        uint32_t freq;
    
    
        while (1)
        {
            __WFI(); //sleep - wake on timer interrupt
        }
    }

  • Hello and thank you for your reply! 

    Yes I implemented it like that for generating clocksignals (based on the timer example). However, it was mainly the usage of app_timer_cnt_get() I was interested in. I think I will need it to time some tasks. 

    I modified based on the prescaler value and have tried to clarify a bit further, please see snippet below. It gives a frequency of the desired 10 Hz for approximately 10 seconds and then goes into 0,3 Hz (T is probably 3.2768 seconds). Could this be overflow related?

    So to clarify, I guess then the clock is not rushing at startup but the counter is rather slowing down after a while. Hence my previous confusion with the prescaler value.

    Thank you!

    #define TEST_PIN			NRF_GPIO_PIN_MAP(0,27)
    //TIMER
    uint32_t millis(void) // Convert RTC ticks to milli seconds
    {
      return(app_timer_cnt_get() /32.768 );
    }
    static void lfclk_request(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    int main(void)
    {
        bsp_board_init(BSP_INIT_LEDS);
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    	nrf_gpio_cfg_output(TEST_PIN);
        uint32_t currentMillis; 
    	uint32_t previousMillis;
    	uint32_t tasktime;
    	lfclk_request();
    	app_timer_init();	
    	uint32_t freq=0;
        while (1)
        {		
    			  //TIMER
    	          previousMillis=currentMillis; // RTC reading from previous loop
    			  currentMillis=millis(); //Current RTC reading
    			  tasktime = currentMillis-previousMillis; //Last loops time consumption
    			
    			  freq=freq+tasktime; //add last loops time to total time since pin toggle
    	
    			  if (freq>(100)){ // If more than 100 ms has passed
    					freq=0; //Reset counter
    					nrf_gpio_pin_toggle(TEST_PIN); //Toggle test pin
    			  } //10 Hz (or rather 5Hz, pin toggle approximately every 100 ms)
    
        }
    }
    

  • Hello,

    Thanks for the additional information. It's starting to make more sense to me now though I'm not sure what makes it slow down after 10 seconds. Which SDK version is this based on? 

    Timer overflow should be accounted for, but the counter register is 24-bit so it does take a while for it to happen. I don't think that's the issue here. Also, maybe it would make sense to increase the prescaler. I see you are dividing the count by 32 now. APP_TIMER_CONFIG_RTC_FREQUENCY == 31 gives you 1024Hz

  • Hello again!

    This is based on 16.0.0.

    I tried modify the RTC frequency to 1024, but the behaviour is the same. The RTC frequency works as expected for approximately 10 seconds (it is exactly 8 seconds independent of prescaler value), and then moves 32 times slower.

    Thanks!

    Edit: I can also add that your example above behaves the same, it works for only 8 seconds: 

    #include "app_error.h"
    #include "app_timer.h"
    #include "app_util_platform.h"
    #include "boards.h"
    #include "nrf_delay.h"
    #include "nrf_drv_clock.h"
    #include "nrf_drv_spi.h"
    #include "nrf_gpio.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "nrfx_clock.h"
    #include <string.h>
    
    
    APP_TIMER_DEF(m_timer_id);
    
    
    #define TEST_PIN NRF_GPIO_PIN_MAP(0, 27)
    // TIMER
    uint32_t millis(void) { return (app_timer_cnt_get()); }
    static void lfclk_request(void)
    {
        ret_code_t err_code = nrf_drv_clock_init();
        APP_ERROR_CHECK(err_code);
        nrf_drv_clock_lfclk_request(NULL);
    }
    
    static void output_signal_update(void * p_context) 
    { 
        nrf_gpio_pin_toggle(TEST_PIN); 
    }
    
    
    int main(void)
    {
        uint32_t err_code;
    
        bsp_board_init(BSP_INIT_LEDS);
        APP_ERROR_CHECK(NRF_LOG_INIT(NULL));
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    
    
        nrf_gpio_cfg_output(TEST_PIN);
    
        lfclk_request();
        app_timer_init();
        err_code = app_timer_create(&m_timer_id, APP_TIMER_MODE_REPEATED, output_signal_update);
        APP_ERROR_CHECK(err_code);
    
        err_code = app_timer_start(m_timer_id, APP_TIMER_TICKS(50), NULL); // Call output_signal_update() every 50 ms to get 10 Hz.
        APP_ERROR_CHECK(err_code);
    
        uint32_t freq;
    
    
        while (1)
        {
            __WFI(); //sleep - wake on timer interrupt
        }
    }

    But the timer example below works for ever

    /**
     * Copyright (c) 2014 - 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.
     *
     */
    /** @file
     * @defgroup nrf_dev_timer_example_main main.c
     * @{
     * @ingroup nrf_dev_timer_example
     * @brief Timer Example Application main file.
     *
     * This file contains the source code for a sample application using Timer0.
     *
     */
    
    #include <stdbool.h>
    #include <stdint.h>
    #include "nrf.h"
    #include "nrf_drv_timer.h"
    #include "bsp.h"
    #include "app_error.h"
    
    #define TEST_PIN			NRF_GPIO_PIN_MAP(0,27) //********************************************
    
    const nrf_drv_timer_t TIMER_LED = NRF_DRV_TIMER_INSTANCE(0);
    
    /**
     * @brief Handler for timer events.
     */
    void timer_led_event_handler(nrf_timer_event_t event_type, void* p_context)
    {
        static uint32_t i;
        uint32_t led_to_invert = ((i++) % LEDS_NUMBER);
    
        switch (event_type)
        {
            case NRF_TIMER_EVENT_COMPARE0:
    					nrf_gpio_pin_toggle(TEST_PIN);
               break;
    
            default:
                //Do nothing.
                break;
        }
    }
    
    
    /**
     * @brief Function for main application entry.
     */
    int main(void)
    {
        uint32_t time_ms = 100; //Time(in miliseconds) between consecutive compare events.
        uint32_t time_ticks;
        uint32_t err_code = NRF_SUCCESS;
    
        //Configure all leds on board.
        bsp_board_init(BSP_INIT_LEDS);
    
    		nrf_gpio_cfg_output(TEST_PIN); //********************************************
    
    	
        //Configure TIMER_LED for generating simple light effect - leds on board will invert his state one after the other.
        nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
        err_code = nrf_drv_timer_init(&TIMER_LED, &timer_cfg, timer_led_event_handler);
        APP_ERROR_CHECK(err_code);
    
        time_ticks = nrf_drv_timer_ms_to_ticks(&TIMER_LED, time_ms);
    
        nrf_drv_timer_extended_compare(
             &TIMER_LED, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
    
        nrf_drv_timer_enable(&TIMER_LED);
    
        while (1)
        {
            __WFI();
        }
    }
    
    /** @} */

    Both are using LFCLK right?

  • Hello,

    This is strange, I can't thank think of anything that can explain this behavior. I tested both your code and mine and verified the output with a logic analyzer. Could you send me the hex file from your build so I can try it here? I want to be sure we are testing the same.  

    The TIMER peripheral uses the HF clock

Reply
  • Hello,

    This is strange, I can't thank think of anything that can explain this behavior. I tested both your code and mine and verified the output with a logic analyzer. Could you send me the hex file from your build so I can try it here? I want to be sure we are testing the same.  

    The TIMER peripheral uses the HF clock

Children
  • Hello again and sorry for the late reply!

    I realize I of course should have tried this first, but it works if I rip my application apart. I guess it will be hard to replicate then.

    There is a TWI device on the chrystal pins, I guess that is the problem. I will find another way of measuring, leaving app_timer. Or is there some way of using the internal rc for timing, similar to BLE?

    What really confuses me is that the clock is working on a dk where the chrystal is removed... 

    Thanks for your help and sorry for the inconvenience! 

  • Hello,

    raspet said:
    I realize I of course should have tried this first, but it works if I rip my application apart. I guess it will be hard to replicate then.

     That explains the discrepancy, thanks. I think this means that the other part of your code must be blocking the readout/output toggle. Do you know if the app starts a particular task or something after 8 seconds that may help explain the timing change?

    raspet said:
    There is a TWI device on the chrystal pins, I guess that is the problem. I will find another way of measuring, leaving app_timer. Or is there some way of using the internal rc for timing, similar to BLE?

     The internal LF RC oscillator is used by default, and the app timer would not have worked if you didn't have an active clock source.

Related