Hi,everybody!
I have encountered a question about using timer/counter1 of ATMEGA168. Originally I wanted to use it to count the time between two specific condition in my ultrasonic project. So I wrote the code as the instructions of the datasheet. But I got the random value of ICR1 as I printed it in the interrupt routine.To compare I also printed the value TCNT1. Here is the code and the printed info.
/**************************************************
target:atmega168
main frequency:16MHz
**************************************************/
//configure timer/counter1 as a counter,captured by input falling edge
void CountTime()
{
pinMode( 8, INPUT );//ICP1,counter1 input capture trigger
TCCR1A = 0x00;//normal mode
TCNT1 = 0x0000;
TCCR1B |= 1<<CS11 | 1<<CS10;//clk/64,period=4us
}
void setup()
{
Serial.begin(9600);
CountTime();
}
void loop()
{
TIMSK1 &= ~(1<<ICIE1);//disable input capture interrupt
TCNT1 = 0x0000;//set counter to zero
delayMicroseconds(100);//delay100us,so TCNT1 should be about 100/4=25 when interrupt is triggered
TIMSK1 |= 1<<ICIE1;//enable input capture interrupt
delay(1000);//do it cyclely, 1s
}
//triggered by a square wave of 1kHz
ISR(TIMER1_CAPT_vect)
{
unsigned int time = 0;
unsigned int tcnt = 0;
TIMSK1 &= ~(1<<ICIE1);
tcnt = TCNT1;
time = ICR1;//time should almost the same as tcnt
Serial.print("time:");
Serial.print(time);
Serial.print(" TCNT1 ");
Serial.print(tcnt);
Serial.print("\r\n");
}
I'm not sure why you're enabling/disabling the interrupt in the main loop, but I believe I understand why you are observing what you are.
The fact that TCNT1 is always 26 tells you that as soon as the interrupt is enabled, it immediately jumps to the ISR. This is because the interrupt flag is already set from the previous input capture event. But because the interrupt is disabled no action has been taken.
You should clear TIFR1:ICF1 immediately before enabling the interrupt.
But even then I don't expect the TCNT1 and ICR values to be exactly the same. The reason is that you have no guarantee that you are resetting TCNT1 in-phase with your 1kHz square wave falling edge.
[Edit: re-reading my own post, this is nonsense. Of course TCNT1 and ICR will have the same value when the interrupt is thrown... it's a compare match afterall. Duh!]
I believe what you should be doing is resetting TCNT1 inside the TIMER1_CAPT ISR. Your main loop should do nothing.
Another note, printing all that information out through the serial port at 9600 baud is likely going to take longer than the (1/1kHz) seconds and you're going to backlog the input capture events.
Hi,Mitch_CA!
Thanks for your analyzing!
I have misunderstood that ICR1 will be update when interrupt routine is executing,and in fact it is updated as soon as interrupt is triggered.
And now I added "TIFR1 |= 1<<ICF1;"before "TIMSK1 |= 1<<ICIE1;" as you suggested,and I got the value of ICR1 almost the same as TCNT1.
/**************************************************
target:atmega168
main frequency:16MHz
**************************************************/
unsigned int time = 0;
unsigned int tcnt = 0;
//configure timer/counter1 as a counter,captured by input falling edge
void CountTime()
{
pinMode( 8, INPUT );//ICP1,counter1 input capture trigger
TCCR1A = 0x00;//normal mode
TCNT1 = 0x0000;
TCCR1B |= 1<<CS11 | 1<<CS10;//clk/64,period=4us
TIMSK1 |= 1<<ICIE1;//enable input capture interrupt
}
void setup()
{
Serial.begin(9600);
CountTime();
}
void loop()
{
TIMSK1 &= ~(1<<ICIE1);//disable input capture interrupt
TCNT1 = 0x0000;//set counter to zero
delayMicroseconds(100);//delay100us,so TCNT1 should be more than 100/4=25 when interrupt is triggered
TIFR1 |= 1<<ICF1; //clear the input cature flag
TIMSK1 |= 1<<ICIE1;//enable input capture interrupt
delay(1000);//do it cyclely, 1s
Serial.print("time:");
Serial.print(time);
Serial.print(" TCNT1 ");
Serial.print(tcnt);
Serial.print("\r\n");
//while(1);
}
//triggered by a square wave of 1kHz
ISR(TIMER1_CAPT_vect)
{
TIMSK1 &= ~(1<<ICIE1);//avoid executing continuely
tcnt = TCNT1;
time = ICR1;//time should almost the same as tcnt
/*Serial.print("time:");
Serial.print(time);
Serial.print(" TCNT1 ");
Serial.print(tcnt);
Serial.print("\r\n");*/
}