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);
}
}