timer and ppi on nrf connect sdk

hello Nordic

i work with nrf52832, nrf52840 with ncs 1.9

i have 2 issues, is there some example or guid fo using 2 ppi channels for the same gpiote, one timer pullup the gpio and the other timer pulls the same gpio down .. what functions are a must ?  gpioite_channel allocation is a must or ppi_channel_allocations are enough etc.

second issue is that i use three timers in my code and it looks like one of them does not work ok, 

also if i look at pin toggle in ppk power profiler i see that if i light a led while the sampling then i the gpio (pulled up by ppi and pulled down by spi_done handler) sometimes miss some toggling every some time .. any ideas why ?

hope to read from you soon

best regards

Ziv

Parents
  • Hi,

    i have 2 issues, is there some example or guid fo using 2 ppi channels for the same gpiote, one timer pullup the gpio and the other timer pulls the same gpio down .. what functions are a must ?  gpioite_channel allocation is a must or ppi_channel_allocations are enough etc.

    You will have to define one GPIOTE channel and tow PPI channels. One PPI channel will connect to the TASKS_SET[n] register of the GPIOTE, and the other PPI channel will connect to TASKS_CLR[n].

    second issue is that i use three timers in my code and it looks like one of them does not work ok, 

    Can you elaborate more on what is not working? How do you configure/use the timers? Note that some timer instances are used by protocol stacks (BLE/Thread/Zigbee, etc) if that is used.

    also if i look at pin toggle in ppk power profiler i see that if i light a led while the sampling then i the gpio (pulled up by ppi and pulled down by spi_done handler) sometimes miss some toggling every some time .. any ideas why ?

    Hard to say without more details. Will both set and clear be missed, or only clearing from the spi_done event? In that case it could be some interrupt issues, for instance the interrupt is blocked by other high-priority tasks. Please also post your code to show how the PPI is configured and how you clear the pin in the spi handler.

    Best regards,
    Jørgen

  • ziv123 said:

    a bit different approach that i am trying .. i wish to have the same timer compare the SET, without clearing the timer and then the second compare event for the CLR will clear the timer (so for example first compare will be for 80 microsec, the second compare will be for 100 microsec and then clearing the timer so actually both events happen every 100 microsec with a small shift between them)

    any idea how to do it ?

    This should not be a problem.

    You configure the timer using COMPARE0 and COMPARE1 events, each connected to one of the PPI channels. Then you enable the timer SHORT->COMPARE1_CLEAR, which will clear the timer only after the second compare event.

  • hi again

    few notes on that .. first the order of the channels seems to be important (if i set ch1 as the first compare and then ch0 is the later compare with clear task it did not work, did not make sense that this will be dependent and maybe there is a different reason but this worked and the other did not ) , correct me if i am wrong. 

    when i try to also add a compare event that will call a timer handler then nothing happens and i am not sure why 

    i have this code if i change from true to false on the compare of ch2 then it works, but trying to make the compare trigger the interrupt does not work for some reason

    nrfx_timer_config_t timer_cfg = {
            .frequency = NRF_TIMER_FREQ_16MHz,
            .mode = NRF_TIMER_MODE_TIMER,
            .bit_width = NRF_TIMER_BIT_WIDTH_32,
            .p_context = NULL,
        };
    
        int res = nrfx_timer_init(timer, &timer_cfg, dummy_timer_handler);
        if (res != NRFX_SUCCESS)
        {
            AUGU_LOG_ERR("error in nrfx_timer_init %x", res);
            return -EINVAL;
        }
    
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, 1, false);
        uint32_t compar_val = nrfx_timer_us_to_ticks(timer, 2);
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL1, compar_val, false);
        compar_val = nrfx_timer_us_to_ticks(timer, 20);
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL2, compar_val, true);
        compar_val = nrfx_timer_us_to_ticks(timer, 1000000 / sample_freq);
        nrfx_timer_extended_compare(timer, NRF_TIMER_CC_CHANNEL3, compar_val, NRF_TIMER_SHORT_COMPARE3_CLEAR_MASK, false);
    
        *time_conv_up = nrfx_timer_compare_event_address_get(timer, NRF_TIMER_CC_CHANNEL0);
        *time_spi_xfer = nrfx_timer_compare_event_address_get(timer, NRF_TIMER_CC_CHANNEL1);
     

    i also tried to use another timer to call on a handler once compare value is reached and for some reason the handler is not called. this is the timer initialisation config and the handler code:

    {
                    ...
                    nrfx_timer_config_t timer_cfg = {
                        .frequency = NRF_TIMER_FREQ_16MHz,
                        .mode = NRF_TIMER_MODE_TIMER,
                        .bit_width = NRF_TIMER_BIT_WIDTH_32,
                        .p_context = NULL,
                    };
    
                    int res = nrfx_timer_init(&timer_cycle_done, &timer_cfg, dummy_timer_handler1);
                    if (res != NRFX_SUCCESS)
                    {
                        AUGU_LOG_ERR("error in nrfx_timer_init %x", res);
                        return -EINVAL;
                    }
    
                    uint32_t compar_val = nrfx_timer_us_to_ticks(&timer_cycle_done, 1000000 / drv_data->sample_frequency);
                    nrfx_timer_extended_compare(&timer_cycle_done, NRF_TIMER_CC_CHANNEL0, compar_val, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);
                    nrfx_timer_compare_int_enable(&timer_cycle_done, NRF_TIMER_CC_CHANNEL0);
                    
                    nrfx_timer_enable(&timer_cycle_done);
                    ...
    }
    
    static void dummy_timer_handler1(nrf_timer_event_t event_type, void *p_context)
    {
        // gpio_pin_set(gpio_dev_red, 25, 1);
        gpio_pin_toggle(gpio_dev_red, 25);
    }

    also on that code, it is not clear if i need to use the ' nrfx_timer_compare_int_enable ' if i put true in the ' 

    nrfx_timer_extended_compare ' function args ?
    hope to read from you soon
    best regards
    Ziv
  • Hi,

    ziv123 said:
    first the order of the channels seems to be important (if i set ch1 as the first compare and then ch0 is the later compare with clear task it did not work, did not make sense that this will be dependent and maybe there is a different reason but this worked and the other did not ) , correct me if i am wrong. 

    No, it should not be a problem to have a higher channel set to a shorter CC value. Each CC value should be evaluated against the count independently of other channels by the hardware. I tested that it works well to set COMPARE0 with a higher CC value than COMPARE1 in the timer example in nRF5 SDK on nRF52832. Please check the registers with a debugger to make sure the CC values and shortcuts are set as you intended.

    ziv123 said:

    when i try to also add a compare event that will call a timer handler then nothing happens and i am not sure why 

    i have this code if i change from true to false on the compare of ch2 then it works, but trying to make the compare trigger the interrupt does not work for some reason

    This sounds strange. If you set the enable_int parameter to false, the interrupt/handler should not be called, it should only work if you set it to true. Can you check with a debugger if the INTEN register have the COMPARE2 enabled or not?

    ziv123 said:
    i also tried to use another timer to call on a handler once compare value is reached and for some reason the handler is not called. this is the timer initialisation config and the handler code:

    Have you remembered to connect the TIMER interrupt using the Zephyr API? IRQ_CONNECT()

    ziv123 said:

    also on that code, it is not clear if i need to use the ' nrfx_timer_compare_int_enable ' if i put true in the ' 

    nrfx_timer_extended_compare ' function args ?

    No, this is not necessary, the same functions are called inside nrfx_timer_extended_compare() if enable_int is set to true. This function can be used if you want to enable interrupts at a different point in your code than where you configure the compare registers.

    Best regards,
    Jørgen

