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

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

For an updated version of this tutorial in PDF format, please see this page of my website.

Newbie's Guide to AVR Timers
(C) Dean Camera, 2007

At last! Yet another tutorial, this one covering a topic which is the main source of frustration to those new with AVRs; timers. What are they, and what can they do for us?

Introduction - The AVR Timer

The timer systems on the AVR series of Microcontrollers are complex beasts. They have a myriad of uses ranging from simple delay intervals right up to complex PWM (more on this later) generation. However, despite the surface complexity, the function of the timer subsystem can be condensed into one obvious function: to time.

We use timers every day - the most simple one can be found on your wrist. A simple clock will time the seconds, minutes and hours elapsed in a given day - or in the case of a twelve hour clock, since the last half-day. AVR timers do a similar job, measuring a given time interval.

The AVR timers are very useful as they can run asynchronous to the main AVR core. This is a fancy way of saying that the timers are separate circuits on the AVR chip which can run independent of the main program, interacting via the control and count registers, and the timer interrupts. Timers can be configured to produce outputs directly to pre-determined pins, reducing the processing load on the AVR core.

One thing that trips those new to the AVR timer is the clock source. Like all digital systems, the timer requires a clock in order to function. As each clock pulse increments the timer's counter by one, the timer measures intervals in periods of one on the input frequency:

Timer Resolution = (1 / Input Frequency)

This means the smallest amount of time the timer can measure is one period of the incoming clock signal. For instance, if we supply a 100Hz signal to a timer, our period becomes:

Timer Resolution = (1 / Input Frequency)
Timer Resolution = (1 / 100)
Timer Resolution = .01 seconds

For the above example, our period becomes .01 seconds - so our timer will measure in multiples of this. If we measure a delay to be 45 timer periods, then our total delay will be 45 times .01 seconds, or .45 seconds.

For this tutorial, I will assume the target to be a MEGA16, running at at 1MHz clock. This is a nicely featured AVR containing certain timer functionality we'll need later on. As modern AVRs come running off their internal ~1MHz RC oscillator by default, you can use this without a problem (although do keep in mind the resultant timing measurements will be slightly incorrect due to the RC frequency tolerance).

In the sections dealing with toggling a LED, it is assumed to be connected to PORTB, bit 0 of your chosen AVR (pin 1 of DIP AVRMEGA16).

To start off, we will deal with basic timer functionality and move on from there.

Part One - Timers running at Fcpu

We'll start with a simple example. We'll create a simple program to flash a LED at about 20Hz. Simple, right?

First, let's look at the pseudo-code required to drive this example:

Set up LED hardware
Set up timer

WHILE forever
  IF timer value IS EQUAL TO OR MORE THAN 1/20 sec THEN
    Reset counter
    Toggle LED
  END IF
END WHILE

Very simple. We're just starting out, so we'll use the polled method of determining the elapsed time - we'll put in an IF statement in our code to check the current timer value, and act on it once it reaches (or exceeds) a certain value. Before we start on the timer stuff, let's create the skeleton of our project:

#include 

int main (void)
{
	// TODO: Set up LED hardware

	// TODO: Set up timer

	for (;;)
	{
		// TODO: Check timer value, reset and toggle LED when count matches 1/20 of a second
	}
}

Extremely simple. I'm going to assume you are familiar with the basics of setting up AVR ports as well as bit manipulation (if you're uncertain about the latter, refer to this excellent tutorial). With that in mind, I'll add in the LED-related code and add in the IF statement:

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	// TODO: Set up timer

	for (;;)
	{
		// TODO: Check timer value in if statement, true when count matches 1/20 of a second
		if ()
		{
			PORTB ^= (1 << 0); // Toggle the LED

			// TODO: Reset timer value
		}
	}
}

Now we need to start dealing with the timer. We want to do nothing more than start it at 1MHz, then check its value later on to see how much time has elapsed. We need to deviate for a second and learn a little about how the timer works in its most basic mode.

The AVR timer circuits come in two different widths, 8 and 16 bit. While the capabilities of the two timer types differ, at the most basic level (simple counting), the only difference is the maximum amount of time the timer can count to before overflowing and resetting back to zero. Those familiar with C will know that an unsigned eight bit value can store a value from 0 to (2^8 - 1), or 255, before running out of bits to use and becoming zero again. Similarly, an unsigned 16 bit value may store a value from 0 to (2^16 - 1), or 65535 before doing the same.

As the name suggests, an 8 bit timer stores its value as an eight bit value in its count register, while the 16 bit timer stores its current count value in a pair of eight bit registers. Each advancement of the counter register for any AVR timer indicates that one timer period has elapsed.

Our project needs a fairly long delay, of 1/20 of a second. That's quite short to us humans, but to a microcontroller capable of millions of instructions per second it's a long time indeed!

Our timer will be running at the same clock speed as the AVR core to start with, so we know that the frequency is 1MHz. One Megahertz is 1/1000000 of a second, so for each clock of the timer only one millionth of a second has elapsed! Our target is 1/20 of a second, so let's calculate the number of timer periods needed to reach this delay:

Target Timer Count = (1 / Target Frequency) / (1 / Timer Clock Frequency) - 1
                   = (1 / 20) / (1 / 1000000) - 1
                   = .05 / 0.000001 - 1
                   = 50000 - 1
                   = 49999

So running at 1MHz, our timer needs to count to 49999 before 1/20th of a second has elapsed - the normal calculated value is decremented by one, as the 0th count of the timer still takes one tick. That's a very large value - too large for an 8 bit value! We'll need to use the 16 bit timer 1 instead.

Firstly, we need to start the timer at the top of our main routine, so that it will start counting. To do this, we need to supply the timer with a clock; as soon as it is clocked it will begin counting in parallel with the AVR's CPU core (this is called synchronous operation). To supply a clock of Fcpu to the timer 1 circuits we need to set the CS10 bit (which selects a Fcpu prescale of 1 - more on that later) in the TCCR1B, the Timer 1 Control Register B.

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	TCCR1B |= (1 << CS10); // Set up timer

	for (;;)
	{
		// TODO: Check timer value in if statement, true when count matches 1/20 of a second
		if ()
		{
			PORTB ^= (1 << 0); // Toggle the LED

			// TODO: Reset timer value
		}
	}
}

Now, with only one line of code, we've started the hardware timer 1 counting at 1MHz - the same speed as our AVR. It will now happily continue counting independently of our AVR. However, at the moment it isn't very useful, we still need to do something with it!

We want to check the timer's counter value to see if it reaches 1/20 of a second, or a value of 49999 at 1MHz as we previously calculated. The current timer value for timer 1 is available in the special 16-bit register, TCNT1. In actual fact, the value is in two 8-bit pair registers TCNT1H (for the high byte) and TCNT1L (for the low byte), however the C library implementation we're using helpfully hides this fact from us.

Let's now add in our check to our code - it's as simple as testing the value of TCNT1 and comparing against our wanted value, 49999. To prevent against missed compares (where the timer updates twice between checks so our code never sees the correct value), we use the equal to or more than operator, ">=".

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	TCCR1B |= (1 << CS10); // Set up timer

	for (;;)
	{
		// Check timer value in if statement, true when count matches 1/20 of a second
		if (TCNT1 >= 49999)
		{
			PORTB ^= (1 << 0); // Toggle the LED

			// TODO: Reset timer value
		}
	}
}

Great! We've only got one more line of code to write, to reset the timer value. We already know the current value is accessed via the TCNT1 register for Timer 1, and since this is a read/write register, we can just write the value 0 to it once our required value is reached to rest it.

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	TCCR1B |= (1 << CS10); // Set up timer

	for (;;)
	{
		// Check timer value in if statement, true when count matches 1/20 of a second
		if (TCNT1 >= 49999)
		{
			PORTB ^= (1 << 0); // Toggle the LED

			TCNT1 = 0; // Reset timer value
		}
	}
}

And there we have it! We've just created a very basic program that will toggle our LED every 1/20 of a second at a 1MHz clock. Testing it out on physical hardware should show the LED being dimmer than normal (due to it being pulsed quickly). Good eyesight might reveal the LED's very fast flickering.

Next, we'll learn about the prescaler so we can try to slow things down a bit.

Part Two - Prescaled Timers

In part one of this tutorial we learned how to set up our 16-bit timer 1 for a 1/20 second delay. This short (to us humans) delay is actually quite long to our AVR - 50,000 cycles in fact at 1MHz. Notice that Timer 1 can only hold a value of 0-65535 - and we've almost reached that! What do we do if we want a longer delay?

One of the easiest things we can do is to use the timer's prescaler, to trade resolution for duration. The timer prescaler is a piece of timer circuitry which allows us to divide up the incoming clock signal by a power of 2, reducing the resolution (as we can only count in 2^n cycle blocks) but giving us a longer timer range.

Let's try to prescale down our Fcpu clock so we can reduce the timer value and reduce our delay down to a nice 1Hz. The Timer 1 prescaler on the MEGA16 has divide values of 1, 8, 64, 256 and 1024 - so let's re-do our calculations and see if we can find an exact value.

Our calculations for our new timer value are exactly the same as before, except we now have a new prescaler term. We'll look at our minimum resolution for each first:

Timer Resolution = (1 / (Input Frequency / Prescale))
                 = (Prescale / Input Frequency)

For a 1MHz clock, we can construct a table of resolutions using the available prescaler values and a Fcpu of 1MHz.

Prescaler Value | Resolution @ 1MHz
              1 |  1uS
              8 |  8uS
             64 | 64uS
            256 | 256uS
           1024 | 1024uS

If you recall our equation for calculating the timer value for a particular delay in part 1 of this tutorial, you'll remember it is the following:

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

However, as we've just altered the prescaler term, the latter half is now different. Substituting in our new resolution equation from above we get:

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

Or rearranged:

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

Now, we want to see if there is a prescaler value which will give an *exact* delay of 1Hz. One Hertz is equal to one cycle per second, so we want our compare value to be one second long, or 1000000uS. Let's divide that by each of our resolutions and put the results in a different table:

Prescaler Value | Target Timer Count
              1 |  999999
              8 |  125000
             64 |  15624
            256 |  3905.25
           1024 |  975.5625

The results are interesting. Of the available prescaler values, we can immediately discount 256 and 1024 - they do not evenly divide into our wanted delay period. They are of course usable, but due to the rounding of the timer count value the resultant delay will be slightly over or under our needed delay. That leaves us with three possible prescales; 1, 8 and 64.

Our next task is to remove the values that aren't possible. On an 8-bit timer, that means discounting values of more than (( 2 ^ 8 ) - 1), or 255, as the value won't fit into the timer's 8-bit count register. For our 16-bit timer, we have a larger range of 0 to ((2 ^ 16) - 1), or 65535. Only one of our prescaler values satisfies this requirement - a prescale of 64 - as the other two possibilities require a timer count value of more bits than our largest 16-bit timer is capable of storing.

Let's go back to our original timer program and modify it to compare against our new value of 15624, which we've found to be 1 second at a prescale of 64 and a Fcpu of 1MHz:

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	// TODO: Set up timer at Fcpu/64

	for (;;)
	{
		// Check timer value in if statement, true when count matches 1 second
		if (TCNT1 >= 15624)
		{
			PORTB ^= (1 << 0); // Toggle the LED

			TCNT1 = 0; // Reset timer value
		}
	}
}

Note I've removed the timer setup line, as it is no longer valid. We want to set up our timer to run at Fcpu/64 now. To do this, we need to look at the datasheet of the MEGA16 to see which bits need to be set in which control registers.

Checking indicates that we need to set both the CS10 and CS11 prescaler bits in TCCR1B, so let's add that to our program:

#include 

int main (void)
{
	DDRB |= (1 << 0); // Set LED as output

	TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

	for (;;)
	{
		// Check timer value in if statement, true when count matches 1 second
		if (TCNT1 >= 15624)
		{
			PORTB ^= (1 << 0); // Toggle the LED

			TCNT1 = 0; // Reset timer value
		}
	}
}

Compile it, and we're done! Remembering that our timer runs as soon as it gets a clock source, our program will now work, flashing the LED at a frequency of 1Hz.

Part Three - Long Timer Delays in Firmware

So far, we've learned how to use the timers in their most basic counting mode to delay a specified duration. However, we've also discovered a limitation of the timers - their maximum duration that their timer count registers can hold. We've managed to get a 1Hz delay out of a prescaled 16-bit timer with a prescale, but what if we want a delay of a minute? An hour? A week or year?

The answer is to create a sort of prescaler of our own in software. By making the hardware timer count to a known delay - say the 1Hz we created earlier - we can increment a variable each time that period is reached, and only act after the counter is reached a certain value. Let's pseudocode this so we can get a better understanding of what we want to do:

Set up LED hardware
Set up timer
Initialise counter to 0

WHILE forever
  IF timer value IS EQUAL TO 1 sec THEN
    Increment counter
    Reset timer
    IF counter value IS EQUAL TO 60 seconds THEN
      Toggle LED
    END IF
  END IF
END WHILE

The above pseudocode will build on our last experiment - a timer with a one second count - to produce a long delay of one minute (60 seconds). It's very simple to implement - all we need extra to our last example is an extra IF statement, and a few variable-related lines. First off, we'll re-cap with our complete code as it stands at the moment:

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

   for (;;)
   {
      // Check timer value in if statement, true when count matches 1 second
      if (TCNT1 >= 15624)
      {
         PORTB ^= (1 << 0); // Toggle the LED

         TCNT1 = 0; // Reset timer value
      }
   }
}

We need some code to create and initialise a new counter variable to 0, then increment it when the counter reaches one second as our pseudocode states. We also need to add in a test to see if our new variable reaches the value of 60, indicating that one minute has elapsed.

#include 

