Frequency Measure Using ICP on Atmega16

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello, 

Using Ateml studio 6,
Am trying to measure period of incoming signal from 555 timer with a variable capacitor hence variable output freq. range from 7351 HZ to 6033 HZ which gives a cycle period of 136 uS to 165 uS ALL on Proteus simulator 
am detecting rising edges of incoming signal using icp of 16 bit timer1 on atmega16 with 8MHZ XTAL and a prescaler of clk/8 for the timer so every on up tick is 1 uS then when subtracting two values of ICR i got period on uSeconds 
PROBLEM is i got wrong period reading on LCD which is so less than expected reading
where is my code :

#include <avr/io.h>
#define F_CPU 8000000UL
#include<util/delay.h>
#include "lcd.h"

int main(void)
{
    unsigned int t1=0;
    unsigned int t2=0;
    unsigned int p=0;
    //unsigned int Freq=0;
    LCD_Init();
    TIFR=0x00;	
    TCCR1A=0x00;	                        // Start timer
    TCCR1B=0x42;                        	// rising edge , clk/8
    TCNT1L=0;	
    while((TIFR&(1<<ICF1))==0);            	// wait till first +ve edge
    t1=ICR1;				            	// save reading
    TIFR=(1<<ICF1);			            	// Clear flag of ICP
    while((TIFR&(1<<ICF1))==0);         	// wait for next +ve edge
    t2=ICR1;				            	// Save reading
    p=(t2-t1);			            	// get period of cycle
    TIFR=(1<<ICF1);			            	// clear flag
    LCD_SendString_XY(convertData(p),0,0);  // display period in Useconds
    _delay_ms(500);
    LCD_Clear();
        
    while(1)
    {
                
    }
}

 

This topic has a solution.
Last Edited: Fri. Feb 6, 2015 - 05:55 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I gather timer1 is 16 bit? If so, why do you only clear the low part of it. Also clear the capture flag before looking for the first edge.

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

I was clearing only the low part cause the values that TCNT1L hold will not exceed 255 tick or 255uS to be accurate 

i did as you said and re-arranged the code 

/*
 * Freq_measure_using_ICP.c
 *
 * Created: 04/02/2015 05:16:09 م
 *  Author: Allam
 */ 


#include <avr/io.h>
#define F_CPU 8000000UL
#include<util/delay.h>
#include "lcd.h"

int main(void)
{
	unsigned int t1=0;
	unsigned int t2=0;
	unsigned int p=0;
	//unsigned int Freq=0;
	LCD_Init();
	TIFR=0x00;	
	TCCR1A=0x00;	// Start timer
	TCCR1B=0x42;	// rising edge , clk/8
	TCNT1L=0;
	TCNT1H=0;	

    while(1)
    {
		TIFR=(1<<ICF1);
		while((TIFR&(1<<ICF1))==0);	// wait till first +ve edge
		t1=ICR1;					// save reading
		TIFR=(1<<ICF1);				// Clear flag of ICP
		while((TIFR&(1<<ICF1))==0);	// wait for next +ve edge
		t2=ICR1;					// Save reading
		p=(t2-t1);				// get period of cycle
		TIFR=(1<<ICF1);				// clear flag
		LCD_SendString_XY(convertData(p),0,0); // display period in Useconds
		_delay_ms(500);
		LCD_Clear();
		LCD_SendString_XY("clearing",0,0); 
		LCD_SendString_XY("change capacitance",0,1); 
		_delay_ms(500);
		LCD_Clear();
				
    }
}

i got way better results 
i cleared the flag before reading the edge 

i give a frequency of 6655 HZ to ICP which correspond to 150.3uS as cycle period

So i got on my LCD a 149uS cycle period

so at some frequencies of the range it give one or two Usec's more or less
if there is any way to get it better that will be awesome  
I'm so thank-full , Kartman. 

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

Since you've set the timer for a 1us clock, its resolution is +/- 1 clock. Use a smaller prescaler and you'll get a more accurate result. As long as the maximum period is less than the clock period * 65536, it will be ok. There will also be a minimum period due to the execution time of your code - you'll have to measure this to determine what it is.

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

I was clearing only the low part cause the values that TCNT1L hold will not exceed 255 tick or 255uS to be accurate 

But, be aware of the datasheet section about 16-bit registers, and the temporary register:

 

