How to increase Atmega Timer resolution?

Go To Last Post
58 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everybody,

in my design i need a frequency counter but, for the resolution of 8/16 bits timers i have and the system clock frequency, i should have at least 18-20 bits.
My system clock is 16MHz, the frequency i'm going to measure is up to 1MHz, i need 5 readings/seconds so i use the 16 bit timer 1 to get a 200mSec gate where
i add up how many overflows had the 8 bits timer 0, plus its actual value in TCNT0.
So, i tried to nest the 8 bit timer 0 to the 16 bit timer 1 in this simple way:

void timer_init(void) 
{
	TCCR0 =7;  /*clock from input pin */
	TCCR1B=5; /*clock from fsystem / 1024 */

	TIMSK=(1<<TOIE0) | (1<<TOIE1);	
}

ISR(SIG_OVERFLOW0) /* 8 bit tctr*/
{
	fctr++;	
}
ISR (SIG_OVERFLOW1) //16 bit tctr 200mS interrupt 
{
static unsigned char cnt=0;
TCCR0=0;//stop freq. input to timer0

	if (TIFR & (1 << TOV0)) //check if it reached overflow
	{
		fctr++;
		TCNT0=0;
	} 
		TIFR |= (1 << TOV0); //anyway clear overflow

	fvalue =  (fctr * 256) + TCNT0; //calculate frequency  
	PORTD ^=(1<<PD7); //just a test point
	events |= DONE;//signal to main routine that a new freq. value is ready
		
	TCNT0=0; //clear counters
	fctr=0;  //clear counters
	TCNT1=62322; //reload timer1 for 200mS timeout
TCCR0=7; //enable timer0 clock input
}

Everything seems more or less fine, the problem
is cooking my mind is that there is a sudden step in the value i get when the input frequency is around an integer value of timer counter 0, that is:

fq: input 1.000MHz
fctr value: 781
TCNT0 value: 64
fvalue: 200000
in this case all is ok, fvalue jumps +/- 1 digit back and forth and this is normal.

fq: input 1.00096 MHz
fctr value: 782
TCNT0 value: 0
fvalue: 200192
in this case i have the problem, because TCNT0 jumps from 255 to 0 (and it is normal) but also sometimes from 0 to 5 (why 5??).

I think the problem is caused by the concurrence of the two interrupts that, when the input frequency is an even dividend of the two, cause the interrupts to overlap in some way.

Any suggestion welcome.

Thanks
Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have posted a similar code https://www.avrfreaks.net/index.p... but for 100ms count period, I didn't notice any problem with it but I don't access the Timer0 overflow flag from the timer1 interrupt.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Alex,

first of all many thanks, i tested your code right now.
It seems that it does not present the problem i described, although it seems to have a +/- 2 units of precision (?).
If i measure, say 1.000MHz, the reading continously jumps from 1.001 1.002 1.003 1.002 1.000 1.002 1.000 .
With my code i was reading 1.001 0.999 1.000 0.999 ...

Had you ever noticed it or is it my fault?

Bye
Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The routine is sampling 100ms periods and then multiply by 10.
If you sample for 200ms and multiply by 5 the error will be reduced.
If you sample for 500ms and multiply by 2 the error will be reduced even more.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With your code you will always have jitter.

First, use Timer1 in CTC mode. With that you can get an interrupt at exactly 200ms intervals.

Second, don't stop or clear Timer0, only read the value (and the state of the overflow flag), compare it with the previously read value, then save off the current value to compare to the next time.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
the error will be reduced
Only if the frequency is constant.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Alex,

yes, this make sense, but i modified your code to
sample at 200mS so it should have the same
error as mine.

I cannot explain why, but my code, athough buggy, seems to have a rock-steady reading (i may post a video) which filckers only 1 unit. (1.000 1.001 1.000 1.001..) as it should be.
Maybe because i do stop the 8bits timer, like you do, but i never stop the 200mS timer; this seems to me the most evident difference.
I think that if you stop both timers, you increase the uncertainity error because when you start both them again, they will start effectively at different times.
I'll try this.

Bye
Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Any gated period frequency counter will have a minimum +1 to -1 count error in every measurement. This error is because the start/stop of the counter gate period is not synchronized with the input frequency pulse timing. This result of this +/- 1 count error is affected by the length of the gated period.