int main (void)
{
   // TODO: Initialise a new counter variable to zero

   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

   for (;;)
   {
      // Check timer value in if statement, true when count matches 1 second
      if (TCNT1 >= 15625)
      {
         TCNT1 = 0; // Reset timer value
         // TODO: Increment counter variable

         // TODO: Check here to see if new counter variable has reached 60
         if ()
         {
            // TODO: Reset counter variable

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

Now that we have our new program's structure, replacing the TODOs becomes very simple. We want a target count of 60, which is well within the range of an unsigned integer variable, so we'll make our counter variable of type unsigned integer. The rest of the code is extremely simple, so I'll add it all in at once:

#include 

int main (void)
{
   unsigned char ElapsedSeconds = 0; // Make a new counter variable and initialise to zero

   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

   for (;;)
   {
      // Check timer value in if statement, true when count matches 1 second
      if (TCNT1 >= 15624)
      {
         TCNT1 = 0; // Reset timer value
         ElapsedSeconds++;

         if (ElapsedSeconds == 60) // Check if one minute has elapsed
         {
            ElapsedSeconds = 0; // Reset counter variable

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

Compile and run, and the LED should toggle once per minute. By extending this technique, we can produce delays of an arbitrary duration. One point of interest is to note that any timing errors compound - so 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. For this reason it is important to ensure that the timer's clock is as accurate as possible, to reduce long-term errors as much as possible.

Part Four - The CTC Timer Mode

Up until now, we've been dealing with the timers in a very basic way - starting them counting, then comparing in our main routine against a wanted value. This is rather inefficient - we waste cycles checking the timer's value every time the loop runs, and slightly inaccurate (as the timer may pass our wanted compare value slightly while processing the loop). What if there was a better way?

Well, there is. The AVR timers usually incorporate a special function mode called "Clear on Timer Compare", or CTC for short. The CTC operating mode does in hardware what we've previously experimented in software; it compares in hardware the current timer value against the wanted value, and when the wanted value is reached a flag in a status register is set and the timer's value reset.

This is extremely handy; because the comparing is done in hardware, all we have to worry about is checking the flag to determine when to execute our LED toggling - much faster than comparing bytes or (in the case of the 16-bit timer) several bytes.

CTC mode is very straightforward. Before we look into the implementation, let's pseudocode what we want to do.

Set up LED hardware
Set up timer in CTC mode
Set timer compare value to one second

WHILE forever
  IF CTC flag IS EQUAL TO 1 THEN
    Toggle LED
    Clear CTC flag
  END IF
END WHILE

Very short, and very simple. Note that the name of the mode is Clear on timer compare - the timer's value will automatically reset each time the compare value is reached, so we only need to clear the flag when the delay is reached. This set-and-forget system is very handy, as once the timer is configured and started we don't need to do anything other than check and clear its status registers.

Now then, we'll grab our previous example, modifying it to fit with our new pseudocode:

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   // TODO: Configure timer mode to CTC
   // TODO: Set compare value for a compare rate of 1Hz

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {
      if () // TODO: Check CTC flag
      {
          PORTB ^= (1 << 0); // Toggle the LED

          // TODO: Clear CTC flag
      }
   }
}

Now, we need to flesh out the skeleton code we have. First up we need to configure our timer for CTC mode. As you might be able to guess, we want to configure our timer, thus the bits we want will be located in the timer's Control registers. The table to look for is the one titled "Waveform Generation Mode Bit Description", and is located in timer Control register descriptions for each timer. This table indicates all the possible timer modes, the bits required to set the timer to use those modes, and the conditions each mode reacts to.

You should note that our previous examples have ignored this table altogether, allowing it to use its default value of all mode bits set to zero. Looking at the table we can see that this setup corresponds to the "Normal" timer mode. We want to use the CTC mode of the timer, so let's look for a combination of control bits that will give us this mode.

Interestingly, it seems that two different combinations in Timer 1 of the MEGA16 will give us the same CTC behaviour we desire. Looking to the right of the table, we can see that the "Top" value (that is, the maximum timer value for the mode, which corresponds to the compare value in CTC mode) uses different registers for each. Both modes behave in the same manner for our purposes and differ only by the register used to store the compare value, so we'll go with the first.

The table says that for this mode, only bit WGM12 needs to be set. It also says that the register used for the compare value is named OCR1A.

Looking at the timer control registers (TCCR1A and TCCR1B) you should notice that the WGM1x bits - used to configure the timer's mode - are spread out over both registers. This is a small pain as you need to find out which bits are in which register, but once found setting up the timer becomes very easy. In fact, as we only have one bit to set - WGM12 - our task is even easier. The MEGA16's datasheet says that WGM12 is located in the TCCR1B register, so we need to set that.

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
   // TODO: Set compare value for a compare rate of 1Hz

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {
      if () // TODO: Check CTC flag
      {
          PORTB ^= (1 << 0); // Toggle the LED

          // TODO: Clear CTC flag
      }
   }
}

The second task for this experiment is to set the compare value - the value that will reset the timer and set the CTC flag when reached by the timer. We know from the datasheet that the register for this is OCR1A for the MEGA16 in the first CTC timer mode, so all we need is a compare value. From our previous experiment we calculated that 1Hz at 1MHz with a prescaler of 64 needs a compare value of 15624, so let's go with that.

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {
      if () // TODO: Check CTC flag
      {
          PORTB ^= (1 << 0); // Toggle the LED

          // TODO: Clear CTC flag
      }
   }
}

There, almost done already! Last thing we need is a way of checking to see if the compare has occurred, and a way to clear the flag once its been set. The place to look for the compare flags is in the timer's Interrupt Flag register - an odd place it seems, but the reason will become clear in the next section dealing with timer interrupts. The MEGA16's Timer 1 interrupt flags are located in the combined register TIFR, and the flag we are interested in is the "Output Compare A Match" flag, OCF1A. Note the "A" on the end; Timer 1 on the MEGA16 has two CTC channels (named channel A and channel B), which can work independently. We're only using channel A for this experiment.

Checking for a CTC event involves checking the OCF1A flag in this register. That's easy - but what about clearing it? The datasheet includes an interesting note on the subject:

Quote:
...OCF1A can be cleared by writing a logic 1 to its bit location

Very strange indeed! In order to clear the CTC flag, we actually need to set it - even though it's already set. Due to some magic circuitry inside the AVR, writing a 1 to the flag when its set will actually cause it to clear itself. This is an interesting behaviour, and is the same across all the interrupt bits.

Despite that, we can now add in our last lines of code to get a working example:

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {
      if (TIFR & (1 << OCF1A))
      {
          PORTB ^= (1 << 0); // Toggle the LED

          TIFR = (1 << OCF1A); // clear the CTC flag (writing a logic one to the set flag clears it)
      }
   }
}

Note that when clearing the OCF1A flag, we assign its value to the TIFR register. This is because it will assign a logic one to the flag's position (clearing it) but also because as writing zeros to the other flags won't affect them, we can go ahead and use the smaller (codesize-wise) direct assignment, rather than ORing the register to form a read/modify/write sequence. This is also beneficial because it prevents the compiler from writing logic one to the other flags if they were already set via the read/modify/write, which would clear unwanted flags.

And there we have it, a working 1Hz LED flasher using the CTC timer mode!

Part Five - CTC Mode using Interrupts

Important: The interface for defining and working with Interrupts has changed in the more recent versions of WinAVR - please make sure you've updated your installation to the latest version if you encounter errors relating to the unknown macro "ISR".

For all our previous experiments, we've been using a looped test in our main code to determine when to execute the timer action code. That's fine - but what if we want to shift the responsibility of choosing when to execute the timer code to the AVR hardware instead? To do this, we need to look at the timer interrupts.

Interrupts are events that when enabled, cause the AVR to execute a special routine (called an Interrupt Service Routine, or ISR for short) when the interrupt conditions are met. These interrupts can happen at any time and when executing the main routine is paused while the ISR executes, the main routine continues until the next interrupt. This is useful for us, as it means we can eliminate the need to keep checking the timer value and just respond to its interrupt events instead.

The AVR timers can have several different Interrupts - typically Overflow, Compare and Capture. Overflow occurs when the timer's value rolls past it's maximum and back to zero (for an 8 bit timer, that's when it counts past 11111111 in binary and resets back to 00000000). However, for this section we'll deal with the Compare interrupt, which occurs in CTC mode when the compare value is reached.

Again, we'll pseudocode this to start with:

Set up LED hardware
Set up timer in CTC mode
Enable CTC interrupt
Enable global interrupts
Set timer compare value to one second

WHILE forever
END WHILE



ISR Timer Compare
   Toggle LED
END ISR

We can start off this by working with our skeleton main code, used in previous examples. I'll skip the details on the parts already discussed in previous sections.

#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   // TODO: Enable CTC interrupt
   // TODO: Enable global interrupts

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

// TODO: Add compare ISR here

Note how it's a modified version of the non-interrupt driven CTC example covered in the last section. All we need to do is tell the timer to run the compare ISR we define when it counts up to our compare value, rather then us polling the compare match flag in our main routine loop.

We'll start with creating the ISR first, as that's quite simple. In AVR-GCC - specifically, the avr-libc Standard C Library that comes with it - the header file for dealing with interrupts is called (unsurprisingly) "interrupt.h" and is located in the "avr" subdirectory. We need to include this at the top of our program underneath our include to the IO header file. The top of our code should look like this:

#include 
#include 

int main (void)
{
  ...

This gives us access to the API for dealing with interrupts. We want to create an ISR for the Timer 1 Compare Match event. The syntax for defining an ISR body in AVRGCC is:

ISR(VectorName_vect)
{
   // Code to execute on ISR fire here
}

Where "VectorName" is the name of the ISR vector which our defined ISR handles. The place to go to find this name is the "Interrupt" section of the datasheet, which lists the symbolic names for all the ISR vectors that the chosen AVR supports. When writing the vector name into GCC, replace all spaces with underscores, and append "_vect" to the end of the vector's name.

Like in part four we are still dealing with Channel A Compare of Timer 1, so we want the vector named "TIMER1 COMPA". In GCC this is called "TIMER1_COMPA_vect", after performing the transformations outlined in the last paragraph. Once the ISR is defined, we can go ahead and write out it's body, adding the LED toggling code.

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   // TODO: Enable CTC interrupt
   // TODO: Enable global interrupts

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

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

Notice how we don't clear the CTC event flag like in part four - this is automatically cleared by the AVR hardware once the ISR fires. Neat, isn't it!

Running the code so far won't yield any results. This is because although we have our ISR all ready to handle the CTC event, we haven't enabled it! We need to do two things; enable the "TIMER1 COMPA" interrupt specifically, and turn on interrupt handling on our AVR.

The way to turn on our specific interrupt is to look into the second interrupt-related register for our timer, TIMSK. This is the Timer Interrupt Mask register, which turns on and off ISRs to handle specific timer events. Note that on the MEGA16 this single register contains the enable bits for all the timer interrupts for all the available timers. We're only interested in the Timer 1 Compare A Match interrupt enable bit, which we can see listed as being called OCIE1A (Output Compare Interrupt Enable, channel A).

By setting that bit we instruct the timer to execute our ISR upon compare match with our specified compare value. Let's put that line into our program's code and see how it all looks.

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   TIMSK |= (1 << OCIE1A); // Enable CTC interrupt

   // TODO: Enable global interrupts

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

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

Only one more thing to do - enable global interrupts. The AVR microcontrollers have a single control bit which turns on and off interrupt handling functionality. This is used in pieces of code where interrupt handling is not desired, or to disable interrupts while an ISR is already being executed. The latter is done automatically for us, so all we need to do is turn on the bit at the start of our code, and our compare interrupt will start to work.

The command to do this is called "sei" in the avr-libc library that ships with WinAVR, and is named to correspond with the assembly instruction which does the same for AVRs (the SEI instruction). That's irrelevant however, as we just need to call the command in our code.

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   TIMSK |= (1 << OCIE1A); // Enable CTC interrupt

   sei(); //  Enable global interrupts

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

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

And our example is finished! Running this will give a nice 1Hz LED flashing, using the timer's event interrupts. The nice thing is that the timer operation is now completely handled for us in hardware - once set up, we just need to react to the events we've configured. Notice that our main loop is now empty; if this is the case you may put sleep commands inside the main loop to save power between compares.

Part Six - Pure Hardware CTC

You probably think by now that we've improved our example as much as possible - after all, what more improvements are there to make? Well, it's time to finish of the CTC topic by looking at the hardware outputs.

All AVRs' pins have alternative hardware functions. These functions (currently non re-routable) when activated interface the IO pins directly to the AVR's internal hardware - for instance the Tx/Rx alternative functions which are the direct interface to the AVR's USART subsystem. Alternative pin functions can be very useful; as they can be internally connected straight to a hardware subsystem, the maximum possible performance can be achieved.

In this section, we'll be looking at the Compare Output settings of the AVR timer.

Looking at the timer 1 control registers, we can see a few pairs of bits we've previously ignored, called (for timer 1) COM1A1/COM1A0 and COM1B1/COM1B0. Bonus points to anyone who's linked the "A" and "B" parts of the bit names to the timer compare channels - you're spot on.

These bits allow us to control the hardware behaviour when a compare occurs. Instead of firing an interrupt, the hardware can be configured to set, clear or toggle the OCxy (where "x" is the timer number, "y" is the channel letter for timers with more than one channel) hardware pins when a compare occurs. We can use the toggle function with our LED flasher, so that the hardware toggles the LED's state for us automatically, making it a true set-and-forget operation.

Before we do anything else, let's work out which pins of our MEGA16 are linked to the Compare Output hardware - we want the pins with alternative functions starting with "OC". On our PDIP package version, that maps to:

Quote:
PB3 = OC0
PD4 = OC1B
PD5 = OC1A

So timer 0 has one Compare Output channel, while timer 1 has two (channels A and B) as we've already discovered. As always we'll just deal with Channel A in our example.

Now we have a problem. All the previous chapters have assumed the LED is attached to PORTB, bit 0 - but we'll have to move it for this chapter. As stated above the alternative functions cannot be moved to another pin, so we must move moses...I mean, our LED, to the pin with the required alternative function.

Timer 1 Channel A's Compare Output is located on PD5, so move the LED there for the rest of this example. Now, let's psudocode:

Set up LED hardware
Set up timer in CTC mode
Enable timer 1 Compare Output channel A in toggle mode
Set timer compare value to one second

WHILE forever
END WHILE

Amazing how simple it is, isn't it! Well, we can already fill in almost all of this:

#include 
#include 

int main (void)
{
   DDRD |= (1 << 5); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   // TODO: Enable timer 1 Compare Output channel A in toggle mode

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

All we need is to configure the timer so that it'll toggle our channel A output each time the timer value is equal to our compare value. The datasheet has several descriptions for the functionality of the COM1Ax and COM1Bx bits, so we need to find the table corresponding to the mode we're using the timer in.

CTC mode isn't listed - instead the appropriate table is listed as "Compare Output mode, Non PWM". PWM stands for "Pulse Width Modulation", and will be covered later on in this tutorial. For now, it is sufficient to know that the CTC mode is not a form of PWM and thus the non-PWM bit description table is the one we're looking for.

To make the channel A Compare Output pin toggle on each compare, the datasheet says we need to set bit COM1A0 in TCCR1A. That's our missing line - let's add it in!

#include 
#include 

int main (void)
{
   DDRD |= (1 << 5); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode

   OCR1A   = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

Simple, isn't it! We've now created the simplest (code-wise) LED flasher possible using pure hardware functionality. Running this will cause the LED to flash at 1Hz, without any code other than the timer initialization!

Part Seven - The Overflow Event

Well, now that we've had fun creating a LED flasher via a variety of software and hardware CTC methods, we'll move on to one last LED flashing program. This time we'll be using a different Timer event to manage the toggling of the LED - the overflow.

As previously stated, timers store their values into internal 8 or 16 bit registers, depending on the size of the timer being used. These registers can only store a finite number of values, resulting in the need to manage the timer (via prescaling, software extention, etc) so that the interval to be measured fits within the range of the chosen timer.

However, what has not been discussed yet is what happens when the range of the timer is exceeded. Does the AVR explode? Does the application crash? Does the timer automatically stop?

The answer is simple, if rather boring. In the event of the timer register exceeding its capacity, it will automatically roll around back to zero and keep counting. When this occurs, we say that the timer has "overflowed".

When an overflow occurs, a bit is set in one of the timer status registers to indicate to the main application that the event has occured. Just like with the CTC hardware, there is also a corresponding bit which can enable an interrupt to be fired each time the timer resets back to zero.

So why would we need the overflow interrupt? Well, I leave that as an excersize to the reader. However, we can demonstrate it here in this tutorial - via another LED flasher, of course.

Calculating the frequency of the flashing is a little different to our previous examples, as now we have to calculate in reverse (to find the frequency from the timer count and timer resolution rather than the timer count from a known frequency and timer resolution). We'll still be working with our 16-bit timer 1 for this example, to be consistent with previous chapters.

Let's go back to our one of the timer equations we used back in chapter 2:

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

We want to determine the Target Frequency from the other two variables, so let's rearrange:

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

And swap the left and right hand sides to get it into a conventional form:

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

Since we know that for the overflow equation, the "Target Timer Count" becomes the maximum value that can be held by the timer's count register, plus one (as the overflow occurs after the count rolls over from the maximum back to zero). The formula for the maximum value that can be held in a number of bits is:

Max Value = (2 ^ Bits) - 1

But we want one more than that to get the number of timer counts until an overflow occurs:

Counts Until Overflow = (2 ^ Bits)

Change "Max Value" to the more appropriate "Target Timer Count" in the first timer equation:

(1 / Target Frequency) = Counts Until Overflow * (Prescale / Input Frequency)

And substitute in the formula for the counts until overflow to get the timer period equation. Since frequency is just the inverse of period, we can also work out the frequencies of each duration as well:

Target Period = (1 / Target Frequency)
Target Period = ((2 ^ Bits) * (Prescale / Input Frequency)

Which is a bit complex, but such is life. Now's the fun part - we can now work out the overflow frequencies and periods for our 16-bit Timer 1 running at different prescales of our AVR's 1MHz system clock:

Target Period = ((2 ^ 16) * (Prescale / 1000000)
Prescaler Value | Overflow Frequency | Overflow period
              1 |  15.259 Hz         |   65.5  ms
              8 |  1.907 Hz          |   0.524 s
             64 |  .2384 Hz          |   4.195 s
            256 |  .0596 Hz          |   16.78 s
           1024 |  .0149 Hz          |   67.11 s

Note how our frequency decreases (and period increases) as our prescaler increases, as it should. Because we have a reasonably slow main system clock, and a large timer count register, we end up with frequencies that are easy to see with the naked eye (with the exception of the case where no prescaler is used). Unlike the CTC method however, we are limited to the frequencies above and cannot change them short of using a smaller timer, different prescaler or different system clock speed - we lose the precision control that the CTC modes give us.

For this example, we'll use a prescaler of 8, to give a 1.8Hz flashing frequency, and a period of about half a second.

Almost time to get into the code implementation. But first, pseudocode! I'm going to extrapolate on the preceding chapters and jump straight into the ISR-powered example, rather than begin with a polled example. It works in the same manner as previous polled experiments, except for the testing of the overflow bit rather than the CTC bit.

Set up LED hardware
Set up timer overflow ISR
Start timer with a prescale of 8

WHILE forever
END WHILE

ISR Timer Overflow
   Toggle LED
END ISR

Let's get started. As always, we'll begin with the skeleton program:

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   // TODO: Enable overflow interrupt
   // TODO: Enable global interrupts

   // TODO: Start timer at Fcpu/8

   for (;;)
   {

   }
}

// TODO: Add overflow ISR here

Let's begin with filling in the bits we can already do. The ISR code is easy - we can use the same ISR as part five, except we'll be changing the compare vector to the overflow vector of timer 1.

Looking in the MEGA16 datasheet, the overflow interrupt for timer 1 is obvious - it's listed as "TIMER1 OVF" in the Interrupts chapter. Just like in part five, we need to replace the spaces in the vector name with underscores, and add the "_vect" suffix to the end.

We can also fill in the "Enable global interrupts" line, as that is identical to previous chapters and is just the "sei()" command from the header file.

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   // TODO: Enable overflow interrupt
   sei(); // Enable global interrupts

   // TODO: Start timer at Fcpu/8

   for (;;)
   {

   }
}

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

Next, we need to figure out how to enable the overflow vector, so that our ISR is run each timer the overflow occurs. The datasheet's 16-bit Timer/Counter section comes to our rescue again, indicating that it is the bit named "TOIE1" located in the Timer 1 Interrupt Mask register, TIMSK:

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TIMSK |= (1 << TOIE1); // Enable overflow interrupt
   sei(); // Enable global interrupts

   // TODO: Start timer at Fcpu/8

   for (;;)
   {

   }
}

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

The last thing we need to do, is start the timer with a prescaler of 8. This should be easy for you to do - if not, refer back to chapter 2.

The MEGA16 Datasheet, Timer 1 section tells us that for a timer running with a prescaler of 8, we need to start it with the bit CS11 set in the control register TCCR1B. Adding that to our code finishes our simple program:

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TIMSK |= (1 << TOIE1); // Enable overflow interrupt
   sei(); // Enable global interrupts

   TCCR1B |= (1 << CS11); // Start timer at Fcpu/8

   for (;;)
   {

   }
}

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

All done! This simple program will cause the LED to toggle each time the overflow occurs and serves as a practical use of the overflow interrupt.

Part Eight - Overflow as CTC

One neat application of the overflow event is for creating a CTC timer on AVRs which don't support true hardware CTC. It's not as neat as the pure hardware CTC discussed in part six, but faster than the pure software CTC discussed in part two.

CTC works by having a fixed BOTTOM value - that's the timer's minimum value - of zero, and a variable TOP value, the value at which resets the timer and fires the event. However, with the overflow event we seemingly have a fixed BOTTOM of again zero, and a fixed TOP of the maximum timer's value. Not so - with a small trick we can adjust the BOTTOM value to give us the equivelent of a CTC implementation standing on it's head.

This tecnique is called timer reloading. When configured, we preload the timer's count register (which is both readable and writeable) with a value above zero. This shortens the time interval before the next overflow event, although only for a single overflow. We can get around that by again reloading the timer's value to our non-zero value inside the overflow event for a hybrid software/hardware CTC.

Psuedocode time!

Set up LED hardware
Set up timer overflow ISR
Load timer count register with a precalculated value
Start timer with a prescale of 8

WHILE forever
END WHILE

ISR Timer Overflow
   Toggle LED
   Reload timer count register with same precalculated value
END ISR

Let's examine the maths again. From part two, we know that the formula for determining the number of timer clock cycles needed for a given delay is:

Target Timer Count = (Input Frequency / Prescale) / Target Frequency

Which works for a fixed BOTTOM of zero, and a variable TOP. We've got the "upside-down" implementation of a fixed top of ((2 ^ Bits) - 1) and a variable BOTTOM. Our BOTTOM value becomes the TOP minus the number of clock cycles needed for the equivelent CTC value, so the formula becomes:

Reload Timer Value = ((2 ^ Bits) - 1) - ((Input Frequency / Prescale) / Target Frequency)

Let's go with the previous example in part two: a 1Hz flasher, using a 1MHz clock and a prescale of 64. We found the timer count to be 15625 for those conditions. Plugging it into the above Reload Timer Value formula gives:

Reload Timer Value = ((2 ^ Bits) - 1) - 15624
                   = ((2 ^ 16) - 1) - 15624
                   = 65535 - 15624
                   = 49911

So we need to preload and reload our overflow timer on each overflow with the value 49911 to get our desired 1Hz delay. Since we've already gone over the code for an interrupt-driven overflow example in part seven, we'll build upon that.

#include 
#include 

int main (void)
{
   DDRB |= (1 << 0); // Set LED as output

   TIMSK |= (1 << TOIE1); // Enable overflow interrupt
   sei(); // Enable global interrupts

   // TODO: Preload timer with precalculated value

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Set up timer at Fcpu/64

   for (;;)
   {

   }
}

ISR(TIMER1_OVF_vect)
{
   PORTB ^= (1 << 0); // Toggle the LED
   // TODO: Reload timer with precalculated value
}

Both the reloading and preloading of the timer takes identical code - we just need to set the timer's count register to the precalculated value. The timer's current count value is avaliable in the TCNTx register, where "x" is the timer's number. We're using the 16-bit timer 1, so the count value is located in TCNT1.

We'll now finish of the example, replacing the unfinished segments:

#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 Fcpu/64

   for (;;)
   {

   }
}

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

And done! This is less preferable to the pure hardware CTC mode, as it requires a tiny bit more work on both the programmer and the AVR to function. However, it will work just fine for AVRs lacking the complete CTC timer functionality.

Due to forum software length limits, the PWM section of this tutorial is continued HERE

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean :twisted:

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

Last Edited: Sat. Feb 4, 2012 - 02:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Question, should

if (TCNT1 == 50000)

be

if (TCNT1 >= 50000)

Since chances are you'll miss it being at exactly 50000 since the timer and cpu clocks are the same and you'll have to go through multiple ops just to check the timer value?

Edward

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

Quite right - I even alluded to that in the text. I've fixed it up and added in a better explanation as to why the >= is used over a straight compare (==). Thanks for the feedback!

- Dean :twisted:

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

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

Thanks Dean! This tutorial is awesome and I can't wait for all 8 sections :) Keep up the good work!

- chris, AVR Newb

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

Nice work dean! :) Keep them coming!

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

I agree, this is a good tutorial that is easy to follow.

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

That was really good
Hope the others come fast

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

Thanks for the encouragement guys ;). I'll try to work on getting another section finished this week, but mif-year Uni exams are comming up fast (another two weeks or so).

Rest assured it'll get done!

- Dean :twisted:

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

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

thanks dean,its really good for beginers like me
just give a notes on counters for counting external pulses......

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

Dean, I am a total AVR newb. I've gotten the book and kit from SmileyMicros and am about halfway through the experiments. Your tutorial was a refreshing different perspective. I am totally building this example tonight! I will of course look in my databook for the 169 though and try to figure out any different pin/register names first :D

Write a book Dean I'll buy it! Ooh better yet, make a robot kit with a tutorial book. That would be SWEET.

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

Cheers Sakko - don't forget to tell me how you went! You're not the first person to tell me to write a book - perhaps I might look into the possibility at the end of the year in my break from University. I'll have to work on my writing abilities first!

I've added part three, but I've currently got Uni exams. Once those are over around the 20th I'll pick up where I left off.

- Dean :twisted:

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

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

Dean,

Thanks for the tutorial. You explained everything really well. This was my first time playing with timers and I was able to follow everything without a problem on a tiny2313.

I'm not a cartoon character but I play one in real life.

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

Good luck with the exams Dean!

Keep up the good work, helped me too.

But spotted one little thing;

if (TIFR & (1 << OCF1A)) 
      { 
          PORTB ^= (1 << 0); // Toggle the LED 

          TIFR |= (1 << OCF1A); // clear the CTC flag (writing a logic one to the set flag clears it) 
      }

Should be

if (TIFR1 & (1 << OCF1A)) 
      { 
          PORTB ^= (1 << 0); // Toggle the LED 

          TIFR1 |= (1 << OCF1A); // clear the CTC flag (writing a logic one to the set flag clears it) 
      }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TIFR or TIFR1 depends on the AVR, Dean is writing for mega 16 where the register is shared between timers and is called TIFR.

One thing to note about clearing flags:

TIFR = (1 << OCF1A);

is better, the |= method might end up clearing more flags than you intend.
/Lars

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

A extremely good n easy to learn tutorial...
Good job. :D
we were greatly helped by your tutorial on timers.
we are new to avr programming.we want to program avr system in c.Please tell where can we get examples of c codes for avr and also tell where we can learn to program and stimulate interrupts in c .

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

Good job.
pls tell more on the PWM modes..
thks...
again great job.

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

this was really helpful, thanks :)

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

Lajon wrote:
TIFR or TIFR1 depends on the AVR, Dean is writing for mega 16 where the register is shared between timers and is called TIFR.

One thing to note about clearing flags:

TIFR = (1 << OCF1A);

is better, the |= method might end up clearing more flags than you intend.
/Lars

My bad, I am working on a Atmega128, did not look into the mega16 datasheet.
Can you explain why the |= could clear more than one flag?

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

Quote:
Can you explain why the |= could clear more than one flag?

It can when it compiles to something like this (pseudo code):

tmp = TIFR;
tmp |= (1<<OCF1A);
TIFR = tmp;

That last statement will set OCF1A to a 1 (thus clearing this flag which is what we want). But all flags that were 1 in in TIFR will be set to 1 also (and so cleared).
/Lars

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

Lajon,

Very good point, and one that I hadn't considered before. I've updated the tutorial.

I'll probably write the next section tomorrow, now that I'm on holidays for the next month and have plenty of free time.

- 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 this tutorial, its been very useful.

Any idea what time the next section will be up, as i'll be trying to set up interrupts on my AVR tomorrow :D

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

I'll probably write up the interrupt-driven CTC tutorial today. Then I'll write up the overflow interrupt, then PWM.

- Dean :twisted:

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

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

Nice Dean, I'll try the interrupt example tonight.

Shouldn't we include the '#include ' line in the code?

Thanks,
Marc.

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

Damn! I had missed most of the fun! Why isn't AVRFreaks software notifying me! You edit your post adding 2booksworth of info and I miss it all :( Now I have to read it all in one go.. ;D

Good work Dean! I can't understand, from where can you find the time... Do you sleep? do you eat? or is it just so, that you have an AVR implanted with a small WiFi transceiver so you can be online 24/7 and data input is from your brain? It sure seems so ;)

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

Rumor has it Dean is really a robot his father made!

Resistance is futile…… You will be compiled!

Last Edited: Sun. Jul 1, 2007 - 04:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DESTROY ALL HUMANS DESTROY ALL HUMANS....

But seriously. I knew this tutorial would be a long one (I'm only just realizing how long when I went over it again - it's 5800 words and counting) when I started it, hence why I'm writing it in sections when I have the time. Now that I'm on Uni break I have more time to write.

Just doing what I love doing - sharing knowledge. Perhaps I should start asking for a dollar donation per person, that way I could skip my working life and retire now ;).

Quote:

Shouldn't we include the '#include ' line in the code?

Oops! I explained that that header file was needed in the text, then ignored it when I wrote the full code. Fixed.

- Dean :twisted:

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

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

Dean, can you produce a pdf? It is more convenient for off-line use.

Thanks for your effort.

Pop

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

pop48m wrote:
Dean, can you produce a pdf? It is more convenient for off-line use.

Thanks for your effort.

Pop

Sure, I will later tonight. Technically the tutorial's not finished yet, but I don't think it'll be too much of a hassle to maintain a current PDF download.

This tutorial's getting really big!

- 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:
...
This tutorial's getting really big!

- Dean :twisted:

And really good. This is your best yet, Dean :!:
Thanks,
John

Resistance is futile…… You will be compiled!

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

Quote:

And really good. This is your best yet, Dean Exclamation

Thanks John. It's a pretty broad subject and I think it ought to do some good.

Quote:

Dean, can you produce a pdf? It is more convenient for off-line use.

It was a right pain formatting all the code snippets, but it's done and attached to the original post. Enjoy!

- Dean :twisted:

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

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

Nice tutorial! I'm waiting for the next chapters.

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

I'm trying to get 36KHz frequency so i can later use an ir led and a receiver.

#include 
#include 

int main(void)
{
	
	DDRB |= ((1<<0) | (1<<1)); // set led 1 and 2  as output

	TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

	OCR1A   = 102; // Set CTC compare value to 36000Hz at 3.686000MHz AVR clock, with a prescaler of 1

	TIMSK |= (1 << OCIE1A); // Enable CTC interrupt

	TCCR1B |= (1 << CS10); // Start timer at Fcpu/1

	sei(); //  Enable global interrupts 

	for(;;) // loop
	{

	}

}

ISR(TIMER1_COMPA_vect)
{
   PORTB ^= (1<<0); // toggle led
}

I am using STK500 and clock source from it at 3686000 Hz. I am not sure about the OCR1A value is 102 right (clock / prescale / needed frequency)?

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

Yes, 106 (0x66) is correct for your given clock, prescaler and required frequency.

As an aside, if you check your AVR's model, some can toggle the output pin state if a logic 1 is written to the AVR port's PIN register. That might make the ISR smaller and faster - if supported the ISR would become:

ISR(TIMER1_COMPA_vect)
{
   PINB = (1<<0); // toggle led
}

I'll also be discussing in the next chapter the generation of CTC waveforms via the output toggle modes. There's actually a bit you can set which will toggle the OCRxy pin of the AVR automatically each time the compare occurs - so if you can change your hardware so that the output pin resides on the pin with the alternate function "OCR1A", you can do all the compare and toggling purely in hardware.

- Dean :twisted:

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

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

Good tutorial Dean. I did notice a few typos though (sorry it's a habit) and I figured I would let you know just in case you are attempting to write a book!

Part One:

Quote:
TCNT1 and comparing against out wanted value, 50000.
out = our

Part Three:

Quote:
all we need extra to our last example is an exta IF statement,
exta = extra

Quote:

We need some code to create an initialize a new counter variable to 0,
an = and

Quote:
The rest of the code is extreamly simple, so I'll add it all in at once:
extreamly = extremely

Part Five:

Quote:
you may fill that will other code which executed when the timer is counting up to the compare value,
will = with (I think? Entire sentence needs revising.)

That's all I've got. I don't mean to be rude or anything by pointing these out. If you do write a book, I can be your editor! ;) JK

I look forward to reading the rest of your tutorial.

Danielle

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

Thanks Danielle!

Perhaps I'll write a book at the end of the next semester, as it seems to be common here to suggest me to do so ;).

Don't know WHAT I was doing in that last sentence, but the rest are fixed. I think it was the common occurrence of me being distracted by something while my hands are still typing my previous thought! I've replaced it with what I meant to say instead.

- Dean :twisted:

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

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

Added in part 6, detailing how to do the CTC flasher purely in hardware.

- Dean :twisted:

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

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

Does compare output channel A in toggle mode set the PD5 as output, or did you mean DDRD |= (1<<5) instead of DDRB |= (1<<0)

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

Awww bugger. Yes, I meant to change the DDRB to DDRD. Enabling the toggling IIRC just toggles the appropriate PORT bit, it doesn't force the pin to be an output.

- Dean :twisted:

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

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

Hi dean, nice tutorial.
I have some basic question.
First, is the timer running on the cpu clock, or based on the instruction? I try to simulate the code in AVR Studio and find that the timer increment is not fixed although I set the prescaler to one. Instead, the timer is increase with the value of cycle of instruction.
Second, when the timer is configured as CTC with OC0 activated, is there any chance that the AVR misses in the comparing of OCR0 and TCNT0 register? I think if the timer is run based on the cycle instruction maybe this misses have a chance to be happen. Conversely, it will not be happen if the timer is run on the cpu clock.
Maybe this is out of scope because you haven't come with PWM tutorial. I do some project using AVR m8535 and use its timer1 as Phase and Frequency Correct PWM with ICR1 as TOP value. I use OC1A and OC1B as PWM output and define OCR1A and OCR1B to control the duty cycle. Then I check the output using oscilloscop and find that if I use the time/div in high time (25mS), there is void in the output. It seem that the AVR misses the compare so the output is not working. Any suggestion?

Thanx

Deni

There's no such as destiny, there are only different choices.

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

The timer is clocked from the system clock by default - I'll cover using an external crystal later. With a prescaler of 1, that means the timer value is advancing one count for each clock of the CPU.

If the Compare Outputs are enabled, there should be no chance of missing the compare - it's done via hardware and is independent of the CPU core. Same with the interrupts - you might process them several cycles after the compare has occurred, but the hardware is responsible for doing the compare.

The only chance of missing a compare is in the earlier examples, with a software compare testing the TCNT value against a constant, where the timer value may wrap back to 0 before it's read and compared.

- Dean :twisted:

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

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

Oh...maybe thats the problem, I use external crystal to clock the avr, so the timer is not clocked by system clock. Can't wait your tutorial then.

Deni

There's no such as destiny, there are only different choices.

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

Great tutorial, very usefull.
I was a bit confused:
when count matches 1 second,
PORTB ^= (1 << 0); // Toggle the LED

> flashing the LED at a frequency of 1Hz
I think you mean toggles every second.
But a frequency counter will count 2 Hz right ?
The LED is flashing at 2 Hz ? Right ?

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

Uhmmm, wellll.. Sorry if I misunderstood you, but, if it is toggled every second, then a full on-off cycle will take two seconds and that is 0.5 Hz (not 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

JohanEkdahl wrote:
Uhmmm, wellll.. Sorry if I misunderstood you, but, if it is toggled every second, then a full on-off cycle will take two seconds and that is 0.5 Hz (not 2 Hz).

Yes of course, you are right, I am making a similar error or even worse !! :P

After reading this great tutorial I made a conclusion:
Because one can load a well defined value in OCR1A :
One can use a cristal of 14.7456 Mhz (or 12.288 Mhz), have a error free baudrate of 9600 and make an error free RTC !
Is this correct ? If yes, great :D

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

Thanks for the great tutorial. It has clarified so many things regarding timers and interrupts. I'm looking forward to the next sections.

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

Part Five -

I translated here the c-program from
"Part Five CTC Mode using Interrupts" into assembler.
Also I changed the following settings to have faster Timer-1 increment and to see how the Interrupt is simulated in Studio AVR without to hit F11 so many times.
Should you run this in hardware, note that the LED will blink to fast this way !
> Prescaler = 1 : (CS11 not set)
> Compare value in OCR1AL set to 0x1F

.include "m16def.inc"
.def temp =r16 	; assign the name "temp" to register r16
.org $0000
    rjmp start 	; reset
.org OC1Aaddr		; set Vector TIMER1 COMPA 
    rjmp toggle	; jump to toggle 
	
start:

; load upper memory address in the Stack pointer
	ldi temp, high(RAMEND)
	out SPH, temp
	ldi temp, low(RAMEND)
	out SPL, temp           
	sbi DDRB, 0x00   ; set the first bit of port B as output
	ldi temp, (1<<WGM12)
	out TCCR1B, temp  ; set timer1 CTC mode bit
	ldi temp, (1<<OCIE1A)	; Enable CTC interrupt
	out TIMSK, temp
	sei						; enable global interupt
	ldi temp, 0x1F 			; 31
	out OCR1AL, temp    	; set CTC compare value 
	ldi temp, ((1<<WGM12)|(1<<CS10)) ; 
	out TCCR1B, temp	; write byte, CTC mode + Start timer1
	       
loop:
	jmp loop		; endless loop

toggle:
	in temp, PORTB		; reload PORTB in temp
	ldi r17, 0x01
	eor temp, r17		; exlusive or (r16 XOR r17 > r16)
	out PORTB, temp     	; toggle LED on PORTB bit 0
	reti
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know if this should go here, but I can't get the ctc code in part 4 to work on an atmega8. All the registers seem to be the same. Sorry if this is not the place to ask.

-Mason

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

Hi Mason,

I'll try to take a look at the code tonight to see if I can spot any errors in the code or changes in the MEGA8's timers that would affect it, but I don't think I'll have much time (plenty of Physics homework to get through). In the meantime, has anyone confirmed the CTC code to be working on a MEGA16 or MEGA8?

- 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:
Hi Mason,

I'll try to take a look at the code tonight to see if I can spot any errors in the code or changes in the MEGA8's timers that would affect it, but I don't think I'll have much time (plenty of Physics homework to get through). In the meantime, has anyone confirmed the CTC code to be working on a MEGA16 or MEGA8?

I can confirm all codes work on atmega88 (which is very similar to atmega8). I have written a very simple 24hr clock with the CTC interrupt routine, works perfectly:


#include 
#include 
#include "../lcd_ctrl.h"
#include 

int s=0;
char s_c[2];
int min=0;
char min_c[2];
int hour=0;
char hour_c[2];

ISR(TIMER1_COMPA_vect)
{
   //enters here once a sec
  
	if(++s == 60){
		s=0;
		if(++min == 60){
			min=0;
			if(++hour == 24){
			hour=0;
		}
		}
	}

        //Write the time to LCD
	LCD_clear();
	if (hour<10) LCD_putchar('0');
	LCD_putstring(itoa(hour,hour_c,10));
	LCD_putchar(':');
	if (min<10) LCD_putchar('0');
	LCD_putstring(itoa(min,min_c,10));
	LCD_putchar(':');
	if (s<10) LCD_putchar('0');
	LCD_putstring(itoa(s,s_c,10));
	
}


int main(void){

//Init LCD
	LCD_init();
	LCD_putstring("00:00:00");
//Init timer 
	//Setup timer in CTC mode
	TCCR1B |= (1<<WGM12);
	//Enable CTC interrupts
	TIMSK1 |= (1<<OCIE1A);
	OCR1A=15625; //->1Hz
	TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 
	
//Enable Global interrups
	sei();
	while(1){
	}
		
	return 1;
}

I will not include the lcd routines unless asked though.. wrong thread

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

Thanks mejrum, very helpful. In that case, please post your code here z2trillion (or email it to me if it is sensitive) and I'll give it a once-over.

- Dean :twisted:

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

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

Added chapter 7, on the overflow interrupt event.

Sorry for the delay everyone, I've got a million things to do these days, and not a whole lot of time to do it all in.

- 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 heads up on chapter 7, Dean.
Superman has no need to apologize to us mortals for being faster than a speeding bullet.

Dean thanks so much for all you do for the Freaks,
John

Resistance is futile…… You will be compiled!

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

Dean,

Wonder if I'm the only one who thinks more easily in terms of timer period rather than timer frequency? The 16bit, 1MHz overflow table would therefore be:

Prescaler Value | Overflow Frequency | Overflow period
              1 |  15.259 Hz         |   65.5  ms
              8 |  1.907 Hz          |   0.524 s
             64 |  .2384 Hz          |   4.195 s
            256 |  .0596 Hz          |   16.78 s
           1024 |  .0149 Hz          |   67.11 s

I can get more excited about "67 seconds - like wow!" than I can about 0.0149 Herz which is very difficult for a human brain to "visualise"

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

Quite right Cliff - I've updated the tutorial. Thanks for the feedback guys!

- 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 the code I am using. When I run it, portb jsut stays low. Is it possible there are some fuse settings that would mess this up?

#include 
#include 

int main (void)
{
   	DDRB |= 0x01;; // Set LED as output

   	//TCCR1A = 0;
   	TCCR1B |= (1 << WGM12);
   	OCR1A   = 15625;
	TCCR1B |= ((1 << CS10) | (1 << CS11));
	sei();
   	while(1)
   	{
      	if (TIFR & (1<<TOV1))
      	{
          	PORTB ^= (1 << PB0); // Toggle the LED

          	TIFR = (1 << OCF1A); // clear the CTC flag (writing a logic one to the set flag clears it)
      	}
   	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You're using the CTC mode of the timer, but polling the overflow interrupt bit (TOV1). You want to instead check for the compare match bit, which is OCF1A.

- Dean :twisted:

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

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

first sorry for bad english and thanks for this tut.

but i have some problems with chapter 7
in your final code there is TIMSK1

Quote:
TIMSK1 |= (1 << TOIE1); // Enable overflow interrupt

why TIMSK1 and not TIMSK, TIMSK1 causes compiler warnings ?

and when i compile without

#include 

there are warnings too.

greets from germany

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

You're right, the first was a typo, the second an error of omission. You do need the header file, and it is indeed just TIMSK, not TIMSK1 as I wrote down.

Thanks for the feedback, I've corrected the tutorial.

- Dean :twisted:

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

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

hi, I,m just started learning the AVR and i having some problem to understand the following code so can anyone give me a help or guidance . thanks

i)if (TIFR & (1 << OCF1A)) // can explain what this condition mean?

ii)for 16bits timer , is this after counter to 65535 then it will restart over again from 0? unless the overflow setting is select then i will exceed the limet .

Location : Singapore

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

1) that line is simply checking whether the OCF1A bit is set in the TIFR register. You probably need to read the "101" thread in the Tutorial Forum to really understand the syntax

2) not quite sure what you are asking but yes a 16 bit timer counts 0 to 65535 and then overflows back to 0 (unless it's in one of the modes that sets a low limit for it to count to)

Cliff

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

hi Dean,

wow what a detailed guide you have in this topic... it's really cool... looking forward for your PWM posting... how do i keep a update of what you have posted? i worried that i missed it and as a result i have to read all your post and editing at one whole shot... which will spend me lots of time to understand and follow...

so can anyone tell me how to keeop myself update for Dean's or anyone's new tutorial posted?

will*

hi wish to learn more about programming stuffs so do add me in msn for me to learn for you better... :)

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

Next time you post a message to a thread you want to monitor put a tick in that box to the left that says "notify me when a reply is posted"

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

o ok.. hi clawson again.. thanks.. hehe... em actually i wanted to know is like when someone, eg you, dean, etc, have a new topic being posted in AVR and when you all do the 1st posting, i will be notify about it... so i can go take a 1st quick look at the start of the tutorial posted...

thanks again :)
will*

