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

High frequency sampling loses IRQ calls

Hi

I'm trying to measure the frequency of a pulse signal that runs at a moderate pace, varying between 50 and 500Hz while the SoftDevice is active. The softdevice needs to be active because I must send that frequency over ANT. I need to have the exact amount of time that passes between pulses and the exact number of them over time to accurately calculate the frequency.

The problem I'm facing is that I somehow "miss" some of the pulses, because the IRQ that is being called from the GPIOTE fails to be called from time to time. I assume that happens due to the fact that the SoftDevice is using the cpu while those missing interrupts happen. Is that assumption correct? What I see is that, for example, from a steady pulse train at 250Hz I measure 125 clock ticks between pulses, but from time to time, the measure goes to 250 or even 375 ticks, meaning I've lost 1 or 2 irq calls. That ruins my frequency calculations.

This is how I setup the GPIOTE

   NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
                       | (SENSOR_PIN << GPIOTE_CONFIG_PSEL_Pos)  
                       | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
    NRF_GPIOTE->INTENSET  = (GPIOTE_INTENSET_IN0_Set << GPIOTE_INTENSET_IN0_Pos);
    err_code = sd_nvic_ClearPendingIRQ(GPIOTE_IRQn);
	err_code = sd_nvic_SetPriority(GPIOTE_IRQn, NRF_APP_PRIORITY_HIGH);	  
	err_code = sd_nvic_EnableIRQ(GPIOTE_IRQn);

Then the handler

void GPIOTE_IRQHandler(void)
{					
    if ((NRF_GPIOTE->EVENTS_IN[0] == 1) && 
        (NRF_GPIOTE->INTENSET & GPIOTE_INTENSET_IN0_Msk))
    {
        NRF_GPIOTE->EVENTS_IN[0] = 0;					
		service_IRQ();
	}
}

And the timer I'm using

    NRF_TIMER2->MODE           = TIMER_MODE_MODE_Timer;        
    NRF_TIMER2->PRESCALER      = 10;                           
    NRF_TIMER2->BITMODE        = TIMER_BITMODE_BITMODE_16Bit;  
    NRF_TIMER2->TASKS_CLEAR    = 1;   

And the tick measuring in service_IRQ

        NRF_TIMER2->TASKS_CAPTURE[1]=1;
		_elapsed_ticks = NRF_TIMER2->CC[1];
		NRF_TIMER2->TASKS_CLEAR = 1;
        _cycle_tick_counter += _elapsed_ticks;		

Apart from that timer I'm using app_timer_create to create 4 additional low frequency timers.

Is there any way I can fix or avoid this problem or any tip on how to achieve the same objective in any other way?

Thnaks a lot!

Parents
  • if you set up a PPI between your GPIO and the COUNT event on one of the available timers then each time you get a pulse it will count. That won't help you missing interrupts, however when you do get an interrupt you will be able to check the count and see how many pulses you received since the last IRQ you serviced.

    That does mean you will have to use another timer, one in count mode, one in time mode, but as long as you have a spare one that may work for you.

    I believe you can both set up the PPI from GPIOTE->COUNT and also enable either the GPIOTE or the COUNT event to generate the interrupt you want.

Reply
  • if you set up a PPI between your GPIO and the COUNT event on one of the available timers then each time you get a pulse it will count. That won't help you missing interrupts, however when you do get an interrupt you will be able to check the count and see how many pulses you received since the last IRQ you serviced.

    That does mean you will have to use another timer, one in count mode, one in time mode, but as long as you have a spare one that may work for you.

    I believe you can both set up the PPI from GPIOTE->COUNT and also enable either the GPIOTE or the COUNT event to generate the interrupt you want.

Children
  • Thanks for your suggestion RK

    I've tried your appoarch, something like this

    NRF_TIMER1->MODE           = TIMER_MODE_MODE_Counter;
    	NRF_TIMER1->PRESCALER      = 10;                            
    	NRF_TIMER1->BITMODE        = TIMER_BITMODE_BITMODE_08Bit;	
    	NRF_TIMER1->TASKS_CLEAR    = 1; 	
    
    	// Connect GPIOTE in event to RTC1 count task
    	sd_ppi_channel_assign(0, &NRF_GPIOTE->EVENTS_IN[0], &NRF_TIMER1->TASKS_COUNT);
    	sd_ppi_channel_enable_set(PPI_CHENCLR_CH0_Msk);	
    

    But I'm not able to make it work. Every time I read the CC of the timer I get 0, no matter how many in events have occurred in the gpiote

    NRF_TIMER1->TASKS_CAPTURE[0]=1;
    		passes = NRF_TIMER1->CC[0];
    		NRF_TIMER1->TASKS_CLEAR = 1;	
    

    passes is always 0. I don't start or stop the timer because I don't want it to count time, but even if I manually call the NRF_TIMER1->TASKS_COUNT task, I'm not able to increment the timer counter.

    Any idea what I'm missing?

  • Sorry, the prescaler value in the previous code is invalid, must be 0-9, anyway, I have no clue of what value to use, as the timer is a counter.

  • This from the reference manual ...

    "The TIMER can operate in two modes, Timer mode and Counter mode. In both modes the TIMER is started by triggering the START task, and stopped by triggering the STOP task. After the timer is stopped the timer can resume timing/counting by triggering the START task again. When timing/counting is resumed the timer will continue from the value it had prior to being stopped."

    So you need to start it with the START task else it won't count. This actually makes sense if you think about it, especially in the case we're trying out here where the COUNT is triggered by a PPI (or hopefully it will be when you start the timer) you'd want a way to quickly and simply turn the counter on and off.

    PRESCALAR is irrelevant in this case, again if you look at the diagram in the reference manual you'll see the COUNT event is after the scaling.

  • I started the timer as stated in the manual and now the timer counts. But I'm seeing that the original problem stays the same. Each time the IRQ is serviced the counter has a value of 1, but the time elapsed is not always the same at a constant input rate. So inputs are being lost.
    how can it be? The only workaround I've found is to check if the latest time is way bigger than the last one and in that case try to calculate how many input ticks may have been lost based on the last time stored.

  • How frustrating! OK my guess would be that the GPIOTE IN EVENT is triggered and that triggers the COUNT correctly through the PPI, but the EVENT stays triggered (1) until you are able to clear it in the interrupt handler thus STILL missing any toggles. Sorry I should have expected that but didn't think it far enough through. What you'd really want is for the EVENT not-only to trigger the counter (which you have) but also then self-clear. I just scanned the manual again and I don't think you can do that. A couple of the other boards I have at home (not nordic) let you connect a pin directly to a counter, but I don't see that option either.

    I'm now somewhat out of ideas but I'll think on it a little more see if there is anything else.

Related