[TUT] [C] Newbie's Guide to AVR Timers

Go To Last Post
482 posts / 0 new

Pages

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

Of the 6 timer program samples, I can't get the 3 that supposed to generate IRQs to work. The interrupts simply are not happening.

My setup:
. ATMega16
. Win7 64-bit
. WinAVR command line tools
. AvrDude programming an STK500; and:
. AvrDude programming an Olimex P40 via an STK500

Something has changed since 2007 that's making the code in Part 5, Part 7, and Part 8 nonfunctional.

Have you tested this code lately on an ATMega8, 16 or 32 ?

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

Quote:
The interrupts simply are not happening.

How are you determining this? Are you toggling a LED in the ISR, just as in Deans code? Have you made sure that the LED is actually wired up correctly.

Quote:
AvrDude programming an STK500

Just nitpicking, but the STK500 is the hardware doing the programming. What you are programming is the ATmega16.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I tested the code from part 5, but on an ATmega2560. One small thing had to be changed, since the 2560 has a whole bunch of 16-bit counters: There is no register TIMSK, but rather a register TIMSK1.

Apart from that I took Deans code as it stands, did a build, and flashed the 2560 sitting on an STK-600. The STK-600 has the same LED circuitry as the STK-500. My LED is flashing with 0.5 Hz, just as expected.

Given this, I believe you should not fixate on

Quote:
Something has changed since 2007 that's making the code in Part 5, Part 7, and Part 8 nonfunctional

but rather go through your wiring and then make sure you've done everything "by the book" (i.e. not changed anything in Deans code, built for the correct device, ATmega16 in the correct STK-500 socket etc).

I did this with Atmel Studio 6, ATmega2560 on an STK-600, Windows Vista Pro.

- Show your code.
- Show your build output.
- Show your AVRDUDE command to program the ATmega16, and the put from it.
- Tell us how you've wired up the LEDs on the STK-500.
- And, again, tell us how you're determining that interrupts do not occur.

Perhaps verify your setup by testing a simple delay-based "blinky". Should take less than five minutes:

#include 
#define F_CPU 1000000ul
#include 

int main(void)
{
	DDRB = (1<<0);

	while (1)
	{
		PORTB ^= (1 << 0); // Toggle the LED
		_delay_ms(1000);
	}

	return 0;
}

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

I am running ATMega16's, not the ATMega2560 or the ATMega128.

My description may have been a little too terse: I tested the code both on an STK500 *programming itself* and the STK500 programming an Olimex P40, both of which are running an ATMega16 @ 16 MHZ. Both boards have their on-board LED wired to PORTB4 - No extra LED wiring is necessary for the following code.

The code examples that poll the counter register and status flags work fine, but the ones that are supposed to generate IRQs do not. I verified the lack of output activity with a logic analyzer just in case the pulse widths happened to be too small duty-cycle-wise to light the LED.