hi wish to learn more about programming stuffs so do add me in msn for me to learn for you better... :)

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

i'm newbie
thank you very much! :P

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

abcminiuser, I wanted to thank you for putting together this amazing tutorial. It is very easy to read and quickly got me up and running using timers. They really aren't as mysterious and difficult as I originally thought they were. :)

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

Thanks for the compliments guys! Rest assured that when I get some free time, the next chapter will be written. I think it's about time to start on the PWM side of things....

- Dean :twisted:

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

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

Hey guys, I'm rather new to AVRs, anyways this might not be the most appropriate place to ask this, but I have an stk500 board with a mega8515 installed on it right now. And I'm only able to set the STK500 oscillator to 921.6 kHz. (I've tried typing in 1Mhz and writing to the chip, but once I read it I get 921.6 kHZ back)

Is there anyway I can force the chip to run at 1 Mhz? Or is the 921.6 kHz the closest I can get?

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

hello,

am i the only one who gets a error on this sentence code :

TCCR1B |= (1 << CS10); // Set up timer 

i get undefind symbol CS10, do i have to include someting to get it work?

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

What AVR?
you did include atleast avr/io.h?

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

bloody-orc,

That was actually a cross-post that's been answered in AVR Forum - his problem is that he's using CV where the reg names (TCCR1B etc.) are defined but the bit names (CS10 etc.) are not. For anyone reading this later the solution (as posted to the other thread) is to use xmlconvert to generate a set of .h files with bit definitions.

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

Hey Dean good work on all of this. Just wondering if you have completed your PWM tut or if it's posted. I didn't find it in search. I am looking for a PWM tutorial to reference; don't want to 'rewrite the wheel'.

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

Quote:

Hey Dean good work on all of this. Just wondering if you have completed your PWM tut or if it's posted. I didn't find it in search. I am looking for a PWM tutorial to reference; don't want to 'rewrite the wheel'.

Sorry, not yet. I've been (and still am) busy trying to complete all my end-of-year Uni assignments so I can pass the course for this year. I'll try to start writing it next week, but my time is very limited until the holidays in about four weeks, so don't expect it to be all done for at *least* two weeks. Sorry for the inconvenience.

- Dean :twisted:

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

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

Quote:
Sorry for the inconvenience.

Are you kidding?? Your readers should be apologizing for the inconvenience! Keep up in school it should come first. Cheers.

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

Thanks for the marvelous tutorial. This was written perfectly, and with a good understanding.

Many tutorials go over my head and assume I know what TOIE or OCR1H is. But you did a very good job at explaining what the functions are, and how to handle them - in normal basic terms.

If you write a book, I would probably buy it!

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

Hi guys,

This is my first attempt to post, as I'm a total newbie both to AVR and forums,

I've been watching this forum for some time, and i have learned a lot, and when I found this excellent tutorial,
I was amazed with the simplicity of it that guided me, I did all the experiment I found here with no problem, then I combined a code, which drove me nuts :shock: I realized that I'm not even a newbie

here it is , I'm trying to make an 8 bit counter on the stk500, I'm using the following
AVR Studio 4.13.557 Service Pack 1
GUI Version 4, 13, 0, 557
JTAGICE mkII 1, 0, 1, 140
ATmega644 77

the code I wanted to run is this.

Code display:

code
#include 
#include 

uint8_t led;

ISR(TIMER1_OVF_vect) {
	led++;
	PORTD = ~led;	
}

int main(void) 
{
	DDRD=0xff;
	TIMSK1 = 1<<TOIE1; 	 // enable timer overflow interrupt enable
	TCNT1 = 0 ; 	         // set timer counter   initial value
	TCCR1B = (1<<CS11  );    // start timer with FPU / 8
	sei();			 // enable interrupts
	led = 0x00 ;		 //Initial led value

	for (;;) {}		 // loop forever timer interupts will change the led value 
}

This works with no problem, I geta an 8bit LED counter at near 1 second intervals.
but when i move the "PORTD = ~led;" to the for loop, interrupts work, led var increments, but the LEDs won't change state,
I tried by clickin on the PORTD bits in the I/O view interface , it's Ok LEDs state change , but for some reason the instruction "PORTD = ~led;" does nothing inside the for loop;

here is the zombie code:
Code display:

#include 
#include 

uint8_t led;

ISR(TIMER1_OVF_vect) {
	led++;
}

int main(void) 
{
	DDRD=0xff;
	TIMSK1 = 1<<TOIE1;    //enable timer overflow interrupt enable
	TCNT1 = 0 ; 	      //set timer counter   initial value
	TCCR1B = (1<<CS11  ); // start timer with FPU / 8
	sei();		      // enable interrupts
	led = 0x00 ;	      // Initial led value

	for (;;) {	      // loop forever timer interupts will change the led value
        PORTD = ~led;    /**led var counts but  NOTHING CHANGES **/
        }
}

How comes the instruction cannot change the data on the port?
before I call myself a total idiot :oops: rather than total newbie,Can anyone please explain me why is this happening?

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

What optimisation level do you use?
With no optimisation it should work.
With -oS it won't.
The reason is that with higher levels of optimisation the compiler will guess (wrongly) that led never changes and so remove your code. Cheeky isn't it?

the 'volatile' keyword is what you want when you declare led. This is generally the case when a variable is changed/used in an ISR and the main thread.

The other time it is commonly needed is if you are setting up an external memory mapped peripheral, such as a UART or a SCSI chip. Here you might send multiple commands to the same address, and the last thing you want is for the compiler to decide, "the last value written was 0x3b, I don't need to write the earlier ones". That is wrong.

volatile uint8_t led;

'volatile' tells the compiler, "I know what I'm doing. Don't get fancy and change anything to do with this variable. OK?"

Inspect the disassembled code to check this.

see FAQ#1 at the end of any posting from clawson (you won't have to wait long!!)

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

Wow! thank you Broxbourne, You're fast, I wouldn't have guessed the answer, not at all, not in my lifetime....
so tricky, so the compiler says " you don't know what you're doing and I know better"???

I just added 'volatile', and it worked, but still the configuration is selected -oS,
where can I find more details about this optimization and compiler behavior ?
and thanks again and again..... :D

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

Quote:
where can I find more details about this optimization and compiler behavior ?

Some of the big guns can probably explain the differences between the various optimisation settings, but it is out of my league. The manuals are the ultimate reference if you really want to know it all, but sometimes a pointer helps to direct your learning.
I'm always glad to find a question I can actually answer correctly :wink:

'volatile' works for all settings. The idea is that a volatile variable can change at any time, independent of how the program is running, so it must be left untouched (as written by you).

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

Alain,

Believe it or not but the volatile question is answer number 1 in the FAQ section of the avr-gcc manual:

http://www.nongnu.org/avr-libc/u...

If you haven't read the manual yet now might be a good time to have a skim through.

By the way those should be / and not \ in your #include's

Cliff

PS both PDF and HTML copies of that user manual exist on your hardrive in \winavr\doc\avr-libc\

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

Hi Dean,

After reading your article, I decided to try it out with my ATmega16 which runs on a 16 MHz clock source. I decided to go with the CTC mode to toggle an LED at 1 Hz.

My calculations are as follows:
Input frequency = 16 MHz

Timer Resolution = 1/(Input frequency/Prescale)
= Prescale/Input frequency

Prescale Resolution @ 16 Mhz
1 1/16MHz = 0.0625 uS
8 8/16MHz = 0.5 uS
64 64/16MHz = 4 uS
256 256/16MHz = 16 uS
1024 102/16 MHz = 64 uS

Target frequency = 1 Hz

Target timer count = (1/target frequency) / (Prescale/Input frequency)

Using the above formula I got the following values:

Prescale Target timer count
1 16000000
8 2000000
64 250000
256 62500
1024 15625

Using a 16-bit timer I decided to go with a prescale of 1024

This is the code I have used:

#include 

int main(void){

	DDRB |= _BV(0);

	TCCR1B |= ((1 << CS12) | (1 << CS11) | (1 << CS10));	//fCPU/1024
	
	TCCR1B |= (1<<WGM12);	// Configure timer 1 for CTC mode

	OCR1A = 15625;	// Set CTC compare value to 1Hz at 16MHz AVR clock, with a prescaler of 1024

	for(;;){
		if(TIFR & (1 << OCF1A)){

			PORTB ^= _BV(0);	//toggle LED

			TIFR = (1 << OCF1A); // Clear the CTC flag (writing logic one to the set flag clears it)
		}
	}
	return (0);
}

However, the LED did not toggle at all.

What could be the problem?Do you think the calculations are wrong?

Thanks for your help.
Regards,
Sumair

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

Hi Dean,

After reading your article, I decided to try it out with my ATmega16 which runs on a 16 MHz clock source. I decided to go with the CTC mode to toggle an LED at 1 Hz.

My calculations are as follows:
Input frequency = 16 MHz

Timer Resolution = 1/(Input frequency/Prescale)
= Prescale/Input frequency

Prescale Resolution @ 16 Mhz
1 1/16MHz = 0.0625 uS
8 8/16MHz = 0.5 uS
64 64/16MHz = 4 uS
256 256/16MHz = 16 uS
1024 102/16 MHz = 64 uS

Target frequency = 1 Hz

Target timer count = (1/target frequency) / (Prescale/Input frequency)

Using the above formula I got the following values:

Prescale Target timer count
1 16000000
8 2000000
64 250000
256 62500
1024 15625

Using a 16-bit timer I decided to go with a prescale of 1024

This is the code I have used:

#include 

int main(void){

	DDRB |= _BV(0);

	TCCR1B |= ((1 << CS12) | (1 << CS11) | (1 << CS10));	//fCPU/1024
	
	TCCR1B |= (1<<WGM12);	// Configure timer 1 for CTC mode

	OCR1A = 15625;	// Set CTC compare value to 1Hz at 16MHz AVR clock, with a prescaler of 1024

	for(;;){
		if(TIFR & (1 << OCF1A)){

			PORTB ^= _BV(0);	//toggle LED

			TIFR = (1 << OCF1A); // Clear the CTC flag (writing logic one to the set flag clears it)
		}
	}
	return (0);
}

However, the LED did not toggle at all.

What could be the problem?Do you think the calculations are wrong?

Thanks for your help.
Regards,
Sumair

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

EDIT: The post below by me is not correct. I am leaving it here only so as not to disturb the flow of the thread, but please be aware that the information given is not correct.

ORIGINAL POST:

   DDRB |= _BV(0); 

If you want to set PORTB pin 0 as output the above wont do it. This will:

   DDRB |= _BV(1<<0); 

For more information on things like "1<<0" look in the tutorials forum for the tutorial named Bit manipulation (AKA "Programming 101").

"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]

Last Edited: Sun. Nov 4, 2007 - 09:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Johan,

I tried your suggestion, but still couldn't get it working. I tried both the statements without much luck :(

DDRB |= (1<<0);

DDRB |= _BV(1<<0);

I am not sure what else could be wrong?
Sumair

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

you are moving 12 0 places to the left. so this is hardly doing anything... What are you actually trying to do? turn PB0 an output?

DDRB |= (1<<PB0);
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi guys,

Actually I realised my mistake now. My actual clock source which comes from a 16 MHz crystal was running at 1 MHz speed. I found this out from the following extract in the ATmega16 datasheet:

Quote:

The device is shipped with CKSEL = “0001” and SUT = “10”. The default clock source setting is
therefore the 1 MHz Internal RC Oscillator with longest startup time. This default setting ensures
that all users can make their desired clock source setting using an In-System or Parallel
Programmer.

So I then modified the code as below, and there I got my LED toggling :)

