Author Topic: AVR: Creating a 24-bit Input Capture Timer  (Read 1676 times)

0 Members and 1 Guest are viewing this topic.

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 2799
  • Country: us
AVR: Creating a 24-bit Input Capture Timer
« on: September 28, 2022, 03:49:49 pm »
I have a some questions about the correct way to create a 24-bit input capture timer on an 8-bit AVR such as the atmega328.

First, of course, you have to extend TIMER1 by keeping track of overflows. But what's the right way to avoid race conditions between the TIMER1 overflow interrupt and the input capture interrupt? Does this code do the job? Assume that TIMER1 is running in "Normal" mode (mode 0).

Code: [Select]
volatile uint8_t overflows;

ISR(TIMER1_OVF_vect) {
    overflows++;
}

ISR(TIMER1_CAPT_vect) {
    uint16_t t = ICR1;
    uint32_t when = (overflows << 16) | t;
    if (TIFR1 & 0x01) {    // check if TOV1 is set
        when += 0x10000;
    }
    // ... do something with when ...
}

I couldn't find anything the scheduling or priorities of interrupts, so it seems that the capture interrupt routine needs to check the TOV1 flag. Is that right?

Also, I have a question about app note AVR130:

https://ww1.microchip.com/downloads/en/Appnotes/Atmel-2505-Setup-and-Use-of-AVR-Timers_ApplicationNote_AVR130.pdf

At the top of page 14 it states about the input capture interrupt:

Quote
Note:  This implementation has one disadvantage: A timer overflow is not detected. Thus, if the duty
cycle of the wave has a longer period than what can be covered the 16-bit counter, an overflow will
happen before the next edge occurs. A global variable which is set in a timer overflow ISR can be used to
sense whether a timer overflow has happened before the capture is performed. If this variable is set, then
the effective capture value will be (0xFFFF + contents of ICR1).

Why do they say to add 0xFFFF instead of 0x10000?

 

Online Kleinstein

  • Super Contributor
  • ***
  • Posts: 12225
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #1 on: September 28, 2022, 04:28:19 pm »
Extending the resolution of the timer is a bit tricky with the race condition. AFAIR the ICP interrupts has the higher priority than the overflow - it is the sequence of the interrupt vectors. The point here that the priority is in effect when the interrupt is actually served, not when the interrups actually happend. The old AVR studio simulator (version 1, before about 2005) actually had this wrong, but with the nwer one is was corrected.


Here is a German langish side that shows a solution for extending the resolution:
https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture
 
The following users thanked this post: ledtester

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 2799
  • Country: us
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #2 on: September 29, 2022, 02:01:24 am »
Here is a German langish side that shows a solution for extending the resolution:
https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture

To summarize this solution, to handle the situation where the input capture happens before the timer overflow but the input capture ISR is not called until after the overflow, check the value of the ICR register. If it is >= 0x8000 then assume the input capture happened before the overflow and otherwise assume it happened after.

As code:

Code: [Select]
volatile uint8_t overflows;

ISR(TIMER1_OVF_vect) {
    overflows++;
}

ISR(TIMER1_CAPT_vect) {
    uint16_t t = ICR1;
    uint32_t when = (overflows << 16) | t;
    if ((TIFR1 & 0x01) && (t < 0x8000)) {    // check if TOV1 is set
        when += 0x10000;
    }
    // ... do something with when ...
}

The code on the rn-wissen.de site has an optimized version of this logic.
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 269
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #3 on: September 29, 2022, 04:01:26 am »
First, of course, you have to extend TIMER1 by keeping track of overflows.

Do you? My immediate idea would be to cascade two timers and wire and set them up to capture the same event. But, I have not thought this through, let alone tried it. Maybe that just shifts the problem somewhere else.

Edit, well, maybe you do -  should have thought to RTFM first. Looks like ATMEGA328 has only one input-capture capable timer. I probably had in mind ATMEGA32U4, which has an extra timer.
« Last Edit: September 29, 2022, 04:10:59 am by ozcar »
 

Online magic

  • Super Contributor
  • ***
  • Posts: 5412
  • Country: pl
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #4 on: September 29, 2022, 06:56:02 am »
If the captured signal is not synchronous with the MCU clock there is no guarantee that two I/O pins will pick it up in the same cycle. Perhaps you could figure out some way of cleaning it up in software.

It might be doable using the events system of more modern AVRs (particularly, a GPIO triggered synchronous event delivered to both timers).
« Last Edit: September 29, 2022, 06:58:27 am by magic »
 

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 2799
  • Country: us
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #5 on: September 29, 2022, 10:22:04 am »
My immediate idea would be to cascade two timers and wire and set them up to capture the same event. ...

