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

How to use multiple timers

I have a device that I need to control, that needs 2 different square wave signals of different frequencies (although they may be multiples of each other), one at around 4 MHz and one at 10 KHz. Additionally (while the other two are running in the background) I need to communicate to the device with SPI upon an interrupt on a separate pin. To make matters worse, I will also need the SoftDevice for communications via BLE.

I have read that the nRF51822 only comes with 3 timers (one of which is reserved for th SoftDevice). Is there still a way to use one timer for multiple tasks?

  • Hi,

    Yes, the nRF51 has 3 timers and the Softdevice needs one of them. So then you have two left for you application. The nRF51 also has two RTC timers, and once again the Softdevice seizes one of them. Maybe you can use the other one for your low frequency square wave? I suppose you can also use PPI and GPIOTE with your timers to make the square waves operate without involving interrupts and the CPU. Then it shouldn't be a problem to do both SPI and BLE as well.

  • Thanks Martin for your answer. I will go the PPI/GPIOTE way then. Am I correct in assuming, that given the frequencies are multiples (of 2) of each other I will be able to use a single timer for both square waves?

    I am still not very comfortable with dealing with this syntax. I found this sample code:

    static void ppi_init(void) {
    /* Configure PPI channel 0 to toggle PWM_OUTPUT_PIN1 on every Timer 1 COMPARE[0] match. */
    NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0];
    NRF_PPI->CH[0].TEP = (uint32_t) &NRF_GPIOTE->TASKS_OUT[0];
    
    /* Configure PPI channel 1 to toggle PWM_OUTPUT_PIN1 on every Timer 1 COMPARE[1] match. */
    NRF_PPI->CH[1].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[1];
    NRF_PPI->CH[1].TEP = (uint32_t) &NRF_GPIOTE->TASKS_OUT[0];
    }
    

    How can I set the compare event for TIMER1? Do I need to set the GPIOTE "out task" to "toggle" explicitly? Where do I determine the pin to be toggled? Are there any higher level libraries for what I am trying to achieve?

  • My followup question would have been too long for a comment. I therefore added it as an answer.

  • I'll apologize in advance for the sloppiness of this code. Writing your actual code would take awhile. This is just copied out of bits I have done. You still have to write your own code.

    You have most of it already for ppi/gpiote. Basically you point events to eep's, teps, etc. All the available configs and events are in the spec. The only trick with the SD is you have to use the API to do the channel assign so the SD knows what you are doing.

    Timer config stuff:

    	uint32_t          m_DelayuS     		 = 2160;                          	 /**< some number you choose*/
    			
    		NRF_TIMER1->TASKS_STOP        = 1;                      // Stop timer, if it was running
    NRF_TIMER1->TASKS_CLEAR       = 1;
    NRF_TIMER1->MODE              = TIMER_MODE_MODE_Timer;  // Timer mode (not counter)
    NRF_TIMER1->EVENTS_COMPARE[0] = 0;                      // clean up possible old events
    NRF_TIMER1->EVENTS_COMPARE[1] = 0;
    NRF_TIMER1->EVENTS_COMPARE[2] = 0;
    NRF_TIMER1->EVENTS_COMPARE[3] = 0;
    	
    	NRF_TIMER1->SHORTS      = (1 << TIMER_SHORTS_COMPARE0_CLEAR_Pos);  //Shorts can allow for reset of count after compare.  ie, like an oscillator
    	NRF_TIMER1->PRESCALER   = 4;                                     // Input clock is 16MHz, timer clock = 2 ^ prescale -> interval 1us
    	NRF_TIMER1->CC[0]       = m_DelayuS;                        	 // Delay for tx start for pa control
    

    //***Then do same for other cc registers as needed.

    So config and pin assign look like (look up the options you need in the guides, this is only an example):

    	NRF_GPIOTE->CONFIG[2] =((GPIOTE_CONFIG_MODE_Task       << GPIOTE_CONFIG_MODE_Pos)
               |(22                         << GPIOTE_CONFIG_PSEL_Pos)
               |(GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos)
               |(GPIOTE_CONFIG_OUTINIT_Low     << GPIOTE_CONFIG_OUTINIT_Pos));
    

    Then the eep and tep are set with the API. Again this is just an example. Look up with functions you need.

    	sd_ppi_channel_assign(2, &(NRF_TIMER1->EVENTS_COMPARE[0]), &(NRF_GPIOTE->TASKS_SET[2]));
    	sd_ppi_channel_assign(3, &(NRF_TIMER1->EVENTS_COMPARE[1]), &(NRF_GPIOTE->TASKS_CLR[2]));
    
    sd_ppi_channel_enable_set(PPI_CHEN_CH2_Msk);
    sd_ppi_channel_enable_set(PPI_CHEN_CH3_Msk);
    

    So, what did we learn here? Well there are both gpiote channels and ppi channels. They are different and both get assigned to something for a complete gpio solution. You have to watch out for the reserved channels in the SD guides. And you cannot assign your ppi/gpiote channels until after you start the SD.

    Then for your solution just use one of the extra cc registers for the other timer since they are related. Hopefully the registers are big enough to accommodate the differential.

  • Also, don't forget to start the timer. That is just: NRF_TIMER1->TASKS_START = 1;

    You also need to make sure the SD keeps the 32MHz clock running. Coming out of power manage it'll switch to HFINT and not HFEXT. Just look in the devzone on how to do this.

Related