uint32_t measure_frequency(void)
{
…...
    while(timer1_ovf != 1);  // wait for flag
    return (((TCNT0 + (256UL * timer0_ovf)) * 10));

This works, but is not optimal for high accuracy since it takes time for the software to poll the timer1_ovf and then capture the TCNT0 result (read the TCNT0 value), all while TCNT is still running. It also has a very tiny vulnerability if the still running timer0 overflows and runs the overflow0 ISR immediately after TCNT0 is read. This is an error of 256 counts and this tiny vulnerability window is every 256 TCNT0 counts, but it is not likely to occur because the window is so small. It could still result in random occasional result errors.

uint32_t measure_frequency(void)
{
    unsigned long freq;
…...
    while(timer1_ovf != 1);  // wait for flag
    cli();                                // however your compiler does this
    freq = (((TCNT0 + (256UL * timer0_ovf)) * 10));
    sei();                                // however your compiler does this
    return (freq);

This will eliminate the vulnerability. WinAVR / GCCAVR uses #include ATOMIC_BLOCK() to prevent C compiler optimization of when the cli() and sei() occur in the code, so this needs to be changed for this compiler. Only if needed, CodeVision uses #pragma optsize- and #pragma optsize+ to selectively disable C compiler optimization.

You still have the polling loop detection time and the time to read TCNT0. If you really want to eliminate this, one way would be to use the 16 bit timer ICP input pin as the measurement gate period input and feed the frequency input pulses to the 16 bit timer clock input pin.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
uint32_t measure_frequency(void)
{
…...
    while(timer1_ovf != 1);  // wait for flag
    return (((TCNT0 + (256UL * timer0_ovf)) * 10)); 

Ok, Mike,
got what you mean, but if you look at Alex's code, when timer1 overflows it will block both timer0 and timer1 (here's my adaptation from Alex' original).

ISR (SIG_OVERFLOW1) //16 bit tctr interrupt 200mS
{
	TCCR0=0;
	TCCR1B=0;
	events |= DONE;
}

So when i access TCNT0, there should not be any problem.

Anyway i modified also the measure_frequency routine according your suggestion:

unsigned long measure_frequency(void)
{
	unsigned long freq;
	TCNT1 = 62410;
	TCNT0=0x00; 
	events=0;
	fctr=0;
	_delay_ms(1);

	TCCR1B=5; /*clock from clk / 1024 */
	TCCR0 =7;  /*clock from input pin */
	while(events == 0);  // wait for flag
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE);
        cli();
	freq=(TCNT0 + (256UL * fctr));
        sei();
	return freq;//(TCNT0 + (256UL * fctr));
}

but nothing has changed.
Btw, i was wondering why compiler (AtmelStudio6) is executing no code after ATOMIC_BLOCK(ATOMIC_RESTORESTATE);
Looking at the assembler, it saves sreg, then disables interrupts, then restore sreg (below what i mean).

	cli
 ;  0 "" 2
.LVL2:
/* #NOAPP */
.LBE13:
.LBE12:
.LBB14:
.LBB15:
	.loc 3 70 0
	out __SREG__,r24
	.loc 3 71 0
.LBE15:
.LBE14:
.LBE11:
	.loc 1 61 0
/* #APP */

It should save sreg, execute code and, before exiting, restore sreg..shouldn't it?
I added those cli() and sei() to force interrupt disabling when i calculate the frequency, but no success.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:

yes, this make sense, but i modified your code to
sample at 200mS so it should have the same error as mine.

I guess there is room for improvement in my code.
I have used it for an inductance meter based on the oscillator frequency (about 400KHz range) and sampling for 500ms and was accurate enough so I didn't work on it any further.

The only think I had different was that I was using a slightly higher TCNT1 value to account for the clocks I loose from overflow of timer 1 until I reach the interrupt and turn off the timer0 counter, that improves the accuracy.

Koshchi wrote:
Quote:
the error will be reduced
Only if the frequency is constant.

But the accuracy can only be compared for the same frequency, I don't see another way.
For any input if you try to sample for 100ms, 200ms, 500ms, 1sec you will get the most accurate result for 1sec.

Mike B wrote:

uint32_t measure_frequency(void)
{
…...
    while(timer1_ovf != 1);  // wait for flag
    return (((TCNT0 + (256UL * timer0_ovf)) * 10));

This works, but is not optimal for high accuracy since it takes time for the software to poll the timer1_ovf and then capture the TCNT0 result (read the TCNT0 value), all while TCNT is still running.

Timer 0 is not running, neither is timer 1 , both timers are stopped inside timer 1 interrupt when the sampling duration expires so polling the end flag doesn't introduce a problem no matter how long it takes.
Inside timer 1 interrupt in my code you will see

#define DISABLE_TIMER1    TCCR1B=0x00;
#define DISABLE_TIMER0    TCCR0B=0x00; 

// Timer1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
    DISABLE_TIMER1
    DISABLE_TIMER0
    timer1_ovf = 1;      // flag that measure has finished
} 

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

Last Edited: Tue. Dec 11, 2012 - 06:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
This works, but is not optimal for high accuracy since it takes time for the software to poll the timer1_ovf and then capture the TCNT0 result (read the TCNT0 value), all while TCNT is still running. It also has a very tiny vulnerability if the still running timer0 overflows and runs the overflow0 ISR immediately after TCNT0 is read. This is an error of 256 counts and this tiny vulnerability window is every 256 TCNT0 counts, but it is not likely to occur because the window is so small. It could still result in random occasional result errors.

I just woke up, so perhaps it will be clearer after a little caffeine...

But it wasn't clear to me why the vulnerability window is so small.

Every time the TC1 overflow fires and one reads the TC0 value, with the TC0 still running, isn't there a 1 in 256 chance that it will trip the TC0 ISR? Hence the "error" where one has an overflow in the middle of the read sequence would statistically be expected to occur 1 in 256 times.

I would consider this to be more frequent than "not likely to occur", but maybe I missed the concept and misinterpreted the occurence rate.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:

	ATOMIC_BLOCK(ATOMIC_RESTORESTATE);
        cli();
	freq=(TCNT0 + (256UL * fctr));
        sei();


I think you're supposed to do it like this:
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
    freq=(TCNT0 + (256UL * fctr));
}

Notice that cli() and sei() are redundant - that's what the ATOMIC_BLOCK is supposed to do for you.

Sid

Life... is a state of mind

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oops, I missed that timer shutoff code execution sequence and stand corrected.

So the response time for the ISR TIMER1_OVF_vect to stop the timers is the only inherent accuracy problem. The polling code response time is usually faster than the ISR response time, but what happens in a more complicated program where the AVR global interrupt system may be disabled by other non-interrupt or executing interrupt code, when the ISR TIMER1_OVF_vect is triggered? For a global non-interrupt code disable, the polling while loop would complete and calculate the frequency while TCNT0 is still running. It might even be more accurate than the ISR unless another ISR is running to block the polling execution.

To use this frequency counter code and maintain accuracy, you had better be very careful with what you do with any other AVR interrupt code. It is kind of fragile when the other AVR interrupt vectors get significant usage. The only generic solution I see is to include the while polling and frequency calculation in the cli()/sei() body, then make sure the timer1 overflow clears before starting a new frequency measurement. This global interrupt clear has the drawback of limiting the availability of AVR interrupts during the polling, but it ensures other AVR interrupts will not interfere with the frequency counter accuracy. But this does not work because the global interrupt disable prevents the timer0 overflow interrupt and breaks the code. On the other hand if you dedicate the entire AVR chip interrupt system to just measuring frequency, then what you have is not fragile and works just fine.

A minor tune up would be to do the DISABLE_TIMER0 first in the ISR, since TCNT0 is your critical measurement data source.

DocJC wrote:
But it wasn't clear to me why the vulnerability window is so small.
The window for the vulnerability was based on the TCNT and timer0_ovf reads being atomic, incorrectly assuming TCNT0 was still counting. The way to make this not atomic requires updating the ISR timer0_ovf value exactly between these two reads. Depending on the compiler generated code this might be only a single CPU cycle vulnerability window.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Mike,

Got it.

The window's occurence is rare, but based upon the method used, even when the window "hits" the probability of the error occuring will still be only 1/256, making it even "rarer".

Off topic: A "random occassional result error", from a rarely occuring, dualing interrupts issue is what pushed me into buying a DSO a few years ago...

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass,

If you are using WinAVR / GCCAVR then SIG_OVERFLOW1 is depreciated, as in no longer used in new source code. In fact SIG_OVERFLOW1 may disappear in a future release of these compilers. Use the ISR vector names Alex used (assuming you are using the same AVR chip as Alex) or consult the compiler manual for the correct interrupt vector names. If you have never looked at the compiler manual then it would be a very good thing to learn how to do.

Sorry, as in my last post above I was wrong about the cli()/sei() for Alex's code, but there are other possible problems.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please have a look at these videos:

https://docs.google.com/file/d/0...
https://docs.google.com/file/d/0...
https://docs.google.com/file/d/0...
(dunno why cannot attach them directly)

alex.mov (the first one) is Alex code running with attached a frequency of 1.000MHz.
You got a reading of 2000015 (the error is due to the system clock crystal which is not correctly trimmed).

tom.mov (the second)is my code when performing correctly.
Below the frequency we have the timer0 overflows (781) and the timer0 remainder when the gate time is expired.

tom_anomaly.mov (the third link) is my code running around timer0 overflow zone, i raise the input frequency by 10Hz every 5 seconds and look what happens:
0..5 secs: all ok, the flickering is 1 unit
5..10 secs: the frequency is raised so timer0 interrupt is often concurrent with timer1 interrupt...some mess.
10..15 secs: frequency is raised again, now timer0 reminder always read 0, no flickering at all (i cannot believe this also...it is not normal).
15..20 secs: frequency is raised further, now flickering of the remainder is between 0 and 5 (??).

Where's the exorcist?

Regarding:

Quote:

I think you're supposed to do it like this:
Code:

ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
freq=(TCNT0 + (256UL * fctr));
}