Code, last example (#6 of 6):
Prescaler and count value adjustment have not yet been made for the 16 MHz F_CPU being used. None the less, the LEDs should still light up.
// OVF ISR With Count Preset for Exact Timing:

#include  
#include  

int main (void) 
{ 
    DDRB |= (1 << 0) ;          // Set LED as output 
    
    TIMSK |= (1 << TOIE1) ;     // Enable overflow interrupt 
    sei() ;                     // Enable global interrupts 
    
    TCNT1 = 49911 ;         // Preload timer with precalculated value.
    
    TCCR1B |= ((1 << CS10) | (1 << CS11)) ; // Set up timer at F_CPU/64 
    
    for (;;)  {} 
} 

ISR(TIMER1_OVF_vect) 
{ 
    PORTB ^= (1 << 0) ;     // Toggle the LED 
    TCNT1  = 49911 ;        // Reload timer with same precalculated value.
}

Note that my files are numbered 0..5, not 1..6.
Build output:
I--- {AVR-Comp.CMD}
I---
I--- PROJECTNAME [ 5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset ]
I--- MCU_TARGET [ atmega16 ]
I--- F_CPU [ 16000000 ]
I--- C_SRCS [ D:\DATA\!MYDOC~1\!PROJE~1\!ELECT~1\!!AVRD~1\AVRCOU~1\5.BA
D.CTC_Timer_OVR_IRQ_WithCountPreset.CPP ]
I--- EXTRACDEFS [ ]
I--- EXTRACINCS [ ]
I--- CINCLUDES [ -I. ]
I--- EXTRACDEFS [ ]
I--- CFLAGS [ -Wall -mmcu=atmega16 -DF_CPU=16000000UL -Os -Wno-unused-
variable ]
I--- ASFLAGS [ -aglm ]
I--- CCLDFLAGS [ -Wl,-Map,5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.map,--cr
ef,-lm ]
I--- LDFLAGS [ ]
I--- EXTRALIBS [ ]
I--- PGMFFORMAT [ ihex ]
I--- BURN_EXT [ hex ]

X--- Compiling [ 5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.CPP ] ...

avr-gcc.exe -c -Wall -mmcu=atmega16 -DF_CPU=16000000UL -Os -Wno-unused-variable
-I. -ID:\DATA\!CURRE~1\!ELECT~1\AVRPRO~1\!LCD~1\ARDUIN~2\ARDUIN~1\hardware\too
ls\avr\avr\include 5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.CPP -o D:\DATA\!MYDO
C~1\!PROJE~1\!ELECT~1\!!AVRD~1\AVRCOU~1\5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.
o

X--- Linking to [ 5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.elf ] ...

X--- Creating FLASH Burn File [ 5.BAD.CTC_Timer_OVR_IRQ_WithCountPreset.hex ]
...[/code]
---------------------------
It seems to me that something extra needs to be done to the mega16 to get it to generate interrupts.

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

Quote:

I tested the code both on an STK500 *programming itself*

You are utterly confusing/confused. The STK-500 does not program itself. (Unless we where to talk about an upgrade of the firmware of the STK-500 itself - which we are not.) I assume you are talking about "on an STK500 programming an Atmega16 in it's target area".

I can't make heads or tails of your build output. (E.g. I can see a compile command line, but no link command line. It just says "Linking to [...]") Can we see your makefile?

Again: Can we see your AVRdude command line, and the output from it.

The code examples that poll the counter register and status flags work fine

Can we see one such example? (To double-check that it accesses the same registers, assumes LEDs on same port etc..)

Quote:
Code, last example (#6 of 6)

Since I tested part 5, not 6, why could you not follow me there? (My STK-600 is now stuffed into it's bag for a 50 mile car trip and then I'll hit the sack. Any STK won't come out until tomorrow at the earliest, so I'm more or less pausing here for 12 to 24 hours. Someone else will have to jump in, or you'll have to wait.)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

This is really awesome tutorial.
Thanks a lot for such nice and clear explaination.

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

There's an obvious bug in each code example that use interrupts: [ sei() ] must be called in each ISR routine to re-enable interrupts that are automatically turned off each time when entering an ISR function. Otherwise, an IRQ-->ISR will happen just once.

It's obvious the code hasn't been tested recently in the real world ! Perhaps the WinAVR compiler has changed since 2007, but I doubt that very, very much.

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

Quote:

There's an obvious bug in each code example that use interrupts: [ sei() ] must be called in each ISR routine to re-enable interrupts that are automatically turned off each time when entering an ISR function. Otherwise, an IRQ-->ISR will happen just once.

What on earth are you talking about? sei() almost never should appear in an ISR(). One of the attributes of an ISR() (that is __attribute__((signal))) is that the return is made by a RETI not a RET. That re-enables the I bit in SREG and to do so any sooner (such as sei() within the ISR() code) is immensely dangerous program design - what then happens if the same interrupt event occurs before this handler has finished and it interrupts the code between the SEI and the RET? If it happens a few times you risk eating the stack. The advantage of RETI is that the re-enabling of I does not actually occur until one cycle later. So the code is firmly back into the interrupted code before the event can reoccur.

Suggest you have a read of any AVR datasheet and try to understand the interrupting mechanism as you have a dangerous mis-knowledge of how it works. It is also very unwise to go correcting a tutorial aimed at other beginners if you don't understand how an underlying mechanism you are commenting on works in the first place!

Last Edited: Mon. Oct 22, 2012 - 05:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

There's an obvious bug in each code example that use interrupts: [ sei() ] must be called in each ISR routine to re-enable interrupts that are automatically turned off each time when entering an ISR function. Otherwise, an IRQ-->ISR will happen just once.

What?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
"One of the attributes of an ISR() (that is __attribute__((signal))) is that the return is made by a RETI not a RET."

You are talking in "assembly", I am talking in "C". There is no RET(I) instruction in C ! Returns are inserted automatically in C compilers, unless a value needs to be returned.

I am using the WinAVR-20100110 toolchain. What are you using ?

The IRQ/ISR examples simply don't work until a sei() instruction is put anywhere in the ISR functions. I have proved this beyond a shadow of a doubt.

Have you actually compiled, tested and verified these examples recently on an actual ATMega8 or 16 ?

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

I think I need to do this a bit more gently:

Pascor: I'm afraid you are mistaken. The ISR macro in AVR-GCC will apply the GCC specific "signal" attribute to the function, as Cliff says. Functions marked with this attribute are treated specially by the compiler, meaning they save and restore used registers, but also are generated with a RETI instruction rather than a RET instruction when exiting the function. Attempting to put this in yourself is dangerous, as it will allow for potentially infinite stack recursion.

I honestly don't mind and even warmly welcome all corrections to my tutorials, but your very, very incorrect statements are worrying, as if someone chooses to follow them they could be putting flaws in medically-, safety- and/or reliability- critical applications.

Please take a look at a disassembly listing of a program with an ISR() macro. I assure you, it will be generated correctly for any version of GCC included in any WinAVR release since 2004.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

There's no need to look at the assembly code. I said

Quote:
There is no RET(I) instruction in C ! Returns are inserted automatically in C compilers, unless...

The example code is in C, not assembly, so the generated assembly code is a moot point. My C program works as expected only if the sei() instruction is added to the ISR function. Let me rephrase this: The code does not work if an sei() instruction does not appear in the ISR function. I don't understand what is so difficult to understand about this.

Quote:
Have you actually compiled, tested and verified these examples recently on an actual ATMega8 or 16 ?

Have you verified you own code using the WinAVR-20100110 compiler ? Don't you think you should since this is the most common toolchain ? I believe you will verify my findings.

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

Quote:

Have you verified you own code using the WinAVR-20100110 compiler ? Don't you think you should since this is the most common toolchain ? I believe you will verify my findings.

I honestly think you are joking at this point, but just in case, yes, I have validated the way the compiler works, as has everyone else in the world that has ever written an AVR application that targets AVR-GCC.

Quote:

The example code is in C, not assembly, so the generated assembly code is a moot point.

What in the world gives you that idea? The AVR microcontroller runs instructions, which have corresponding assembling mnemonics. Your C code is compiled into the same instructions, thus a dissassembly gives you a human readable representation of the instructions the compiler generates. Studying the listing file of your compiled application for 10 seconds would allow you to validate what I am saying is true.

A different tact:

1) Do you think someone else would have pointed this out before now if what you are saying is true?

2) Do you think other members would have written universally incorrect and non-functional code, and shipped them in products if the code obviously did not work?

3) Do you think I would have been hired by Atmel with such a horrible misunderstanding of a fundamental compiler function?

