Electronic – Achieving 32-bit timer resolution

stm32timer

I am trying to achieve 32-bit resolution using 16-bit timer on STM32F103. I found that ST provides an application note AN2592, however, I have two concerns regarding it:

1) I don't understand why are they multiplying MSB by 65535 (see 3.1). Shouldn't it be 65536 instead (assuming I use period of 65535)?

2) In 3.2 they say that you should process update interrupt first (to update MSB), to account for possible race between update and capture happens. I think, this is not correct.

In my understanding, two races are possible: capture after overflow and overflow after capture. They might happen for various reasons, the simplest one is that interrupts are disabled and both flags are raised before handler begins executing. Or handler starts handling one of them and before code gets to process the event, another event happens.

Depending on which race happened, you either need to update MSB before processing capture event or after. So, the way I think it should be done is to check the captured value first. If it is "pretty low" (less than, let's say, 10, 100, 1000, doesn't really matter as it will either be very low or very high to race to happen) and overflow is pending, then your overflow event happened before the capture. Otherwise, overflow happened after the capture.

This is my interrupt handler:

if (tim->SR & TIM_FLAG_CC1) {
    tim->SR = ~TIM_FLAG_CC1;

    uint32_t lsb = TIM2->CCR1;
    // Captured value is very low and overflow is pending -- need to account for "msb" increment, as it happened
    // before the capture event. Otherwise, we don't care -- if overflow interrupt is pending, we will handle
    // it on the next handler invocation.
    if (lsb < 1000 && (tim->SR & TIM_FLAG_UPDATE)) {
        tim->SR = ~TIM_FLAG_UPDATE;
        // Capture happened just after the overflow: need to increment upper "msb"
        msb++;
    }
    msb_captured = msb;
    lsb_captured = lsb;
    msb = 0;
} else if (tim->SR & TIM_FLAG_UPDATE) {
    tim->SR = ~TIM_FLAG_UPDATE;
    msb++;
}

I haven't tested it yet (need to figure out the best way to test race handling…).

Does it look plausible?

Best Answer

1) I don't understand why are they multiplying MSB by 65535 (see 3.1).

The appnote is wrong on that - not that unheard off.

Shouldn't it be 65536 instead (assuming I use period of 65535)?

That part isn't clear. If the lower (16 bit) timer rolls over from 65535 to 0, its period of 65536.

If it rolls over at a number less than 65535 ( via auto reloading for example) the math is slight more different but the concept is the same.

Btw, using pointers to a union will greatly improve the execution.

The basic concept can be used to great synthetic timers with really long overflow periods.