2
\$\begingroup\$

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?

\$\endgroup\$
4
  • \$\begingroup\$ What is the measure of time your trying to achieve? \$\endgroup\$
    – Voltage Spike
    Commented May 10, 2017 at 15:24
  • \$\begingroup\$ It will be a tachometer with range of something like 10RPM to 6000RPM. Frankly, it will fit into 16-bit (if I take 0.1ms per tick, that range will map to ~ 60000 to 100 ticks), but I would like to avoid working close to the limits and also get better precision at high RPM. \$\endgroup\$ Commented May 10, 2017 at 18:24
  • \$\begingroup\$ there might be a systick timer that is 24 bits you can use which is in the arm itself. \$\endgroup\$
    – old_timer
    Commented May 11, 2017 at 1:00
  • \$\begingroup\$ Your idea about the race condition is correct. I had verified on my case. \$\endgroup\$
    – bill wang
    Commented Nov 19, 2021 at 6:13

1 Answer 1

-1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.