If your code only works with a sei() in the ISR, your code is incorrect. Show us the code, we will show you where you are wrong. I really can't think of a gentler way to put this; figuring out a band-aid to "fix" your code doesn't mean the compiler is broken.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Pascor, as I said you clearly haven't got a clue what you are talking about so you are not in position to correct a tutorial until you have learnt some basics of C programming. Here is a program in which I've deliberately included a "normal" function and also an ISR() function:

#include 
#include 

volatile uint8_t count;

__attribute__((noinline)) void timer_init(void) {
	TCCR1B = (1 << CS01);
	TIMSK = (1 << TOIE1);
}	

ISR(TIMER1_OVF_vect) {
	count++;
}

int main(void) {
	DDRB = 0xFF;
	timer_init();
	sei();
	while(1) {
		if (count == 10) {
			PORTB ^=0xFF;
			count = 0;
		}
	}
}

the code generated for timer_init() is:

00000092 :
#include 

volatile uint8_t count;

__attribute__((noinline)) void timer_init(void) {
	TCCR1B = (1 << CS01);
  92:	82 e0       	ldi	r24, 0x02	; 2
  94:	8e bd       	out	0x2e, r24	; 46
	TIMSK = (1 << TOIE1);
  96:	84 e0       	ldi	r24, 0x04	; 4
  98:	89 bf       	out	0x39, r24	; 57
}	
  9a:	08 95       	ret