Notice that cli() and sei() are redundant - that's what the ATOMIC_BLOCK is supposed to do for you.


It's ok, thanks ChaunceyGardiner.
I'd never used ATOMIC_BLOCK before (i always did
it directly with sei and cli) and i didn't know how to use it correctly.

Quote:

if you are using WinAVR / GCCAVR then SIG_OVERFLOW1 is depreciated, as in no longer used in new source code. In fact SIG_OVERFLOW1 may disappear in a future release of these compilers.

Yes Mike, i'm using AtmelStudio which is based on Winavr/GCC ... the names was "vintage style" because i reused a unit made under old winavr, so the names remained the same.
I renamed them to TIMERx_OVF_vect, but nothing changed.

Last Edited: Wed. Dec 12, 2012 - 08:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:
I renamed them to TIMERx_OVF_vect, but nothing changed.
Correct, the only thing that changed is the compatibility of your source code with future releases of WinAVR / GCCAVR. For example, the next update of AVRstudio could remove the old SIG_OVERFLOW1 and break your old source code.

This also means if you copy this program code for another project in the future (just like you did with your older code for this project) you will have better source code to start with.

Also be aware that some AVR chips use TIMER0_OVF0_vect while most AVR chips use TIMER0_OVF_vect. The compiler manual will tell which AVR chips use which interrupt vector names. The different interrupt vector names also occur for other interrupt vectors, not just timer overflow vectors.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
i should have at least 18-20 bits.
If you are not locked into any chip yet the Xmega can concatenate 2 16 bit timers to make a 32 bit timer in hardware.

No, I don't know how to do it yet myself. :-) part of some homework I neve completed.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Js,

i know Xmegas, i had worked on them.
I could use one Xmega, but i would like to know why such a thing cannot run on a normal Atmega.
It has become a matter of principle.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course this works on an ATmega. To increase accuracy:
1) Use one timer to generate the gate time period on an output pin. Either an 8 or 16 bit timer as required.
2) Connect the gate period timer output pin to a 16 bit timer Input CaPture (ICPn) input pin.
3) Enable the 16 bit timer overflow interrupt and use an unsigned 16 bit integer overflow variable.
4) Connect the frequency source to the 16 bit timer clock Tn input pin.
5) Let the 16 bit timer free run (do not stop or change this timer).

Set the 16 bit timer ICESn to a one, enable the ICPn interrupt and start the gate period timer. When the positive edge ICPn interrupt is triggered, read the ICRnH / ICRnL registers along with the unsigned overflow variable and save this as the unsigned long starting reading. Then change the ICESn pin to a zero. Later when the negative edge ICPn interrupt is triggered, read the ICRnH / ICRnL registers along with the unsigned overflow variable and subtract this unsigned long result from the unsigned long starting reading. Now you have the exact number of counts while the gate period timer output was high. This negative edge ICPn trigger should also disable the gate period timer until the next time you want to do another frequency reading, when the gate period timer is started over again.

The gate period timer can use a CTC toggle output mode, starting its initial count at TOP minus 1. The first TOP will quickly produce the positive edge gate starting value. The next TOP will produce the negative edge gate stop value. This leaves lots of extra time to stop this gate period timer after it is done. It appears a dual slope Phase Correct PWM mode might be used to double an 8 bit timer maximum gate period?

Because the ICRnH / ICRnL registers have captured the frequency count in hardware, the time it takes to respond to the ICPn interrupt is not critical. You do need to respond within a reasonable amount of time, it is just your response time does not affect the frequency counter accuracy.

Because you have an unsigned long starting value and an unsigned long final value, the final-starting subtraction does not need to zero or change the 16 bit timer TCNTn value. This 32 bit subtraction always works because it is unsigned 32 bit values combined with 32 bit unsigned integer subtraction.

Notice the 16 bit timer overflow is read and used in the ICPn ISR. Because the ICPn interrupt has a higher AVR interrupt vector priority than the 16 bit overflow AVR interrupt vector priority, the overflow interrupt cannot execute until the ICPn interrupt is done. There is one correction to make. If both the ICPn and overflow interrupts trigger and are active at the same time, the higher AVR interrupt vector priority ICPn interrupt always wins priority arbitration, even if the overflow interrupt happened first. The fix is while in the ICPn ISR, check for the overflow interrupt flag. If this overflow interrupt flag is set, look at the 16 bit ICRnH / ICRnL register value. If it is a high value above 0x8000 then the ICPn interrupt was first and nothing needs to be done. If it is a low value below 0x8000 then the overflow interrupt happened first and the overflow variable needs to be incremented before using its value in the ICPn ISR, then the overflow interrupt flag is cleared by your ICPn ISR software since overflow was already incremented.