#include 

int main(void){

	DDRB |= (1 << 0);	//set PB.0 as LED output

	TCCR1B |= (1 << WGM12);	// Configure timer 1 for CTC mode

	OCR1A = 15625;	// Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

	TCCR1B |= ((1 << CS11) | (1 << CS10));	//start timer at fCPU/64

	for(;;){
		if(TIFR & (1 << OCF1A)){

			PORTB ^= (1 << 0);	//toggle LED

			TIFR = (1 << OCF1A); // Clear the CTC flag (writing logic one to the set flag clears it)
		}
	}
	return (0);
}

All this time I was thinking that my code was running at 16 MHz crystal clock source....silly me.
But thanks for all your help guys, appreciate you all replying to my queries :)

It would be really nice if someone could explain how to use the actual supplied clock source (in my case 16 MHz crystal).

Regards,
Sumair

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

Did you change the MEGA16's fuses from their default settings? If not, your AVR will still be running from its internal 1MHz RC oscillator, even if you have a 16MHz clock source of some description set up. That would cause the program to run at 1/16 the speed, so your 1Hz timer would take a full 16 seconds to reach your compare value.

- Dean :twisted:

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

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

Hi Dean,

Well I haven't changed the ATmega16's fuse settings as I don't know how. Earlier I was just experimenting assuming that it was running @ 16MHz.But i changed the code as above, and I got an exact 1 sec delay for toggling the LED.