Note that the C compiler has (as you might expect) ended this function with a RET. Now look at the code of the ISR():

0000009c <__vector_8>:

ISR(TIMER1_OVF_vect) {
  9c:	1f 92       	push	r1
  9e:	0f 92       	push	r0
  a0:	0f b6       	in	r0, 0x3f	; 63
  a2:	0f 92       	push	r0
  a4:	11 24       	eor	r1, r1
  a6:	8f 93       	push	r24
	count++;
  a8:	80 91 60 00 	lds	r24, 0x0060
  ac:	8f 5f       	subi	r24, 0xFF	; 255
  ae:	80 93 60 00 	sts	0x0060, r24
}
  b2:	8f 91       	pop	r24
  b4:	0f 90       	pop	r0
  b6:	0f be       	out	0x3f, r0	; 63
  b8:	0f 90       	pop	r0
  ba:	1f 90       	pop	r1
  bc:	18 95       	reti

Unlike before the function ends with RETI not RET and the prologue and epilogue of the function are more complex as the C compiler has to get things into the right state to run the C code whatever foreground code may have been interrupted and no matter what it was doing (such as a MUL which might mean that R1 did not contain 0).

What's more look at it when I add sei() to the ISR() code:

0000009c <__vector_8>:

ISR(TIMER1_OVF_vect) {
  9c:	1f 92       	push	r1
  9e:	0f 92       	push	r0
  a0:	0f b6       	in	r0, 0x3f	; 63
  a2:	0f 92       	push	r0
  a4:	11 24       	eor	r1, r1
  a6:	8f 93       	push	r24
	count++;
  a8:	80 91 60 00 	lds	r24, 0x0060
  ac:	8f 5f       	subi	r24, 0xFF	; 255
  ae:	80 93 60 00 	sts	0x0060, r24
	sei();
  b2:	78 94       	sei
}
  b4:	8f 91       	pop	r24
  b6:	0f 90       	pop	r0
  b8:	0f be       	out	0x3f, r0	; 63
  ba:	0f 90       	pop	r0
  bc:	1f 90       	pop	r1
  be:	18 95       	reti

What happens in this case is the I bit is set back to 1 five opcodes too soon. During the execution of those opcodes another interrupt might occur. If so then the handling of this first ISR() was not finished and R1, R0, SREG and R24 may be on the stack. So the next interrupt occurs and another copy of those four is stacked. Then another interrupt occurs and another 4 and so on. Eventually the stack runs out and the AVR crashes.