The maximum input frequency is determined by the ATMEL recommended AVR clock speed divided by 2.5. So, a 20 MHz AVR can accept a maximum Tn input pin frequency of 8 MHz. This information is from a data sheet timer/counter prescaler section.

The reasonable response time to the ICPn ISR is determined by the 16 bit 0x8000 value, or within 32,768 TCNTn 16 bit counts maximum. If exceeded the interrupt vector priority conflict detection fails. This is allot of time to respond to the ICPn ISR. If you immediately turn off the Tn clock input pin after a frequency measurement you have until you want to do another new frequency measurement before you need to respond to the ICPn interrupt (an as long as you want time to respond).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:
Quote:
This works, but is not optimal for high accuracy since it takes time for the software to poll the timer1_ovf and then capture the TCNT0 result (read the TCNT0 value), all while TCNT is still running. It also has a very tiny vulnerability if the still running timer0 overflows and runs the overflow0 ISR immediately after TCNT0 is read. This is an error of 256 counts and this tiny vulnerability window is every 256 TCNT0 counts, but it is not likely to occur because the window is so small. It could still result in random occasional result errors.

Every time the TC1 overflow fires and one reads the TC0 value, with the TC0 still running, isn't there a 1 in 256 chance that it will trip the TC0 ISR? Hence the "error" where one has an overflow in the middle of the read sequence would statistically be expected to occur 1 in 256 times.

I would consider this to be more frequent than "not likely to occur", but maybe I missed the concept and misinterpreted the occurence rate.

JC

It's a late reply but I just processed what you said.
Assume that the timer1 overflow fires and you get in the timer 1 interrupt but timer0 is still running so you get an overflow just before you have a change to turn off timer0.
You say that you will get a error because you don't account for this overflow but I thing there is no such case because as soon as the timer 0 overflow occurs it will set the overflow flag so as soon as you exit the timer 1 interrupt handler you will execute the waiting timer 0 interrupt handler (I never disable interrupts) which will increment the overflow flag.
So by the time that the execution reaches the line in main which polls for the end of measurement flag the overflows counter will be correct.

Alex

PS maybe you don't refer to my code that reads the result out of the interrupt but to the version that reads the result in timer 1 interrupt in which case what I wrote doesn't apply

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Mike,

what an opera!
Many thanks to have found the time to write down all this ingenious method.

I have some questions:

Quote:

1) Use one timer to generate the gate time period on an output pin. Either an 8 or 16 bit timer as required.
2) Connect the gate period timer output pin to a 16 bit timer Input CaPture (ICPn) input pin.

Ok, i have 16mhz system clock and i need a gate of 200mS.
I cannot get this period out of a 8 bit timer, the maximum is about 16mS, so i should use a 16 bit timer for the gate, but then i'll not have another 16bit timer for frequency input.
May i count, say, 20 overflows of an 8 bit timer running for 10mS timeout?
I should count into this timer ISR and toggle the output pin every 20 overflows.
Is there any problem in doing this ?

Quote:

Set the 16 bit timer ICESn to a one, enable the ICPn interrupt and start the gate period timer. When the positive edge ICPn interrupt is triggered, read the ICRnH / ICRnL registers along with the unsigned overflow variable and save this as the unsigned long starting reading. Then change the ICESn pin to a zero. Later when the negative edge ICPn interrupt is triggered, read the ICRnH / ICRnL registers along with the unsigned overflow variable and subtract this unsigned long result from the unsigned long starting reading. Now you have the exact number of counts while the gate period timer output was high. This negative edge ICPn trigger .....

Got it, but since the measurement has to be always running, may i trigger on every, say, rising edge and take the 32 bits difference from the preceding one?

I cannot try all this right now, but i will in one/two days, then i'll be back with results.

Sooo many thanks for the moment!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

It has become a matter of principle.

I really don't see why there should be a lot of fuss about this. But I can be convinced, if I'd know more about the app...

1) Does it really make any difference in the app if 123456 or 123457 is reported?
2) If you say in response to 1) "Yes, it IS important" then remember the note earlier: "... if the frequency remains constant...". How often are you going to record or update the display with your frequency? Be careful how you answer that...

2a) If you answer "once a second" that is pretty slow--the frequncy could be wildly ramping.
2b) If you answer "continually" that is virtually useless for display.
2c) If you answer "a few times a second" that is appropriate for display. But then do you want the "[nearly] exact value" for the past 250ms, or the total over the past e.g. second?

Indeed, I've had apps where I extended the counter a bit. (High-speed industrial ultrasonic distance sensor, for one.) The overflows were only a few. But for usual AVR work, I "empty the bucket" of the 16-bit counter before it overflows. If it is 1MHz max as mentioned I might use 1/16 second samples and then keep the last second's worth in a circular buffer. Indeed there still needs to be care taken when emptying the bucket but the leftovers will usually then go to the next bucket. Display can use the latest bucket or the last second total. Again, what exactly is the 123456 vs. 123457 to represent?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Tomass wrote:
May i count, say, 20 overflows of an 8 bit timer running for 10mS timeout? I should count into this timer ISR and toggle the output pin every 20 overflows. Is there any problem in doing this?
Using an ISR to generate/control gate period timing is part of the original accuracy problem. Using hardware for this instead is what overcame this problem. When interrupt response time is critical, the use of the AVR interrupt system for other tasks becomes an accuracy problem unless you dedicate the entire AVR interrupt system for frequency measurements (while you are actually measuring frequency). There is nothing wrong with the original program if you dedicate these resources.

At 16 MHz with a 1024 prescaler you cannot get simple integer .01 second gate periods (you get 0.009984). Can you select a different AVR clock speed? Like a common USART baud rate friendly 14.7456 MHz crystal will produce exact .01 second gate periods.

Consider a .025 second gate period. Now you multiply the count by a nice quick binary 4 to get your frequency in Hz. This has the same 16 MHz gate period timing problem that something like using a 14.7456 MHz crystal will solve. A .0125 second gate would be multiply by 8.

Did you look at using dual slope Phase Correct PWM mode? This counts up and down with a range of 256 for each slope. Frankly I have not looked into this and there are limitations, so I do not know for sure it is usable or not.

