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

BLE affects TIMER1/2 accuracy

Hi,

I'm building a product using RF Digital's Simblee that uses the nrF51822 ver 3 chip. I've narrowed down a TIMER accuracy issue to BLE. The code below starts and stops TIMER1 (16 bit, prescaler=9) on low-to-hi and hi-to-low, respectively, of a GPIO pin. I'm driving the GPIO pin with a function generator configured to send a 1 second pulse with a period of 2 seconds. An interrupt is called on hi-to-low to print out the counter value. (Simblee allows programming via the Arduino IDE.)

Given TIMER1 prescaler of 9 (Ftimer = 31250), I should be getting counter values of 31250. This is the case if I comment out the SimbleeBLE.begin() line. But if SimbleeBLE.begin() executes, I see counter values of around 31589 ±3. Same behaviour if I use TIMER2.

I thought TIMER peripherals were not impacted by other system resources. Any possibilities to eliminate the impact of BLE on timer accuracy?

Thanks. -Tim

Code:

#include <SimbleeBLE.h>

int functionGeneratorPin = 6;

void setup() {
  
  Serial.begin(9600);
  
  delay(1000);
  
  pinMode(functionGeneratorPin,INPUT);
  
  // Configure TIMER1 as timer
  NRF_TIMER1->TASKS_STOP = 1; // Stop timer
  NRF_TIMER1->MODE = TIMER_MODE_MODE_Timer;  // Set to timer mode
  NRF_TIMER1->PRESCALER = 9;  // overflow is every 2.097152 seconds
  NRF_TIMER1->TASKS_CLEAR = 1;  // Clear the timer
  NRF_TIMER1->BITMODE = TIMER_BITMODE_BITMODE_16Bit;  // Set to 16 bit
  
  // Configure GPIOTE channel 0 as event that occurs when functionGeneratorPin pin changes from digital
  // low to high.
  NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
              | (functionGeneratorPin << GPIOTE_CONFIG_PSEL_Pos)
              | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
  
  // Configure GPIOTE channel 1 as event that occurs when functionGeneratorPin pin changes from digital
  // high to low.
  NRF_GPIOTE->CONFIG[1] =  (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)
              | (functionGeneratorPin << GPIOTE_CONFIG_PSEL_Pos)
              | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
  
  // Interrupt only on high to low.
  NRF_GPIOTE->INTENCLR = GPIOTE_INTENSET_IN0_Msk;
  NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN1_Msk;
  
  // Clear all events.
  NRF_GPIOTE->EVENTS_IN[0] = 0;
  NRF_GPIOTE->EVENTS_IN[1] = 0;
  NRF_GPIOTE->EVENTS_IN[2] = 0;
  NRF_GPIOTE->EVENTS_IN[3] = 0;
  
  // Attach interrupt handler.
  dynamic_attachInterrupt(GPIOTE_IRQn, reportTime);
  
  // Configure PPI channel 0 to start TIMER1 on low to high.
  simblee_ppi_channel_assign(0, &NRF_GPIOTE->EVENTS_IN[0], &NRF_TIMER1->TASKS_START);
  // Configure PPI channel 1 to stop TIMER1 on high to low.
  simblee_ppi_channel_assign(1, &NRF_GPIOTE->EVENTS_IN[1], &NRF_TIMER1->TASKS_STOP);

  SimbleeBLE.begin();
}


void loop() {
  
}


void reportTime(void) {
  
  if (NRF_GPIOTE->EVENTS_IN[1] != 0) {

    // clear event
    NRF_GPIOTE->EVENTS_IN[1] = 0;

    // timer has been stopped, capture value
    NRF_TIMER1->TASKS_CAPTURE[0] = 1;

    // get timer value
    unsigned long timerValue = NRF_TIMER1->CC[0];

    // clear timer for next cycle
    NRF_TIMER1->TASKS_CLEAR = 1;
    
    Serial.println(timerValue);
    
  }
}
  • Yes - do a search - this is discussed many times a week.

    The softdevice has a higher interrupt priority than anything else, it has to have, it has critical timing requirements. So your interrupt can get delayed by up to several milliseconds, the more the bluetooth stack does, the more you get delayed. All the timings are in the softdevice specification. Once you're in a connection shoving data around, your delays are going to get only larger.

    You cannot do interrupt-based precise timings with the softdevice in use, it doesn't work. You need to use PPI or the PWM module which Nordic provides which uses PPI to work independently of the softdevice.

  • oh note you can probably get the cycle more to the correct length by using the SHORT between COMPARE and CLEAR instead of clearing in your IRQ handler - however the result of that will just be to get the average period about right, but increase the amount of variability your IRQ handler gets called with.

  • Thanks RK. I think I share your understanding. I realize that interrupt routine calls can be delayed due to softdevice priority. As I see it, my code does use PPI (simblee_ppi_channel_assign), along with GPIOTE, to start and stop the timer according to the function generator's 1 second pulse. Before my interrupt routine is called, PPI has already stopped the timer, so no matter what the delay is into the interrupt routine, the timer value should reflect the duration of the 1 second pulse. The interrupt routine then clears the timer to get ready for the next pulse. This code is just for investigation purposes. You give me hope that using TIMER, PPI and GPIOTE, I can get a stable, accurate timer. Thx.

  • You said that the capture value is always 31589 ±3 when you start BLE, this is very interesting. What LFCLK clock are you using when you enable the softdevice? is it RC LFCLK with regular calibration? If so, the calibration of RC clock starts crystal HFCLK until the calibration is done. This could affect the accuracy of the TIMER because of the switch happening many times during these two seconds?

  • Thanks Aryan. The call to SimbleeBLE.begin() starts the softdevice. Unfortunately I don't have access to that code. Before calling SimbleeBLE.begin(), I change the LFCLK source to Xtal and no difference in behaviour. I did notice something interesting though. When I check the NRF_CLOCK->HFCLKSTAT register before calling SimbleeBLE.begin(), I get the value 0x00010001, which, according to the Nordic nRF51822 v3 reference manual, means the HFCLK source is Xtal (16 MHz HFCLK crystal). After calling SimbleeBLE.begin(), the NRF_CLOCK->HFCLKSTAT register changes to 0x00010000, which means the HFCLK source is now RC (16 MHz RC oscillator). Unlike LFCLK, I don't see a task for setting the HFCLK source. Regardless, though, I presume I can't change it once the softdevice has started. Also interesting, if I repeat the above test using RTC1 instead of TIMER1, RTC1 values are accurate. Thoughts? Thx!

Related