If someone has taught you that cli() or sei() should ever appear in an ISR() they are a complete idiot and do not know what they were talking about. You should correct the website or the course tutor (or God forbid the author of a book!) who told you this garbage before more people such as yourself are seriously mis-led. Unless you REALLY know what you are doing (and the only example I've ever seen that justified it was actually written by Dean!) then sei() and cli() have no place in ISR() code and to suggest it is utter madness.

As I said above, read an AVR datasheet and see how the interrupt process actually occurs. The original event will set some kind of flag bit in a status register (in my example it is the TOV1 it in the mega16's TIFR register. When the AVR makes the next opcode fetch it (in vector table order) scans all the bits that may have latched the fact that an interrupt event occurs. When it finds a bit such as TOV1 set it then sets I in SREG to 0 then initiates a call to the vector location associated with that flag, this in turn pushes the PC address (next opcode) of the interrupted code. At the vector location the WinAVR or AS6 C compiler will have put a JUMP/RJMP to the handler code you defined with ISR(). It arrives there something like 12 cycles after the vector process started. The C compiler (as in this example) will typically PUSH 3 or 4 registers. All this occurs with the I bit in SREG clear so cannot be interrupted. The body of the ISR then executes and finally the PUSH'd register are restored and, because the function was defined as ISR() the C compiler will have ended it with RETI so that unstacks the interrupted PC adddress back into PC and during the next opcode executed the I bit in SREG is returned to 1 so that further interrupts can occur.

if you want to read (and I think you should as your knowledge is terrible) then see the description of the signal attribute on this page of the avr-gcc manual:

http://gcc.gnu.org/onlinedocs/gc...

When you use ISR(vect_name) it is really just short form for the signal attribute if you take another view of my example code above after it has passed pre-processing but before it is compiled:

void __vector_8 (void) __attribute__ ((signal,used, externally_visible)) ; void __vector_8 (void) {
 count++;
 __asm__ __volatile__ ("sei" ::: "memory");
}

It is the very uise of "signal" in that which caused the compiler to generate the prologue and epilogue it has for this special function.

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

Quote:

sei() ] must be called in each ISR routine to re-enable interrupts

I'll join the choir, but from a slihtly different angle: This is simply not true. Looking through some well-worked examples or projects here at AVRfreaks should hit to this. Or looking at some of the sample applications from Atmel.

After you've seen a lot of ISR()'s in those, none doing an explicit sei() before it returns, you should back away from your misconception, and start pondering if you've been misinformed elsewhere or just jumped to an erroneous conclusion.

I.e. whenever one finds what one think is a blatantly obvious bug, and it is repeated over and over (or, as in this case, occurs in a place where many, many people ought to have yelled loudly about the bug a long time ago) then it is time to stop and ponder that the mistake is at one's own end.

Someone said that the meek shall inherit the world - be a winner and join the humble team! :wink:

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Well I suppose he did say:

Quote:

The IRQ/ISR examples simply don't work until a sei() instruction is put anywhere in the ISR functions. I have proved this beyond a shadow of a doubt.

It would be interesting to explore this further and find out what was actually wrong in the code he was testing and how it could have been that the erroneous sei() made it work. Maybe two ISR()s with a higher priority one absorbing all the CPU time so another one or main() could hardly run? we would need to see the non-working code to have any chance of analysing how he was so dangerously mis-led though.

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

Dean,

If I use timer0 with overflow and reload mode it seems that I can't go faster then +/-50Khz Do you reconize this problem?
ATmega168P 8Mhz int clk.

TCCR0A=(0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);
TCNT0=0xFE;

TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (1<<TOIE0);

ISR (TIMER0_OVF_vect)
{
PORTC ^= (1<<PC2);
TCNT0=254;
}

Alex

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

Do you have the CKDIV8 fuse cleared?

Also, the ISR takes several clocks to activate and execute. It is likely more than the 16 clocks it takes for your timer to overflow.

Quote:
I can't go faster then +/-50Khz
I didn't know that you can produce a -50kHz signal on an AVR ;)

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
Do you have the CKDIV8 fuse cleared?

You can also try to add this in the start oif main

CLKPR=0x80;
CLKPR=0x00;

TCNT0=254; can also be set to 255 although it won't help much.

Have you considered using the compare match to toggle the output pin?

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

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

Quote:

It is likely more than the 16 clocks it takes for your timer to overflow.

I've only just woken up, but why 16 clocks? The code is using DIV1 from FCPU, and sets the start value to 0xFE, thus it should take two clock cycles to overflow rather than 16.

In any case, yes, the ISR will be soaking up execution cycles and slowing it down. Using overflow for this purpose isn't the best way to do it; in later chapters of my tutorial you'll learn about CTC mode which will give you better results.

Quote:

You can also try to add this in the start oif main

That isn't optimization safe, use the avr-libc provided inline assembly macro instead:

#include 

clock_prescale_set(clock_div_1);

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I read that I could better use the counter in the ctc mode, but I just needed a simple 500Khz signal to replace a function generator and I didn't want to use the _delay function..

The problem is that if I change de clk devide from 64 to 8 to non and it still gives me max 51.3Khz If I devide it more until 1024 I can see the calculated value on the scoop.

I also saw a problem with AVR studio if I select the atmega168PA instead of just P, the debugger will fail. And it really says atmega168PA on the chip. But I don't think this is the problem
-Alex-

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

Try this

#include 

int main(void)
{
    // OC0A = PD6 on megaXX8
    DDRD = (1<<6);
    // Timer0 in CTC mode for 500kHz square wave on OC0A
    // Toggle OCR0A on compare match, CTC mode with TOP = OC0A
    TCCR0A = (1<<COM0A0) | (1<<WGM01);
    // Set TOP for 500kHz (toggle at 1MHz), for 8MHz F_CPU
    OCR0A = 8000000UL/2/500000 - 1;  // = 7
    // Start timer without prescaling
    TCCR0B = (1<<CS00);
        
    while(1)
    {
        // Nothing to do, harware handles the signal generation.
    }
    return 0;
}
================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The code is using DIV1 from FCPU
Sorry, I misread the CSxx bits. I can't stand the (0<<CS02) crap. To me it adds confusion rather than making things clear.
Quote:
I read that I could better use the counter in the ctc mode, but I just needed a simple 500Khz signal to replace a function generator and I didn't want to use the _delay function..
But CTC is far simpler than what you have, and using _delay wouldn't work at all.
Quote:
I also saw a problem with AVR studio if I select the atmega168PA instead of just P, the debugger will fail. And it really says atmega168PA on the chip. But I don't think this is the problem
The "A" variants are simply a die change. There should be no functional difference in the simulator.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hi Lund,
I tried your code.. And yes this works, its 500 Khz the only strange thing is that I would expect a 100% 50/50 duty cycle If you look at the scoop image every 4/5 cycles it "glitches" but for my application it will do, but do you know why this appears?

Koshchi,
The overflow should have worked..I'll look into that when I have more time
Adding the A to atmega168P gives realy a problem in my debugger (ver.6.0.1843)

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

Problem with the ctc mode solved.
Forgot to enable the int.
Now the duty signal is perfect 50/50%

TIMSK0=(1<<OCIE0A)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But snigelen code didn't use any interrupt :roll:

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

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

Yes, it should work without interrupts, I can't see why you have the glitches, I don't have them.

And it will take more than 8 clock cycles to enter the ISR, do something and get back, so you don't want to execute interrupt handlers at that rate anyway.

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

Quote:
The overflow should have worked..
If by that you mean the overflow method using interrupts should have worked, you are wrong. The ISR takes longer than the timer overflow, so the ISR can't possibly toggle the pin at a fast enough rate.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
But snigelen code didn't use any interrupt

You are right,
TIMSK0=(1<<OCIE0A) 

can be set to 0. I replaced the uC and it still works without setting the int.

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

Guys check this site, it is very well explained about AVR Timers,
http://www.zembedded.com/avr-tim...
http://www.zembedded.com/avr-tim...
http://www.zembedded.com/avr-tim...
Just thought of sharing with all of you...thanks!

Success is optional, choose wisely!

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

Hey,

first off, very nice tutorial, it helped me understand timers quite well.

In part one, you talk about Timer resolution = 1 / Input Frequency.

Well a frequency is 1 / time yielding something in Hz. The first Input Frequency is 100 Hz, so our timer resolution would be .01 seconds per tick or 10ms.

So far so good. All makes perfect sense so far.

But then, the second math bit, you start with (1 / Target Frequency) / (1 / Input Frequency).
Which is Timer Resolution / Input Resolution? Would make sense. Now here comes the confusing bit:

For the Target Frequency, you choose 1 / 20 of a second and while that's legal way, a second in fractions. It reads like some frequency however. Anyhow when filling in the target frequency, where one would expect then 1 / (1/20), you do some conversion or ... I don't know :)