I'm interested in the details of this (given that it could be implemented on the MCU). Do you mean run the second timer at a different clock divisor, e.g. TIMER1 at f_clk and TIMER2 at f_clk/1024?
 

Online Kleinstein

  • Super Contributor
  • ***
  • Posts: 12225
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #6 on: September 29, 2022, 11:26:08 am »
The 2nd capture would run from a slower clock. If this is still significant faster (e.g. 2 x or more) than just the overflows one would not even need to care if the 2 really capture at exactly the same µC clock clycle.  So f_clk/1024 is sensible for the 2nd timer.
It still needs some care and checks for the boarderline cases close to the steps of the slower timer.  So the coding does not get much simpler than with counting the overflows in software.
 

Offline PCB.Wiz

  • Frequent Contributor
  • **
  • Posts: 645
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #7 on: September 29, 2022, 07:38:58 pm »
My immediate idea would be to cascade two timers and wire and set them up to capture the same event. ...

I'm interested in the details of this (given that it could be implemented on the MCU). Do you mean run the second timer at a different clock divisor, e.g. TIMER1 at f_clk and TIMER2 at f_clk/1024?

The simplest cascade of timers is via a true hardware option, but not all MCUs offer this capability.
SiLabs parts and others can cascade capture on 2 16 bit timers, but the mega328 is a very old part that lacks this feature.

eg The just announced EA series AVR has this in the data  (as also do the AVR DD series etc)

Two 16-bit Timer/Counter Type B (TCBn) can be combined to work as a true 32-bit input capture:
One TCB is counting the two LSBs. Once this counter reaches MAX, an overflow (OVF) event is generated, and the counter wraps around. The second TCB is configured to count these OVF events and thus provides the two MSBs. The 32-bit counter value is concatenated from the two counter values.
To function as a 32-bit counter, the two TCBs and the system have to be set up as described in the following paragraphs.


Maybe this link works  https://onlinedocs.microchip.com/pr/GUID-838DDB25-4D69-4519-815B-A48DBACEED23-en-US-9/index.html
hmm, nope, you need to use that link, and then search for a portion of that quoted text.

« Last Edit: September 29, 2022, 08:57:44 pm by PCB.Wiz »
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 269
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #8 on: September 30, 2022, 09:22:06 pm »
If the captured signal is not synchronous with the MCU clock there is no guarantee that two I/O pins will pick it up in the same cycle. Perhaps you could figure out some way of cleaning it up in software.

So I tried this on an ATMEGA128 board that I had lying around. Like the ATMEGA32U4, that has two 16-bit timers that have input capture. Sure enough, with the two ICP pins tied together (in a not very tidy fashion), and the two timers synchronised and clocked at the same rate, the capture counts are occasionally out by one. Just for the heck of it, I changed the test signal to a 1kHz ramp, and with the timers running at 16MHz, the captured counts can be out by 40 or 50.

The 2nd capture would run from a slower clock. If this is still significant faster (e.g. 2 x or more) than just the overflows one would not even need to care if the 2 really capture at exactly the same µC clock clycle.  So f_clk/1024 is sensible for the 2nd timer.

I guess I was thinking of in effect concatenating the timer counters (wired together externally if required), rather than just select different prescaler taps for the two timers, but the latter sounds like a better option.

It still needs some care and checks for the boarderline cases close to the steps of the slower timer.  So the coding does not get much simpler than with counting the overflows in software.

Yes, having looked at it in more detail now it seems to cause more trouble than it is worth, with other things to worry about.

One thing that is does allow, and for the type of things I do could perhaps turn out to be useful one day, is to synchronise the two timers and set the one timer to capture the rising edge, and the other the falling edge.  Otherwise you have to be quick-smart to get in and change ICESx.

The simplest cascade of timers is via a true hardware option, but not all MCUs offer this capability.
SiLabs parts and others can cascade capture on 2 16 bit timers, but the mega328 is a very old part that lacks this feature.

If changing hardware is an option, some of the not so bleeding edge processors I tinker with these days have two 32-bit timers, and for all I know maybe those can be chained together. 64-bits should to be enough for anybody?
« Last Edit: September 30, 2022, 09:24:24 pm by ozcar »
 

Offline ledtester

  • Super Contributor
  • ***
  • Posts: 2799
  • Country: us
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #9 on: September 30, 2022, 09:27:50 pm »
So I tried this on an ATMEGA128 board that I had lying around. ...

Thanks for testing this out, even though it didn't work!
 

Online magic

  • Super Contributor
  • ***
  • Posts: 5412
  • Country: pl
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #10 on: October 01, 2022, 06:11:27 am »
If the captured signal is not synchronous with the MCU clock there is no guarantee that two I/O pins will pick it up in the same cycle. Perhaps you could figure out some way of cleaning it up in software.

