Skip to content Skip to sidebar Skip to footer

Get 64bit Timestamps From A 32bit Timer

On my stm32wb55, I am using the 32bit-timer 'tim2' for reading the time from 32bit-register 'CNT' since system startup. With prescaling, I display the time in microseconds on my pu

Solution 1:

The tim2 timer is a 32bit resolution timer, you want a 64bit resolution. There are two ways to emulate a 64bit counter, to keep track of your uptime.

  1. One would be incrementing a variable each time you reach the unit of time that you want to keep track of. But that would be extremely inefficient giving that the microcontroller would be doing a lot of constant context switching.

  2. The second way would be to extend the timer with a 32bit variable. Then incrementing such variable on an overflow.

MSB                           LSB
+--------------+ +--------------+
|  32bit uint  | |  32bit timer |
+--------------+ +--------------+

The way this works is that after the timer reaches 0xffffffff which is the maximum for a 32bit unsigned counter, the timer will overflow and start back at 0. If there was another bit after that 32'th bit, it will flip on(which is the same as incrementing). What you can do is emulate this exact behavior by incrementing a variable.

First, set up your timer.

static TIM_HandleTypeDef s_TimerInstance = { 
    .Instance = TIM2
};

voidsetup_timer()
{
    __TIM2_CLK_ENABLE();
    s_TimerInstance.Init.Prescaler = ##; //Chose the correct value that fits your needs
    s_TimerInstance.Init.CounterMode = TIM_COUNTERMODE_UP;
    s_TimerInstance.Init.Period = 0xffffffff; //Chose the correct value that fits your needs
    s_TimerInstance.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //Also choose this value
    s_TimerInstance.Init.RepetitionCounter = 0;
    HAL_TIM_Base_Init(&s_TimerInstance);
    HAL_TIM_Base_Start(&s_TimerInstance);
}

Your handler, this has to be called each time your timer reaches 0xffffffff

externvoidTIM2_IRQHandler();
voidTIM2_IRQHandler()
{
    HAL_TIM_IRQHandler(&s_TimerInstance);
}

uint32_t extension;

voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    extension++; //Increment
}

Combine the extension variable and the timer value. Use this function each time you want to get the extender counter value. You can make it inline to avoid extra calls, or as a macro.

uint64_t get_time()
{
    return (extension << 32) & (__HAL_TIM_GET_COUNTER(&s_TimerInstance));
}

Now glue everything together

int main(void)
{
    HAL_Init(); //Initialize HAL libraryInitializeTimer(); //Initialize timerHAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM2_IRQn);

    while(1);
}

Note, that now tim2 will be used until it overflows. It should not be changed, or the following code will not work. Also, setup the divider, so the timer increment each microsecond as you stated earlier.

Also, you can use the timer to count seconds and then calculate the microsecond instead. If you count seconds instead you can count up to 2^32 seconds which is 4294967296. A year has about 31536000 seconds. With a 32bit counter (4294967296/31536000) you can count up to 136.19252 years of uptime. Then get the microseconds by dividing the uptime with 1000000 (uptime/1000000). I don't know what are you planning to do with the microcontroller, but counting seconds sounds more sensical for me.

If you really want precision, you can still do it by counting seconds, you can add the timer counter value to the microsecond count, which you can get by diving the seconds down into microseconds, that way you offset microseconds that haven't been added to the second count.

Solution 2:

If you only access this from a non-ISR [non interrupt service] context, it's pretty simple.

If you have an ISR, the base level needs to lock/unlock interrupt handling. The ISR does not have to be related to the timer interrrupt. It could be for any ISR (e.g. tty, disk, SPI, video/audio, whatever).

Here's some representative code for a simple semi-baremetal implementation [this is similar to what I've done in some R/T commercial products, notably in a microblaze inside a Xilinx FPGA]:

typedefunsignedint u32;
typedefunsignedlonglong u64;

volatileint in_isr;                    // 1=inside an ISRvolatile u32 oldlo;                     // old LSW timer valuevolatile u32 oldhi;                     // MSW of 64 bit timer// clear and enable the CPU interrupt flagvoidcli(void);
voidsti(void);

// tmr32 -- get 32 bit timer/counteru32 tmr32(void);

// tmrget -- get 64 bit timer valueu64
tmrget(void){
    u32 curlo;
    u32 curhi;
    u64 tmr64;

    // base level must prevent interrupts from occurring ...if (! in_isr)
        cli();

    // get the 32 bit counter/timer value
    curlo = tmr32();

    // get the upper 32 bits of the 64 bit counter/timer
    curhi = oldhi;

    // detect rolloverif (curlo < oldlo)
        curhi += 1;

    oldhi = curhi;
    oldlo = curlo;

    // reenable interruptsif (! in_isr)
        sti();

    tmr64 = curhi;
    tmr64 <<= 32;
    tmr64 |= curlo;

    return tmr64;
}

// isr -- interrupt service routinevoidisr(void){

    // say we're in an ISR ...
    in_isr += 1;

    u64 tmr = tmrget();
    // do stuff ...// leaving the ISR ...
    in_isr -= 1;
}

// baselevel -- normal non-interrupt contextvoidbaselevel(void){

    while (1) {
        u64 tmr = tmrget();
        // do stuff ...
    }
}

This works fine if tmrget is called frequently enough that it catches each rollover of the 32 bit timer value.

Post a Comment for "Get 64bit Timestamps From A 32bit Timer"