cancel
Showing results for 
Search instead for 
Did you mean: 

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

arro239
Senior
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.

3 REPLIES 3
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
Up vote any posts that you find helpful, it shows what's working..
arro239
Senior
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 :(

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
Up vote any posts that you find helpful, it shows what's working..