16.3 Accessing 16-bit Registers
The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR CPU via the 8-bit data bus.
The 16-bit register must be byte accessed using two read or write operations. Each 16-bit timer has a single 8-bit
register for temporary storing of the high byte of the 16-bit access. The same temporary register is shared between
all 16-bit registers within each 16-bit timer. Accessing the low byte triggers the 16-bit read or write operation. When
the low byte of a 16-bit register is written by the CPU, the high byte stored in the temporary register, and the low
byte written are both copied into the 16-bit register in the same clock cycle. When the low byte of a 16-bit register is
read by the CPU, the high byte of the 16-bit register is copied into the temporary register in the same clock cycle as
the low byte is read.

Not all 16-bit accesses uses the temporary register for the high byte. Reading the OCR1A/B 16-bit registers does
not involve using the temporary register.

To do a 16-bit write, the high byte must be written before the low byte. For a 16-bit read, the low byte must be read
before the high byte.

 

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:
But, be aware of the datasheet section about 16-bit registers, and the temporary register:

To do a 16-bit write, the high byte must be written before the low byte. For a 16-bit read, the low byte must be read

before the high byte.

Yes.

 

If the high byte is not written, then the previous contents of the temporary register for that timer will be used.  The only other access to 16-registers to TIMER1 in the OP's code was a read (two actually) of ICR1, so the temp register would contain the high byte of ICR1.  TCNT1H would be written with the previous value of ICR1H.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

With the avr-gcc compiler,  it is not really an issue.    You just use TCNT1 or ICR1 in an expression.   The compiler looks after the correct H-L write and L-H read of the 16-bit register-pair.

 

With Codevision,    it will handle these 16-bit pairs if they are in IN/OUT memory.    e,g. Tiny2313 or mega32

Modern AVRs like ATmega328P have TCNT1 and ICR1 in LDS/STS memory.    You have to access TCNT1L and TCNT1H by hand.

 

@Allam88,

 

I am impressed that you have written the code neatly and added sensible comments.    You will go far !!!

You can get better resolution if you use div1 for the prescaler.    However you will need to handle overflows.   And do your maths in 32-bit.

 

David.

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

I am impressed that you have written the code neatly and added sensible comments.    You will go far !!!

Indeed.

 

Am trying to measure period of incoming signal from 555 timer with a variable capacitor hence variable output freq. range from 7351 HZ to 6033 HZ which gives a cycle period of 136 uS to 165 uS ALL on Proteus simulator 

I realize that this might be just an exercise. But for us 'Freaks, 555 excitation circuits are getting rarer and rarer.  If I read between the lines, you are using the AVR to "tune" the [imprecise?] 555 output.

 

Why not just generate the frequency with the AVR in the first place?

 

Another side note:  The mention of 8MHz implies internal oscillator to me.  Remember that on a real AVR the uncalibrated internal oscillator may be off by some percent, affecting your frequency calculation results.

 

You can get better resolution if you use div1 for the prescaler.    However you will need to handle overflows.   And do your maths in 32-bit.

 

???  Isn't the reach of the 16-bit timer about 8ms at 8MHz and /1?  More than enough reach for the OP's signal range of about 100us period.  No overflow or 32-bit math needed, right?

 

But that does bring up a related topic.  Consider what happens when the signal is lost--the program will hang. 

		while((TIFR&(1<<ICF1))==0);	// wait for next +ve edge

So one might "enhance" the waiting loops, counting overflows and clearing the flag.  One is normal; more than one indicates loss of signal, or period too slow to measure at this setup.  Then "abort", put up a Loss of Signal message, and then perhaps try again. (about 100Hz in this case, with /1)

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 can also do away with the subtraction.  On the first rising edge, clear the counter.  On the next rising edge, the counter will contain the period.  Save the counter value for later, clear the counter, repeat.

 

I also agree - nice code.

 

Cheers,

 

 

Tom

Cheers,

Tom

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

Quote:
clear the counter
This has the potential to introduce jitter, especially at low timer prescaler values (1 or 8).  Better to do the subtraction, which costs only a few cycles/words.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

???  Isn't the reach of the 16-bit timer about 8ms at 8MHz and /1?  More than enough reach for the OP's signal range of about 100us period.  No overflow or 32-bit math needed, right?