Tomass wrote:
Got it, but since the measurement has to be always running, may i trigger on every, say, rising edge and take the 32 bits difference from the preceding one?
Great plan. If you only trigger on a single edge of the gate period timer output pin, then using timer toggle output mode doubles the time of each gate period for same edge to same edge output pin timing (and you do not change the ICESn value). This change should be able to get your .02 ms timing with a CTC mode 8 bit timer. The difference is you do not get an easy to identify very first frequency measurement count. So, your very first frequency measurement will have a garbage result that you have to accept, or you will need extra program logic to detect and reject the very first garbage reading.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There goes Lee being practical again :).

Tomass wrote:
I could use one Xmega, but i would like to know why such a thing cannot run on a normal Atmega. It has become a matter of principle.
If nothing else this is a good learning exercise and the OP insisted on improved accuracy.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure that all this discussion about accuracy can have a significant effect in a real circuit.
For example you worry for a 10Hz error in a 1MHz measurement, this is just 10ppm.
I think you forget that all these measurements are based on the mcu crystal and at best can be as accurate as the crystal so my question is what kind of crystal do you intend to use and how much error will it introduce?
If you are going to use a 50ppm crystal do you thing that a 1 or 5 or 10ppm accuracy for the measuring process will make any difference?

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is one really valuable lesson Lee has for all of us. What we want is not always what we need. For example, we get irritated when a frequency reading alternates between 10000 to 09999 and we want it to stop doing that, but does it really matter at all to our embedded application? I think usually not. As people gain experience over time, they eventually learn what they really need and do not waste time on distractions. However, discussions like this build experience and understanding about how/what we are doing (I hope). If you gain enough experience like Lee has, then you also become practical :).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I really don't see why there should be a lot of fuss about this. But I can be convinced, if I'd know more about the app...

Usually i hate to do trickery and fill the mcu to the ultimate clock cycle, so i have no problems in using the Xmega.
What i was meaning for a "matter of principle" is exactly what Mike said:
Quote:

If nothing else this is a good learning exercise and the OP insisted on improved accuracy.

Simply i would like to learn for the future if such a resolution extension can be made without assembler black magic, or other trickery and i think it could be useful to other people also.
Anyway:
Quote:

1) Does it really make any difference in the app if 123456 or 123457 is reported?

If i got what you mean, no, at all. It is the normal error.
The problem is that with my code i get this right behaviour:
https://docs.google.com/file/d/0...
but when interrupts are concurrent i get some mess:
https://docs.google.com/file/d/0...
you can see what happens when the TCNT0 is near overflowing (the number on the bottom row). In the above video i raise the frequency every 5 seconds.
With Alex's code, i don't have this problem, but you can see that the last figure has about +/- 2 units of flickering (??):
https://docs.google.com/file/d/0...
Quote:

2a) If you answer "once a second" that is pretty slow--the frequncy could be wildly ramping.
2b) If you answer "co....

Once a second would be good.
Quote:

If it is 1MHz max as mentioned I might use 1/16 second samples and then keep the last second's worth in a circular buffer. Indeed there still needs to be care taken when emptying the bucket but the leftovers will usually then go to the next bucket. Display can use the latest bucket or the last second total. Again, what exactly is the 123456 vs. 123457 to represent?

Essentially i work in the RF field and often i need to wind inductances or measure them.
I owe an HP network analyzer, but i cannot stand to use it just to measure inductances.
So i found that omnipresent-widespread diagram with the LM311 (i substituted with ST TS3022) multivibrator and modified it for my needs.
As an exercise i'm writing the firmware myself.
First, for the resolution i need (at least 1nH), the frequency generated has to be higher than 1MHz, now i'm using 4MHz maximum and i'm quite satisfied.
Now the problem is to correct the resonant circuit temperature drift, but this would not be a problem.
Back to your first question, if the last figure of frequency counter "dance" around +/- 1, depending on the inductance i'm measuring, so the frequency generated, i have the reading dancing around this value roughly squared.
This is not a concern if you are measuring a 100uH inductance, but if you have to build a 100nH inductor, the error i got, having a frequency error of +/- 2 units, was about (if i remember well) +/- 20nH ... too much.

So, initially the idea was to build this inductance meter, then i had this problem in extending the timer resolution so now i'm curious if it can be solved.
If i was designing a "professional" instrument or your industrial ultrasonic meter, i had used immediately a bigger micro.

Quote:

I'm not sure that all this discussion about accuracy can have a significant effect in a real circuit.
For example you worry for a 10Hz error in a 1MHz measurement, this is just 10ppm.

Alex, i'm not struggling with 10Hz error, what you say is perfectly correct.
What initially i was asking you is why, using your code (see attached videos), i get +/- 2 units of error on frequency reading while with my buggy code just +/-1.

Last Edited: Wed. Dec 12, 2012 - 07:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually I wasn't able to view any of your videos because 4share asks me to register and I don't want to register just to watch a video.
Why not choose any open file sharing service like dropbox, google drive, mediafire etc?

Quote:

What initially i was asking you is why, using your code (see attached videos), i get +/- 2 units of error on frequency reading while with my buggy code just +/-1.

I'm not sure why it happens but I think it can be trimmed if the timer1 gating period is reduced to account for the overhead cycles until the timer 1 interrupt is executed and timer0 turned off.
The duration now is 1sec+interrupt overhead and in high freq this will make a difference.
The higher the speed of timer 1 (timer freq) the best trimming you can achieve

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Actually I wasn't able to view any of your videos because 4share asks me to register and I don't want to register just to watch a video.
Why not choose any open file sharing service like dropbox, google drive, mediafire etc?

nice...i wasn't remember that 4share was asking to register.. sorry.
Here are the google drive links:
your code
https://docs.google.com/open?id=...
my code performing ok
https://docs.google.com/open?id=...
my code going crazy
https://docs.google.com/open?id=...

Regarding this last, i raise the input frequency every 5 secs, so you see:
0..5 S: all ok as before
5..10 S: as tcnt0 overflows, something strange happens.
10..15 S: increasing frequency further, seems to have have stuck the code...there's no figure flickering, but this is not normal unless you synchronize both freqeuncy input and system clock..and this is not our case.
15..20 S: increasing frequency again, now the flickering appears again but is from 0 to 5 (why 5?).