So I tried this on an ATMEGA128 board that I had lying around. Like the ATMEGA32U4, that has two 16-bit timers that have input capture. Sure enough, with the two ICP pins tied together (in a not very tidy fashion), and the two timers synchronised and clocked at the same rate, the capture counts are occasionally out by one. Just for the heck of it, I changed the test signal to a 1kHz ramp, and with the timers running at 16MHz, the captured counts can be out by 40 or 50.
Yep, this was expected :P

However, I think you could work around it. Since you only want 24 bits of resolution, you could run the slower 16 bit timer at /256 rate rather than /65536 so that the low byte of the slow timer is equal to the high byte of the fast timer. Then you could verify that the captured middle bytes are equal, and if not, check if the highest byte read from the slow timer requires correction.

If you can guarantee that rise time is fast enough for the offset to be at most one cycle 32767 cycles 8), the scheme could be extended even to 31 bits of resolution if a /32768 prescaler is available.

This technique is completely timing insensitive, you could read the ICR registers millions of cycles after the captured event and still get the correct time, as long as the timers (or one of them (!)) don't capture a later event. (IIRC, AVR overwrites ICRs on each event, I don't think there is a one-shot mode.)
« Last Edit: October 01, 2022, 06:15:36 am by magic »
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 118
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #11 on: October 01, 2022, 12:24:27 pm »
This technique is completely timing insensitive, you could read the ICR registers millions of cycles after the captured event and still get the correct time, as long as the timers (or one of them (!)) don't capture a later event. (IIRC, AVR overwrites ICRs on each event, I don't think there is a one-shot mode.)

Did you ever needed it: capture today - readout at christmas?

The easiest way has been shown above: rn-wissen.de. Only one timer is used and no special µC. I've done so since AT90S2313-10PU ;-)

Capture events has to be handled first and after that take a look at overflows. At some controllers it can be difficult, to handle the right priority.

Maybe "ledtester" likes to use STM32 one day:
Some timers of STM32Fxxx use different ISR-vectors for capture and overflow events so overflows would be handled first.
TIM1_UP_TIM10-vector (position 25) of STM32F407 has higher priority then TIM1_CC-vector (position 27). To avoid incorrect readout TIM1_UP_TIM10-handler should call TIM1_CC-handler first before counting overflows itself.
Another way is not using separated overflow-ISRs but take a second timer channel to generate a compare event triggered by compare value 0x0000. So capture and compare ISR-hanlder is the same where capture events can be handled first as required.
 

Offline ozcar

  • Frequent Contributor
  • **
  • Posts: 269
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #12 on: October 01, 2022, 08:18:22 pm »
Maybe "ledtester" likes to use STM32 one day:
Some timers of STM32Fxxx use different ISR-vectors for capture and overflow events so overflows would be handled first.
TIM1_UP_TIM10-vector (position 25) of STM32F407 has higher priority then TIM1_CC-vector (position 27). To avoid incorrect readout TIM1_UP_TIM10-handler should call TIM1_CC-handler first before counting overflows itself.
Another way is not using separated overflow-ISRs but take a second timer channel to generate a compare event triggered by compare value 0x0000. So capture and compare ISR-hanlder is the same where capture events can be handled first as required.

STM32F4 processors have two 32-bit timers, each with four capture channels. Capture can also trigger DMA.

 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1465
  • Country: gb
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #13 on: October 02, 2022, 10:24:23 pm »
I vaguely remember wrestling with this problem several years ago using PIC's CCP to capture RPM accurately.

IIRC, I used a software timer extension to get 24-bit from the 16-bit timer, but had to add a check in the capture ISR if the timer had overflowed whilst servicing that particular interrupt, then, check the value of the timer to see if the overflow had occured just before, or just after, the capture (if it had overflowed just before the new capture, the new capture value would be close to 0, since it had just rolled over).

It's been a long time, but I had to run several tests because occassionally, without the above hack/fix, it would glitch out. 

I might flash a chip and test it out if I can dig out the firmware.
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 118
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #14 on: October 03, 2022, 08:22:56 am »
STM32F4 processors have two 32-bit timers, each with four capture channels. Capture can also trigger DMA.

Yes, STM32F407 has two timers TIM2+TIM5 offering 32 bits at Hclk/2 (84 MHz). I don't see DMA would help.

What do you do if you need full speed (168 MHz) for max. timing resolution or ext. event counting?
What do you do if TIM2/TIM5 pins are locked by other functions (USART, USB, ETH, ...)?
What do you do if you need more capture channels?
What do you do if you need more bits (40, 48, ...)? 32 bit counters @168 MHz will overflow after about 25 s.

