[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

Dean, thank you. This is an excellent tutorial and has given me a great understanding of timers. The step-by-step and explanation of each line of code that you add is invaluable.

My project(s) are coming, and when I get either somewhere or really stuck I'll post to AVR freaks what I've been up to.

Also hello to the homeland, I live in Canada but I'm from Australia.

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

Hi Dean!

Thaks for a great tutorial. It really saved me!

One question though, I've implemented the simple code for toggle led once each second on my butterfly and the LED BLINKS in 1Hz. In the code we calculated it to TOGGLE in 1Hz. I'm getting confused.

I've found the CLKPR in the datasheet, so I can adjust to make it do what I want. But still..

Does your LED _blink_ or _toggle_ in 1Hz?

By accident I put my led on PORTB = 0x20, same as the piezo, so my clock ticks! :)

Once again, great tutorial!

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

Nice balancing robot Paul! Thanks for the offer for the PWM code. It's not a matter of myself being unable to write a trivial example, it's finding the motivation to write the tutorial around it!

Oh, and G'day cmetom! Make those crazy Canadians see the true culture of us Aussies :).

agising: The code is designed for an AVR running off its internal 1MHz RC oscillator, and should toggle at 1Hz. The Butterflies' bootloader sets the RC oscillator to 2MHz, so you're seeing twice the toggle rate (.5Hz).

- Dean :twisted:

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

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

Quote:

should toggle at 1Hz.

Quote:

twice the toggle rate (.5Hz).

:shock:
Last time I checked, twice the rate of 1 Hz was 2 Hz.

"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

Quote:

Last time I checked, twice the rate of 1 Hz was 2 Hz.

I wrote the tutorial and so what I say goes. You are wrong.

- Dean :twisted:

PS: Just kidding! Heck of a brain fart for a post made in the middle of the day.

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

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

abcminiuser wrote:

agising: The code is designed for an AVR running off its internal 1MHz RC oscillator, and should toggle at 1Hz. The Butterflies' bootloader sets the RC oscillator to 2MHz, so you're seeing twice the toggle rate.

- Dean :twisted:

Gr8

Thank you very much!

Awesome tutorial Dean! :D

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

Dean
I've just done a few rounds with the 16 bit Timer1 on a Mega168 and think that there are a few gotcha's that are worth mentioning. I was using the input capture pin to measure pulse width so the timer was in NORMAL mode, what could be easier?

1) On the mega168 the TCNT1 register is in the extended I/O memory area (it's at 0x84 and 0x85). The code examples shown in the data sheet use in and out instructions, these examples are wrong for the 168 and who knows how many more chips.

The registers still need to be written in reverse order to use the TEMP register properly.

2) The input capture interrupt is tricky! I was using the rising edge on ICP1 for an interrupt and it had a few 'bounces'. When an ISR is executed the interrupts are disabled automatically and you'd think that any rising edges will be ignored until it is re-enabled. Thinking, big mistake, that the ISR is long enough because I had included a short delay to bypass the bounces, I couldn't understand the random behaviour of my pulse width measurement. It took the best part of 4 days to track down the fact that the input capture flag CAN be set in the ISR and as soon as it terminates and interrupts are re-enabled, back it goes to the ISR for a quick rerun.

Now in all fairness, this is mentioned in the data sheet but there's a lot to read before you find it. I now set ICF1 as the last instruction before leaving the Timer1 ISR.

Note that it needs to be set not cleared....

Cheers

Klave

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

Hi Dean/everyone,

I am following this tutorial and it has been a fantastic insight into the AVR timers. Got through the first few with flying colours but hitting a brickwall on Part Four.

