Skip to main content
arro239
Associate III
May 7, 2014
Question

1MHz timer in one pulse mode - no interrupt in case of small auto-reload value

  • May 7, 2014
  • 3 replies
  • 722 views
Posted on May 07, 2014 at 23:04

I am trying to build a scheduler on top of a 1MHz tim5 32 bit timer operating in one pulse mode:

void initMicrosecondTimer(void) {

    RCC ->APB1ENR |= RCC_APB1ENR_TIM5EN;   // Enable TIM5 clock

    nvicEnableVector(HW_TIMER_IRQ, CORTEX_PRIORITY_MASK(HW_TIMER_PRIORITY));

    TIM->DIER |= TIM_DIER_UIE;   // Enable interrupt on update event

    TIM->CR1 |= TIM_CR1_OPM; // one pulse mode: count down ARR and stop

    TIM->CR1 &= ~TIM_CR1_ARPE; /* ARR register is NOT buffered, allows to update timer's period on-fly. */

    TIM->PSC = 84 - 1;   // 168MHz / 2 / 84 = 1MHz, each tick is a microsecond

}

/**

 * sets the alarm to the specified number of microseconds from now.

 * This function should be invoked under kernel lock which would disable interrupts.

 */

void setHardwareUsTimer(int timeUs) {

    TIM->ARR = timeUs - 1;

    TIM->EGR |= TIM_EGR_UG; // generate an update event to reload timer's counter value

    TIM->CR1 |= TIM_CR1_CEN; // restart timer

    lastSetTimerTime = getTimeNowUs();

    isTimerPending = TRUE;

    timerRestartCounter++;

}

Full file: https://sourceforge.net/p/rusefi/code/HEAD/tree/trunk/firmware/hw_layer/microsecond_timer.c

Problem: while everything works as expected in case of timeUs parameter above 300, I am not getting the expected callback for smaller timeUs parameter values like 5 or 11. Just for clarity: I am not even trying to constantly invoke 5us, 5us, 5us, 5us - even a single 5us between 300us invocations fails.

Currently I am trying relatively low #define HW_TIMER_PRIORITY 12 but I was still expecting the callback to happen eventually?

Please advice how can I get the callback to happen, even if it would be a bit late.

    This topic has been closed for replies.

    3 replies

    Tesla DeLorean
    Guru
    May 7, 2014
    Posted on May 08, 2014 at 01:03

    What happens in getTimeNowUs(); ? Is it causing a race condition on the isTimerPending flag?

    Here's a thought, why don't you just run TIM5 in maximal mode, and let it keep clocking at 1 MHz (1us), use that as your time base TIM5->CNT, not keep resetting it, and set TIM5->CCR1 = TIM5->CNT + us, and interrupt on the CC1?
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    arro239
    arro239Author
    Associate III
    May 8, 2014
    Posted on May 08, 2014 at 03:37

    getTimeNowUs() is just a 64-bit counter based on CYCCNT, alone the lines of

        int value = DWT_CYCCNT;

        if (value < currentValue)

            currentBase += 0x100000000LL;

        currentValue = value;

        return (currentBase + currentValue) / (CORE_CLOCK / 1000000);

    so while yes it is not perfect because of unguarded concurrent write to curentValue I do not see how it would be affecting the timer or the flag.

    I think I can try to implement your suggestion with channel #1 interrupt, it would be on the edge of my low-level skills but I would try. Still, curious why the resetting implementation does not work :(

    Tesla DeLorean
    Guru
    May 8, 2014
    Posted on May 08, 2014 at 13:36

    Yeah, I don't know why.

    You might want to benchmark that 64-bit division code though. I don't see it taking 100's of microseconds
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..