Please don't tell me about STM32H7xx or any other µC or FPGA.
 

Offline hgl

  • Regular Contributor
  • *
  • Posts: 101
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #15 on: October 03, 2022, 02:59:30 pm »
The STMF4 chips have an internal peripheral interconnect matrix and there are 4 lines reserved for timer interconnection.
The master timer sends its overflow via the TRGO event to the matrix and the slave timer receives it via ITR0.... ITR3 and use it as clock input. For example STM32F411 (black pill):  TIM5-> TIM3 -> TIM2 -> TIM4 results in 32+16+32+16 = 96 bits. The capture inputs can then be connected externally in parallel. No CPU cycles, no Interupt, no DMA are necessary only the capture event generates an interrupt to read the 4 timer capture registers. 
 

Offline PCB.Wiz

  • Frequent Contributor
  • **
  • Posts: 645
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #16 on: October 03, 2022, 07:19:06 pm »
Yes, STM32F407 has two timers TIM2+TIM5 offering 32 bits at Hclk/2 (84 MHz). I don't see DMA would help.

What do you do if you need full speed (168 MHz) for max. timing resolution or ext. event counting?
What do you do if TIM2/TIM5 pins are locked by other functions (USART, USB, ETH, ...)?
What do you do if you need more capture channels?
What do you do if you need more bits (40, 48, ...)? 32 bit counters @168 MHz will overflow after about 25 s.

Please don't tell me about STM32H7xx or any other µC or FPGA.

If you do not want a solution, why ask the questions ?
The Parallax P2 has 60+ 32b capture channels, and a 64b system counter, so it can cover most capture problems.
For very narrow pulse captures, you would use two channels one _/= and one =\_
_
 

Offline PCB.Wiz

  • Frequent Contributor
  • **
  • Posts: 645
  • Country: au
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #17 on: October 03, 2022, 07:21:31 pm »
The STMF4 chips have an internal peripheral interconnect matrix and there are 4 lines reserved for timer interconnection.
The master timer sends its overflow via the TRGO event to the matrix and the slave timer receives it via ITR0.... ITR3 and use it as clock input. For example STM32F411 (black pill):  TIM5-> TIM3 -> TIM2 -> TIM4 results in 32+16+32+16 = 96 bits. The capture inputs can then be connected externally in parallel. No CPU cycles, no Interupt, no DMA are necessary only the capture event generates an interrupt to read the 4 timer capture registers.
Has anyone tried that ?  - Do the delays through the matrix cause overflow skews ? (lower timer counts not exactly on the 0xffff -> 0x0000 edge ?)
 

Online Kleinstein

  • Super Contributor
  • ***
  • Posts: 12225
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #18 on: October 03, 2022, 07:31:48 pm »
Using different capture pins combined may cause a race condition and thus the need to handle special cases with the overflow separate, similar to the linked code from the 2nd post. So the HW coupling of additional counter may be of limited use as one may still check for boarderline cases and essentially also count in SW.
 

Offline hgl

  • Regular Contributor
  • *
  • Posts: 101
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #19 on: October 03, 2022, 09:25:47 pm »
The chips are fully synchronous so there are no delays in the timer clocks. I was unclear, the master overflow enabled the slave timer for the next system clock would have been more correct. The capture inputs have a delay of 2 or 3 system clocks to synchronize and unless one line is much longer in parallel it should be the same for all timers. The principle for 2 timers in series is described in a ST Timer document.

I have not tried this case because I have never used more than 32 bit. 


I built a pulse generator this way with one timer as master which outputs a sync pulse after a delay and triggers parallel 4 slave timers which generate output pulses with different delay and pulse width.  In this case I used the compare function and the slave timers are not triggered by the overflow but by the capture event of the sync pulse and run all with system clock.     

For STM32F411 max system clock is 100MHz or 96 MHz if USB is used.
 

Offline hgl

  • Regular Contributor
  • *
  • Posts: 101
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #20 on: October 03, 2022, 09:46:40 pm »
Quote
STM32F4 processors have two 32-bit timers, each with four capture channels. Capture can also trigger DMA.

These 4 capture channels use the same counter and the counter determines the resolution.
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 118
  • Country: de
Re: AVR: Creating a 24-bit Input Capture Timer
« Reply #21 on: October 04, 2022, 10:36:05 am »
If you do not want a solution, why ask the questions ?

Cascading capture counters I've done very, very often. As I said the solution has been shown above: counting overflows by software and synchronise it at last.
On ATtiny44 I used 10 bit fast PWM (Timer1). The 10 bit capture result was expanded to 32 bit to get exatly timing up to 100 seconds. That's no problem if you know what has to be done.
No one needs Parallax or any other special device.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf