Timer or counter?

I need to measure the pulse width of something fast. The pulse’s range is 4.25uS to 12.5uS. I red that the best way to do this is to use the rising edge of the pulse to be measured as a trigger to open the gate, letting the system clock increment the counter. Then use some math to figure out the pulse width from the amount of known period pulses. So this made sense so i got to making the board, 20MHz crystal on a mega88. ended up putting the pulse input on T1 for timer1 the 16 bit timer. The plan was to use a prescaler of 1 from the system clock to drive the counter when the rising edge was detected on t1 pin. After reading the data sheet I am so confused on how I i implement this. I fear that i SHOULD have put the input on ICP now, but the data sheet diagram dosent look like what i thought it would, the Clock select has nothing to do with the Input Capture Unit.

Can someone steer me in the right direction?

Its been a while since I did a project using external timer capture, but I am pretty sure ICP was used. T0/T1 are external clock inputs for the counter used to drive them from an arbitrary source. Assuming you did not use ICP for anything else, just short T1 to ICP and then program T1 for input w/o pull-up and use ICP as your event trigger.

Ok i just cut the T1 trace and have my input on ICP now. The data sheet makes this seem more cumbersome than I had thought. It seems the pseudo code is as follows, plz correct me if im wrong:

Enable the timer1 with prescaler of 1

Set ICP tigger to rising edge

wait for an ICP interrupt

When interrupt received record value of ICRN1, and set ICP to trigger on falling edge.

Wait for interrupt.

When interrupt received record value of ICRN1

subtract the two values to get the period of the wave.

the test code i am currently using:

	TCCR1A=0x00; //normal port operation, no outputs
	TCCR1B=(1<<ICES1)|(1<<CS10);//timer 1 clocked directly from system clock, ICP rising edge
	TCCR1C=0x00;
	TIMSK1=(1<<ICIE1);//enable the input capute interrupt
	while(!ICF1){
		;	//wait for that flag to be set
		}
	count_start=ICR1;	//set count to the current number on ICR1	
	TCCR1B=(0<<ICES1); //set the ICP to trigger on the falling edge
	TIFR1=(0<<ICF1); //clear the flag
	while(!ICF1){
		;	//wait for that flag to be set
		}
	count_stop=ICR1;
	number=count_stop-count_start

count_stop and count_start seem to always be getting the same value.

Man i thought this was going to be just like using an on board AND gate to enable the prescaler clock to the counter.

Are you actually using an interrupt handler? If not, then you should not be trying to enable interrupts. If you are not, then I assume you have not enabled interrupts on the AVR (SEI in assembly language) else the CPU would crash without an interrupt handler.

The “while(!ICF1)” looks incorrect to me. ICF1 is a bit-field reference. If you are trying to see if that bit is set, then it should be:

while (!(TIFR1 & (1<<ICF1))) { }

Assuming you are not using interrupts, you need to manually clear this flag after each capture. Oddly, that is done by writing a 1 according to the datasheet:

TIFR1 |= (1<<ICF1);

So assuming you are not using interrupts, I would kill the TIMSK1=(1<<ICIE1) line and use the above clear sequence before each while loop.

The bogus count start/stop values are likely caused by the bad reference to ICF1 as a variable (SRAM must be non-zero at that location so it just drops through both your while loops immediately). Because your pulse width is a minimum of 4us, you should get a very accurate capture down to your 20mhz clock accuracy.

Gerrr this is aggravating. I scraped the idea of measuring pulse width, and just want to measure the period now. No big deal.

I took your advice and changed the appropriate things and Am still having problems. The result is always jumpy to the extent of ±20. Which dosent seem right. I am currently using a func gen to test this with a scope hooked up to it and the wave is not jumping so i know it has to do with my code.

As of right now I am not using interrupts, im just polling the flags.

Heres currently what i have:

void timer_setup(void){
	TCCR1A=0x00; //normal port operation, no outputs
	TCCR1B=(1<<CS10)|(1<<ICES1);//timer 1 clocked directly from system clock
	TCCR1C=0x00;
	}
unsigned int timer_lookup(void){
	unsigned int count_start, number;
	while (!(TIFR1 & (1<<ICF1))){
		;   //wait for that flag to be set
	}
	count_start=ICR1;   //set count to the current number on ICR1   
	TIFR1 |= (1<<ICF1); //clear (by setting,lol?) the flag
	while (!(TIFR1 & (1<<ICF1))){
		;   //wait for that flag to be set
	};
	number=ICR1-count_start;
	return number;
}

why cant i seem to hold a constant reading?

I think i figured it out, i need to also clear ICG1 before i start the process, as well as in the middle. The readings are steady now.

unsigned int timer_lookup(void){
	unsigned int count_start, number;
//	gate(1, 0);  // gate sens 0
	TIFR1 |= (1<<ICF1); //clear (by setting,lol?) the flag 
	while (!(TIFR1 & (1<<ICF1))){
		;   //wait for that flag to be set
	}
	count_start=ICR1;   //set count to the current number on ICR1   
	TIFR1 |= (1<<ICF1); //clear (by setting,lol?) the flag 
	while (!(TIFR1 & (1<<ICF1))){
		;   //wait for that flag to be set
	};
//	gate(0,0);
	number=ICR1-count_start;
	return number;
}

A little problem though… according to my math a frequency of 40kHZ being fed into the counter has a period of (1/40.0k)=25uS. The timer having a prescaler of 1, it is running at 20MHz which has a period of 50nS. I should expect to be seeing a count of (25uS/50nS)=500 on the timer when the input is 40kHz. However I am only seeing ~63, which is 500/8. I checked TCCR1B expecting to see i had incorrectly set it to /8, but it was not. It was correctly set to /1. This is also true when i set my fun gen to the other side of the range i expect to see in the field, ~120kHz. I should see ~166, but am seeing ~21 instead, which is again 166/8.

I assume somewhere something is dividing my timer clock by 8, and I know its not the Clock select prescaler bits. (unless AVR studio is lieing to me). I am trying to squeeze every bit of resolution i can out of this!

CKDIV8 :evil: gerrrrrrrrrrrrrr

all better now. Ty!

ughh, now im having a different problem. I cant seem to enable debugWIRE anymore. I can erase and flash with ISP without any problem. And when i set the DWEN fuse it seems successful, but i can still read the fuse bits after that which i don’t remember being able to do before while DW was enabled. What did i do wrong here?

The CLDIV8 fuse catches a lot of folks when they first start working with the AVR. Note that you can change the divisor via software rather than the fuse. I also wonder if this is the source of your debugwire problem. No idea if this will work, but try the following if nothing else does:

Set the CLDIV8 fuse and see if DebugWire starts working. Then, change your code and add the following to the beginning:

CLKPR=(1<<CLKPCE); // enable divisor change

CLKPR=0; // set divisor to 1

This may or may not work in C. The issue is that the first store to CLKPR enables a 4 cycle internal timer in which the second write actually updates the system clock prescaler. However, I don’t know if the compiler will generate fast enough code. It shound since its just an LDI/OUT combo, but who knows (I always program in assembly so have not run into any timing problems).

Vraz:
Set the CLDIV8 fuse and see if DebugWire starts working. Then, change your code and add the following to the beginning:

CLKPR=(1<<CLKPCE); // enable divisor change

CLKPR=0; // set divisor to 1

This may or may not work in C. The issue is that the first store to CLKPR enables a 4 cycle internal timer in which the second write actually updates the system clock prescaler. However, I don’t know if the compiler will generate fast enough code. It shound since its just an LDI/OUT combo, but who knows (I always program in assembly so have not run into any timing problems).

I thought the problem was related to the DIV8 fuse as well so I tried what you said and its still acting strange. I dont know whats up at this point. It seems like its not enabling DW at all and then falsely reporting the fuse is in enabled (which shouldn’t even be possible if it is enabled, i think.)

I was about to start programing the divisor of 1 into my program while I was reading the section in the data sheet, and i also thought the same thing as you, It needed to be done in 4 clock cycles, and im not sure about the C overhead. But then I got to the end of the section and found out I could just disable the fuse bit. Its not that difficult to do inline assembly is it?

I think my debug/isp issue was caused by a short on the board. Its all better now. i gota go to sleep…

I re enabled DIV8 and dropped thoes lines in the program and ran it. It works, and I am running at /1 after now. Is there any advantage to doing this in code rather than fuse?

Here is disassembly listing of it

20:       	CLKPR=(1<<CLKPCE); // enable divisor change
+00000045:   E6E1        LDI     R30,0x61         Load immediate
+00000046:   E0F0        LDI     R31,0x00         Load immediate
+00000047:   E880        LDI     R24,0x80         Load immediate
+00000048:   8380        STD     Z+0,R24          Store indirect with displacement
21:       	CLKPR=0; // set divisor to 1 
+00000049:   8210        STD     Z+0,R1           Store indirect with displacement

thanks for your help.

I don’t use DW myself (purely ISP) so I don’t have many ideas. The fuse settings are inverted so enabling DW actually involves clearing the fuse. Assume you are doing that, but will throw it out just in case. I also believe that ISP programming and DW are mutually exclusive. So if ISP is working, then DW is definately disabled. Also, it sounds like you need to power cycle the AVR after enabling DW for it to take effect (its unclear if a reset is enough). Finally, are you running the board at 5v? With the 20mhz clock, you need to power above 4.5v to be in spec.

As to the assembly part-- the code is simple:

LDI r16,(1<<CLKPCE)

OUT CLKPR,r16

ldi r16,0

OUT CLKPR,r16

However, I am not sure how you encode the opcodes with whatever C compiler you are using. Still, I would try the C version first and see if that works. Since you know the cycle times for your measurement, you can use that to determine if it works.

We crossed paths. I updated my above post with better information. Every things better now! thanks for your help!

We crossed paths. I updated my above post with better information.

Good news. Any chance it was power cycling your board? The references I found say that is required after changing the fuse. The short is certainly another obvious answer, though I wonder why ISP programming kept working.

What I like about keeping the CLDIV8 fuse enabled is that the AVR will run with almost any crystal, resonator or whatever. Even if you change it mid-design and already programmed, the device is going to startup reliably with the /8 frequency. You can then use software to adjust as appropriate. Also, if you are going to do any serious power management, then it involves using the prescaler anyway so why not use it for everything.

The ASM code generated is fine and very fast since it does the loads first and then the stores in a single operation. Its less cycles to use the more direct approach I posted, but who cares. The compiler code works and is within spec so stick with it.

Glad it all came together. Hopefully it provides the precision you need for your underlying project. Good luck!

Yes you must power cycle the chip after enabling DW. AVR studio even reminds you to do it when you do so. my boards a mess right now with all the green wires and cut trace fixes I have done to it so im not surprised something went wrong.

Thanks for all your help, I really appreciate it. time for sleep…