the numbers you see are:
frequency measured / 5 (1MHz input)
overflows counter
tcnt0 remainder

They are shown only on my code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Simply i would like to learn for the future if such a resolution extension can be made without assembler black magic, or other trickery and i think it could be useful to other people also.

I've been thinking about this a bit. If using ICP1 (input capture) or T1 (external clock) on an AVR8, one is limited to [I forget--/2? /4] some small divisor of AVR clock speed. So the short answer is that the max input frequency is "a few MHz".

At those speeds, it is a microsecond or less between timer1 counts. How does one best capture the value and then move on to the next period?

I think I'd do as one would with ICP: Let timer1 "free run". Ensure that the sampling time is such that timer1 doesn't get more than 64k counts during the sampling period. If it is 1MHz max as mentioned early in the thread, then say 1/16 or 1/20 second for the sampling period.

With ICP, one gets jitter-free sampling. With a second timer, not so--especially if an ISR is invoked there will be some jitter. At the sampling time, I'd grab TCNT1 but not reset it. As mentioned earlier, I'd save TCNT1 as "prev" and then "counts" = "curr" - "prev", as one would do with ICP.

OP and alexan_e are using 8-bit timer and its counts and overflows. And timer1 is used for the sampling period. I'd do it the other way.

If you sample continuously as I mentioned earlier, e.g. 1/16 or 1/20 second and the use a circular buffer for the last full second, then yes, there could be a microsecond or so jitter. Most of it will catch up into the next bucket, I would think, so the value over the past full second should be pretty good.

Now, how could we get better? There was an early mention of routing a signal. I think the below will give good results up to the clk/n limits of external clocking on T1:

-- Pick a sampling period such that the 16-bit counter doesn't overflow. 1/16 second e.g for a 1MHz input signal.
-- Set up an 8-bit timer with a CTC period of 1/2 of the sampling frequency, e.g. 1/32 second.
-- Have the 8-bit timer toggle its output pin OCnx upon compare match.
-- Connect OCnx pin to ICP1.
-- Enable ICP interrupt on either edge.
-- Do "normal" ICP in the ISR as mentioned earlier -- count=curr-prev.
-- Save "count" in a circular buffer that holds e.g. 1 second of values.

As I see it, you now have no races, you might be +/- 1 TCNT1 count on any particular sample but the next should catch up, you have many milliseconds to get to the ICP servicing ISR, you can total/average/display whenever the spirit moves you, with just a bit of attention paid to atomic access to the circular buffer and the current index. Brute-force and simple: Turn off interrupts and make a shadow copy of the circular buffer and the current index. Remember that you have many milliseconds before anything would be lost.

Now you end up with one ISR for ICP, where timing isn't critical, latency becomes a non-issue (within reason), there is no jitter from sampling period to sampling period, there is no overflow counting, there are no overflow races, ...

Quote:

This is not a concern if you are measuring a 100uH inductance, but if you have to build a 100nH inductor, the error i got, having a frequency error of +/- 2 units, was about (if i remember well) +/- 20nH ... too much.

I'd have to follow up on your inductance-meter references but if low Henry values were important, can't you construct the measuring circuit such that there are different timings for "low range" and "high range"?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

I'd have to follow up on your inductance-meter references but if low Henry values were important, can't you construct the measuring circuit such that there are different timings for "low range" and "high range"?

Since the circuit uses a comparator that oscillates based on LC values, an additional capacitor can be connected in parallel with the high range capacitor to lower the frequency when very low inductances are measured.

http://electronics-diy.com/lc_me...
http://cappels.org/dproj/EvenBet...

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Solved :D

I knew about input capture unit existence but i'd never used it.
I had a glance at it and i was thinking it was just a kind of simple interrupt driven by the analog comparator (in effect this last is another source for it).
Instead, the fact that it freezes the 16 bit time stamp of the timer counter on another register is very powerful!
No jitter at all and just +/-1 unit flickering.
I've done exactly what Mike wrote on his "e-book" :) and it's working like a charm .
Just one note, but i think you all know it, there's no need to hardwire the timer output to the capture
input, simply you can toggle the pin writing to the port register.
Thanks everybody, thanks Mike, i owe you a beer.

I was able to measure up to 6MHz without problems, then i don't know if it is the comparator or the timer counter input collapsing (i was lazy and i didn't take out the oscilloscope to check for this).

Quote:

I'd have to follow up on your inductance-meter references but if low Henry values were important, can't you construct the measuring circuit such that there are different timings for "low range" and "high range"?

Yes this was also a solution but i wanted to avoid as much as possible switches and passives since both wear out and, being temperature dependent, make the oscillation frequency to drift.
As Alex suggest with his links, i could insert some extra capacitance when measuring low inductances, but it would introduce other errors sources to take into account, and using a higher test frequency give a greater frequency-delta for the same inductance-delta, so i can have a better accuracy.

The post topic was however about extending timer resolution, i learned that fascinating aspect of input capture unit and the problem is solved.

Bye

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Just one note, but i think you all know it, there's no need to hardwire the timer output to the capture
input, simply you can toggle the pin writing to the port register.

Huh? When you wire the pin, you eliminate ISR latency (or loop latency when polled) and reduce/eliminate jitter. If you want to allow jitter, then what is the purpose of this whole exercise?

That said, does ICP trigger if the ICP pin is configured as an output? On Mega164 family,

(PCINT30/OC2B/ICP)  PD6

the ICP pin is shared by an 8-bit timer output. Perhaps the wire can be skipped in this case.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Huh? When you wire the pin, you eliminate ISR latency (or loop latency when polled) and reduce/eliminate jitter. If you want to allow jitter, then what is the purpose of this whole exercise?

No i don't want to allow jitter, but just to understand, i have the timer that should toggle the icp which is not (again) so long to allow me for 200mS gate, so i had to insert a counter into it:

volatile unsigned char tm0_count=0;
ISR(TIMER0_OVF_vect) /* 8 bit tctr*/
{
	tm0_count++;
	if (tm0_count>=12)
	{
		PORTB |= 0x01;
		asm("nop");
		PORTB &=~0x01;
		tm0_count=0;
	}
}

