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

TASKS_CAPTURE cannot read the correct value

Hello!

I compare the timer every 1ms and process it.
In order to check whether it can be executed every 1ms, I am going to refer to the register value by "TASKS_CAPTURE".
The description of the timer itself is as follows.

Timer 1 is used.

nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;
err_code = nrf_drv_timer_init(&TIMER, &timer_cfg, timer_twi_event_handler);
APP_ERROR_CHECK(err_code);

time_ticks = nrf_drv_timer_us_to_ticks(&TIMER, time_us);

nrf_drv_timer_extended_compare(
    &TIMER, NRF_TIMER_CC_CHANNEL1, time_ticks, NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK, true);

nrf_drv_timer_enable(&TIMER);



The description of "TASKS_CAPTURE" is as follows.

void timer_twi_event_handler(nrf_timer_event_t event_type, void* p_context)
{

    switch (event_type)
    {
        case NRF_TIMER_EVENT_COMPARE1:
            if(j == 48){
                j = 0;
            }
					
            NRF_TIMER1 -> TASKS_CAPTURE[0] = 1;
            uint8_t caputured_value = NRF_TIMER1 -> CC[0];

            flags = true;
            time++;
            //receive_data();
            ret_code_t ret = nrf_drv_spi_xfer(&spi,&xfer_spi,flags);//Function for starting the SPI data transfer with additional option flags
            if(ret == NRF_SUCCESS)
            {
                uint32_t start_tsk_addr = nrf_drv_spi_start_task_get(&spi);
            }

            if(j < 24){
              //put_in_array(buf);
              for(int a = 0; a < 20; a++){
                  buf[j*21+a] = mpu_buf[a];
              }
              buf[j*21+20] = caputured_value;
            }else if(j >= 24){
              //put_in_array(buf2);
              for(int a = 0; a < 20; a++){
                  buf[j*21+a+8] = mpu_buf[a];
              }
              buf[j*21+20+8] = caputured_value;
            }
            j++;
            //data_handler();
					
            break;

        default:
            //Do nothing.
            break;
    }
}

The captured value is confirmed by writing to the SD card.
The values are always as follows:

253.00000
253.00000
253.00000
253.00000
253.00000
253.00000
255.00000
255.00000
228.00000
228.00000
255.00000
75.00000
255.00000
252.00000
252.00000
252.00000
252.00000
252.00000
252.00000
252.00000

This will be repeated.
Is my code something wrong?
Since "NRF_TIMER_CC_CHANNEL1" is used in the comparison, cc [0] is used when reading the register. Is this correct?