If you'd rewrite the sentance

Quote:
Our project needs a fairly long delay, of 1/20 of a second.
to
Quote:
Our project needs a fairly long delay, of 1/20 of a second or rather 0.05 second.
then filling it into the formula would yield:
1 / (1 / 0.05).

Now if you'd put Timer resolution in the first bit, the formula would read:

(Target Frequency) / (1 / Timer Clock Frequency) -1

This sounds logical as 1/20 of a second, or 0.05 seconds is the target resolution and we need to 1/0.05 to get the target frequency. The math bit still seems to be accurate though.

Further below, when you simplify the formula.
[code]Target Timer Count = (Input Frequency / Prescale) / Target Frequency - 1

while we don't see a math example, it is correct again I think.

Anyway, just my 2 cents :)

edit: Corrected; yes, 1/20 is 0.05. My brain was allready sleeping.

Last Edited: Wed. Jan 9, 2013 - 10:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Our project needs a fairly long delay, of 1/20 of a second or rather 0.5 second.
1/20 != 0.5

Regards,
Steve A.

The Board helps those that help themselves.

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

I must say this is a great tutorial. But I am having trouble with my output frequency.
I perform the calculation for timer count.

Target Frecuency   = 30000

Target Timer Count = (1/30000)/(1/8000000)-1
                   = (33.33E-6 / 125E-9)-1
                   = 264.67