Is there a tutorial you can point me to for programming those fuses?You see I am a newbie and still learning :)

Thanks,
Sumair

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

Sorry, no tutorials that I know off the top of my head (and I'm glad my timers tutorial worked out for you!). It's quite easy however if you're using AVRStudio for your programming - the fuses tab makes it as simple as checking the desired setting's box and pressing program.

- Dean :twisted:

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

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

Great tutorial! It got me going into timer events in no time!
Just a small remark about your counter calculations:
If you'r using a 1MHz clock this means you have 1,000,000 cicles per second ;-)
So you will have to count until 1,000,000 to have an event for each second. Of course your counter will overflow because you only have 16 bits, so you have to use the frequency division but as you program in C you can write down:
// CTC value to 1Hz at 1MHz clock, prescaler of 64
OCR1A = 1000000 / 64;

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

The complete code for my last comment would be:
// CTC value to 1Hz at 1MHz clock, prescaler of 64
OCR1A = 1000000 / 64;
// Start timer at Fcpu/64
TCCR1B |= ((1 << CS10) | (1 << CS11));

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

Hi Dean,

I did change the fuse settings to run @ 8 MHz and I could get my LED toggling at 1 Hz with a few calculation and code changes.Thanks for letting me know that.

However, I have created a new problem for myself :(
I accidentally clicked on some fuse setings for the clock settings and now I am unable to program my AVR. I tried erasing the device too, but I keep getting this error:

Entering programming mode.. FAILED!
Leaving programming mode.. OK!

I am using the AVR Studio version 4.13.I am using the ISP mode to program the AVR. I get an "ISP Mode Error" pop-up each time I click on program or erase the device.Appreciate any help on this as I am unable to proceed due to these errors.Please see the attached screen shot of the error pop-up.

Thanks,
Sumair

Attachment(s): 

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

I wrote:

   DDRB |= _BV(0); 

If you want to set PORTB pin 0 as output the above wont do it. This will:

   DDRB |= _BV(1<<0); 

Upon returning from the weekend trip I re-read this, and realized what a load of crap this posting of mine is. I honestly can not figure out what part of my brain actually did that. Sorry for any confusion I might have created...

"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

Sumair,

Did you catch which fuse setting you changed your AVR to? Chances are you changed it to use an external *clock* rather than an external *crystal*. They're two seperate things, and one will not work with the other attached.

To fix your AVR, you could either use a Dragon/STK500 in parallel programming mode, or feed in a fast clock into XTAL1 via an external source (such as a square wave oscillator made from a NES555). Just remember to reduce your ISP programming speed to at most 1/4 of the speed of the clock you input into the AVR when taking the latter route.

Once you regain communications with your AVR, change the fuses to use an external crystal and resume normal operations.

- Dean :twisted:

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

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

Hi Dean,

I guess I'll try feeding in an external 1 Mhz clock using a 555 timer. Hope it works otherwise I'll have to replace my microcontroller with a new one :) as I don't have parallel programming capability on my development board.

Thanks for your help.
Sumair

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

Hi Dean,
Just wanted to say "hats off" for that great, easy to follow tutorial.:lol: You really should write a book. I'd buy it sight unseen.

Your style of changing only one variable at a time and fully explaining every parameter and each step of the process makes seemingly complex things, easy to understand. Keep up the great work!

Lachlan

What we need to learn,
we learn by doing.

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

Cheers Lachlan!

I try to write my tutorials in a way that I would understand, which is usually simple and verbose enough for the majority to learn from. I do like changing only one variable at a time; I've had years of maths texts doing multiple operations in one step, which causes me to get lost quite easy.

No plans on writing a book at the moment, but it's certainly not out of the question. I'd hate to muscle in on the fellow forum's members livelihoods (such as Smokey) and don't currently have the time.

- Dean :twisted

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

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

hi everyone ,

I have a question.Regarding the Part Five - CTC Mode using Interrupts , by changes [/code] OCR1A , I able to control the interval of interrupt...But is that a way for me to control when I wan to start my interrupt, other then let the interrupt fired thoughout the whole program ?

Location : Singapore

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

You have plenty of control over the timer. You could turn the timer on and off (via the prescaler bits - turn them off to disconnect any clock to the timer circuitry) which would halt the count. You could change the TOP register in CTC mode to change the interval.

If what you want is to allow the timer to keep running but block the CTC interrupt until you want it to occur (defeating the point of having a known interval in the first place), you could just disable the CTC interrupt enable bit in the timer control registers. When you're ready to process it again, re-enable the bit and if the CTC event occurred while the CTC interrupt was disabled, it will immediately fire the ISR.

- Dean :twisted:

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

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

Hi Dean, thanks for the prompt reply, but now we have another problem. We have programmed our interrupt function to calculate a value. Is it possible to return a variable from the interrupt function or can we just declare the value as a global variable?

Location : Singapore

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

ISR's can't return values directly - where would the return value go to? You need to use a global, and mind that you make the variable volatile (do a search on 'Freaks for an explanation) if you want to share the value between the ISR and your main code.

- Dean :twisted:

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

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

Hi Dean,
thanks for ur advice..
We have declared our variables below:

unsigned volatile int verticalspeed=0; 
int volatile reference=0;
int volatile altitudeR_int=0;

int main(void)
{
// initialise interrupt here
while(1)     
{
// at here the altitudeR_int  changes according to our sensor 


printf("Pump ON \n" );
printf("Altitude= %d ft \n", altitudeR_int);
printf("reference= %d mb \n",reference);
printf("verticalspeed:%u ", verticalspeed);
}

}


ISR(TIMER1_COMPA_vect) 
{ 
verticalspeed=((altitudeR_int-reference)*60);
		
reference = altitudeR_int;
  	
} 

Is this the correct way of using voletile global variables to return the verticalspeed into the main?

Location : Singapore

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

That should be fine, however, there's one more thing you're not accounting for - atomic access of the variables.

It's possible for your COMPA interrupt to fire in between the reading of a multi-byte global variable, which results in corrupted data being used. The solution is to read the volatile value into a normal local variable while interrupts are disabled in your main code:

volatile unsigned int verticalspeed = 0;
volatile int reference=0;
volatile int altitudeR_int=0;

int main(void)
{
// initialise interrupt here
while(1)     
{
// at here the altitudeR_int  changes according to our sensor

cli(); // Read in the globals here with interrupts disabled to prevent the ISR from updating them while reading the values
unsigned int verticalspeed_LCL = verticalspeed;
int reference_LCL = reference;
int altitudeR_int_LCL = altitudeR_int;
sei();

printf("Pump ON \n" );
printf("Altitude= %d ft \n", altitudeR_int_LCL);
printf("reference= %d mb \n",reference_LCL);
printf("verticalspeed:%u ", verticalspeed_LCL);
}

}


ISR(TIMER1_COMPA_vect)
{
verticalspeed=((altitudeR_int-reference)*60);
      
reference = altitudeR_int;
     
}

The added benefit of this in your code, is that since all globals are cached locally with the interrupts disabled, you prevent the update from also occuring between the variable reads. Without this you might get the corrupt data as above, but also correct data, but with half the variables from one sample, and half from another.

- Dean :twisted:

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

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

Dean,

Just to note that when you want to interrupt protect access to a variable you may not always know that the current interrupt state is SEI or CLI so rather than wrapping the access in a cli() before and an sei() afterwards it sometimes makes sense to define a 'uint8_t sreg_state' then do 'sreg_state = SREG; cli();' before the access and then 'SREG = sreg_state;' afterwards which recovers the pre-existing interrupt state rather than making the assumption that it was SEI.

Cliff

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

Right as always Cliff, I was oversimplifying for his example, which seemed to always need interrupts enabled. The "correct" way to do it is to save and restore SREG:

uint8_t SREG_Save = SREG;
cli();
// Atomic variable access here
SREG = SREG_Save;

Which I wrapped up into a handy macro and submitted to the avr-lib-c project, so it's going to be part of the library in the next avr-lib-c release or so.

- Dean :twisted:

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

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

hi Dean,
thanks for ur help ... we are not sure if we have disable our interrupt correctly...

int main (void)
{	
	TCCR1A= (1 << WGM12); 				TCCR1B = 0x04;	// prescaler 256		
	TIMSK |= (0 << OCIE1A); 	disable the interrupt				
	sei(); //  Enable global interrupts 

function1(); //
function2(); // interrupt start and end here 
funciotn3(); //
}

function2()
{

                TIMSK |= (1 << OCIE1A); 
		OCR1A  = 625;     // we set the 20ms  interrupt here 	
while(1)      
{ 
// at here the altitudeR_int  changes according to our sensor 

cli(); // Read in the globals here with interrupts disabled to prevent the ISR from updating them while reading the values 
unsigned int verticalspeed_LCL = verticalspeed; 
int reference_LCL = reference; 
int altitudeR_int_LCL = altitudeR_int; 
sei(); 

printf("Pump ON \n" ); 
printf("Altitude= %d ft \n", altitudeR_int_LCL); 
printf("reference= %d mb \n",reference_LCL); 
printf("verticalspeed:%u ", verticalspeed_LCL); 
} 
	TIMSK |= (0 << OCIE1A);  // disable CTC interrupt  
}

ISR(TIMER1_COMPA_vect) 
{ 
verticalspeed=((altitudeR_int-reference)*60); 
      
reference = altitudeR_int; 
      
} 

is this a correct way to disable and enable the interrupt? and another problem is even we intend to set to 20ms interrupt but the program seem to perform one second interrupt? did we set the interrupt interval correctly ?

Location : Singapore

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

That's correct for the atomic access, assuming that you want interrupts on at all times except for the global reads (otherwise you need to save and restore SREG as discussed above).

This line:

TIMSK |= (0 << OCIE1A);    disable the interrupt

Does nothing - I suggest you read the "Bit Manipulation" tutorial. The correct code is:

TIMSK &= ~(1 << OCIE1A);    disable the interrupt

Although since that bit is cleared when the AVR is reset, it *still* doesn't achieve anything.

What clock speed are you feeding into the AVR? It comes with it's internal 1MHz RC oscillator enabled by default, you need to change the fuses to use an external crystal/clock source.

- Dean :twisted:

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

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

Great tutorial! I successfully ran several of the code examples in this tutorial on an Olimex AVR MT 128 board (ATMega 128-based) after changing the timer prescale to 1/1024 to match a 16MHz system clock. I also changed the pin assignment to Port E Pin 2 because that pin on my board is adjacent to a ground pin. I'm just starting out with the AVR, and code from your tutorials is the first code I've successfully run on an AVR.

You write very well. You definitely have the right mindset when you write these tutorials - assume NOTHING of the reader. That's where many tutorials fall short. I teach for a living, and I've seen many "introductory" books in several different areas that miss this point. If you ever do get the time to write the book everyone is suggesting, I'll certainly buy it.

Ken

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

Thanks for the great tutorial. Using your tutorial alone, I've just wrote my very first interrupt implementation, to drive a servo with PWM signal in software, on a atmega8. Loaded it up, and it worked first time. Great tutorial! I'm very impressed. You've taught me how periodic interrupts work.

Thank you.

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

FYI, the PDF of this tutorial is not in sync with the online version at the top of the thread. I just spent two hours chasing a mistake in the PDF that was fixed online a while ago. Once I figured out that the PDF was old, I fixed it in minutes. On the flip side, I have spent two hours reading the mega168 datasheet trying to figure this mess out :) .

Dean, is there a plan to update the PDF? If not, it might be a good idea to either take it offline or add a disclaimer at the beginning that the document is not up to date.

The tutorial was great even though I had problems. Thanks for putting it together.

You can have my mac when you pry my cold dead fingers off of it.

Kevin McEnhill -- mcenhillk@gmail.com

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

Kevin,

Sorry about that, my fault. I'm going to remove the PDF version for the meantime, while I try to find an automated solution; maintaining two formats is a pain in the rear and I want to simplify things. Ideally I'd want to go from BBCode from the post straight to a formatted PDF, but that's a pipe-dream. I think the best I can hope for is a BBCode-HTML converter, then a HTML to PDF converter. I'll have a look around.

- Dean :twisted:

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

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

Actually, I've been thinking today about the comments others have made about me writing a book. Books on AVR and the C language has been done, and I don't want to take away money from hungry mouths like Smokey (and that, as he will tell you, is a big mouth :lol:). However, I think that a book that is essentially a set of worked examples using the different AVR peripherals might fill a niche while not treading on any toes. A bit like a book of my tutorials, but ones that flow from one to the next. Thoughts?

- Dean :twisted:

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

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

Well, speaking from my days of having to tackle that cursed Perl language, the Perl Cookbook was a lifesaver. The reference books (i.e. datasheets) are not a good place to learn the basics. The beginner books (i.e. turorials) are not much good once you have run through them once because the examples are too simple. Something like a cookbook that illustrates each function helps bridge the gap.

For example, as of right now, I would KILL for an example of a working input capture counter! I am not ready to give up and go to the AVR Forum yet but a cookbook would be high on my list to Santa right now.

You can have my mac when you pry my cold dead fingers off of it.

Kevin McEnhill -- mcenhillk@gmail.com

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

Hey All,

I have made a simple timer program that would toggle 3 LEDs after set time. I am using tiny13 uc. The program works perfect if I simulate it using AVR simulator or debug it using JTAGICE mk II but doesn't work at all if I flash program using ISP. All 3 LEDs stay ON instead of toggling after a set time.

I don't think there is an error with ISP. I have developed few other programs (not shown below) and they work fine when I flash them using ISP.

Any idea whats wrong ? I have tried playing with different clock prescales and different time delays (as explained using variable elapstedtimeseconds for longer time delays) but it didn't really help.

Below is program code :

[/code:1]

#include <avr/io.h>
#include "fuelgauge.h"


void main(void)
{

  unsigned char counter;

  init();				  // Initialize the controller

  TCCR0A |=  (1<< WGM01) | (0 << WGM00);

  OCR0A= 0xFF; 
  
  TCCR0B |= ((0 << CS02) | (0 << CS01) | (1 << CS00)); // Set up timer at Fcpu/1024

  
  for (;;) 
   { 

   counter=TCNT0;


   if (TIFR0 & (1 << OCF0A)) 
      { 
          PINB = 0x07; // Toggle the LED 

          TIFR0 = (1 << OCF0A);

	   }

   } 

}

void init(void)
{

  CLKPR = (1 << CLKPCE);
  CLKPR = 0;


  DDRB = (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB4) ;
  PORTB = (1<<PB0)|(1<<PB1)|(1<<PB2);
  PINB = 0x00;
}

</pre></blockquote>
</div>
<div class="bb-quote">
<blockquote class="bb-quote-body">
<pre>
[code:1]

Thanks for all your help !!!

- Umesh

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

Umesh,

If the LEDs appear to be on all the time it suggests that on/off toggling is simply happening too fast for you to see it (which is the difference between the simulator and real life - one is hundreds of times slower than the other). For one thing start by increasing the clock prescaler to the highest setting - you comment says "Fcpu/1024" but that's not what you are setting - I'll bet you speeded it up so you could see something happening in the simulator!

Cliff

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

hi
guys can any one tell me how do i start my AVR studies i m bit confused plz help me with this k where i should i start my AVR program......some of my frends said to start studies of AVR 4m ATMEGA8 datasheet....plz tell me or give any brief or supporting tutorial
thanx....

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

uzairsaeed wrote:
hi
guys can any one tell me how do i start my AVR studies i m bit confused plz help me with this k where i should i start my AVR program......some of my frends said to start studies of AVR 4m ATMEGA8 datasheet....plz tell me or give any brief or supporting tutorial
thanx....

FIRST READ: General Information about Posting in the AVR Forum
http://www.avrfreaks.net/index.p...

ESPECIALLY THE LINK:
http://www.catb.org/~esr/faqs/sm...

After reading those, then start a new thread in the AVR Forum, not the tutorials forum, and give it an informative title like: "How to start with AVR?"

Wilbur

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

:-D

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

Hi,

I tried the short LED Toggling Programm on my CM-5 Atmega 128. But i don´t know which LED is mean with DDRG? Could i see this one on my CM-5 case?

Could you help me let my LED´s on CM-5 like AUX/TXD blink? :-)

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