Please give me an answer.

  • Hi,

    I found thiese DevZone threads quite useful regarding need to update CC register og clear the time in order to be ready  https://devzone.nordicsemi.com/f/nordic-q-a/2010/setting-timer2-interval and https://devzone.nordicsemi.com/f/nordic-q-a/18237/timer-with-two-compared-values/70428#70428

    I see you set up your device to use TIMER1 CHANNEL1 to call your callback every 1ms. The function nrf_drv_timer_extended_compare makes sure the time in CHANNEL1 is cleared upon every compare (1ms). 
    However, I cannot find a similar mechanism in your callback clearing the time in CHANNEL0 which you use to verify the frequency of CHANNEL1 callback.
    I would suggest to capture the value in CC[0] then clear the CHANNEL0, or store the captured value in a static variable and subtract the current value to the old value to find the delta. 


    One thing I see right away, the variable captured_value should be of type uint32_t to not overflow at value 255. 

    Another thing I wonder about.
    Is it possible that the next timer event is ready before you exit the timer callback?
    Looking trough your code it seems the there are some data dependencies, which gives the execution path different length from time to time. 

    I'll look more into this tomorrow to see if I can verify this on my desk.

  • Thank you for your reply!

    The link seems useful
    Thank you

    Is the correct way to capture with CC [0] instead of capturing CC [1]?

    I do not know how to clear after capture

    In "store the captured value in a static variable and subtract the current value to the old value to find the delta", I don't feel like checking 1ms every time.

    The variable capture_calue is set to uint8_t for SD card writing. Therefore,
    NRF_TIMER1-> BITMODE
    Should be set to

    "Is it possible that the next timer event is ready before you exit the timer callback?"
    Since the contents of the call are being executed, we think it will be finished by the next call.

    I prepared a variable time that was not capture_value and wrote it to the SD card so that it is incremented by 1 each time. However, I want to read the timer register instead of this method.

  • Hi,

    I did some testing at my desk today, and figured out a few things which could help you.
    After thinking a little bit of your use case I come up with two solutions. 
    Probably the easiest solution: use another timer to measure the time between each 1ms event. This will use more battery, and uses another timer, but is simplifies the 1ms code.
    Do you have another timer to spare?

    If you want to do this using only one timer the following needs to be considered. 

    • When the NRF_TIMER_SHORT_COMPARE1_CLEAR_MASK is passed to the function nrf_drv_timer_extended_compare, TIMER1 is cleared every time the timer counts enough to meet the CHANNEL1 compare value. 
      This means that the number you read in CHANNEL0 CC[0] is only the time from the CHANNEL1 compare event until you initiate the capture task in CHANNEL0. 
      - This is not what you want to achieve. You want the time from the last time you initiated the capture task. Therefore, call the extended_compare with 0 instead of the clear_mask.
    • In the callback function you need to increment the CC[1] compare register with the number of timer ticks it should take to reach the next 1 ms, minus the eventual extra ticks it took to reach the callback handler. 
    • Calculate the delta time from the previous time you initiated the capture task on channel0 until to verify the frequency. 

    •Is the correct way to capture with CC [0] instead of capturing CC [1]?
    - Yes, since CC[1] is used to compare ticks for 1ms, then CC[0] can be used to verify the time between each time the callback is executed.

    •I do not know how to clear after capture
    - TIMERn->TASKS_CLEAR = 1, but do not want to du this manually in your use case if using one timer. If using two timers. tet the extended_compare function handle this for the 1ms timer, but clear the timer used to verify the 1ms timer. 

    •The variable capture_calue is set to uint8_t for SD card writing. Therefore,
    NRF_TIMER1-> BITMODE Should be set to
    - The number of bits depends on the number of ticks between each 1ms, which depends on the prescaler used.
      If you use one timer, you definitely need 32 bits.
      If you use two timers you could manage with less bits, but this reduces accuracy. 8 bit is possible if you use prescaler which gives you e.g. 250 kHz you should manage with 8 bit resolution (250 ticks between each 1 ms). 

    I have added a small example which uses one timer instance with two channels where channel0 verifies channel1.
    The example uses SDK 16.0.0
    The TIMER1->CC[1] will eventually overflow, so some concideration must be taken into account if this method is used. 

     timer.zip

  • Thank you very much for the polite explanation!

    Thank you for the "timer.zip" code!
    This looks very useful to me!


    ・ I want to use only one timer now because I want to save power as much as possible.

    ・ Call the extended_compare with 0 instead of the clear_mask
    -Set to 0, as taught!

    Calculate the delta time from the previous time you initiated the capture task on channel0 until to verify the frequency
    -When I calculated the delta and wrote it to the SD card, it became a series of numbers "128". What does this number 128 mean?
    If 128 can read the value of the register, how many registers is 1ms?

    If the register is incremented by 1 every 1 ms, does that mean that an interrupt is called every 128 ms?

    Even if you change PRESCALER and change the speed, Delta calculates 128, so if 128 is output, can you think that it is called correctly?

  • Hi,
    Glad it can help you somehow.

    Ok, one timer. 

    The delta you compute should indicate the number of timer ticks since last time you captured. This number will vary depending on the prescaler used for the timer. In the code I uploaded the timer is running on 16 MHz (default). When 1 ms is the target frequency for the callback handler, the timer should tick 16000 times. 

    If you change line 63 in main.c to the use 125 kHz instead of 16 MHz, the timer should tick only 125 times between each 1 ms. 
    #define TIMER_BASE_FREQUENCY                    NRF_TIMER_FREQ_125kHz   // Timer operating frequency

    This should fit within your 8-bit limit. 

    Is it possible that you use the timer, running on 16MHz?
    If you hit the target number of tick, which is 16000 it could make sense.
    The lower 8 bits of 16000 decimal translates into 128 decimal. 
    I assume you only write the 8 lowest bits, then this adds up. 

     

    y001 said:

    If 128 can read the value of the register, how many registers is 1ms?

    If the register is incremented by 1 every 1 ms, does that mean that an interrupt is called every 128 ms?

    Even if you change PRESCALER and change the speed, Delta calculates 128, so if 128 is output, can you think that it is called correctly?

    You misunderstand. You don't read 128 from any register.  Reading CC[0] gives you the number of ticks from when the timer started.

    The delta ticks are calculated from the amount of timer ticks last time, and this time the callback handler is running.

    CC[1] is incremented with the number of ticks it needs to reach the next 1 ms cycle. 

    Prescaler/speed of timer is the same. The number of ticks needed to reach the next 1 ms cycle will decrease as the speed of the timer is decreased. 

    You should think of the case when the timer will eventually overflow.
    This could be done as simple as just resetting the timer using the CLEAR task, and write CC[0]=0 and CC[1]=timer_ticks inside the callback handler when you reach some high value close to the uint32_t limit. 
    This solution does not handle the case when the timer if not reaching the 1 ms requirement. If a delay is detected, you can adjust for this as well by subtracting the delay found when calculating the delta ticks and subtrackt this from CC[1] to make sure that the next time 1ms is not shifted, but stays on the original 1 ms cycle. 

    You are welcome to post your callback once more with some of the updated tweaks if you want me to have look. 

Related