I am actually using a Butterfly which has the ATmega169V. In the doc for the ATmega169V (http://www.atmel.com/dyn/resources/prod_documents/doc2514.pdf)...approx page 95 onwards...I am trying to find the appropriate registers and what have you, but being such a noob, I am somewhat lost!

Can someone please tell me which registers etc I need to be using with the Butterfly to get the CTC functionality as per Step 04?

WGM12 ... I think this is correct
OCR1A ... ?
TIFR ... ?
OCF1A ... ?

Thanks in advance!

Cheers,
Scott.

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

TIFR becomes TIFR1 in the MEGA169, and still contains the OCF1A flag. OCR1A stays the same, and WGM12 lives in TCCR1B.

Really the only change is the TIFR register which gaves the "1" suffix as the entire TIFR1 register is for timer 1, rather than being shared with timer 0 or other timers.

- 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:
TIFR becomes TIFR1 in the MEGA169, and still contains the OCF1A flag. OCR1A stays the same, and WGM12 lives in TCCR1B.

Really the only change is the TIFR register which gaves the "1" suffix as the entire TIFR1 register is for timer 1, rather than being shared with timer 0 or other timers.

- Dean :twisted:

Thanks Dean, I'll give it a whirl first thing in the morning at work and if I have any troubles I'll post another reply.

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

Hi,

In the part five you wrote "TIMSK" instead "TIMSK1"?

Many thanks..

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

Quote:

In the part five you wrote "TIMSK" instead "TIMSK1"?

TIMSK is valid for the MEGA16, the chip I've written the examples on. Newer AVRs add the USART number to the end of the registers to differentiate between then on multi-USART AVRs, and so you're going to need to add in the USART number as I specify in the tutorial and re-iterate above.

- Dean :twisted:

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

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

This is a fantastic tutorial.

Thanks for taking the time to write it.

Please let me know if you ever write a book

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

if i used OCR1A as TOP would it "always" at 50% duty cycle?
could you explain about "changing frequency" in Fast PWM and Phase Correct PWM mode? im a liltle confuse about that. the datasheet said that

Quote:
When changing the TOP value the program must ensure that the new TOP value is
higher or equal to the value of all of the Compare Registers.
Sorry, cant wait for the Dean's updates :D

kurro

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

Dean, it might be a good idea to move Part Nine to another thread (or a copy of it). It is one of the most asked about functions of timers and it is difficult to find this on page 11 of the thread.

Regards,
Steve A.

The Board helps those that help themselves.

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

As this tutorial chain gets a lot of discussion and questions I'd think that moving all the tutorials to a new thread which is then locked would make sense. The locked thread could contain links to this question-and-discussion thread. And as Dean the author is also the mod here he can easily add to, re-arrange or otherwise modify the tutorials, and announce it in the Q'n'D thread.

"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've moved the PWM section to it's own new thread:

http://www.avrfreaks.net/index.p...

Where I can update it and people can see it better. I've also made this tutorial into a formatted PDF document (a task unto itself) and attached it to the first post.

- Dean :twisted:

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

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

I am using the ATINY12 to build a simple device which will produce two alternating frequencies to control a liquid mixing process. Any one think they can help me here? :roll:

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

I am using the ATINY12 to build a simple device which will produce two alternating frequencies to control a liquid mixing process. Any one think they can help me here? :roll:

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

jmyers2 wrote:
I am using the ATINY12 to build a simple device which will produce two alternating frequencies to control a liquid mixing process. Any one think they can help me here? :roll:

Please do not ask for help in the tutorial forum. This should have been asked in the AVR forum.

If you make a double post by mistake, click the X button to delete one of them.

If you think education is expensive, try ignorance.

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

Wow, that was amazing! Where can I donate? :)

I'm just starting out and just trying to remember all those acronyms had my head spinning. Couldn't ever remember which ones I needed for which flag on which timer in which mode on which register. This tutorial has helped a TON.

Thanks.

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

Thank you for this clear tutorial Dean. Very helpful thing for a person who's just started (like me).
This really opens a lot of doors for me. The idea of writing a book is not a bad one. If ever, make sure you get it translated into Polish. Here in poland there are no (little) good books on it. Most writers get only as far as rewriting the datasheets while making heaps of mistakes. So the polish market is yours =D