This generate a sharp pulse to trigger the icp.
Maybe i'm wrong, in case correct me, but i'm thinking that the increment and test i put here just introduces a fixed delay to the whole count. Isn't it?
The number of instructions before the port is triggered should be always the same or, better, from 0 to 11 i execute the interrupt "quickly", on the 12th execution i have a fixed delay before the port is triggered, but the sum of all these delays should be constant.
Sure, using the timer itself for triggering the output pin would be the best, but you find this arrangement so wrong?
However, i hadn't realized another video, but the figures are pretty fixed, like in this:
https://docs.google.com/open?id=...

To be sure i'll check with the oscilloscope then i'll be back.

Thanks.

Update:
i've checked,i had to make the timer 0
interrupt running at 1.5mS because my scope,
at this resolution, cannot delay the
first shot more than 10mS.
The jitter i have is about +/- 64nS.
Initially i was using a max input frequency of 1MHz, now i would use 4MHz, but also for 4MHz i don't think that +/-64nS (about 1/4 of the 250nS period for 4MHz) would be a concern.
What you think about?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The thread title says resolution, but the discussion is about range. A 20MHz AVR has a timer resolution of 50 ns. If 16 bits isnt enough range, then incrementing perhaps a longword in the overflow interrupt handler would increase the range to 32+16 bits. Lots of range, lots of resolution. Everyone is Happy.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

i have the timer that should toggle the icp which is not (again) so long to allow me for 200mS gate,

I gave the sequence whereby you "gate" at a time where overflows are not important. If 1MHz max, then 1/16 swecond or faster would work. Essentially the overflow handling would be replaced by the circular buffer of samples.

You can then total the samples to whatever "gate" window you desire...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are correct that consistent delays at both the start and end of your measurement cycle are not a problem. No, the number of instructions executed before the ISR changes pin 1 PORTB when a timer0 overflow is triggered is not a constant number of CPU cycles.

Now you are back where you started with a timer0 overflow interrupt response time dependency in your gate period timing. Read your data sheet section about Reset and Interrupt Handling. The interrupt handler will always complete the current instruction execution when an interrupt flag is asserted and recognized by the AVR hardware. Typical instructions are usually either one or two words and take from one to four clock cycles to execute. If your AVR is sleeping then it takes an extra four clock cycles (the response time will vary between being asleep or not being asleep). It all depends on what the AVR hardware is doing when any interrupt trigger is detected. This jitter is in AVR clock cycles which are supposed to be at least 2.5 times faster than your maximum input measurement frequency. Still it is jitter.

The next problem is interrupt availability. If any other AVR interrupt is triggered at the same time as the timer0 overflow interrupt, the interrupt priority determines which one executes first (the priority only matters when multiple interrupts trigger at the same time). If the timer0 overflow looses you get a major delay and allot of random length lost time before you change pin 1 on PORTB. More problems occur if global AVR interrupts are disabled for any reason, then you get random major delays again. Every ISR will automatically disable interrupts while the ISR executes. Even if your ISR code re-enables global interrupts, there will be a cycle loss much larger then the previous jitter before the sei() and you run the very real risk of crashing your stack along with your program (preventing nested interrupts is why the hardware automatically disables global interrupts when responding to a triggered ISR). So already running ISR code for other interrupts means up to very large random delays in the gate period ISR before pin 1 PORTB is changed. At the relatively high frequencies you are measuring (for an AVR) the nature of these very large random delays go way beyond normal “jitter” and into the realm of complete failure as far as accuracy goes. All of this is what I meant about dedicating the AVR interrupt system to just accurate frequency measurements, because all these problems go away if AVR global interrupts are always enabled and no other interrupt vectors are used while measuring frequency. If you are willing to dedicate the AVR interrupt system to frequency measurements, your old solution already worked.

In short if you place the gate timing period back into an interrupt response timing critical path, you did all this for no inherent accuracy gain over what you already had.

BTW, which AVR are you using?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Now I'm kind puzzled too about this.
If we use an 8bit timer in CTC mode with 16MHz clock then we will get gate pulses of 16us at best, is seems extremely low duration and in order to extend it we either need several overflows or a second 16 bit timer.

If I understand correctly theusch suggests that we do several such small measurements and add the results?
This would mean 12500 measurements to achieve 200ms from 16us segments :? .

I thought of using the compare match feature in out advantage (no interrupt overhead in the generated pulse) to generate a low pulse on match in the output pin and then after several 8bit timer interrupts, set for high on match but if there are several other interrupts used it may be a problem although there should be plenty of time after the last overflow interrupt to set the compare match value and set for high on match.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If we use an 8bit timer in CTC mode with 16MHz clock then we will get gate pulses of 16us at best

But what about the prescaler or are you suggesting only using /1 for the finest granularity?

most timers allow for /1024 prescaler so 16us becomes 16.384ms.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

If we use an 8bit timer in CTC mode with 16MHz clock then we will get gate pulses of 16us at best

But what about the prescaler or are you suggesting only using /1 for the finest granularity?

most timers allow for /1024 prescaler so 16us becomes 16.384ms.

:roll: yes, I got a brain overload and expressed a stupid calculation.
If we use /1024 then we can get a window of abut 16.3ms

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since the OP only needs a positive or only a negative going edge (choose one, not both) every 200 ms, it should be able to reach 200 ms when going same edge to same edge timing. The ICP hardware input has selectable edge sensing, so this hardware will filter out the OC0A output pin undesirable edge transition automatically (BTW there may be an alternate OC0B, etc.). The TIMER0_COMPA_vect could be used instead of the OC0A output pin, but then the ISR would would have to filter out the undesirable edge transition or feed the ICP with a software generated output pin toggle to use the hardware filtering. However, this gets back into using interrupts for the gate period timing.

Personally I would look at using 250 ms to simplify the result multiplication operation. Not that I would need to, in embedded work its just that I have become used to looking for faster code, especially when multiplication or division is involved. However, an AVR with a hardware multiplier levels this execution timing difference out mostly, depending on how your compiler works.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mike B wrote:
Since the OP only needs a positive or only a negative going edge (choose one, not both) every 200 ms, it should be able to reach 200 ms when going same edge to same edge timing.

Obviously what I said about high output on compare match and then low output on compare match is not what we need.

So if we set T0 to toggle we can get positive pulses every two T0 overflows so basically this will give a window of 16.3ms *2 = 32.6ms

