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

Occasional long cycles (jitter) on looping timer

I use the following code to enable a timer that clears itself to loop continuously via the compare0-clear shortcut. (I then use other compares, PPI, and GPIOTE to output pulses.) The timing is very solid except occasionally (randomly about once per second on average) the cycle is long by consistently about 200us (about 10% of a cycle). What could cause this?

#define IR_SAMPLE_PERIOD_nS         2008032
const nrf_drv_timer_t IR_DRV_AND_SAMPLE_TIMER = NRF_DRV_TIMER_INSTANCE(4);
...
    nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
    APP_ERROR_CHECK(nrf_drv_timer_init(&IR_DRV_AND_SAMPLE_TIMER, &timer_cfg, timer_event_handler));

    time_ticks = timer_ns_to_ticks(&IR_DRV_AND_SAMPLE_TIMER, IR_SAMPLE_PERIOD_nS);

    nrf_drv_timer_extended_compare(
         &IR_DRV_AND_SAMPLE_TIMER, NRF_TIMER_CC_CHANNEL0, time_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

  • This is weird. Not sure what is going on here.

    (I then use other compares, PPI, and GPIOTE to output pulses.)

    Can you show me how this is initialized as well? I see that you enable the interrupt on CC0. Are you using this interrupt, or just CC0 -> PPI -> GPIOTE ?

    Any other code that is running at the same time?

  • timer_event_handler() is an empty function because I haven't figure out how to disable the timer interrupt. Would it be worth figuring out how to disable the interrupt?

    Code for initializing the rest of the hardware:

    #define IR_TIMER_CH_ON          NRF_TIMER_CC_CHANNEL1  // Compare channel to turn on the emitter
    #define IR_TIMER_CH_OFF         NRF_TIMER_CC_CHANNEL2  // Compare channel to turn off the emitter
    #define IR_ON_POINT_nS              1500000 // 
    #define IR_OFF_POINT_nS             2000000 //
    
        // Compare channels
        time_ticks = timer_ns_to_ticks(&IR_DRV_AND_SAMPLE_TIMER, IR_ON_POINT_nS);
        nrf_timer_cc_write(IR_DRV_AND_SAMPLE_TIMER.p_reg, IR_TIMER_CH_ON, time_ticks);
        time_ticks = timer_ns_to_ticks(&IR_DRV_AND_SAMPLE_TIMER, IR_OFF_POINT_nS);
        nrf_timer_cc_write(IR_DRV_AND_SAMPLE_TIMER.p_reg, IR_TIMER_CH_OFF, time_ticks);
    
        // PPI
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
    
        // Set up GPIO and GPIOTE for emitter enable
        nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);
        if (!nrfx_gpiote_is_init()) {
            err_code = nrf_drv_gpiote_init();
            APP_ERROR_CHECK(err_code);
        }
        err_code = nrf_drv_gpiote_out_init(IR_LED_PIN, &config);
        APP_ERROR_CHECK(err_code);
        
        // Assign a timer compare to turn IR emitter ON
        nrf_ppi_channel_t     m_ppi_ch_ir_emitter;
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_ch_ir_emitter);
        APP_ERROR_CHECK(err_code);
    
        uint32_t event_addr_emitter = nrfx_timer_compare_event_address_get(&IR_DRV_AND_SAMPLE_TIMER,
                                                                           IR_TIMER_CH_ON);
        uint32_t gpiote_task_addr = nrf_drv_gpiote_set_task_addr_get(IR_LED_PIN);
        nrf_drv_gpiote_out_task_enable(IR_LED_PIN);
    
        err_code = nrf_drv_ppi_channel_assign(m_ppi_ch_ir_emitter, event_addr_emitter, gpiote_task_addr);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(m_ppi_ch_ir_emitter);
        APP_ERROR_CHECK(err_code);
    
        // Assign a timer compare to turn IR emitter OFF
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_ch_ir_emitter);
        APP_ERROR_CHECK(err_code);
    
        event_addr_emitter = nrfx_timer_compare_event_address_get(&IR_DRV_AND_SAMPLE_TIMER,
                                                                  IR_TIMER_CH_OFF);
        gpiote_task_addr = nrf_drv_gpiote_clr_task_addr_get(IR_LED_PIN);
        nrf_drv_gpiote_out_task_enable(IR_LED_PIN);
    
        err_code = nrf_drv_ppi_channel_assign(m_ppi_ch_ir_emitter, event_addr_emitter, gpiote_task_addr);
        APP_ERROR_CHECK(err_code);
        err_code = nrf_drv_ppi_channel_enable(m_ppi_ch_ir_emitter);
        APP_ERROR_CHECK(err_code);
        
        // Enable timer
        nrfx_timer_clear(&IR_DRV_AND_SAMPLE_TIMER);
        nrf_drv_timer_enable(&IR_DRV_AND_SAMPLE_TIMER);
        
    

    Yes, there is lots of other code running, including the SoftDevice. I can try disabling (nearly) all of it.

    It may not be relevant, but I needed more than 6 CC channels to control various things (GPIOTE and SAADC), so I have a second timer running simultaneously that is also cleared by CC0 of this first one. I have code that changes SAADC settings in time for the trigger for it over PPI. I am using the SAADC interrupt to trigger the code that does those changes.

  • the problem only occurs when the SoftDevice is enabled. A coworker hypothesized that it could be related to the RC clock calibration. Is that possible? I was going to try disabling BLE_COMMON_OPT_EXTENDED_RC_CAL, but I am using a relatively old SoftDevice (s132 6.0.0) that doesn't have it. Maybe I should bite the bullet and update my SDK (v15.0.0) and SoftDevice?

  • That's interesting. If it is related to RC clock calibration I think the only explanation is that during calibration the HF clock source is switched from HFINT to HFXO, and that these two sources have different accuracy. But then again, you are seeing about 10% difference, and it can't really be that much drift between the clocks. Also the calibration lasts for 17ms, so then you should have seen several consecutive pulses with extended intervals.

    HFXO is also switched on during regular BLE events. So if you are advertising with a 20ms interval you should see the clock jitter more often, if it is related to the switching between the two sources.

    Actually what you can do to check this is to just start the HFXO right after softdevice init. https://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v7.0.1/group___n_r_f___s_o_c___f_u_n_c_t_i_o_n_s.html?cp=4_7_3_1_2_7_2_3#ga3e5afb495a1b0307c749cc268df94a74

    Then the HFXO will be running all the time and the source will not be changed during calibration.

    Can you also list down the PPI channel numbers, TIMER peripheral number, CC number etc that you are using? Maybe there are some conflicts with the softdevice. e.g. TIMER0 is used by the softdevice.

  • I found the problem. It is in my software. I had added code that resets the timer if interrupts aren't serviced fast enough and things get out of sync. I need to change how I handle that.

    , I'm sorry to bother you with all of this. Thank you for your help.

Related