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

What is more efficient use nrf_delay_us or TIMER HFCLK for 1 us

Hi there,

How in the question title, what could be more efficient? use a nrf_delay_us or Timer with HFCLK source.

I am creating a custom protocol to transfer data between the nRF52 and AVR microcontroller, but the sequences times requires 1us

I understand using the nrf_delay_us the CPU to create the delay for X time.

but, using the Timer with HFCLK source could use between  5 - 70 uA. if I use it I will require turn off the timer because the protocol will run only when the nRF52 wakes up

which one is better?

Parents
  • PPI is using the peripherals (clock, GPIO) directly, without involving the CPU. I set up a clock using the 16MHz frequency, with a prescaler of 4, causing it to tick every 1µs, as you see in this screenshot:

    If you use some nested timers, one in timer mode, and one in counter mode, counting when the first reaches 1µs, you can have a timer that triggers after n µs. Here set up to fire every

    1 000 000 µs = 1s.

    You can then use the EGU (Event generation unit) to set up a software interrupt for this.

    Attached is a main.c file that does this (you can use it e.g. with the SDK\examples\template_project example.

    /* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
    
    #include "nrf.h"
    #include <stdbool.h>
    #include <stdint.h>
    #include "bsp.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "math.h"
    
    // Peripheral channel assignments
    #define TIMER3_GPIOTE_CH      0
    #define TIMER3_PPI_CH_A       0
    #define TIMER3_PPI_CH_B       1
    #define TIMER3_TIMER_CC_NUM   0
    
    #define TIMER2_GPIOTE_CH     2
    #define TIMER2_PPI_CH_A      3
    #define TIMER2_PPI_CH_B      4
    #define TIMER2_TIMER_CC_NUM  0
    
    
    
    
    #define TIMER3_RELOAD        16
    
    #define TIMER2_TIMER_RELOAD 1000000
    // The timer CC register used to reset the timer. Be aware that not all timers in the nRF52 have 6 CC registers.
    #define TIMER3_RELOAD_CC_NUM 5
    #define TIMER2_TIMER_RELOAD_CC_NUM 3
    
    
    // This function initializes timer 3 with the following configuration:
    // 24-bit, base frequency 16 MHz, auto clear on COMPARE5 match (CC5 = TIMER_RELOAD)
    void timer_init()
    {
        NRF_TIMER3->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
        NRF_TIMER3->PRESCALER               = 0;
        NRF_TIMER3->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER3_RELOAD_CC_NUM;
        NRF_TIMER3->MODE                    = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
        NRF_TIMER3->CC[TIMER3_RELOAD_CC_NUM] = TIMER3_RELOAD;   
    
        NRF_TIMER2->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
        NRF_TIMER2->PRESCALER               = 0;
        NRF_TIMER2->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER2_TIMER_RELOAD_CC_NUM;
        NRF_TIMER2->MODE                    = TIMER_MODE_MODE_Counter << TIMER_MODE_MODE_Pos;
        NRF_TIMER2->CC[TIMER2_TIMER_RELOAD_CC_NUM] = TIMER2_TIMER_RELOAD;       
        
        NRF_TIMER3->CC[TIMER3_TIMER_CC_NUM] = 6;        //This variable is not too important. Only used to trigger the "first" pin. Not needed, as TIMER2 only needs the TIMER3_RELOAD to trigger/count.
        NRF_TIMER2->CC[TIMER2_TIMER_CC_NUM] = 512;      //This variable is not too important. Only used to trigger the "second" pin. Not needed, as the timeout handler only needs TIMER2_RELOAD to trigger.
    }
    
    
    // Starts TIMER3
    void timer_start()
    {
        NRF_TIMER3->TASKS_START = 1;
        NRF_TIMER2->TASKS_START = 1;
    }
    
    void SWI0_EGU0_IRQHandler(void)
    {
        if(NRF_EGU0->EVENTS_TRIGGERED[0])
        {
            // This is the timeout handler that triggers after TIMER3_RELOAD number of �s.
            NRF_EGU0->EVENTS_TRIGGERED[0] = 0;
        }
    }
    
    
    void gpiote_init(void)      // this function sets up the gpiotes. Although the gpiote toggles are not nesescarry, it also connects TIMER2 in Counter mode to TIMER3.
    {  
        NRF_GPIOTE->CONFIG[TIMER3_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                             17 << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;
        
        NRF_GPIOTE->CONFIG[TIMER2_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                             18 << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;
            
    
        NRF_PPI->CH[TIMER3_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER3_TIMER_CC_NUM];
        NRF_PPI->CH[TIMER3_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[TIMER3_GPIOTE_CH];
        NRF_PPI->FORK[TIMER3_PPI_CH_A].TEP = (uint32_t)&NRF_TIMER2->TASKS_COUNT;            // TIMER2 gets incremented every time TIMER3 reaches TIMER3_RELOAD_CC_NUM
        
        NRF_PPI->CH[TIMER3_PPI_CH_B].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER3_RELOAD_CC_NUM];
        NRF_PPI->CH[TIMER3_PPI_CH_B].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[TIMER3_GPIOTE_CH];    
        
        NRF_PPI->CH[TIMER2_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[TIMER2_TIMER_CC_NUM];
        NRF_PPI->CH[TIMER2_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[TIMER2_GPIOTE_CH];
        
        NRF_PPI->CH[TIMER2_PPI_CH_B].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[TIMER2_TIMER_RELOAD_CC_NUM];
        NRF_PPI->CH[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[TIMER2_GPIOTE_CH];
        NRF_PPI->FORK[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_EGU0->TASKS_TRIGGER[0];         // TIMER2 times out after TIMER2 reaches TIMER2_TIMER_RELOAD_CC_NUM
        
        
        NRF_EGU0->INTENSET = EGU_INTENSET_TRIGGERED0_Msk;
        NVIC_EnableIRQ(SWI0_EGU0_IRQn);
        
        NRF_PPI->CHENSET               = (1 << TIMER3_PPI_CH_A) | (1 << TIMER3_PPI_CH_B) | (1 << TIMER2_PPI_CH_A) | (1 << TIMER2_PPI_CH_B);
    }
    
    
    
    int main(void)
    {
        timer_init();
        gpiote_init();
        timer_start();
        while (true)
        {
            //do nothing
        }
    }
    

    The spikes on ch1 are the events.

    This can be done using only one timer, instead of two as well.

    What you can do then, is to set the prescaler on TIMER3 to 4:

    NRF_TIMER3->PRESCALER = 4;

    Set the TIMER3 Reload high enough to not reset before the intended timeout: TIMER3_RELOAD 10000000 (=10s)

    Set NRF_TIMER3->CC[TIMER_3_TIMER_CC_NUM] = n; where n is the number of µs you need before your timeout.

    You can see how the EGU is set up as a fork of one of the PPI channels.

    NRF_PPI->FORK[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_EGU0->TASKS_TRIGGER[0];

    If you change that to be on [TIMER3_PPI_CH_A].TEP, then the timeout will trigger after n CC[TIMER3_TIMER_CC_NUM] µs.

    I suggest you take a look at the attached file, and play around with it. If you have an oscilloscope, or a logic analyser, you can watch the pins on P0.17 and P0.18. If not, try to set some longer delays, and watch the leds LED1 and LED2. At the speed that it is set up to, it looks like they are both on all the time, but they are actually triggering fast.

    Let me know if you have any questions regarding this.

    Best regards,

    Edvin

Reply
  • PPI is using the peripherals (clock, GPIO) directly, without involving the CPU. I set up a clock using the 16MHz frequency, with a prescaler of 4, causing it to tick every 1µs, as you see in this screenshot:

    If you use some nested timers, one in timer mode, and one in counter mode, counting when the first reaches 1µs, you can have a timer that triggers after n µs. Here set up to fire every

    1 000 000 µs = 1s.

    You can then use the EGU (Event generation unit) to set up a software interrupt for this.

    Attached is a main.c file that does this (you can use it e.g. with the SDK\examples\template_project example.

    /* Copyright (c) 2009 Nordic Semiconductor. All Rights Reserved.
     *
     * The information contained herein is property of Nordic Semiconductor ASA.
     * Terms and conditions of usage are described in detail in NORDIC
     * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
     *
     * Licensees are granted free, non-transferable use of the information. NO
     * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
     * the file.
     *
     */
    
    #include "nrf.h"
    #include <stdbool.h>
    #include <stdint.h>
    #include "bsp.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "math.h"
    
    // Peripheral channel assignments
    #define TIMER3_GPIOTE_CH      0
    #define TIMER3_PPI_CH_A       0
    #define TIMER3_PPI_CH_B       1
    #define TIMER3_TIMER_CC_NUM   0
    
    #define TIMER2_GPIOTE_CH     2
    #define TIMER2_PPI_CH_A      3
    #define TIMER2_PPI_CH_B      4
    #define TIMER2_TIMER_CC_NUM  0
    
    
    
    
    #define TIMER3_RELOAD        16
    
    #define TIMER2_TIMER_RELOAD 1000000
    // The timer CC register used to reset the timer. Be aware that not all timers in the nRF52 have 6 CC registers.
    #define TIMER3_RELOAD_CC_NUM 5
    #define TIMER2_TIMER_RELOAD_CC_NUM 3
    
    
    // This function initializes timer 3 with the following configuration:
    // 24-bit, base frequency 16 MHz, auto clear on COMPARE5 match (CC5 = TIMER_RELOAD)
    void timer_init()
    {
        NRF_TIMER3->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
        NRF_TIMER3->PRESCALER               = 0;
        NRF_TIMER3->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER3_RELOAD_CC_NUM;
        NRF_TIMER3->MODE                    = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
        NRF_TIMER3->CC[TIMER3_RELOAD_CC_NUM] = TIMER3_RELOAD;   
    
        NRF_TIMER2->BITMODE                 = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
        NRF_TIMER2->PRESCALER               = 0;
        NRF_TIMER2->SHORTS                  = TIMER_SHORTS_COMPARE0_CLEAR_Msk << TIMER2_TIMER_RELOAD_CC_NUM;
        NRF_TIMER2->MODE                    = TIMER_MODE_MODE_Counter << TIMER_MODE_MODE_Pos;
        NRF_TIMER2->CC[TIMER2_TIMER_RELOAD_CC_NUM] = TIMER2_TIMER_RELOAD;       
        
        NRF_TIMER3->CC[TIMER3_TIMER_CC_NUM] = 6;        //This variable is not too important. Only used to trigger the "first" pin. Not needed, as TIMER2 only needs the TIMER3_RELOAD to trigger/count.
        NRF_TIMER2->CC[TIMER2_TIMER_CC_NUM] = 512;      //This variable is not too important. Only used to trigger the "second" pin. Not needed, as the timeout handler only needs TIMER2_RELOAD to trigger.
    }
    
    
    // Starts TIMER3
    void timer_start()
    {
        NRF_TIMER3->TASKS_START = 1;
        NRF_TIMER2->TASKS_START = 1;
    }
    
    void SWI0_EGU0_IRQHandler(void)
    {
        if(NRF_EGU0->EVENTS_TRIGGERED[0])
        {
            // This is the timeout handler that triggers after TIMER3_RELOAD number of �s.
            NRF_EGU0->EVENTS_TRIGGERED[0] = 0;
        }
    }
    
    
    void gpiote_init(void)      // this function sets up the gpiotes. Although the gpiote toggles are not nesescarry, it also connects TIMER2 in Counter mode to TIMER3.
    {  
        NRF_GPIOTE->CONFIG[TIMER3_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                             17 << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;
        
        NRF_GPIOTE->CONFIG[TIMER2_GPIOTE_CH] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                             18 << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_High << GPIOTE_CONFIG_OUTINIT_Pos;
            
    
        NRF_PPI->CH[TIMER3_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER3_TIMER_CC_NUM];
        NRF_PPI->CH[TIMER3_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[TIMER3_GPIOTE_CH];
        NRF_PPI->FORK[TIMER3_PPI_CH_A].TEP = (uint32_t)&NRF_TIMER2->TASKS_COUNT;            // TIMER2 gets incremented every time TIMER3 reaches TIMER3_RELOAD_CC_NUM
        
        NRF_PPI->CH[TIMER3_PPI_CH_B].EEP = (uint32_t)&NRF_TIMER3->EVENTS_COMPARE[TIMER3_RELOAD_CC_NUM];
        NRF_PPI->CH[TIMER3_PPI_CH_B].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[TIMER3_GPIOTE_CH];    
        
        NRF_PPI->CH[TIMER2_PPI_CH_A].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[TIMER2_TIMER_CC_NUM];
        NRF_PPI->CH[TIMER2_PPI_CH_A].TEP = (uint32_t)&NRF_GPIOTE->TASKS_CLR[TIMER2_GPIOTE_CH];
        
        NRF_PPI->CH[TIMER2_PPI_CH_B].EEP = (uint32_t)&NRF_TIMER2->EVENTS_COMPARE[TIMER2_TIMER_RELOAD_CC_NUM];
        NRF_PPI->CH[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_GPIOTE->TASKS_SET[TIMER2_GPIOTE_CH];
        NRF_PPI->FORK[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_EGU0->TASKS_TRIGGER[0];         // TIMER2 times out after TIMER2 reaches TIMER2_TIMER_RELOAD_CC_NUM
        
        
        NRF_EGU0->INTENSET = EGU_INTENSET_TRIGGERED0_Msk;
        NVIC_EnableIRQ(SWI0_EGU0_IRQn);
        
        NRF_PPI->CHENSET               = (1 << TIMER3_PPI_CH_A) | (1 << TIMER3_PPI_CH_B) | (1 << TIMER2_PPI_CH_A) | (1 << TIMER2_PPI_CH_B);
    }
    
    
    
    int main(void)
    {
        timer_init();
        gpiote_init();
        timer_start();
        while (true)
        {
            //do nothing
        }
    }
    

    The spikes on ch1 are the events.

    This can be done using only one timer, instead of two as well.

    What you can do then, is to set the prescaler on TIMER3 to 4:

    NRF_TIMER3->PRESCALER = 4;

    Set the TIMER3 Reload high enough to not reset before the intended timeout: TIMER3_RELOAD 10000000 (=10s)

    Set NRF_TIMER3->CC[TIMER_3_TIMER_CC_NUM] = n; where n is the number of µs you need before your timeout.

    You can see how the EGU is set up as a fork of one of the PPI channels.

    NRF_PPI->FORK[TIMER2_PPI_CH_B].TEP = (uint32_t)&NRF_EGU0->TASKS_TRIGGER[0];

    If you change that to be on [TIMER3_PPI_CH_A].TEP, then the timeout will trigger after n CC[TIMER3_TIMER_CC_NUM] µs.

    I suggest you take a look at the attached file, and play around with it. If you have an oscilloscope, or a logic analyser, you can watch the pins on P0.17 and P0.18. If not, try to set some longer delays, and watch the leds LED1 and LED2. At the speed that it is set up to, it looks like they are both on all the time, but they are actually triggering fast.

    Let me know if you have any questions regarding this.

    Best regards,

    Edvin

Children
  • Hi Edvin, I used an Oscilloscope to see the output for each pin, and it works very well when I run the example, 

    The pink/purble are the ticks every 1us and the yellow every 1sec

    but when I port it to my project I am unable to build the code. I got the following error:

    .\_build\nrf52832_xxaa.axf: Error: L6200E: Symbol SWI0_EGU0_IRQHandler multiply defined (by app_timer.o and timer_example.o).

    Is possible run app_timer (RTC) and a TIMER1 both in the same project? because it seem the app_timer uses the same IRQ handler than TIMER1

    -----updated:

    I tried to add my custom protocol to the example you shared, but it looks is totally independent of the CPU it is just communication between the GPIO and TIMER peripheral, f, r example If I want to do some bitbanging is it possible with PPI? and I am not sure what are you doing with EGU 

Related