But when I use 264 for my count the output on OC1A is 15khz instead of 30khz.

I could decrease the count to 128 and get 30khz but then my duty cycle resolution decreases. The frequency does not need to be exact. I just need one more bit of resolution to control my duty cycle.

#include 

int main(void)
{
	 DDRB	|= (1 << PORTB1); // PORTB PIN 1(OC1A) SET AS OUTPUT.
			 
	 //CONFIGURE TIMER/COUNTER 1 16BIT
	 TCCR1B	|= (1 << CS10);	                  //NO PRESCALER                 
	 TCCR1B	|= (1 << WGM13);                    // P&F CORRECT                
	 TCCR1A	|= (1 << COM1A1) | (1 << COM1B1);   // SET UP COUNT CLEAR DOWN COUNT
			 
	 //SET ICR1 (TOP) AND OCR1A
	 OCR1A = 75;		                        
	 ICR1  = 264;                              	
	 
	 while(1)

[/code]

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

With Phase correct and Phase/Frequency correct modes you need to divide by two since they count up to TOP then count down to BOTTOM. Fast PWM counts up, then clears to 0 (no count down). Why is it that you think you need Phase/Frequency correct mode?

Regards,
Steve A.

The Board helps those that help themselves.

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

Wow that was quick! I'm actually constructing a avr based switching amplifier for a school project. I don't require phase/frequency correctness. It was there so I figured that I would use it. Anyways your explanation makes sense and i'll try without. But if I wanted to test the difference what would I need, a faster clock?

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

I've done experiments playing audio with both "Fast" and "Phase correct" and try as I might I simply could not hear any difference whatsoever. So I'd pick "Fast" every time. YMMV.

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

hi there i'm new here and i'm newbie, and i want to say thank you so much for this tutorial, realy helped me.

i want to ask something newbie question, before i played more with timer, i decided to played more in #include . to check my crystal is right, to check that its works with delay, to check the looping and variable work find,so this is my case :

#define F_CPU 1000000UL
#include 
#include 
#define setservo1 (PORTB |= (1<<PB0))
#define clrservo1 (PORTB &= (0<<PB0))

int main(void)
{
int i,res1,res2;

 DDRB = (1<<PB0); // set PINB0 as output
 res1 = 1000; // if i change this 2000 and
 res2 = 19000; // this to 18000 everythings fine
	while(1)
	{
			for(i=0;i<=100;i++)
			{
			setservo1;
			_delay_us(res1);
			clrservo1;
			_delay_us(res2);
			}

			for(i=0;i<=100;i++)
			{
			setservo1;
			_delay_us(1520);
			clrservo1;
			_delay_us(18480);
			}

//this problem, when i uncomment two this statemen			
//		res1 = 2000;
//		res2 = 18000;
         }
}

i attached my servo to test that, everyting work fine, but if i uncomment the last comement, the servo goes error, any clue? :(

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

Your question is not related to the timers so I'm not sure why you have decided to post it in this thread.

Relating your question about delay, the _delay_us() is intended to be used with constants, not variables, check this recent thread http://www.avrfreaks.net/index.p...

Alex

"For every effect there is a root cause. Find and address the root cause rather than try to fix the effect, as there is no end to the latter."
Author Unknown

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

alexan_e wrote:
Your question is not related to the timers so I'm not sure why you have decided to post it in this thread.

Relating your question about delay, the _delay_us() is intended to be used with constants, not variables, check this recent thread http://www.avrfreaks.net/index.p...

Alex

thanks for the pointing, sorry before, if i wrong room. :oops:

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

Love your tutorial ...you are as cool as the code...
:)

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

Hello!
I have just read this tutorial, very good one.
However I do have a small problem.
I need to generate a 38kHz signal. I have tried to use the formula from the tutorial to calculate the compare value for the timer.
According to the formula, at 8Mhz CPU frequency, with no prescaler for the timer the value should be 208 for getting a 38kHz output frequency.
With this value my output signal is jut 19kHz. If I use 104 instead of 208 everything is ok, I get 38kHz.

I am ussing an AT90USBKey based on AT90USB127 and my code is:

#include 
#include "avr035.h"
#define F_CPU 8000000UL
#include 
int main(void)
{   CLKPR=0b10000000;
    CLKPR=0b00000000; //setting the cpu at8MHz
    DDRC=0xFF;//setting PORTC as output
    setbit(TCCR3A,COM3A0); //setting the OC.3A toggle
    OCR3A= 208; //compare value
    TCCR3B=0b00001001;// starting Timer3 in CTC mode 
	
   while(1)//this is for testing the cpu frequency
    {   
		setbit(PORTC,0);
		_delay_ms(3000);
		clearbit(PORTC,0);
		_delay_ms(3000);
        
    }
}

Can someone explain me why do I get this behavior?

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

Think about a complete cycle of the wave:

      +-----+
      |     |
      |     |
+-----+     +
|< 1 >|< 2 >|

That takes 2 toggles to get back to where it started.

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

Yes, you are right , I now understand, thank you!

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

Hi Dean,

I'm referring Part Two where you've got

Quote:
Target Timer Count = (Input Frequency / Prescale) / Target Frequency - 1

This can be further simplified to

Target Timer Count = (Input Frequency / Prescale * Target Frequency) - 1

This is simply because (1/a)/(b/c) == c/(a*b) == (c/b)/a

Thanks for such a fantastic guide!

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

Hrm, not sure how I missed that one. There's some deliberately non-optimal formulas early on in the first chapter to show visually how it's all calculated, but I missed the last transformation when optimizing it at the end of the chapter. I've updated the PDFs to fix that, thanks!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
Hrm, not sure how I missed that one. There's some deliberately non-optimal formulas early on in the first chapter to show visually how it's all calculated, but I missed the last transformation when optimizing it at the end of the chapter. I've updated the PDFs to fix that, thanks!

- Dean :twisted:

No worries Dean, I had a read through your PDF and will let you know if I catch anymore. Fantastic guides BTW. I've pretty much worked with Microchip MCUs (mainly) from 2001-2012 @ University. Your guides made it so much easier to figure out which registers need to be set etc. to get something simple working (interrupts etc.)

I posted this video just yesterday http://youtu.be/NxQdCMSOxVo - got a couple others of an omnidirectional robot I built for my Masters if you scroll down my channel.

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

Thanks - please do let me know if you find anything else wonky. Enjoy the guides (old as they are) and I hope you pass them along to others you find that are interested in AVRs.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:
Thanks - please do let me know if you find anything else wonky. Enjoy the guides (old as they are) and I hope you pass them along to others you find that are interested in AVRs.

- Dean :twisted:

BTW, I'm seeing the same equation twice now in the PDF?

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

Hell, I can't catch a break today - fixed.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
if the timer input frequency is 1.1MHz rather than 1.0MHz our one minute timer will be sixty times that small error out in duration.

Great tutorial...do you know how can I compensate the errors due the int. osc. inaccuracy ? If you need 60 sec exactly

Thanks for the tuto Dean

Pages