Added part eight - "Overflow as CTC". Writing PWM section now, just need to figure out the best way to go about it.

- 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 new year gift Dean.
Thanks a lot for ur dedicated work on even the new years eve.
i have a doubt.i simulated the code in part part five of ur tutorial in avrstudio.i am not able to see the counting in the tcnt registers.
is it so that the count is not visibile while using interrupts?

#include  
#include  

int main (void) 
{ 
   DDRB |= (1 << 0); // Set LED as output 

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode 

   TIMSK |= (1 << OCIE1A); // Enable CTC interrupt 

   sei(); //  Enable global interrupts 

   OCR1A   = 15625; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64 

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 

   for (;;) 
   { 

   } 
} 

ISR(TIMER1_COMPA_vect) 
{ 
   PORTB ^= (1 << 0); // Toggle the LED 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Studio has a number of known issues when simulating timers, depending on the version. Check your version's help file -- see if there are any timer-related issues. It should indeed show the timer counting correctly.

- Dean :twisted:

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

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

thanks dean.

i was breaking my head over this.
when i programmed mega16 with this code it worked properly.
only problem was with simulation.
i checked up the help page.there is a problem with tcnt buffering wen we use 16 bit timer.

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

in part eight of ur tutorial you mentioned to use prescale of 64 and in the code you used prescale of 8.

Quote:
Let's go with the previous example in part two: a 1Hz flasher, using a 1MHz clock and a prescale of 64. We found the timer count to be 15625 for those conditions.

TCCR1B |= (1 << CS11); // Start timer at Fcpu/8 

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

Fixed, copied the wrong code template. Thanks for the good bug-spotting.

- Dean :twisted:

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

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

hi
want to ask about the timer

Why I'm getting problem with the TIMSK? The code is like below using m169

//****************************************  
  TCCR1B = _BV(WGM12) + _BV(CS10);  // clear timer on compare match, no prescaler
  OCR1A  =  XTAL/DEBOUNCE;		   		// timer = 5 msec . 40000000/200 = 200000??
  TIMSK  = _BV(OCIE1A);            	// enable Output Compare 1 overflow interrupt
//****************************************

The error i always get is:

../bla.c:385: error: 'TIMSK' undeclared (first use in this function)
../bla.c:385: error: (Each undeclared identifier is reported only once
../bla.c:385: error: for each function it appears in.)

How i want to solve this?

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

Shouldn't it be TIMSK1 instead of just TIMSK?

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

You are not using a mega 16 I guess? Here is an answer to a similar question (from this thread actually):

Quote:
TIFR or TIFR1 depends on the AVR, Dean is writing for mega 16 where the register is shared between timers and is called TIFR.
/Lars

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

I've been playing with the following code on an atmega168@18.432MHz.

#include  
#include  

int main (void) 
{ 
   DDRC |= (1 << 0); // Set LED as output on port c0


// Waveform generation bits - see table 14-8 on page 104 of datasheet atmega168
   TCCR0B |= (0 << WGM02); // Configure timer 0 for CTC mode 
   TCCR0A |= (1 << WGM01) | (0 << WGM00);


   TIMSK0 |= (1 << OCIE0A); // Enable CTC interrupt 

   sei(); //  Enable global interrupts 

   OCR0A   = 1; // Set CTC compare value 

   TCCR0B |= ((1 << CS00) | (0 << CS01) | (0 << CS02)); // Start timer at Fcpu/1 table 14-9

   for (;;) 
   { 

   } 
} 

ISR(TIMER0_COMPA_vect) 
{ 
  
   PORTC ^= (1 << 0); // Toggle the LED on port c0
   // sei(); 
//  if sei(); is omitted then the maximum freq is lower. 
//  Using a 18.432 MHz x-tal, the max freq is 236 kHz (no sei() - with sei() in isr: 368 kHz
} 

I would like to have a faster timer. Why is it so slow? Any idea what to do?

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

Don't put the sei() in the ISR - the compiler takes care of that for you.

The ISR takes quite a few clock cycles to execute, about 9 just to get to it, plus a whole bunch more to execute your code, and the ISR overhead. 368KHz sounds about right for the maximum ISR speed.

You can do two things.

1) If your ISR is very basic, and has no side-effects, register or otherwise, you can make the ISR "naked" and ommit all the prologue and epilogue code. That'll speed it up, but will cause problems if your ISR tries to modify any of the registers or CPU flags.