Reply
  • Hi,

    ziv123 said:
    first the order of the channels seems to be important (if i set ch1 as the first compare and then ch0 is the later compare with clear task it did not work, did not make sense that this will be dependent and maybe there is a different reason but this worked and the other did not ) , correct me if i am wrong. 

    No, it should not be a problem to have a higher channel set to a shorter CC value. Each CC value should be evaluated against the count independently of other channels by the hardware. I tested that it works well to set COMPARE0 with a higher CC value than COMPARE1 in the timer example in nRF5 SDK on nRF52832. Please check the registers with a debugger to make sure the CC values and shortcuts are set as you intended.

    ziv123 said:

    when i try to also add a compare event that will call a timer handler then nothing happens and i am not sure why 

    i have this code if i change from true to false on the compare of ch2 then it works, but trying to make the compare trigger the interrupt does not work for some reason

    This sounds strange. If you set the enable_int parameter to false, the interrupt/handler should not be called, it should only work if you set it to true. Can you check with a debugger if the INTEN register have the COMPARE2 enabled or not?

    ziv123 said:
    i also tried to use another timer to call on a handler once compare value is reached and for some reason the handler is not called. this is the timer initialisation config and the handler code:

    Have you remembered to connect the TIMER interrupt using the Zephyr API? IRQ_CONNECT()

    ziv123 said:

    also on that code, it is not clear if i need to use the ' nrfx_timer_compare_int_enable ' if i put true in the ' 

    nrfx_timer_extended_compare ' function args ?

    No, this is not necessary, the same functions are called inside nrfx_timer_extended_compare() if enable_int is set to true. This function can be used if you want to enable interrupts at a different point in your code than where you configure the compare registers.

    Best regards,
    Jørgen