I have two questions. I could see that to some extent they were answered in this thread already but werent exactly what i needed. (I have to admit to getting only as far as 9th page of this thread, so excuse me if i'm repeating any questions)

1)In case i want to read a 16 bit counter (for ex. reading: TCNT1), do i have to stop it first so as to make sure i don't get to read a New LSB and an old MSB (or the other way around) as the reading could happen in between the update of LSB and MSB? (as it happens in case of enabled interrupts)

2)My Atmega8 datasheet says that clearing all bits of TCCR1B (the prescaller) stops the counter. Is that the only way to stop it? Do i have to set up the prescaller each time again wanting to switch the timer back on?

Greetings

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

Quote:

Wow, that was amazing! Where can I donate? Smile

On my site (http://www.fourwalledcubicle.com) if you're so inclined. No one appreciates donations more than a full time university student!

Quote:

1)In case i want to read a 16 bit counter (for ex. reading: TCNT1), do i have to stop it first so as to make sure i don't get to read a New LSB and an old MSB (or the other way around) as the reading could happen in between the update of LSB and MSB? (as it happens in case of enabled interrupts)

The AVR timer has an internal buffer for the 16-bit timers. It is locked when reading the low byte, and unlocked when reading the high byte. For this reason, you should always read out the LSB before the MSB of the timer to ensure that the bytes correlate to the same data point -- if using GCC and one of the 16-bit psudoregisters such as TCNT1, this sequence is maintained by the compiler. This is the same for writing, and again the GCC compiler will take care of the sequence for you if you use the 16-bit psudoregister name.

Quote:

2)My Atmega8 datasheet says that clearing all bits of TCCR1B (the prescaller) stops the counter. Is that the only way to stop it? Do i have to set up the prescaller each time again wanting to switch the timer back on?

Yes, that's the only way to stop it - once given a clock source via the prescaler, the timer is activated. It would be nice if they put in a start-stop bit so that the timer can be stopped and started without having to preserve the prescaler settings, but as it stands that's your only option.

- Dean :twisted:

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

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

Thanks for the tutorial Dean. This is one of the best tutorials I have seen for any subject.

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

thanks for tutorial.
but i have found some bugs!
you need to use 15624 instead of 15625 in ctc modes because you are counting from 0 and 0-15624 is equal 15625 and in software ctc mode i think you should use 49911 instead of 49910 again because of 0.
and there is another utility that make calculations easier and it's an avr calculator you can get it at:
http://www.b9.com/elect/avr/kavr...

I love Digital
and you who involved in it!

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

Thanks for this tutorial. It is great!! It taught me so much!

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

Dean, your tutorial rocks and your USB stuff rocks as well.

I've been trying to get my head around the timer stuff for ages now, and this just makes it sorta clear.

Now I only need to find a tutorial on how to make a simple IR transmitter. *Starts doing the search thing again*

Code, justify, code - Pitr Dubovich

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

Took me a bit to read (and re-read) through it all but I've found that all your tutorials are extremely easy to follow. I've been afraid of trying to get into timers until i had to after skimming through some other explanations, but I UNDERSTAND now :twisted: , props to you, keep up the good work. :D

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

Thanks for this tutorial. I'm planning on making a intervalometer for my camera and this has just about put me there :)

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

Thanks for the helpful tutorial !

Microcontroller & Electronic Books from Amazon

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

Hi,

First of, thanks for this tutorial. However, I still have a little problem.

I would like a clock of 250Khz. My sys clock is 1Mhz. According to the formula in the datasheet (attiny 2313 in that case), f = f_sys / (2 N (1 + OCR0A)). Thus prescaler of 1 and OCR0A = 2. I set the

[code]

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

Hi,

First of, thanks for this tutorial. However, I still have a little problem.

I would like a clock of 250Khz. My sys clock is 1Mhz. According to the formula in the datasheet (attiny 2313 in that case), f = f_sys / (2 N (1 + OCR0A)). Thus prescaler of 1 and OCR0A = 2. I use the CTC mode. Here is the code:

