problem in Delay Function with Timer 0 and 1

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

Hello

I have codified some functions to provide accurate delays using Timers. First I used Timer 0, and I have noticed that the time that the delay took was twice the time expected. Then I moved to Timer 1 and the effect was the same. My crystal is 16MHz and here it's the code for the delay function using Timer 1.

bool	timerOv;

/**
 * TIMER1 initialize - prescale:8
 * WGM: 0) Normal, TOP=0xFFFF
 * desired value: 10uSec
 * actual value: 10,000uSec (0,0%)
 * Clock is 16MHz
 */
	
void timer1_init (void) {
	TCCR1B = 0x00; //stop
	
	// Value of Timer
	TCNT1H = 0xFF; //setup
	TCNT1L = 0xEC;
	
	// Comparators And Input Capture Setips
	OCR1AH = 0x00;
	OCR1AL = 0x14;
	OCR1BH = 0x00;
	OCR1BL = 0x14;
	OCR1CH = 0x00;
	OCR1CL = 0x14;	
	ICR1H  = 0x00;
	ICR1L  = 0x14;
	
	TCCR1A = 0x00;
	TCCR1B = 0x02; //start Timer
}

void timer1_stop (void) {
	TCCR1B = 0x00; //stop
	TCNT1H = 0xFF; //setup
	TCNT1L = 0xEC;	
}

void timer1_start (void) {
	TCCR1A = 0x00;
	TCCR1B = 0x02; //start Timer	
}

/**
 * eventHandler for Timer1 Timeout
 */
ISR(TIMER1_OVF_vect) {
	// Set Timer OverFlow Flag
	timerOv = true;
}


/**
 * delay10Us 
 * 
 * makes 10us pause using Timer 0
 */
void delay10Us () {
	timerOv = false;
	timer1_stop();
	timer1_start();
	while (timerOv == false);	
}

/** 
 * delayMs
 * 
 * makes 1ms pause using Timer 0
 */
void delayMs () {
	int i = 0;
	for (;i < 100; i++) {
		delay10Us();
	}
}

/**
 * delay
 * @param miliseconds Number of miliseconds to wait
 * calls delayMs 'miliseconds' times.
 */
void delay (u32 miliseconds) {
	int i = 0;
	timerOv = false;
	for (i = 0; i < miliseconds; i++) {
		delayMs();
	}
}

Thanks very much
Ezequiel

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

If your delays take twice the time you think they should then my suspicion would be that the AVR is running at 8MHz not 16MHz. Just adding a crystal is not sufficient to make it run at 16MHz you have to enable the right fuse bits otherwise it'll run off the internal oscialltor (though that should default 1MHz, not 8MHz unless you already changed the CKDIV8 fuse?)

Cliff

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

The last time I set the fuses was in Windows with AVRStudio. And Timer was working. Now I moved to Ubuntu and I'm programming using AVRISP MKII with AVRDude and don't know how to check or program the fuses. But code it's okey, isn't it?

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

Also, I'm using RS232 and I'm using a function to calculate registers values of UART depending on a Baudrate in BPS and that's working okey, with no problems.
Is it the clock source for UARTs and TIMERs different?

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

Ah well if you have based your UART calculation on 16Mhz and you are seeing the baud you expected then my idea of 8 vs. 16 is wrong. The conclusion must therefore be that you DO have an error in your timer functions.

Just out of interest, why try to produce your own delayms() and delayus() routines when they are already provided in avr/delay.h anyway?

Cliff

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

Quote:
the time that the delay took was twice the time expected

How are you measuring this? I can believe that 160(+6) clocks go by between the TCCR1B store in
timer_start and the beginning of the ISR(), but the delay seen by the caller also includes the
entire time of the ISR() and some other function calls. If you were timing, say, milliseconds
rather than microseconds, this overhead could be ignored, but depending on your optimization level
this extra work could be a large component of the measured delay time. (My guess is that you're
using -O0, since you haven't observed any trouble with "timerOv", which needs to be declared
"volatile".)

And since your larger delay()s are built on your core 10us (perhaps more like 15us) delay function,
they inherit the inaccuracy and add some of their own in loop overhead.

You can't really get rid of this inaccuracy, but you can reduce it. Some possibilities:

1) Eliminate the ISR() and poll the TOV1 flag instead (don't forget to clear it).
2) Adjust your initial TCNT1 value to account for the overhead. This is a messy business, similar to
the problems with busy-loop delays, since the generated code can change based on -O setting.
3) Compute the actual time you want to delay (rather than carving it up into 10us quanta) and put
that into TCNT1. Depending on how much range you want this facility to have, you may need an
auxiliary counter (updated in the ISR()) to add more bits to TCNT1.

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

Hello, I checked the fuses and that's okey for 16MHz.
I need 10uS delay, that's why I implemented it, and the make the other delays based on that. I will implement a much greater delay.
I'll check it later but I think I'm running with -O0 option
What problems may I have with this?

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

If you need a 10us delay why not use avr/delay.h ?

(as I also asked above)

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

I solved it. The point is that I wanted to make it on my own. The solution was a mixture of things. Declare Volatile the variable TimerOv. Also implement 1ms delay with timer for larger delays, not using 10uS delay.
Thanks very much to everybody.