Children
  • Hi

    Have you remembered to connect the TIMER interrupt using the Zephyr API? IRQ_CONNECT()

    i haven't done that, i also wonder if i can use IRQ_DIRECT_CONNECT if i will need more real time performance? 

    but before that, i m not sure if the instance number i give to the timer dictates which timer i am working with .. meaning if 

    #define CONFIG_ADS8866_CHAIN_XFER_TIMER_INST 2
    static const nrfx_timer_t timer_cycle_done = NRFX_TIMER_INSTANCE(CONFIG_ADS8866_CHAIN_XFER_TIMER_INST);
    
    and initialisation looks like so :

     

    rfx_timer_config_t timer_cfg = {
            .frequency = NRF_TIMER_FREQ_16MHz,
            .mode = NRF_TIMER_MODE_TIMER,
            .bit_width = NRF_TIMER_BIT_WIDTH_32,
            .p_context = handler_context,
        };
    
        int res = nrfx_timer_init(timer, &timer_cfg, ads8866_spi_done_event_handler);
        if (res != NRFX_SUCCESS)
        {
            AUGU_LOG_ERR("error in nrfx_timer_init %x", res);
            return -EINVAL;
        }
        IRQ_DIRECT_CONNECT(TIMER2_IRQn, 0, nrfx_timer_2_irq_handler, 0);

    but i had to try &error to see if it is TIMER1_IRQ / TIMER2_IRQ / TIMER0_IRQ

    is there a way to know in advance, also what if i use instance 4 of a timer ?

    best regards

    Ziv

  • ziv123 said:
    i haven't done that, i also wonder if i can use IRQ_DIRECT_CONNECT if i will need more real time performance? 

    Yes.

    ziv123 said:

    but i had to try &error to see if it is TIMER1_IRQ / TIMER2_IRQ / TIMER0_IRQ

    is there a way to know in advance

    If you set the instance to 2 in the NRFX_TIMER_INSTANCE() macro, this is the instance that will be used. If your question is how you can use the number from the instance definition to select the IRQn object, this can be achieved using the concatenation operator (##), see e.g NRFX_CONCAT_2() macro.

    ziv123 said:
    also what if i use instance 4 of a timer ?

    Then you should use TIMER4_IRQn.

  • i haven't done that, i also wonder if i can use IRQ_DIRECT_CONNECT if i will need more real time performance? 

    Yes.

    what affects can it have on other parts of my application if i use the DIRECT and does it have limitations to take under consideration, because i see that when i config to use direct my spi_done callback (its my indication via gpio) does not seem to work, it like my flash streaming does not work, maybe cause of use of queues and semaphores and other OS things ?

    is it ok to use all 4 channels of a timer 2 for ppi task, 1 for ppi task and the interrupt call and one for the compare event that clears the timer ?

    to be more specific:

     nrfx_timer_config_t timer_cfg = {
            .frequency = NRF_TIMER_FREQ_16MHz,
            .mode = NRF_TIMER_MODE_TIMER,
            .bit_width = NRF_TIMER_BIT_WIDTH_32,
            .p_context = handler_context,
        };
    
        int res = nrfx_timer_init(timer, &timer_cfg, ads8866_spi_done_event_handler);
        if (res != NRFX_SUCCESS)
        {
            AUGU_LOG_ERR("error in nrfx_timer_init %x", res);
            return -EINVAL;
        }
        // IRQ_DIRECT_CONNECT(TIMER2_IRQn, 0, nrfx_timer_2_irq_handler, 0);
        IRQ_CONNECT(TIMER2_IRQn, 0, nrfx_timer_2_irq_handler, NULL, 0);
    
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL0, 1, false);
        uint32_t compar_val = nrfx_timer_us_to_ticks(timer, 2);
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL1, compar_val, false);
        compar_val = nrfx_timer_us_to_ticks(timer, 14);
        nrfx_timer_compare(timer, NRF_TIMER_CC_CHANNEL2, compar_val, true);
        compar_val = nrfx_timer_us_to_ticks(timer, 1000000 / sample_freq);
        nrfx_timer_extended_compare(timer, NRF_TIMER_CC_CHANNEL3, compar_val, NRF_TIMER_SHORT_COMPARE3_CLEAR_MASK, false);
    
        *time_conv_up = nrfx_timer_compare_event_address_get(timer, NRF_TIMER_CC_CHANNEL0);
        *time_spi_xfer = nrfx_timer_compare_event_address_get(timer, NRF_TIMER_CC_CHANNEL1);

    this is how i set my timer and what i actually see in the picoscop6 is that i have the 'timer_spi_xfer' event which is timed ok (via ppi) but the interrupt handler called by ch2 compare event seems to be late sometimes .. how can i find why and maybe improve it without going to 'DIRECT' ?

    the red line up at beginning of irq handler and down at the end, it can be seen that sometimse it raises closer to the blue lines start (sp_xfer), any idea what can cause this ?

    hope to read you soon

    best regards

    Ziv

  • hi 

    is it possible to use same timer channel , compare event for both triggering the irq_handler and change a gpio via ppi ? 

    if so, how should i configure a gpio to both SET and CLR task (one compare event will pull up and then mentioned compare event on a different channel will pull down the gpio, both via ppi ) ?

    hope to read you soon

    best regards

    Ziv

  • ziv123 said:
    what affects can it have on other parts of my application if i use the DIRECT and does it have limitations to take under consideration, because i see that when i config to use direct my spi_done callback (its my indication via gpio) does not seem to work, it like my flash streaming does not work, maybe cause of use of queues and semaphores and other OS things ?

    The differences are listed in the API documentation:

    This type of interrupt currently has a few limitations compared to normal Zephyr interrupts:

    • No parameters are passed to the ISR.

    • No stack switch is done, the ISR will run on the interrupted context’s stack, unless the architecture automatically does the stack switch in HW.

    • Interrupt locking state is unchanged from how the HW sets it when the ISR runs. On arches that enter ISRs with interrupts locked, they will remain locked.

    • Scheduling decisions are now optional, controlled by the return value of ISRs implemented with the ISR_DIRECT_DECLARE() macro

    • The call into the OS to exit power management idle state is now optional. Normal interrupts always do this before the ISR is run, but when it runs is now controlled by the placement of a ISR_DIRECT_PM() macro, or omitted entirely.

    ziv123 said:
    is it ok to use all 4 channels of a timer 2 for ppi task, 1 for ppi task and the interrupt call and one for the compare event that clears the timer ?

    Yes, all the COMPARE channels can be used, that is the reason they are implemented.

    ziv123 said:
    this is how i set my timer and what i actually see in the picoscop6 is that i have the 'timer_spi_xfer' event which is timed ok (via ppi) but the interrupt handler called by ch2 compare event seems to be late sometimes .. how can i find why and maybe improve it without going to 'DIRECT' ?

    The execution of the handler will depend on other interrupts and priority of the tasks in your application. If this handler is high priority, you should try to increase its priority.

    The PPI signals are handled purely in HW, and are not affected by CPU usage by other tasks.

    ziv123 said:
    the red line up at beginning of irq handler and down at the end, it can be seen that sometimse it raises closer to the blue lines start (sp_xfer), any idea what can cause this ?

    Likely some other higher/equal priority task in the application is blocking the execution of the handler.

    ziv123 said:
    is it possible to use same timer channel , compare event for both triggering the irq_handler and change a gpio via ppi ? 

    Yes, these are different HW concepts, and not mutually exclusive. The COMPARE event will send a HW signal on any connected PPI channels, and also signal the interrupt if that is enabled.

    ziv123 said:
    if so, how should i configure a gpio to both SET and CLR task (one compare event will pull up and then mentioned compare event on a different channel will pull down the gpio, both via ppi ) ?

    You need to use separate PPI channels for SET and CLR to achieve this. On PPI channel connected from COMPARE event to SET task and another PPI channel connected from COMPARE event to CLR task.

Related