Yes,   I missed the 136us - 165us.    This is easily done with the uint16_t TCNT1, ICP1, ...

 

The overflow would not be needed for < 8ms @ 125ns resolution.  (div1)

Neither would you need overflows for < 64ms @ 1us resolution. (div8)

 

If you did want long periods,   it is fairly trivial to just poll the OVF flag.   i.e. you don't even need interrupts.

 

David.

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

david.prentice wrote:

With the avr-gcc compiler,  it is not really an issue.    You just use TCNT1 or ICR1 in an expression.   The compiler looks after the correct H-L write and L-H read of the 16-bit register-pair.

Exactly i depend on that when dealing with any 16 bit register on using avr-gcc .

 

david.prentice wrote:

I am impressed that you have written the code neatly and added sensible comments.    You will go far !!!

 

Thank you David

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

theusch wrote:

I am impressed that you have written the code neatly and added sensible comments.    You will go far !!!

Indeed.


 

Thanks Theusch.

 

theusch wrote:

I realize that this might be just an exercise. But for us 'Freaks, 555 excitation circuits are getting rarer and rarer.  If I read between the lines, you are using the AVR to "tune" the [imprecise?] 555 output.

 

Actually am making some pF capacitors's capacitance reader circuit so i used the 555 in astable mode with fixing the R1 and R2 values so i got a range of frequencies on pin3 corresponding to the capacitor values  .

 

theusch wrote:

what happens when the signal is lost--the program will hang. 

 

You're so right about this point i'll fix it and re-write the code here again with the schematic  

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

tblough wrote:

I also agree - nice code.

 

Thanks Tom.

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

joeymorin wrote:

 

This has the potential to introduce jitter

 

 

Would you please clarify that point ?
thanks.

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

Tblough in #9 suggested you reset the timer and avoid the first capture. That was a bad idea as you get jitter. Your method was sound.

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

Thanks Kartman, but what am asking about why would Tblough method produce jitter , is it something like a general rule or it's in datasheet ?

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

Ok, you have an asynchronous signal on the input capture. Reset the timer then look for the next edge. The timer could start anywhere within the input signal. You'll get a bad reading most of the time. With your method, you look for an edge and it is captured by the icp hardware. You then look for the next edge. That's about as precise as you can get. Where you ran into trouble originally is that you didn't clear the capture flag so your code would've used a previous capture and you got strange results.

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

Actually am making some pF capacitors's capacitance reader circuit so i used the 555 in astable mode with fixing the R1 and R2 values so i got a range of frequencies on pin3 corresponding to the capacitor values  .

 

You should be able to find a number of AVR-based capacitance-inductance meter designs and discussions on the Web.  I'll wager that none of them use a 555 to generate the frequency, but rather use the AVR itself.

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

Kartman wrote:

Ok, you have an asynchronous signal on the input capture. Reset the timer then look for the next edge. The timer could start anywhere within the input signal. You'll get a bad reading most of the time. With your method, you look for an edge and it is captured by the icp hardware. You then look for the next edge. That's about as precise as you can get. Where you ran into trouble originally is that you didn't clear the capture flag so your code would've used a previous capture and you got strange results.

 

How does that differ from using ICP to look for the edge and instead of copying the timer value to a variable, you just reset the timer?  ICR1 = 0 is actually faster than copying the counter value.

Cheers,

Tom

Last Edited: Sat. Feb 7, 2015 - 04:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ICP uses hardware to capture the timer value and store it in ICR1.

 

Waiting for an interrupt latency and the ISR prolog before you reset TCNT1 (or ICR1) is just introducing many cycles.

 

ICP hardware ensures 1 cycle accuracy.    

 

David.

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

Resetting the timer at the first time, introduces extra errors, especially if done by Polling - it may work well with interrupts and starting from idle mode. However even then you need to compensate for the runtime.

The only thing one gain by starting from 0 is, that one could use the overflow flag as a kind of Bit 16, so the 17 Bit resolution without counting overflows in software. Calculating the difference is not that difficult or slow that one needs to avoid it.

 

Doing the math with unsigned int (16 Bit)  already takes care of one overflow that may occur if one start the timer from a more or less random value - so there is no need to start the timer from a predefined value.

 

Counting overflows is also possible, though not as simple as it may look at first sight. Use the search function to find one of the may times this was discussed before, including working and tested code.