2) Switch over to the hardware CTC method I outline. The timer circuitry can toggle the hardware compare pin as fast as the compare flag triggers, with no CPU overhead. That's the preferable 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,

I thought about the issue and it seems that the isr routine needs about 80 cycles to run. A naked one could be ok. I'll try the hardware one.

Oh, thanks for the tutorial.
I ran your examples and monitoring things on my digital scope. One one monitor I had the datasheet and on the other your tutorial and avr studio. The datasheet is quite cryptic and contains hardly any quick to use info. I've been looking at the microchip website lately and they have much more info about different things. Atmel is quite sparse with info.

How far are you with the the pwm tutorial? :wink:

Nick

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

Ah bugger, I've had the PWM section on my "ToDo" list for months, but can never quite seem to bring myself to sit down and really type it out. I lack motivation, and six seasons of Stargate on DVD doesn't help much.

Part Six: Pure Hardware CTC is the section you want to read on the hardware-only toggling solution.

- Dean :twisted:

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

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

Well I know the feeling... Ever heard of Firefly? just one season and a film called Serenity.

Anyway, I would like to have 3 pwm timers in an atmega168. Plenty of questions and the datasheet is cryptic. Perhaps if you just wrote something about the scope of this (strategy of what to use and perhaps why). Then I have enough to work on.

Thanks in advance.

Nick

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

I've been playing with the pure hardware ctc routine. I used the two outputs on an atmega 168.

#include  
#include  

int main (void) 
{ 
   	DDRD |= (1 << 6); // set port PD6 as output. (led-> Timer 0 output on port OC0A (PD6, pin 12))
   	DDRD |= (1 << 5); // set port PD5 as output. (led-> Timer 0 output on port OC0B (PD5, pin 11))
	DDRC |= (1 << 0);

// Waveform generation bits - see table 14-8 on page 104 of datasheet atmega168
// Configure timer 0 for CTC mode 
   	TCCR0B |= (0 << WGM02); 
   	TCCR0A |= (1 << WGM01) | (0 << WGM00);

   	OCR0A   = 10; // Set Clear Timer on Compare Match mode (CTC) value - also known as TOP
   	OCR0B   = 1;  // Set Clear Timer on Compare Match mode (CTC) value 

// set OC0A and OC0B to toggle on compare match. page 102/103
	TCCR0A |= ((1<< COM0A0) | (0 << COM0A1) | (1<< COM0B0) | (0 << COM0B1)); 

// Start timer at Fcpu/64 See prescaler table 14-9 on page 106
	TCCR0B |= ((1 << CS00) | (1 << CS01) | (0 << CS02)); 


	
   for (;;) 
   { 

   } 
} 

OCR0B = 0..10. At 10 the phase relation between OC0A and OC0B is 100% If OCR0B >10 then the output on OC0B is zero.

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

Each timer can only have *one* CTC channel operating at a time - if you use both, the timer will clear when the first channel compares, and the second one will never be reached. The point of the multiple channels is for PWM mode, where you can have dual channels operating simultaneously.

- Dean :twisted:

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

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

Thanks,

Now I've been playing with pwm on timer 0 using two outputs. I would like to get a possibility to set the freq and duty cycle of both outputs independently. But alas. I can only do one channel or use two channels with a fixed freq.
I do not yet understand it completely.
Perhaps you can have a look at the following code and tell me what I'm missing:

/*

PWM on atmega168 @ 10 MHz
two outputs OC0A (Port D6, pin 12) and OC0B (Port PD5, pin 11)

1) case WGM02=0 Mode 1 (table 14-8), PWM, Phase correct, TOP=0xFF

If WGM02=0 
Fixed freq: 39.0 kHz if fast is on (WGM01=1) or 19.6 kHz if fast is off (WGM01=0)
duty cycle set by OCR0A and OCR0B for both output ports.


2) case WGM02=1 Mode 5 (table 14-8), PWM, Phase correct, TOP=OCRA

If WGM02 = 1 then behavior is different: One (1) output only: OCR0B. Output OCR0A is constant high.
Frequency is determined by OCR0A: e.g.

OCR0A   = 100 : freq = 50.0 kHz (fast off)
OCR0A   = 200 : freq = 25.0 kHz (fast off)
OCR0A   = 100 : freq = 99.0 kHz (fast on)
OCR0A   = 200 : freq = 49.75 kHz (fast on)
OCR0A   = 199 : freq = 50.0 kHz (fast on)

The duty cycle of the output is: (OCR0B / OCR0A). 
e.g. OCR0A = 200, OCR0B = 35. Duty cycle=(35/200) * 100% = 17.5%

*/

#include  
#include  

int main (void) 
{ 
   	DDRD |= (1 << 6); // set port PD6 as output. (led-> Timer 0 output on port OC0A (PD6, pin 12))
   	DDRD |= (1 << 5); // set port PD5 as output. (led-> Timer 0 output on port OC0B (PD5, pin 11))
	DDRC |= (1 << 0);

// Waveform generation bits - see table 14-8 on page 104 of datasheet atmega168
// Configure timer 0 for PWM, Phase Correct 
   	TCCR0B |= (1 << WGM02); 
   	TCCR0A |= (0 << WGM01) | (1 << WGM00);

   	OCR0A   = 200; // Set Clear Timer on Compare Match mode (CTC) value - also known as TOP
   	OCR0B   = 9;  // Set Clear Timer on Compare Match mode (CTC) value 

// set OC0A and OC0B . page 102/103
	TCCR0A |= ((0<< COM0A0) | (1 << COM0A1) | (0<< COM0B0) | (1 << COM0B1)); 

// Start timer at Fcpu/1 See prescaler table 14-9 on page 106
	TCCR0B |= ((1 << CS00) | (0 << CS01) | (0 << CS02)); 


	
   for (;;) 
   { 
// Take a hike
   } 
} 

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

As far as I am aware, you set the frequency per-timer, then the duty-cycle per channel. Different frequencies require different timers - but most of the time muti-channel PWM applications all use the same frequency anyway (e.g. robots with servos, LED light controllers, etc).

- Dean :twisted:

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

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

frequency per-timer, duty-cycle per channel

I could not create that for both outputs, its either - or.

How many outputs per timer is normally used?

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

Added the start of the PWM chapter. Not entirely certain that I like it so far, so might be scrapped again (this is the second revision, I lost the first one!) depending on my mood and feedback.

I hate having things in public view that are unfinished, so hopefully this will serve me as a good motivator to finish it off this week.

- Dean :twisted:

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

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

Quote:

frequency per-timer, duty-cycle per channel

I could not create that for both outputs, its either - or.

How many outputs per timer is normally used?

One for CTC modes, and up to as many as it has for PWM. Phase and Frequency correct PWM should allow you to vary both the duty cycle of each channel (UPDATE value), and the frequency (TOP value).

- Dean :twisted:

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

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

Dean,

Can you update pdf, please.

Thanks for fine tutorial.

Pop

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

Hey, Dean.

Just wanted to thank you for all your work on this great resource in the hope that a kind word might add to your motivation to push-on through the PWM section. Looks like you are off to a great start!

As an AVR newbie this site, and especially your timer guide, has been an invaluable resource to me.

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

As a newbie, this is THE most informative guide I have found yet relating timers. WOW, before most the code was greek to me but after the explanations, I can actually understand what is going on. Thanks! I hope you find the motovation soon, I cannot wait until you get the PWM section done!!!!!

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

Anyone know where I can find an easy to follow guide for PWM until this tutorial is done?

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

Atmel's AVR335 app note talks about PWM (in the context of producing sound in fact)

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

hi , this is another great work. i cant describe how its useful. thanks..
I am trying with this tutorial because it doesn't require any tools beside my mega162 and small programmer.
I read the first four parts and try it , i fail :(
i changed anything with respect to my avr. here is the code:

TCCR1B |= ( 1 << CS12) ; // set up timer at Fcpu/256
	for(;;)
	{	
		// Check timer value in if statement, true when count 			//matches 1 second
		if (TCNT1 >= 31250)
		{
			TCNT1 = 0; // Reset timer value 
         		ElapsedSeconds++;
			if ( ElapsedSeconds == 30 ) // Check if 1/2 minute 							    //has elapsed
			{
				
				ElapsedSeconds = 0; // Reset counter variable
				PORTC ^= ( 1 << 0);	//Toggle the LED
				
			}
		}
	}
	
}

another thing to notice is that my programmer has a 4MHZ crystal. but in the program i am asuming i am suing the internal 8mhz.. after programmed i use it in my board without any crystal.!
does it make sense!??

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

abcminiuser wrote:

Quote:

It was a right pain formatting all the code snippets, but it's done and attached to the original post. Enjoy!
- Dean :twisted:

Even after logging in I could not locate the to download the pdf version at original post. My eyes are missing the same.

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Sorry, the PDF was subsequently removed as it was outdated. I would update the PDF, but the data is currently sitting on a backup disk awaiting the repair of my laptop.

- Dean :twisted:

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

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

man this is an awesome tutorial it completely cleared my fundamentals cant wait for the pwm section to come in but i got one doubt i worked ont the principle u had given and i made led flash at a frequency of 0.5 hz that means 2 times in a sec the code i had written is as follows
# include
int main(void)

{
DDRD|=(1<<2);
TCCR1B|=((1<<CS10)|(1<<CS11));
while(1)
{

if(TCNT1>=31250)

{
PORTD^=(1<<2);
TCNT1=0;
}
}
return(1);
}
but when i connected the led nothing happened instead og toggling it never lit on (the led is fine) is something wrong in the code

oh yes iam using atmega16

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

iwas silly enough to connect to the wrong port and see the output but i so much appreciate ur effort but when will the pwm section come coz i need it as soon as possible coz i need to make a robot

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

abcminiuser wrote:
Sorry, the PDF was subsequently removed ... sitting on a backup disk awaiting the repair of my laptop.
- Dean :twisted:

Thanks for your kind reply.

India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

superchiku wrote:
i made led flash at a frequency of 0.5 hz that means 2 times in a sec
No, 0.5 Hz means that it flashs once every 2 seconds. "2 times in a sec" means 2 Hz.

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

I have successfully worked on this Timer Tutorial from Part One till Four. When trying to work with Part Five, I face problem in compiling as displayed below :-

int main(void)
{
} // <=== missed this hence error in ISR ! ! !
ISR(TIMER1_COMPA_vect)
{
	PORTB ^= (1<<0);   // Toggle the LED PB0
}

Compiler Report :-

> "make.exe" all
-------- begin --------
avr-gcc (GCC) 4.1.2 (WinAVR 20070525)
<<<...>>>
Compiling: main_5a.c
avr-gcc -c -mmcu=atmega16 -I.
<<<...>>>
main_5a.c: In function 'main':
main_5a.c:28: error: static declaration of '__vector_6' follows non-static declaration
main_5a.c:27: error: previous declaration of '__vector_6' was here
main_5a.c:30: error: expected declaration or statement at end of input
make.exe: *** [main_5a.o] Error 1
> Process Exit Code: 2
> Time Taken: 00:00

I have verified I am using avr-gcc (GCC) 4.1.2 (WinAVR 20070525). The TIMER1_COMPA_vect is defined in the table under ISR. Any body can indicate if I have missed something.
- India_AVR

Corrected - Sorry error corrected - Why is it that we spot our mistake the moment we post our querry and till then for hours we are not able to spot our mistake ?
- India_AVR

-----------------------------------------
Wonderful world of "0"s & "1"s
-----------------------------------------

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

Dean, Great Work!!

I'm a few month AVR newbie and this is the best timer explanation I have read.

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

Dean, Are you planning to cover using a timer with an external crystal? like a watch crystal to have an accurate 1hz timer.

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

Quote:

Dean, Are you planning to cover using a timer with an external crystal? like a watch crystal to have an accurate 1hz timer.

Hmmm, thought I already covered that -- apparently not. Added to TODO list.

- Dean :twisted:

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

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

Thanks a lot, Dean!

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

Polish version of this tutorial :).
for egzample:
http://www.wkretak.pl/?page_id=71

here is the info about author and avrfreak.net
http://www.wkretak.pl/?page_id=71

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

I wrote a little program to calculate "target timer cycles" for all prescalers. I hope it could be usefull.

http://www.wkretak.pl/upload/tim... 9kb

To run the program you will need Microsoft .Net Framework installed on your PC. URL in readme file.

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

Quote:

I wrote a little program to calculate "target timer cycles" for all prescalers

Are you aware of http://www.avrfreaks.net/index.p... ?

"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

Hello folks,

thanks for the great tutorial, really helped me to start with AVR's.

I'm trying to build a square wave generator (school assignment), with a variable frequency from 100hz up to 100khz.

I'm using an atmega8 with 8mhz crystal.

this works fine, I can get the frequency high enough (100khz) but it isn't nicely variable.

