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);

Parents
  • 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.

Reply
  • 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.

Children
No Data
Related