Half-second delay by Timer 1 in CTC mode is much shorter than expected

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

Hello, 

 

I am writing a half-second delay function using Timer 1 CTC mode (mode4), for the Atmega32.

This is my code 

#include <avr/io.h>

void delay(void);

int main(void)
{	

	DDRD |= (1 << 7);

	while(1)
	{
		PORTD |= (1 << 7);
		delay();
		PORTD &= ~(1 << 7);
		delay();

	}

	return 0;

}

void delay(void)
{
	OCR1AH = 0x16;
	OCR1AL = 0xE2;

	TCNT1L = 0;
	TCNT1H = 0;

	TCCR1A = 0x00;
	TCCR1B = 0x0D;

	while(TIFR & (1 << OCF1A) == 0);

	TIFR |= (1 << OCF1A);

	TCCR1B = 0x00;
}

 

Using a 12MHz CPU frequency, a 1024 prescaler, and CTC mode(mode 4):

 

timer tick time = (1/12MHz)*1024 = 85.3333 us

required number of ticks = 500ms / 85.33us = 5859

OCR1 value = 5859 - 1 = 5858 = 0x16E2

 

I did that in the code, but the delay is much shorter than 500ms, it makes the LED always on because it is very short.

 

Any help will be appreciated, thank you.

 

 

 

 

Last Edited: Mon. Aug 26, 2019 - 09:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How much shorter? That usually gives a clue.
Your code is littered with ‘magic numbers’ - eg: what modes does 0x0d invoke?

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

Kartman wrote:
How much shorter? That usually gives a clue. Your code is littered with ‘magic numbers’ - eg: what modes does 0x0d invoke?

 

I cannot measure how much shorter it is, it is a very short delay that makes the LED always on as we see it.

 

0x0D invokes mode 4

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

google 'C Operator Precedence'

 

while(TIFR & (1 << OCF1A) == 0); --->  while(TIFR &  ( (1 << OCF1A) == 0 ) ); --> while(TIFR & 0); --> never

 

while( (TIFR & (1 << OCF1A)) == 0 );

or 

while( ! (TIFR & (1 << OCF1A)) );

or

while( 0 == (TIFR & (1 << OCF1A)) );

Last Edited: Mon. Aug 26, 2019 - 09:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To complete the list, also is possible:  while( (TIFR & (1<<OCF1)) == 0)

 

It is all in the parenthesis. Personally, I have a hard time remembering some of the details so I go with enough parentheses to remove any ambiguity. So, a line is a few characters longer, but the compiled code won't change. Its not like those parenthesis add machine instructions.

 

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

AhmedH wrote:

TIFR |= (1 << OCF1A);

It will work in this case, but in a full app you do not want the RMW effects of the |= ; it will also clear any other flag bits that are set in that register.

 

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

AhmedH wrote:
Using a 12MHz CPU frequency

How sure are you of this frequency? As that is not one of the internal R/C frequencies, you must be using an external xtal or oscillator.

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

AhmedH wrote:

void delay(void)
{
	OCR1AH = 0x16;
	OCR1AL = 0xE2;

	TCNT1L = 0;
	TCNT1H = 0;

Why are you writing the two 8-bit registers of a 16-bit register this way, when you can write

    OCR1A = 0x16E2;
    
    TCNT1 = 0;

Not only is it much clearer and less prone to programmer error, but there is actually a right way and a wrong way to write many AVR 16-bit registers (which the compiler will take care of for you if you assign to the entire register as I've shown).  The right way is, for a write, write HI reg, then write LO reg.  For a read, read LO reg, then read HI reg.  This is necessary to get a correct value to/from a register which may be changing between the two accesses.  Look up "Accessing 16- bit Registers" in the data sheet.

 

You did it correctly when accessing OCR1A, but did it incorrectly when accessing TCNT1, so you may not be writing 0 to TCNT1 at all.  That may be the cause of your problem.

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

According to the way the high byte buffer works,

the value written to TCNT1 is 0x1600.

Compare that with 0x16E2.

Iluvatar is the better part of Valar.

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

It could be a contest to see how many errors can fit in a minimal piece of code without the compiler complaining :)

 

Operator precedence, flag clearing procedures, 16bit register reading/writing.

 

I guess there are only 3 now. I could probably beat that.

 

 

here is his code-

https://godbolt.org/z/-9vllx

notice how the while loop optimizes away to a single read of  TIFR, which of course means no waiting around for the flag to set.

 

 

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

skeeve wrote:

According to the way the high byte buffer works,

the value written to TCNT1 is 0x1600.

Compare that with 0x16E2.

Right, thanks for posting the numbers.  I hope OP will fix the write order (hopefully by letting the compiler do the dirty work) and report back if that solved the problem.