void init_timer(void) {
	// timer setting: clear timer on compare match
	TCCR0A = (1 << WGM01);

	// clock source without prescaling.
	TCCR0B = (1 << CS00);

	// count to maximum value of
	OCR0A = 1;


	// turn on interrupts
	sei();


	// clear compare match flag
	TIFR |= (1 << OCF0A);

	// enable compare match interrupt
	TIMSK |= (1 << OCIE0A);

}

ISR(TIMER0_COMPA_vect) {

	// toggle clock output pin
	USICR |= (1 << USITC);

}

However, the measured frequency is around 17Khz... It seems this is only possible when OCR0A = 31. This means that I cannot set a TOP (maximum) value smaller than 31. How is that possible? I don't really understand this. Anyone can help me?

Thanks in advance!

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

The ISR will have a certain amount of overhead - about 9 cycles or so IIRC. That means that regardless of the TOP value, if it is smaller than the time the ISR takes to complete, it will have no effect.

31 cycles for an ISR sounds a bit excessive, however. Are you compiling with -Os to compile for size?

- Dean :twisted:

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

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

yes, I'm using the -Os flag...

So what would be the solution? Increase the system clock?

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

I must be missing something here - on my calculator 250KHz is just one quarter of 1MHz. That means you have just 4 cycles to get the pin state changed. There is no way on earth you are going to achieve that using even the tightest ISR hand crafter in assembly. In fact you pretty much just want the main() code to sit in the tightest loop possible doing nothing other than toggling the pin state - there'd be no CPU time left for doing anything else. So if you want to generate 250KHz and you cannot achieve this with hardware assist then you most definitely need to clock the CPU at a faster speed. But doesn't the tiny2313 have the capability for the timer to do the pin toggling for you in silicon?

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

Yeah, it's what I thought... It's weird because at first, we think with this timer thing we can achieve whatever clock we want under the condition that the output clock is smaller that the sys clock. Actually, it's not the case.

There is an upper bound. Maybe it could be a good idea to update the tutorial by giving the upper limit for a clock generated by a timer.

Anyway, thank you guys for your answers!

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

huh... I played with my source code but when I set the prescaler to 1 and OCR0A to 249, it doesn't work anymore. The clock is not generated.

According to the formula, it should be 1Mhz/(2 * N * (1 + OCR0A)) = 2Khz. So the code toggle at 2Khz which should give a 1Khz clock.... but it doesn't do anything....

The source code is the same as in my previous post, except with OCR0A = 249;

Any idea why it is not working properly?

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

so, no answer since my last post. I discovered that with OCR0A <= 27, I have a clock around 17KHz. However, with OCR0A > 27, no clock anymore...

This just drives me crazy...

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

Quote:

The source code is the same as in my previous post,

But that's not a program, it's just a snippet - how do you expect anyone to help if they cannot build/examine the same program that you are trying to use.

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

I took that code, added a simple main() and changed to set OCR0A to 99 and it worked just fine. It hits the ISR every 100 clocks.

Regards,
Steve A.

The Board helps those that help themselves.

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

Ok, here is my full code. I thought it wouldn't be a problem to post a snippet but it seems it's related. Sorry about that.

So when I set OCR0A higher than 27, the clock at the SPI pin stops toggling...

The target is an attiny2313, I'm trying to set up the SPI.

Here is the code:

#define SPI_OUT_REG PORTB
#define SPI_IN_REG PINB
#define SPI_DIR_REG DDRB
#define SPI_DI_PIN PINB5
#define SPI_DO_PIN PINB6
#define SPI_CLK_PIN PINB7

ISR(TIMER0_COMPA_vect) {
	// toggle clock output pin
	USICR |= (1 << USITC);
}

ISR(USI_OVERFLOW_vect) {
	// turn off timer interrupt
	TIMSK &= ~(1 << OCIE0A);
	// clear counter overflow interrupt flag
	USISR = (1 << USIOIF);
}