But I'm still not sure how you intent to get 250ms without using multiple T0 overflows.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never mind. Misunderstood what you were doing.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Thu. Dec 13, 2012 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Alex,

Look at CTC mode timer operation. It will generate a periodic OC0x Interrupt Flag Set up to every 16.3ms. It will also toggle the OC0x output pin state up to every 16.3ms. The resulting OC0x output pin waveform same edge transitions are up to 16.3ms *2 = 32.6ms. The data sheet has a nice picture of this CTC timer process with the resulting waveform, just look at the toggled same edge timing which is two complete timer0 CTC cycles long.

While the OC0x Interrupt Flag is not edge sensitive, if it toggles an output pin that connects to the ICP you get the same up to 32.6ms timing (plus any possible interrupt response jitter/delays). In newer AVRs you can write a one to the PIN register bit to toggle the output PORT pin (see your data sheet I/O-Ports section).

As Lee pointed out, if your particular AVR timer0 has an output compare pin shared with an ICP pin, then you do not need to use a wire to connect the output compare to the ICP.

Koshchi,

The interrupt the OP used is only a patch work of slow timer0 gate period interrupts. The high input frequency on the T1 pin is only limited by the AVR sampling, which ATMEL recommends be at least 2.5 times slower than the AVR clock speed for a 50 percent duty cycle sampled signal. This is covered in the data sheet Timer/Counter Prescaler section.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

But I'm still not sure how you intent to get 250ms without using multiple T0 overflows.


In my mind--Remember that the 200 or 250ms or whatever "gate" time appears to be fairly arbitrary.

So use e.g. 10ms, and then gather the samples into a circular buffer. Yes, there will be a few more ISRs than at a longer gate time. But the fact(s) still remains:
-- x5 after 200ms is arbitrary
-- there are no races
-- there are still many milliseconds to service the ICP trip
-- ... which allows more parts of a full app to run without affecting this measurement
-- AT ANY TIME one can look back in the circular buffer for the past 100ms or 200ms or full second or whatever

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A longer gate period has greater resolution, which is not the same thing as accuracy. A .1 second gate will give 10 Hz resolution +/- 10 Hz. A .2 second gate will give 5 Hz resolution +/- 5 Hz. Since this thread is just about accuracy, I like your solution better.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Mike B wrote:
Alex,

Look at CTC mode timer operation. It will generate a periodic OC0x Interrupt Flag Set up to every 16.3ms. It will also toggle the OC0x output pin state up to every 16.3ms. The resulting OC0x output pin waveform same edge transitions are up to 16.3ms *2 = 32.6ms. The data sheet has a nice picture of this CTC timer process with the resulting waveform, just look at the toggled same edge timing which is two complete timer0 CTC cycles long.

Yes I was clear about the concept but I thought you meant in the previous post that we should feed the ICP input with 200ms pulses that I had no idea how we would generate.

theusch had already mentioned that we can just add several small sample windows to get the total window count value but somehow I was struggling to get the idea.

I final got it, we can a series of results from snapshots of the T1 each taken when the measure window gate pulses reach the ICP input.

For example:
first rising edge to ICP, T1 value stored to ICR reg 5000
10ms rising edge to ICP, T1 value stored to ICR reg 10000
next 10ms rising edge to ICP, T1 value stored to ICR reg 15000
nest 10ms next rising edge to ICP, T1 value stored to ICR reg 20000
next 10ms rising edge to ICP, T1 value stored to ICR reg 25000

If we store the ICR values each time and subtract the previous one we get

10000-5000=5000
15000-10000=5000
20000-15000=5000
25000-20000=5000

If we add all these 10ms window results we get the 40ms window result
5000+5000+5000+5000=20000 T1 counts during 40ms

This is the same as 25000 (last ICP) - 5000 (first ICP) if T1 didn't overflow.

Assuming that the total count fits in T1 without overflow we could even get the first ICP , ignore the next three (just as an example) and get the fourth ICP.
By subtracting this two values we can get the total T1 tick.

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Assuming that the total count fits in T1 without overflow we could even get the first ICP , ignore the next three (just as an example) and get the fourth ICP.
By subtracting this two values we can get the total T1 tick.

But for maximum resolution you don't want to slow down the timer1. The time to overflow at /1 would vary based on AVR speed. (OP still hasn't mentioned AVR model or clock speed IIRC) but for 20MHz and /1 it would be about 1/16 second.

If it was me, I would do the subtraction each time, and save the new as "prev". (Search the thread for "prev" earlier...counts = curr-prev) This ISR shouldn't be much more extensive than the "count the overflows" logic much discussed. In addition, there are no races or latency or cycle counting--within reason. There are many milliseconds to process an ICP trip before the next will happen.

Once you have the list of samples, you can do with them what you care to. Instantaneous reading? Last sample x16 (if 1/16 second is used). Latest second? Last 16 samples. Increasing or decreasing? Straightforward. Deviation from sample to sample? Nearly trivial. Max rate? Roughly, 5+MHz. 20MHz AVR max speed; ICP max rate ~clk/4; 10ms. sample time. That would then be a single ICP trip every 10ms, and up to 20ms allowed latency before servicing the ISR. The cost at that rate would be 100x2 = 200 SRAM bytes if a full second of samples desired in the circular buffer.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Many thanks Mike for your explanations, got what you said before regarding why it is important to hardwire the gate output to ICP input.
Anyway, as mentioned previously, i have a jitter of just +/-64nS on the "software-multiplied" gate counter that is, for 16mhz system clock, +/- 1 clock cycle.
In this particular application running on an Atmega328, i have no other interrupts active, so that's why maybe the jitter is not so high.
Anyway, it's a good information,i'll try hardwiring maybe tomorrow.

ok..it was answered while i was writing my answer
Theusch, forgive me if i don't catch it.
You are suggesting to use a circular buffer:
so, say, i've the gate timer running in ctc mode so, keeping the ICP trigger edge constant, i'll have the ICP to trigger for every 2 * gate timer period.
Then?
Into the ICP ISR, i have to calculate the difference between last 16 bit timer value and the actual, then push this value into a circular buffer.
Then?
I should add N values out the circbuffer where N is the length of my time window.

Wouldn't it be the same to add N values up into the ICP ISR without using a circular buffer?

Bye
Tom

Last Edited: Thu. Dec 13, 2012 - 10:41 PM

Pages