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

TIMER1 with PPI to GPIOTE

I'm trying to repeatedly toggle a GPIOTE on every CC[0] compare within the time period set by CC[1]. CC[1] runs multiple times. Every time CC[1] is reached I want to stop the CC[0] compare, check if I've sent a certain number of toggles and if so, stop TIMER1, else I'll re-enable the CC[0] compare and continue running.

I'm having a problem where the GPIOTE occasionally toggles after the final CC[1] compare occurs and I have an extra pulse being sent. It seems like the PPI is not being disabled quickly enough. I also have the S110 soft device enabled, but I don't see how that could affect it.

Here's my code. Any ideas what could be wrong?

Init Code:

NRF_TIMER1->TASKS_CLEAR = 1;
NRF_TIMER1->CC[0] = sub_period;
NRF_TIMER1->CC[1] = period;
NRF_TIMER1->MODE = (TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos);
NRF_TIMER1->PRESCALER = (0 << TIMER_PRESCALER_PRESCALER_Pos);
NRF_TIMER1->BITMODE = (TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos);
NRF_TIMER1->INTENSET = TIMER_INTENSET_COMPARE0_Msk | TIMER_INTENSET_COMPARE1_Msk;
NRF_TIMER1->SHORTS = (TIMER_SHORTS_COMPARE1_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos);
NRF_PPI->CH[0].EEP = (uint32_t)(&NRF_TIMER1->EVENTS_COMPARE[0]);
NRF_PPI->CH[0].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
NRF_PPI->CH[1].EEP = (uint32_t)(&NRF_TIMER1->EVENTS_COMPARE[1]);
NRF_PPI->CH[1].TEP = (uint32_t)(&NRF_GPIOTE->TASKS_OUT[0]);
nrf_gpiote_task_config(0, TX_OUT, NRF_GPIOTE_POLARITY_TOGGLE, NRF_GPIOTE_INITIAL_VALUE_LOW);

NRF_PPI->CHENSET = (PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos)
                  | (PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos);

err_code = sd_nvic_SetPriority(TIMER1_IRQn, 3);
APP_ERROR_CHECK(err_code);
err_code = sd_nvic_ClearPendingIRQ(TIMER1_IRQn);
APP_ERROR_CHECK(err_code);
err_code = sd_nvic_EnableIRQ(TIMER1_IRQn);
APP_ERROR_CHECK(err_code);

Interrupt:

void TIMER1_IRQHandler() {
    NRF_TIMER1->EVENTS_COMPARE[0] = 0;
    if (NRF_TIMER1->EVENTS_COMPARE[1]) {
        NRF_TIMER1->EVENTS_COMPARE[1] = 0;
        NRF_TIMER1->INTENCLR = TIMER_INTENCLR_COMPARE0_Msk;
        timer_check();
    }
    NRF_TIMER1->EVENTS_COMPARE[2] = 0;
    NRF_TIMER1->EVENTS_COMPARE[3] = 0;
 }

Check if done toggling:

void timer_check() {
    if (--remainingToggles == 0) {
        NRF_PPI->CHENCLR = (PPI_CHEN_CH0_Disabled << PPI_CHEN_CH0_Pos)
                 | (PPI_CHEN_CH1_Disabled << PPI_CHEN_CH1_Pos);
        nrf_gpiote_unconfig(0);
        NRF_TIMER1->TASKS_SHUTDOWN = 1;
        sd_nvic_DisableIRQ(TIMER1_IRQn);
        sd_nvic_ClearPendingIRQ(TIMER1_IRQn);
    }
    else {
        NRF_TIMER1->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
    }
}

Any advice is greatly appreciated!

Parents
  • I'm assuming that sub_period < period.

    As soon as CC[1] triggers, that clears the timer (via PPI) and you have only subperiod ticks until the CC[0] compare is going to trigger the next toggle. I would assume you see this issue mostly when subperiod is small, close to zero, what kind of numbers are you using for subperiod and period?

    What's likely is that indeed TIMER1_IRQ doesn't get run before the timer hits CC[0] again and you get the extra toggle. The softdevice performs regular interrupts and blocks other things happening for up to milliseconds at a time. The more the softdevice is doing, the longer it blocks things for (eg doing nothing does very little, advertising does something, sending data in a connection you can get some very long blocks). Even just having the softdevice loaded and disabled adds 50 cycles to the interrupt start time. I would estimate that in the code above with a softdevice on the chip, the interrupt + the softdevice overhead + the code before you disable the timer is around 80 cycles. That's the best case if the SD isn't servicing its own high priority interrupt when TIMER1 is raised, the worst case is milliseconds.

    You gave TIMER1 an interrupt priority of 3, you could raise that to 1, but if you're talking the kind of microsecond timings you mention, as soon as you start doing things with the softdevice even at priority 1 on the interrupt you're going to have that interrupt delayed, quite possibly by enough to get several toggles of the pin before you turn it off.

    Relying on interrupts for any kind of precise timing when the softdevice is running really doesn't work, not unless you're talking times of the order of 10s of milliseconds or more. Basically assume that TIMER1 interrupt can be delayed by up to 4ms in the worst cases.

    Things you could try:

    Raise the TIMER1 interrupt priority to 1 - but that's not going to work

    Add another PPI channel to stop the timer on CC[1] compare which you only enable in the TIMER1 interrupt one cycle before remainingToggles is 0. So the final toggle also turns off the timer. That will work better, especially if the period value is close to 0xffff, but will still glitch in the worst case. The lower 'period' is, the worse it will glitch.

    Use TIMER2 in count up mode triggered by CC[1] compare using another PPI, clear it, load its compare with the number of toggles you want and set up yet another PPI to stop TIMER1 when you get the compare. Now you have a solution entirely within PPI which should be immune to interrupts, at the cost of 2 timers and 4 PPIs.

  • Thank you for the very detailed response! I'm using a period of 25 microseconds, and sub_period is equal to the period divided by a value from 1 to 5. I think I'm pushing the limits with such small timer increments. The softdevice broadcaster is only called after this cycle is complete, but it may still be affecting the timing. Let me discuss your recommendations with my team and see what we can do!

Reply
  • Thank you for the very detailed response! I'm using a period of 25 microseconds, and sub_period is equal to the period divided by a value from 1 to 5. I think I'm pushing the limits with such small timer increments. The softdevice broadcaster is only called after this cycle is complete, but it may still be affecting the timing. Let me discuss your recommendations with my team and see what we can do!

Children
No Data
Related