void spi_initMaster() {
	// configure DO and CLK pins as output, DI as input
	SPI_DIR_REG |= (1 << SPI_DO_PIN) | (1 << SPI_CLK_PIN);
	SPI_DIR_REG &= ~(1 << SPI_DI_PIN);
	SPI_OUT_REG |= (1 << SPI_DI_PIN); // pull-up

	// 3 wire mode: DO, DI, USCK
	USICR |= (1<< USIWM0)|
		(1 << USICS1)|(0 << USICS0)|(1 << USICLK)| // external clock
		(1 << USIOIE); // counter overflow interrupt enable

	// timer setting: clear timer on compare match
	TCCR0A = (1 << WGM01);

	// clock source with no prescaling.
	TCCR0B = (1 << CS00);

	// count to maximum value of
	OCR0A = 28;

	// turn on interrupts
	sei();
}


uint8_t spi_rxTx(uint8_t data) {
	USIDR = data;

	// clear compare match flag
	TIFR |= (1 << OCF0A);

	// enable compare match interrupt
	TIMSK |= (1 << OCIE0A);

// wait for overflow flag is set to 1 (transfer complete)
	while(!(USISR & (1 << USIOIF)));

	return USIDR;
}

The main:

int main(void) {
	spi_initMaster();
	for (;;) {
		spi_rxTx(0xFF);
	}
	return 0;
}

Thanks for your help!

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

Again, in the simulator this is working correctly. Your ISR takes about 27 cycles to complete, so any value of OCRA less than that and the ISR will run continuously (the next interrupt will be queued before the previous one ends).

Regards,
Steve A.

The Board helps those that help themselves.

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

mmmh, thanks for testing this in a simulator. I think the only possibility left is a hardware bug. I'll try to change the uC to another 2313 or maybe a 45 for instance and see if it's working.

Another possibility is the tool I use to measure the frequency... I'm using a multimeter with a max freq of 10Mhz. However, if you increase OCR0A, the frequency decreases. Thus it shouldn't be a problem with the multimeter...

I'm interested in this simulation thing. Could you please explain me how it works and what tools you use?

I'm developing under Linux. Do these tools also exist with Linux? Could you pinpoint me a tutorial or some documentations? I'm kind of new with AVR development.

Thanks again for your help!

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

Quote:

I'm interested in this simulation thing. Could you please explain me how it works and what tools you use?

I'm developing under Linux.


I'm afraid those two paragraphs are oxymoronic. The simulator is a great component within AVR Studio. The IDE from Atmel that not only includes their own assembler but that can be used as a "wrapper" for building GCC projects (when WinAVR is also installed). Sadly for you this is ONLY available as a Windows program.

Now (if you search) you may find that some people have had success running it in Linux using Wine and I guess that if your plan is only to use it for the simulator (and not interfacing to real programmers/debuggers) then it will probably work sufficiently for you.

Another option is to use either Vmware Server or Sun's VirtualBox to run a virtual Windows machine within your Linux host - but the problem there is that you would need a licenced copy of M$ Windows to install it into the virtual machine.

Cliff

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

Hi Cliff,

Thanks for your answer. I only need a simulator to add to my linux environment. I checked the forum and read some posts about AVR Studio and Wine. I think I'll go with this solution.

It's a pity not to have native tools for linux!

Thanks again!

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

Well there's also simulavr but it's not maintained so only has limited support for early AVRs

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

Thank you very much for this tutorial :)

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

Hey,
a very good tutorial indeed. helped in grasping many concepts abt timers.

But am facing a problem on execution of

ISR(TIMER1_COMPA_vect)
{
PORTB ^= (1 << 0); // Toggle the LED
}

when i do make all, following warning is displayed:

warning: return type defaults to `int'
In function `ISR':
warning: control reaches end of non-void function

Plz help me out with this as i have been trying to work out with this code for quite a long time...

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

Quote:

warning: return type defaults to `int'

#include

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

i have included the avr/interrupt.h file.

But still the problem persists.

Pages