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

GPIOTE/PPI/TIMER errors

Hello,

nRF52832 BLE project using SDK 15.3.0 and SoftDevice 112 v6.1.1. Circuit has the output of a comparator connected to two GPIO pins (COMPARATOR_PIN1, COMPARATOR_PIN2). From time to time, a sensor will cause the comparator output to go LoToHi and a short time later (typically 100-300 milliseconds) HiToLo. I need to accurately measure the duration the pin is hi in milliseconds.

I use two timers along with GPIOTE and PPI. Timer2 is in timer mode with prescaler 9 (NRF_TIMER_FREQ_31250Hz). Timer1 is in counter mode and counts the number of times Timer2 overflows (compare value 0). Code to initialize timers:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void timers_init() {
// Configure TIMER2 as timer
nrfx_timer_config_t timer2_cfg = NRFX_TIMER_DEFAULT_CONFIG;
timer2_cfg.mode = NRF_TIMER_MODE_TIMER;
timer2_cfg.bit_width = NRF_TIMER_BIT_WIDTH_16;
timer2_cfg.frequency = NRF_TIMER_FREQ_31250Hz;
nrfx_timer_init(&m_timer2, &timer2_cfg, timer_event_handler);
nrfx_timer_enable(&m_timer2);
nrfx_timer_pause(&m_timer2);
nrfx_timer_compare(&m_timer2,NRF_TIMER_CC_CHANNEL0,0,false);
nrfx_timer_clear(&m_timer2);
// Configure TIMER1 as counter
nrfx_timer_config_t timer1_cfg = NRFX_TIMER_DEFAULT_CONFIG;
timer1_cfg.mode = NRF_TIMER_MODE_COUNTER;
timer1_cfg.bit_width = NRF_TIMER_BIT_WIDTH_16;
nrfx_timer_init(&m_timer1, &timer1_cfg, timer_event_handler);
nrfx_timer_clear(&m_timer1);
nrfx_timer_enable(&m_timer1);
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

GPIOTE and PPI are used to:

- start Timer2 when GPIO pin COMPARATOR_PIN1 goes LoToHi
- increment Timer1 when Timer2 overflows (compare value 0)
- capture Timer2 value when GPIO pin COMPARATOR_PIN2 goes HiToLo
- capture Timer1 value when GPIO pin COMPARATOR_PIN2 goes HiToLo

GPIOTE interrupt is enabled only for the HiToLo transition. Here's the code to set up GPIOTE and PPI:

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void gpiote_ppi_init(void) {
nrfx_err_t err_code = NRF_SUCCESS;
// Configure GPIOTE channel 0 as event that occurs when pin COMPARATOR_PIN1 changes from digital low to hi.
NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
| (COMPARATOR_PIN1 << GPIOTE_CONFIG_PSEL_Pos) // using GPIO photoTransistorDigitalPin as input
| (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
// Configure GPIOTE channel 1 as event that occurs when pin COMPARATOR_PIN2 changes from digital hi to low.
NRF_GPIOTE->CONFIG[1] = (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)
| (COMPARATOR_PIN2 << GPIOTE_CONFIG_PSEL_Pos) // using GPIO photoTransistorDigitalPin as input
| (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
// Interrupt only on HiToLo transition.
NRF_GPIOTE->INTENCLR = GPIOTE_INTENCLR_IN0_Msk | GPIOTE_INTENCLR_IN2_Msk | GPIOTE_INTENCLR_IN3_Msk;
NRF_GPIOTE->INTENSET = GPIOTE_INTENSET_IN1_Msk;
// Clear all events.
NRF_GPIOTE->EVENTS_IN[0] = 0;
NRF_GPIOTE->EVENTS_IN[1] = 0;
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Finally, when the GPIOTE interrupt handler is called, it calculates the duration the comparator output was hi, then resets for next event.

Fullscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GPIOTE_IRQHandler(void) {
// clear events
NRF_GPIOTE->EVENTS_IN[0] = 0;
NRF_GPIOTE->EVENTS_IN[1] = 0;
// disable GPIOTE interrupts until finished processing event
sd_nvic_DisableIRQ(GPIOTE_IRQn);
// stop timer2
nrfx_timer_pause(&m_timer2);
// timer1 and timer2 values have been captured into capture registers 0
uint32_t numberOfOverflows = nrfx_timer_capture_get(&m_timer1,0);
uint32_t timerValue = nrfx_timer_capture_get(&m_timer2,0);
// factor in number of times TIMER2 overflowed.
uint32_t durationInTicks = timerValue + (numberOfOverflows * 65535);
// calculate duration in milliseconds * 100 to preserve accuracy
double durationMillisecondsTimes100Double = durationInTicks * 3.2; // based on prescaler of 9
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

I use a function generator to simulate the comparator output. It pulls the GPIO pins hi for a specific duration and repeats. About once every 100 samples, the calculated duration is out significantly. Expect 250 ms and see 1559, for example.

Any errors in my code?

Should I be using sd_ppi_channel_alloc() instead of nrfx_ppi_channel_alloc()?

This all happens with a BLE link to a central (iOS device).

Many thanks,

Tim