I'm using the hardware ctc part of this tutorial, and created this very simple code (the part to check button states is left out, because it is of no need here).

int main (void)
{
   DDRB |= (1 << 1); // Set LED as output

   TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

   TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode

   OCR1A   = (8000000 / 99500 - 1) / 2; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64

   TCCR1B |= (1 << CS10); // Start timer at Fcpu/64

   for (;;)
   {

   }
}

as you can see, I'm trying to get a frequency of 99,5khz here.
this does not work for me, since it outputs a nice 100khz :(

I think it is because the timer value for 100khz and 99,5khz (in this case) are very close to each other:

(8000000 / 99500 - 1) / 2 equals 39,701.
the value for 100khz is 39,5.

does anybody have a solution? I'm a bit stuck right now :S

thanks a lot

v3n0m

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

DANG, nobody has an answer for this? :S

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

Another brilliant tutorial! Cheers :D

   TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 

If this starts the timer, what can I use to stop the timer?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
TCCR1B = 0;

Giving the timer a clock source starts it, so taking it away stops it. You'll need to manually reset it for the next time (TCNT1 = 0) if required.

- Dean :twisted:

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

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

AWSOME Tutorial!
Helped me out alot.

I originally learned these things on a Motorola 68hc11 and this tutorial helped me migrate flawlessly.

Can't wait to see the pwm Section.

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

Hello.....
thank you very very much for this topics about timers..

I want to ask you about using watchdog timer...eg:
setting this timer to turn off the micro controller after 5minutes....

thanks in advance..

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

How would one go about makeing the time interval much shorter (say for example .1 of a second?)

I am using a 4Mhz Clock


TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode

TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt
OCR1A   = 12500; // Set CTC

TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer

--

ISR(TIMER1_COMPA_vect)
{
// Flash LED
}

?

Thanks :)

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

Lower the OCR1A value. The tutorial explains how to calculate the compare value from the input clock, prescaler and desired interval.

- 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 sorry for posting this, and might have over looked .. but did you manage to finish the PWM part of the tutorial?..

- Kent

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

Sorry, I've been busy with Uni, my MyUSB and my new USB book. I know, I'm lazy!

- Dean :twisted:

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

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

A long awaited TUT i think for many people here, but anywayz a big thanx for the tutorials you have written.. Will probably after the information on Timers figure out how PWM works, hopefully ;) ..

- Kent

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

Brilliant! Please keep up the good work. Looking forward to the next PWM posting.

Dave

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

Thank you very much for a great tutorial!
It is the most helpful thing I found on the internet for setting up timers and interrupts... awesome

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

Hey Dean,

Nice job so far. Timers can be quite a pain sometimes. I have a hard time remembering how my own PWM code works sometimes.

I'd attack the PWM section with several different examples, since PWM is used in so many different ways. Maybe single timer fixed frequency-variable duty cycle examples first like switching power supplies, then a variable frequency-variable duty cycle example using one timer, then end it with a more complicated multiple timer example.

Good luck, feel free to use my teslaphonic code or webcam servo code for examples if you want.

Scott

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

Thanks Scott, good idea (and thanks for all the kudos posts above guys!).

I'm currently busy with MyUSB development and writing my USB book, but I'll come back to this eventually.

- 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:
...

I'm currently busy with MyUSB development and writing my USB book, but I'll come back to this eventually.

- Dean :twisted:

Very proud of you!! Way to go Dean!
John

Resistance is futile…… You will be compiled!

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

Hi Dean

I'm new to AVRs, but I use to work on 80C31 (very old tech I know, but that is varsity).

Thanx for the great tut. I understand timers now and I finaly got them to work. Great work, great tut.

Just wish you completed the PWM chapter as that is what I'm trying to sort out now, but I understand, varsity is hectic.

I do have another question. I've taken your code, in chapter 2 I think. I've added another LED on a different port. But for some odd reason, the timing changes. Why is this? Any ideas?

Here is the code.

#include
int main (void)
{
DDRB |= (1 << 1);
DDRB |= (1 << 2);
TCCR1B |= ((1 << CS10) | (1 << CS11));
for (;;)
{
if (TCNT1 >= 15625)
{
PORTB ^= (1 << 1);
PORTB ^= (1 << 2);

TCNT1 = 0;
}
}
}

btw, I'm using the Atmega8, so timing is different from what you calculated. I'm not trying to get exact timimg at the moment, just getting the basic.

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

By how much does the timing change? Just adding in a second port XOR command shouldn't make the timing wildly different.

- Dean :twisted:

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

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

Well, actually by quiet alot. I know it shouldn't make much of a difference , but the difference is big enough to notice it easilly. I'll say with about half a second or so.

Surelly something must be wrong on this side, but I don't know what.

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

So you can reliably making the timing change wildly by commenting out the second XOR in the loop?

That baffles me. Your timer prescaler is 64, so any code with an execution length of less than 64 clock cycles won't change the timer's count value by more than 1. The code uses a >= comparison, so any slight overshoot won't make any difference.

Perhaps someone else can jump in. I don't see how a three cycle operation (IN EOR OUT) inside the IF statment (which executes after the duration has elapsed) would vary the timing so much.

Is that an exact copy of the code you are working with?

- Dean :twisted:

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

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

mmmm, ok if I use this code
#include
int main (void)
{
DDRB |= (1 << 1);
DDRB |= (1 << 2);
TCCR1B |= ((1 << CS10) | (1 << CS11));
for ( ; ; )
{
if (TCNT1 >= 15625)
{
PORTB ^= (1 << 1) | (1 << 2); //Code I change
TCNT1 = 0;
}
}
}
timing stayes the same, but if I use this code;
#include
int main (void)
{
DDRB |= (1 << 1);
DDRB |= (1 << 2);
TCCR1B |= ((1 << CS10) | (1 << CS11));
for ( ; ; )
{
if (TCNT1 >= 15625)
{
PORTB ^= (1 << 1) ;
PORTB ^= (1 << 2); //Code I change from 1st program
TCNT1 = 0;
}
}
}

Timing changes completely and only 1 pin flashes, PINB1, while 2 stayes of.

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

I have read the tutorial about the topic and now i need help in PWM with Atmega88. The PWM will turn the RGB Led on and off. I dont know if i doing it correctly or not. Please guide me.

int main(void)
{
	
	wait(10000);			//wait a while for power up


	DDRB 	= _BV(3);   	// LED red
     		
	DDRD 	= _BV(3)   		// LED blue
     		| _BV(6);  		// LED green
    DDRC 	= 0x0f;			
	
	sei();					
	timer0();				
	timer1();				
	timer2();				
	lcd_init();				

	TCNT0=134;
	TCNT2=134;
	TCNT1=134;

	for (;;) 
   { 

   }

}


void timer0(void)
{
	// Mode of Operation: Phase Correct PWM Mode
	// Clock select: Quartz / 256 (Prescaler)
	// Timer Overflow Interrupt Enable
	TCCR0A |= (1<<WGM00) | (0<<WGM01) | (1<<COM0A1);
	TCCR0B |= (1<<WGM02) | (1<<CS02);
	TIMSK0 |= (1<<TOIE0) | (1<<OCIE0A);
}

void timer1(void)
{
	// Mode of Operation: Phase Correct PWM Mode
	// Clock select: Quartz / 256 (Prescaler)
	// Timer Overflow Interrupt Enable
	TCCR1A |= (1<<WGM10) | (0<<WGM11) | (1<<COM1A1);
	TCCR0B |= (0<<WGM12) | (1<<CS12);
	TIMSK0 |= (1<<TOIE1) | (1<<OCIE1A);
}

void timer2(void)
{
	// Mode of Operation: Phase Correct PWM Mode
	// Clock select: Quartz / 256 (Prescaler)
	// Timer Overflow Interrupt Enable
	TCCR0A |= (1<<WGM20) | (0<<WGM21)| (1<<COM2A1);
	TCCR0B |= (1<<WGM22) | (1<<CS22) | (1<<COM2B1);
	TIMSK0 |= (1<<TOIE2) | (1<<OCIE2A);
}

ISR(TIMER1_OVF_vect) 
{ 
 
TODO: Toggle LED when  OCR1A = 230;			//LED blue 4.5V, 90%

   TCNT1  = 134; // Reload timer with precalculated value 

}


ISR(TIMER0_OVF_vect) 
{ 
TODO: Toggle LED when OCR0A = 112;			//LED green 2.2V, 44%
   TCNT0  = 134; // Reload timer with precalculated value 


}

ISR(TIMER2_OVF_vect) 
{ 
TODO: Toggle LED when OCR2A = 102;			//LED red 2.0V, 40%
   TCNT2  = 134; // Reload timer with precalculated value 

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

i've read the tutorial. again and again.. but i can't figured out whats wrong with my project.

my project is purposed to create some ballistic time counter using ATMEGA8535.

and here is the code:

#include 

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x15 ;PORTC
#endasm
#include 
#include 
#include 

unsigned long counter=0;
unsigned long time_out=0;
unsigned long extra=0;
unsigned long extra_out=0;
char *time;
char *ext;

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
        TCNT0=252;
        TCCR0=0X01;

}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
        TCCR0=0X00;
        time_out=counter;
        extra_out=extra;
        
        sprintf(time,"Time = %u us",time_out);
        sprintf(ext,"Extra = %u",extra_out);
        lcd_puts (time);
        lcd_gotoxy(0,1);
        lcd_puts (ext);
        delay_ms(5000);
        lcd_clear();
        lcd_putsf("Ready");
        counter=0;
        extra=0;
}


interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
        TCNT0=252;
        if (++counter==50000)
        {
                counter=0;
                extra++;
                }

}

// Declare your global variables here

void main(void)
{
PORTA=0x00;
DDRA=0x00;

PORTB=0x00;
DDRB=0x00;

PORTD=0x00;
DDRD=0x00;

TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;


// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: On
// INT1 Mode: Rising Edge
// INT2: Off
GICR|=0xC0;
MCUCR=0x0F;
MCUCSR=0x00;
GIFR=0xC0;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// LCD module initialization
lcd_init(16);
lcd_putsf("Ready");
// Global enable interrupts
#asm("sei")

while (1);
}

the time counter started when INT0 is raising, and stopped when INT1 is raising.

the problem is, it's always return ZERO result.

is it the speed just goes too fast ? ( in theory it can reach about 164 m/s and the distances is 4 cm)

Last Edited: Wed. May 21, 2008 - 05:50 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suspect no-one will bother to read your code unless you change the QUOTE tags to CODE tags and fix the indentation.

"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

The timer is never started (make a prescaler selection using the CS02-CS00 bits in TCCR0). Also, you will be better off using the CTC mode (read about it in this tutorial). Any code that assigns the TCNTx register from an interrupt will suffer (i.e., timing will be inaccurate) because of interrupt latency.
/Lars

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

Lajon wrote:
The timer is never started (make a prescaler selection using the CS02-CS00 bits in TCCR0). Also, you will be better off using the CTC mode (read about it in this tutorial). Any code that assigns the TCNTx register from an interrupt will suffer (i.e., timing will be inaccurate) because of interrupt latency.
/Lars

but the thing is, my project is working when i tested with a free falling object (it shows about 600 us when it falls from 1 meter).

but when i give some velocity here (blowing it with 2 bar pressure), the system (just like i say) gave me ZERO result.

and about the latency things, have you read the tutorial ?. i've done my code just like the tut's.

see this in part 8?

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

and now....

what went wrong...
realy ?

nb: johan...... you're not helping at all

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

Quote:
and about the latency things, have you read the tutorial ?. i've done my code just like the tut's.
Read "Part Five - CTC Mode using Interrupts ". That said, the timer 1 input capture function is a better fit for your application. It would make better use of the timer HW so your program is not so busy with timer interrupts. I suspect your current solution suffers from this but I don't think that this alone explains the problem you have.
/Lars

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

Have you checked the signals connected at INT0 and INT1 (with a oscilloscope). Do they look ok also when a higher speed test is done?
/Lars

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

Why are you preloading timer? This is normally done when you want to control the time ( such as flashing an LED at a set rate) It seems here you want to measure the time in which case I would just zero and start the timer on the first int0 and forget the reload values. If the device you are using has an input capture that would be an even better way to accomplish this.

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

Lajon wrote:
Read "Part Five - CTC Mode using Interrupts ". That said, the timer 1 input capture function is a better fit for your application. It would make better use of the timer HW so your program is not so busy with timer interrupts. I suspect your current solution suffers from this but I don't think that this alone explains the problem you have.
/Lars

i missed that part.... sorry

but you were right about the busy part..
a friend of mine told me that my code in interrupt overflow need more than 4 cycle. and thats it.

Quote:
Have you checked the signals connected at INT0 and INT1 (with a oscilloscope). Do they look ok also when a higher speed test is done?
/Lars

the signal always ok i think.
because the result is always zero... not the "Ready" words.

problem is....
i have to waited a week until my lab is open again.

until then i'll report to you guys.

thanks a lot....

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

Hello everybody..

first of all thank you very much Dean for this wonderful tutorial.
next..
pleeease help me..

I'm using this function to control the speed of a small DC motor using pwm, and the problem is that the output of OC1A is still VCC whether I give OCR1A any value including 0xFFFF and 0x0000 and any value between them.. this is the function...


/*********************************************
Chip type           : ATmega8535
Program type        : Application
Clock frequency     : 4.000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 128
*********************************************/

#include 
//MOTOR:
//**********************************************************
//Motor Pins:
//--------------
//PIND.5=OC1A:PWM source for the motor .
//
//The following function is used to set Timer1 in fast pwm mode
void settings(void)
{
DDRD |=0b00100000;//PORTD.5=OC1A is output
/*
// Bit7:6=COM1A1:0(Compare Output Mode for Channel A)=10>>(Clear OC1A/OC1B on Compare Match, 
// set OC1A/OC1B at TOP (Non-Inverting)) with   WGM13:0=1111
// Bit5:4=COM1B1:0(Compare Output Mode for Channel B)=00>>Normal port operation, OC1B disconnected.
// Bit3=FOC1A(Force Output Compare for Channel A)=0>>No Forcing for Output Compare of Channel A 
// Bit2=FOC1B(Force Output Compare for Channel B)=0>>No Forcing for Output Compare of Channel B
// Bit1:0=WGM11:0(Waveform Generation Mode)=11+WGM13:2=11>>Mode: Fast PWM top=OCR1A
*/
TCCR1A=0b10000011;

TCNT1=0xFFFE;// MAX count value;
/*
// Bit7=ICNC1(Input Capture Noise Canceler)=1>>Input Capture Noise Canceler On
// Bit6=ICES1(Input Capture Edge Select)=0>>Falling Edge
// Bit5=Reserved Bit=0;
// Bit4:3=WGM13:2(Waveform Generation Mode)=11+WGM11:0=11>>Mode: Fast PWM top=OCR1A
// Bit2:0=CS12:0(Clock Select)=001>>>clock source clkI/O/1 (No prescaling).
*/
TCCR1B=0b10011001;

OCR1A=0x9999;//PWM Duty
}

thanks in advance

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

Readers beware! The above post by