Introduction: A Complete Starter Guide to AVRs

Have you played with Arduino's and now have a taste for microcontrollers?
Have you tried to go beyond Arduino but got stopped by the dense datasheets?

This is the instructable for you!
I was working on an instructable for the epilog contest which would wirelessly link two smoke alarms.
I wrote all the code and prototyped on a breadboard.
As the deadline drew near I built the hardware but no matter what I tried I couldn't make it squeeze into the tiny space available.

As I wallowed in self pity, I thought of what a waste it would be for all that I learnt to be left in my head, so here it is!

This instructable will take you from the basics of bitwise manipulation to making good use of standard AVR peripherals such as timers, interrupts & serial communications.
It will also cover common extras such as Infrared remote controls, radio communications and more.

So jump in, skip what you know and brush up on what you need to.
Create the breadboard projects presented and learn new things!

Step 1: Binary and Bitwise Operations

Before we dive into datasheets we need to understand some low level concepts that you will use every time you want to do anything interesting with a micro.

Lets start at the most basic, the bit.
A bit is the smallest unit of data to a computer, it is either a '1' or a '0'.
In other words it is either on, or off.

To do anything useful in a computer though, a single bit is not enough, so they are combined into bytes.
A byte is 8 bits together, and using binary counting a byte can either store 0-255 (256 numbers) or -128 to 127 (Thanks to Joel in the comments for noticing the error).
For example:
00000000 = 0
00000001 = 1
00000010 = 2
00000011 = 3
00000100 = 4

Just like when you are counting in decimal (base 10, as in 10 fingers!), when you go past 9, you put a 1 in front and put the second digit as 0.
It is the same with binary, except as there is only 0 or 1, when you go past '1' you put a 1 in front and a '0' as the second digit.

The features of a microcontroller are controlled by registers. In the case of an 8-bit AVR they are either 8 or 16 bits long.
Instead of the whole thing representing a number however, each bit determines if a certain feature is on or off.

We will look at a lot of different registers later, but for now let's make up an imaginary one so we can see how to manipulate them.

Imagine we find a register in the datasheet that connects to 8 LEDs called LEDREG.
Each bit in the register is either a 1 or a 0, the LED is either on, or it is off.

We could turn on the first LED like so:
LEDREG = 1; // 00000001
2nd LED
LEDREG = 2; //00000010

But what about the 3rd LED?
If we did this:
LEDREG = 3; //00000011
We wouldn't get the 3rd LED, we would get LED 1 & 2 lit up instead!

With registers it isn't the number that matters, but what the underlying bits look like.
The 3rd LED would look be like this:
LEDREG = 4; //00000100

If you wanted to turn on different patterns of LEDs you could work out all 256 combinations, but that would be tedious.
There is a better way!

Boolean logic
Computers at the lowest level work with something called boolean logic.
They use logic gates to combine binary numbers.
For example an AND gate will only output a 1 if both inputs are 1.
An OR gate will output a 1 if either of it's inputs are 1.

Have a look here:
http://www.electronics-tutorials.ws/boolean/bool_6.html

It shows the 'truth tables' for common logic gates.

So, what use is all this to us?
We can make use of these logic gates directly using bitwise operators

Bitwise operations

Lets say we already have the 3rd LED on:
LEDREG = 4; //00000100

How can we turn on the first LED?
We could put in the value of 5 (00000101) but we don't want to have to figure out which combination each time.
Instead we can use an OR operator like so:
LEDREG = LEDREG | 1;
What this means is this
00000100 OR
00000001
--------------
00000101

The OR operator lines up each bit and if it is on in either byte, it will be on in the output.
A quicker way to write "LEDREG = LEDREG | 1;" is:
LEDREG |= 1;

You can also chain your operators like so:
LEDREG |= 1 | 2;
This will come in handy when we learn shift operators.

What if you wanted to toggle all of the LEDs?
You could use a NOT operator:
LEDREG = ~LEDREG;

A NOT operator flips all the bits, so you get:
00000101 NOT
--------------
11111010

Next lets look at an AND operator.
AND is used to mask a value.
That is, it is used to blank out sections of a byte but keep the value of the unmasked section, like so:

11111010 AND
00001111
-------------
00001010

You can use an AND to turn off a whole group of LEDs without changing the state of the rest.
In the above example it would be used like:
LEDREG &= 15; //00001111

One more useful operator before we move on is the XOR.
XOR stands for eXclusive OR.
It is similar to an OR gate, however if both bits are on, then they are turned off.
For example:

LEDREG ^= 15;

00001010 XOR
00001111
-------------
00000101

Now, by themselves the operators are limited, you still need to convert from binary to the equivalent decimal number.
This is where shift operators come in

Shift operators
A shift operator simply shifts all the digits left or right.

For example a left shift of 1:
x = 5 << 1;
gives you x == 10
in binary that is:
00000101 <<
-------------
00001010

A right shift simply moves the bits to the right:
00001010 >>
--------------
00000101

Any bits at the ends are simply lost (or stored in a carry register, but lets ignore that for now!)
A shift operator can be used as a very fast multiplication & division, as long as you are happy with only multiplying / dividing by powers of 2, eg:

x << 1 == x*2
x << 2 == x*4
x << 3 == x*8

Shifts are used a lot in setting registers.
Lets return to our LED example.

If we want to set the 6th bit, instead of working out what number that would be and then ORing it with the register, we can use a left shift like so:
LEDREG |= (1<<5);

NB: It is '5', not '6' as we only need to move the '1' 5 times.
In binary it would look like so:

00000001 <<
-------------
00100000

Which is then ORed with the register like so:

00000101 OR
00100000
-------------
00100101

In datasheets, the position of a certain bit is usally given a name such as "RXEN" which is used to turn on receive mode in the serial peripheral and "TXEN" for transmit.
Together they would look something like this:
UCSRB |= (1<

This should be enough to get you started, lets move onto some real examples!

Step 2: IO Pins

So, now armed with all of this bitwise knowledge, lets put it to some use!
One of the first things you will want to do with a microcontroller is to read a button and turn on an LED.

For this example I am going to use an ATtiny2313 MCU.They are cheap and come with 18 IO pins, 2K of Flash and only 128 bytes of RAM!
You should open up the datasheet to follow along: ATtiny2313 Datasheet

Start out by building the circuit on a breadboard.The MCU can use between 1.8/2.7 and 5.5v depending on the version you have.

To power the chip, I am using a 3.3v regulator, you can use a different value, however stay inside the operating values, and keep above 2v so you can easily power the LED.
Connect the regulator to a power source such as a 9v battery or a plug pack (I have one rated at 9v that actually delivers 12v!).
On the MCU connect pin 10 (GND) to the negative / gnd of your regulator and pin 20 to VCC (power).
Connect a push button between GND and pin 13 (any IO pin would do, but stick with the same so the code just works for you).
Connect the LED's cathode (a flat spot on the LED and the shortest lead) through a resistor to ground.Connect the Anode (longest lead) to pin 12.


Now, onto the code!
If you don't know how to put code on an AVR, there will be an appendix at the end of this instructable explaining it, go read it now and come back when you are ready.



For so little code, we have a lot of explaining to do!
DDRB is the "Data Direction Register for PortB"
This register determines if the IO pins are used just to measure voltage on the pin or if they can sink / source current.
When 0 they are high impedance and won't supply enough current to power an LED for example, but they can be used to check if a pin is high or low.
When 1 they are low impedance and can supply or sink up to generally 20mA, enough to power an LED or a transistor (to switch bigger loads).
Here PB0 (Pin 12) is set to low impedance to drive the LED.

The next line sets the PORTB register.This register is used to set the value of a pin, as this pin (PB1 = Pin 13) is being used for a button, we are going to set it high by default.
When the button is pressed, connecting to ground, this pin will be pulled low.
Next up is our infinite loop.
This for loop will run until the power is cut!

Inside the loop we test the state of the button, it is a busy line, so lets break it down.For the if condition we have:
!(PINB & (1<<PB1))
We start with a NOT (!) which turns a true into a false and visa versa.
Next we have:
PINB & (1<<PB1)
PINB is the register you read to find out the value of the IO pins.
The "&" is being used to mask out just the value of (1<<PB1) which as we learnt earlier, is a single bit turned on in the PB5 position.

So all up we are checking if the bit at PB1 in PINB is high, and then inverting it.
So we are checking if the PB1 pin is low (the button pressed).

If it is pressed we turn the LED on:PORTB |= (1<<PB0)

This is using the OR operator and the left shift that we covered earlier, setting the pin high.If the button isn't pressed we want to clear the pin (pull it low):
PORTB &= ~(1<<PB0)

This uses the AND operator along with the NOT and left shift to turn just that one pin off (low).

Step 3: Datasheets

Now that you have an idea of how it all works, you need to know where to find all of the registers, their names and what they do.

This is when you turn to the datasheet.
Datasheets are where the manufacturer places all the important information from electrical ranges and tolerances to how to program and use the device.
They exist for any electronics component you could think of, but here we are just going to focus on microcontrollers, AVRs in particular.

Head to Google and do a search like so:
attiny13 datasheet
attiny2313 datasheet

etc
Look for a PDF from atmel's site.
You should find something like these:
www.atmel.com/dyn/resources/prod_documents/doc2535.pdf
www.atmel.com/dyn/resources/prod_documents/doc2543.pdf

The first page is almost like a marketing pamphlet, telling you how much flash & ram it has etc
The next page will usually be the pin configurations, this is very handy to keep close, you might even want to print out this one page.
It lists the locations of each pin, it's name (Port name such as PB5) and it's alternative functions.

The IO pins can be more then just IO, some double as programming pins, others might be an analogue pin or the pins needed for a serial or SPI bus.
Any of the "INT*" or "PCINT*" pins are interrupts, which we will cover later.

From here there are some explanations of what the pins are, and links to other parts of the PDF which tell you about the alternate functions.

However the best way to proceed from here is to look at Table of contents to find the features you are interested in using.

Open the second PDF linked above (the attiny2313) and jump to the USART section.
We will cover the USART in depth later, for now lets get a feel for how the information is presented in the datasheet.

The first thing we see is the UDR register, for now lets skip over that and have a look at the UCSRA register.
The register is shown as a set of named boxes, each one represents a bit of the register.
The next line lets you know if you can read this register, write to it or read and write.
Some bits are one way only.
The third row tells you the default value that register is set to when the device powers on.

After the diagram each bit is described.
Some bits are used by the peripheral to tell your code something, and others are used by your code to change the behaviour of the peripheral.

If you scroll down to the UCSRC register you will see tables telling you the different configurations you can set based on the combinations of bits set.
The UCSZ set (UCSZ2, UCSZ1, UCSZ0) of bits are used in combination for 5 different modes and 3 reserved settings.

Reserved settings should not be used, as they will have no effect now, but in a future MCU they might mean something you won't know about.
So to keep your code portable, don't use reserved settings or bits.



Step 4: USART As a Serial Port


The USART peripheral that comes with many AVR micros is a flexible port that can act like many different protocols.
Some micros have a UART which while similar, can only perform as a standard serial port.

While it can act as many different types of serial port, we are just going to use it as a basic serial port, which will allow us to easily use the XBee radio later on.
You could also use this serial port to connect to an old computer, or via an FTDI chip to give you a USB-Serial connection to a new computer.
This is actually how many arduinos work, they use an FTDI chip to provide a USB-Serial connection to the microcontroller.

For this example we are going to program two ATtimy2313 micros to send characters over serial to each other.
Build up the breadboard as shown in the picture.
Let's call the left micro A, and the second one B.

Connect pin 2 of A to pin 3 of B.
Connect pin 3 of A to pin 2 of B.

Pin 2 is the RXD (Receive) and pin 3 is TXD (Transmit).

Connect an LED to B on pin 11 (PD6) and connect a button on A to pin 19 (PB7).

Use the attached code to program the two chips.
Set the clock of each to 1MHz
On Chip A uncomment this line:
#define TRANSMIT
and comment out this line:
#define RECEIVE

On chip B do the opposite:
//#define TRANSMIT
#define RECEIVE

Now, when you press the button on A, the LED on B will turn on!

At first it might not seem very impressive, but you could use this to talk to chips far away over an XBee radio, or to a computer which can be handy to help debug your code.

So, how does it work?
Lets look at the interesting lines:

UBRRH = 0;
UBRRL = 12;

Have a look in the datasheet for the UBRR register.
This is a 12 bit register, so as the 2313 is an 8-bit micro, this is split up into two bytes, high (UBRRH) and low (UBRRL).
The high register isn't a full register, with the top half (4 bits) reserved (not used, but shouldn't be touched for future compatibility).

If you scroll down to the next page, you will see a handy table of different baud rates depending on your micro's clock.
For this example we want 9600 baud (useful later for the XBee).
Looking at the table for 1MHz we get a value of 6 for the UBRR.
However it has a -7% error rate, which is a bit too high.
But if you look one column over there is one with 0.2% error rate.
What is different?
The U2X flag is set, and the UBRR is set to 12.

We will use that and set the U2X flag.
Next we have different settings to configure:
UCSRA = (1<<U2X);
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<UCSZ1)|(1<<UCSZ0);

These 3 registers are described in the datasheet so we will look at them only briefly.
U2X is described as "double the UART transmission speed"
RXEN tells the USART to enable Receive mode and TXEN is to enable transmit
UCSZ1 & UCSZ0 are set based on the table on page 133, setting the character size as 8-bits.


Next on the transmit side we have this line of code:
while ( !( UCSRA & (1<<UDRE)) );
This is checking the "USART Data Register Empty" bit of the "USART Control and Status Register A".
When it is set to 1, the loop exits and the next line of code can run:
UDR = 'F';

This is passing in the character 'F' to the transmit buffer, this can only be written when it is clear, which is why we have the wait loop before it.

On the receive side it is much the same:
while ( !(UCSRA & (1<<RXC)) );

Here we wait for the receive buffer to be empty, and then we read UDR to see what value it has:
if(UDR == 'F')

You could just as easily put this in a variable like so:
char x = UDR;

You have to read UDR to clear it, and you can only read it once (after which it clears and allows a new character in), so if the value is important to you, make sure you save a copy of the value on the first read!


Now, this example is split into transmit and receive, but you can have both in one, I just wanted to make a simple example




Step 5: XBee Radios

An XBee is a radio much like WiFi, except it uses a different protocol.

Have a look at it's datasheet here:
http://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-Datasheet.pdf

The XBee has a lot of features, however for now we are interested in only one, which is the ability to transparently transmit serial data.

So what does that mean?
When configured correctly, the XBee can replace the wires between your micros that we set up in the previous step.
Imagine the possibilities.
You could make your own wireless doorbell or a remote control car.
You could wire up sensors in the roof and read the values without running wires through the walls or having to keep climbing into the roof!

So, how do we configure an XBee to act as a transparent serial connection?

XBees are configured via their serial port.
If you have a windows machine you can use X-CTU to set the values, but if your like me on a Mac (or linux) you will need to talk serial directly to the radio.
I have no experience with X-CTU, but ladyada put up a good tutorial here:
http://www.ladyada.net/make/xbee/configure.html

If you need to use the serial interface directly you will need some way to make a serial connection from your computer to the XBee. It can't be a direct connection either, as the voltages are wrong (XBee has a maximum of 3.3v, serial from a computer can be as high as -25v -> 25v!

You could use the XBee adaptor and FTDI chip from adafruit.com, or if you are like me, you can improvise!

Initially I used an Arduino with a serial passthrough sketch (attached), you just need to make sure you are using 3.3v power.
Alternatively (my arduino died recently :( ) you could use something like a bus pirate.
The bus pirate is now one of my favourite tools after my multimeter, if you are seriously getting into this stuff, consider buying one, it will make your life easier!

So, assuming you have a serial connection to your XBee, what do you do?

To get out of transparent mode, you need to send "+++" (no quotes, no linefeed/ carriage return).
If you are using the serial console that comes with the arduino IDE, make sure you select "No line ending" before sending the +++

If you did it right, the XBee will respond with "OK"
Now enable linefeed again and you can inspect and set values.
This mode will timeout, so if it stops responding, resend the +++ (with no linefeed!)

To inspect a setting, type in it's name like so:
NI

To set a value:
NI="Roof Sensor"

When you want to save your settings (otherwise you lose them when the power is turned off):
WR

Look through the datasheet for all the options, I am going to list the settings I was using for my project.

I wanted all the radios to work in a broadcast mode, but you could easily set the address either before hand (as I have done with the broadcast address) or you can set it from inside your microcontroller code.

So, here are my settings:

ID = 0x0508
DL = 0xFFFF
DH = 0x0
SM = 1
MY = 0x1
RN = 0
NI = "Alarm 1"
RO = 0

I am just going to explain a few of the important ones, you can look through the datasheet for the rest.
ID: This is a group ID
DL & DH: These are Destination address. In this case it is set to broadcast, so all radios in the same group will receive the data
SM: This is the sleep mode, in this case I was using Pin 9 to put the radio to sleep (by pulling the line high). If you wake from sleep, give the radio about 14ms to wake up before using it (_delay_ms(14) would work).
MY: This is the address / ID of this particular radio, they should be unique within a group
NI: Node Identification, a human readable name you can give the radio


When setup, substitute the radio for the wires from the serial step and pull pin 9 to ground (or don't set a sleep mode).
It should work just like before, but now the two chips can be 30 meters (~100 feet) away indoors or 90 meters (~300 feet) away outside!

Step 6: Timers

Timers are a versatile feature of micros, once you learn how to use them you will find a host of things to use them for!

A timer is a simple counting device in your micro, it is also known as a counter for this reason.
On every tick they increment themselves.
You can check the value when you need an idea of how much time has passed, or you can get them to trigger an interrupt to let you know when a certain value has been reached.
Timers are not very accurate, if you need accuracy you will want a real time clock (RTC).
They are however good enough for most tasks

So before we get into it, lets explore how a timer actually works.
Every clock tick of the micro is fed into the timer module.
Here it can be divided by different amounts to produce a slower tick (we will get to that later).
On the tick coming out of the prescaler, the timer adds 1 to the value of it's register.

Like the registers we have already covered, this one can be read or written.
You can see what the tick count is at, and based on the clock speed and the divider, you can determine roughly how much time has passed.
When the timer hits it's limit (255 for an 8 bit timer, 65535 for a 16-bit timer) it resets to 0 and starts counting again.
If you enable interrupts it will alert your code that this has happened.
You can also set a compare value that will trigger an interrupt if you want to be informed of a time the is shorter then overflow.

If you need to measure a longer period of time, you can use a clock prescaler (think of it as a divider).
For example, a prescaler of 8 will only send a tick to the counter after it has received 8 ticks from the system clock.
You lose accuracy but you can measure a longer period of time.

If you want to measure a really long period of time (say 10 minutes, which is a long time for your speedy micro!), you can count how many times the over flow interrupt is triggered and act based on that.

An easy way to work out clock settings is to use a calculator like this one:
http://frank.circleofcurrent.com/cache/avrtimercalc.htm


So, enough explanation, let's get into some code!

The example here is going to show how to use the overflow interrupt to time longer periods of time, to see how to time smaller periods (microseconds & milliseconds for example), have a look at the IR receiver code, which makes extensive use of them to time incoming remote control signals.

Set up your breadboard as in the picture and download the attached code. (Set the micro's clock to 1MHz)
When you press the button, the LED will light up, and stay on for 30 seconds.
If you press the button again, the timer will reset.

So let's examine the code.
The main loop is pretty standard, set up the LED and the button, enable interrupts (don't forget this!).
When the button is pressed call the "start_timer" function.

start_timer sets the prescaler to 1024, enable the overflow interrupt for timer0, clear our overflow_count and clear the timer.
Now, every time the clock goes over 255, the interrupt is called.

The interrupt is the specially named function ISR(TIMER0_OVF_vect)

Here we check if we have overflowed enough times (calculated with the calculator website above), and if we have we disable the interrupt for the timer and turn the LED off.
Otherwise we simply increment our count and let the timer start again.

Pretty simple right?
The beauty of it though is that you can have other code running while the timer counts.
When it overflows there will be a slight detour to the Interrupt Service Routine (ISR), and once it is done it will return to your previously running code as if nothing happened!
Better then a simple delay, as you can either do more work or go to sleep to save power!

Step 7: IR Remotes

With my smoke alarm project, I had a plan to have a feature where you could temporarily disable the alarm via remote control, for when the birthday cake comes out for example.

So I started by following ladayada's IR tutorial:
http://www.ladyada.net/learn/sensors/ir.html

It was a really quick way to get into IR and I am vary thankful for it, however it was designed with a spacious arduino in mind and I was working with an ATtiny2313 which didn't have anywhere near enough RAM to do the job.

Using her sketch with a small remote I had lying around I recorded the power button and got this:
int PowerSignal[][2] = {
// ON, OFF (in 10's of microseconds)
{1138, 574},
{66, 76},
{66, 78},
{68, 70},
{70, 76},
{66, 76},
{68, 74},
{68, 78},
{68, 210},
{72, 216},
{68, 214},
{70, 216},
{78, 206},
{70, 214},
{70, 216},
{70, 216},
{68, 72},
{74, 72},
{68, 216},
{68, 76},
{66, 78},
{70, 214},
{68, 78},
{66, 72},
{70, 74},
{68, 216},
{74, 70},
{68, 214},
{72, 216},
{72, 68},
{74, 214},
{66, 216},
{70, 214},
{72, 5014},
{1134, 292},
{62, 5532},
{1142, 294},
{58, 0}};

38 on/off pairs, some of them too large for a single byte, so if I was to do it like this I would need 152 bytes.
I had enough flash, although it would take a good amount of space I could spare it, but if I were to record the incoming signals like that I would blow my RAM budget without even considering the other things I need RAM for.

I could have checked the code as it came in, but then timing gets a little difficult.

I thought about it for a while and I noticed a pattern in my IR code.
The signals were either less then 1ms or they were greater.
Using ladyada's code, it had a 20% tolerance, which would mean any of the low numbers would match each other.
There was essentially no difference between a 680 and 780 microseconds, with the tolerance.

It was a similar case for the larger then 1ms values, there are a few exceptions, but most fit the rule.

I thought to myself, what if I treat them as single bits, a 0 for less then 1ms, and a 1 if it were greater.

Packed in like that I came up with this:
11000000 = 0xC0
00000000 = 0x00
01010101 = 0x55
01010101 = 0x55
00000100 = 0x04
00010000 = 0x10
00010001 = 0x11
01000101 = 0x45
01011101 = 0x5D
1100

After ignoring the last nibble (half a byte) I was able to store the incoming signal all at once in only 9 bytes, and the compare signal would only take up 9 as well!
If I wanted more features I wouldn't be running out of flash to store codes, and being so small they could easily have been stored in EEPROM if I needed to.

An added benefit was that checking if the code matched was no more then 9 simple byte comparisons.

Have a look at the attached code.

At the top there are a set of defines to make the code easier to follow and maintain.
Following that is the pattern mentioned above, stored as a char array and entered in hexadecimal.
Everything else is basically in the listenForIR function which is called continuously from the main loop.
Note, this is just simple demo code, in a final project you will want to use an interrupt to wake when the IR detects something.
I have a version of code that does that.


So, how does listenForIR work?
Lets break it down.

We start with a while loop that tries to get 9 bytes of data from the IR.
We start by setting the timer to a 256 prescaler.
At 1MHz this lets us time up to 65ms without overflowing.

IR receivers hold their data pin high by default, going low when a modulated signal is received.
So we wait until we get a low, and if we take longer then 56ms waiting we bail out.

Once we have a low we need to time it, but we don't need to time as long as before, and we could use a higher level of accuracy, so we change the prescaler to 8.
If the low is held for longer then 1ms, then we jump out, otherwise the loop ends early as the pin goes high.

The next bit of code has a nice use of bitwise operations that lets us keep track of the changes quickly and cheaply (cheap in cpu terms and memory use):

current_byte <<= 1;
current_byte |= state;
while(IR_LOW); //in the case of a low longer then the above timeout

We start by shifting all the bits left.
We then OR in the value of either 1 or 0, which adds its value to the LSB (Least Significant Bit).
We then wait, incase it was a long low that we skipped out on earlier.

We then do the same for the high part of the code.

Once we have all 8 bits (4 pairs of low & high), we store it and start again.

Assuming we get all 9 bytes, we exit the loop and simply compare it against our code.
If they all match, we toggle the LED.


Step 8: Sleep

If you are building something that is going to run from battery, it is a good idea to save power wherever you can.
One of the first things to do is run the micro at the slowest clock you can while keeping the code doing it's job, the other thing is to put the micro to sleep when you don't need it.

If you are recording a slow sensor, or you only need to take samples every so often, you might be able to sleep for extended periods of time and save a lot of power.

When you put your micro to sleep you need some way to wake it up, or you won't be able to do anything.
You have a couple of options.
One is to set a watchdog timer, and the other is to setup interrupts.

A watchdog timer is a separate clock that runs independently of the micro's code.
When it overflows it can either trigger an interrupt or reset the micro.
You can use this to either wake up some time in the future, or you can use it to get you out of code that is in an infinite loop.

We are only going to focus on waking up the chip here.

The other option, a pin interrupt is the ability to have the micro wake up when something happens on the interrupt IO pins.
In power down mode, the interrupt only fires when the pin goes low (like when a button is pressed for example), but in standby mode you can interrupt on a change in state of the pin (such as going from low to high, or visa versa).


So, now we know about ways to get out of sleep, how do we get into it?

Here is an example from the attached code:

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_cpu();

//wake up
sleep_disable();
MCUSR = 0;

We set the mode first, and then set sleep enabled, followed by a call to sleep_cpu.
The process is broken into steps like this to prevent bad code from accidentally entering sleep.
It is at this point that the micro will be asleep.
When it is woken by one of the above mentioned sources, it continues on, where you should disable the sleep option immediately (again, to protect against bad code).
The final line clears the cause of the reset (such as from the watchdog timer).

So, how do we setup the watchdog timer?
WDTCSR |= (1<<WDP2) | (1<<WDP1) | (1<<WDP0) | (1<<WDIE);

The first 3 values set the amount of time until the watchdog fires (page 43 on the datasheet), in this example it is 2 seconds (again, approximately).
The last one enables the watchdog interrupt.
This should be done before entering sleep mode as in the example code.

You will need to supply an interrupt handler for the watchdog like so:
ISR(WDT_OVERFLOW_vect)
{
    LED_TOGGLE;
}

You can leave it empty if all you want is to wake the microcontroller.
One last step is to enable interrupts globally by calling sei(); at the start of your code.

As for pin interrupts, we will cover that in a later step.

If you build the circuit in the picture and use the attached code, every 2 seconds the LED will toggle, and in the meantime the micro will go to sleep to save power.

Step 9: 7 Segment LEDs

7 Segment LEDs are the LEDs arranged like the number 8 and by turning different elements on and off, you display from 0-9 and even a few letters from the alphabet.

But how do you write code to go from a number, to the right combination of lights to make that number?
Lets start by wiring up a single module to our ATtiny2313, and later we can add more to display numbers beyond 0-9.

To start with, you will need to refer to the datasheet for your LED module.
I got some nice blue ones from eBay:
http://cgi.ebay.com.au/Lot-20-pcs-7-Segment-Blue-LED-Display-0-5-/370412362039?pt=LH_DefaultDomain_0&hash=item563e4c1937#ht_2770wt_920
If the link doesn't work, try this store:
http://stores.ebay.com.au/Asia-Engineer

He has some nice bargains to get you started.

So, looking at the specs on the first link, I got common anode, which mean all the LEDs in the module share the anode and are controlled by pulling the cathode to ground.
You could have common cathode, which will need to be wired differently, so read the datasheet!


For my display, the pins are numbered from the bottom left counter-clockwise.
Pins 3 & 8 are the anode and both are the centre pin on bottom and top respectively.
I connected the cathodes to PORT B.
I like PORTB as it is a full 8 bits and altogether on the one side in order.
If you don't need the decimal point, PORT D is 7 bits and will do the job nicely.

Now, to turn on the different elements, all we need to do is set the relevant pins low, so the sink the current coming from the LEDs.

To start with, lets have a little light show!
Use the code from the 4th picture.
Your LED module should cycle through different combinations as the Port pins are counted down from 255 (when they hit zero they will just loop around).

Ok, so now we know the lights work and driving the pins low will light the LEDs, how do we make numbers display?

Have a look at the attached code, we define the LEDs based on the names given in the layout and the pins that are connected to them.
We then use the OR operator to join the correct segments to provide the number we want in an array.

When it comes time to display the number, we need to invert it with the NOT operator as the pins are sinking current instead of sourcing current as before.

Here is a video of it in action:

Step 10: Appendix: Programming AVRs

I forgot to add this step until someone helpfully pointed it out in the comments!

So, how do you get your code onto an AVR?

There are two steps.
1. Compiling
2. Programming / Burning

Compiling your code is converting it from the text that you write, to the instructions the micro can understand.
There are official compilers from Atmel, but my preference is to use GCC which is an established compiler used almost everywhere.
It is used to compile Linux, OS X and even microcontrollers like our little AVRs.

For the AVR GCC is combined with a C Library tailored for AVRs called AVR-LIBC.
GCC is also customised for the task and is known as AVR-GCC (shocking I know!).

You should bookmark the AVR-LIBC manual:
http://www.nongnu.org/avr-libc/user-manual/index.html
It is a very useful resource to discover how to do different things with your micro.

So, now that you know about the compiler, how do you get it?
If you are on a mac, get CrossPack:
http://www.obdev.at/products/crosspack/index.html
It provides a nice installer, but be warned, it is only installing the command line tools, there is no GUI element to it.

For Windows users you will want WinAVR:
http://sourceforge.net/projects/winavr/
I have no personal experience with it, so you might want to refer to LadyAda's tutorial if you are stuck:
http://www.ladyada.net/learn/avr/setup-win.html

Lastly if you use Linux or other Unix derivatives, you will need to start with LadyAda's tutorials again:
http://www.ladyada.net/learn/avr/setup-unix.html

It has been a while since I used linux, and never for micros (GCC is usually installed in most distros, however the AVR extensions won't be).


So, if you have read this far, you would have realised I do all my work on a mac, so I most of my knowledge will come from there, if something I say doesn't work for your platform you might want to try the forums at www.avrfreaks.com which have some very useful content!


So, you have a working avr setup, how do you get started with it?

On the command line (on a mac, run "Terminal" from the utilities folder inside Applications), run the following command:
avr-project my_first_project

This will create a directory based on a template.
It also provides an XCode project, but I never used it so you are on you own if you want to.
Instead head to the firmware directory.

In here you have two files, main.c and Makefile
You will need to edit the Makefile to match the particular micro you are using and the pogrammer you are using to burn the firmware.
You will also need to set the fuses, and the easiest way is to use a fuse calculator:
http://www.engbedded.com/fusecalc

When done, open main.c and write your code!
Next up, on the command line type: make

Programming / Burning
After you "make" your code, you will have a .hex file ready to put on the micro.
For this step you will need an AVR programmer.

AVR programmers are devices that use the SPI protocol to put the code on the chip.
Anything that can talk SPI can be used as a programmer, even an Arduino can be made to work, however you will probably want a dedicated device such as this:
http://www.adafruit.com/index.php?main_page=product_info&cPath=6&products_id=46

To burn the .hex using your programmer, we use avrdude.
However you don't have to use it directly, if your Makefile is configure right, you can simply type:
make install

You can even skip the original "make" step, as make install will both build and burn.


This is just a general overview, to give you an idea of the process.
If you need a more detailed explanation, please have a look here:
http://www.ladyada.net/learn/avr/index.html

It is more detailed and well rounded then I can write for just an appendix.

Comments

author
AshishS62 (author)2015-11-15

i am unable to view the above video even when i'm logged in

author
NathanWilliams (author)AshishS622015-11-15

That was odd, youtube changed all my videos to private for some reason!

It should be fixed now, thanks for letting me know

author
JoelG5 (author)2015-01-26

Small correction in Step 1:

A byte is 8 bits together, and using binary counting a byte can either store 0-255 (256 numbers) or -128 to 128.

Should be "-128 to 127" using two's compliment (still 256 values, but we can't forget 0)

author
NathanWilliams (author)JoelG52015-01-28

Good catch!
I wrote the whole thing in a hurry and didn't proof-read as much as I normally would have.

Thanks

author
Mickael PintoO (author)2014-12-05

Hello, thanks for sharing. I have a question, how did you translate the reading time to binary?

Nice work

author

I've tried it with a Atmega328 and it doesn't work... I don't know why, I think it isn't reading the IR..

author

If you have an Atmega328, you should start with Ladyada's tutorial (Adafruit):
Old link: http://www.ladyada.net/learn/sensors/ir.html
New link: https://learn.adafruit.com/ir-sensor

It is a much better tutorial, and if you have the space of a 328 it is simpler code to follow and understand.

Good luck!

author

I cheated a little bit!
I noticed that the timings I got from my remote were either greater than 1ms or less.

For example, these are in 10s if microseconds:
{68, 78},
{68, 210},
{72, 216}

In microseconds that is:
,{680, 780},
{680, 2100},
{720, 2160}

Or in milliseconds:
{0.68, 0.78},
{0.68, 2.10},
{0.72, 2.16}

I looked at all of the numbers and found that it would be enough to detect the pattern if I simply checked if a value was more, or less than 1ms.

So the above example becomes:
{less, less},
{less, more},
{less, more}

Now we only have 2 states (less, or more) which is binary (0 or 1, false or true).

The last thing I did was to bit pack those values.
So the above example would become:
000101

You don't read that as a single number, but just look at each individual bit in turn.

I hope that makes sense, but if not, please ask some more questions, I'm happy to help.

author
vignesh-ramamoorthy (author)2014-06-11

fantastic explanation even a lay man will understand coding after coming across this ...thanks a lot, its amazingly explained......thank u for the post

author

Thank you, I'm really glad you enjoyed it.

author
vignesh-ramamoorthy (author)2014-06-11

fantastic explanation even a lay man will understand coding after coming across this ...thanks a lot, its amazingly explained......thank u for the post

author
harrypotterrocks (author)2013-09-18

what is the name of the programmer { chip } the link to the website brought me to a site in a different language

author

I'm not quite sure what you mean sorry.
The AVR programmer I used is called a "USBtinyISP AVR programmer", which I got from adafruit.com

The updated link to that product is here: http://www.adafruit.com/products/46

Does that help?
If I have misunderstood, let me know, and try and explain what you are trying to do, what you have so far, what you know already and what you don't yet understand and I will try to help you get started.

(It is a fun hobby, once you get past the setup!)

author
harrypotterrocks (author)2013-09-15

What programmer did you use and what software

author

Have a look in the final step "Appendix" for more detail, but in brief:

To program the AVRs I used a USBtinyISP that I built from a kit from adafruit.com

As for software, I set up CrossPack which gave me a GCC for AVRs, and the AVR libraries.
That is the basic setup for a mac (and linux uses the same tools, but installed through the package manager), I hear good things of the WinAVR software if you are on Windows.

For actually coding, I use VIM for my editor and Makefiles to manage the build & burn.

But if you are just starting out, you might want to set up something a bit more beginner friendly like Eclipse (I don't have a lot of experience with it, too bloated for my tastes).

Check out the tutorials from adafruit (linked in the appendix), they are really well done and should give you a good starting point.

author
harrypotterrocks (author)2013-09-09

Thanks so much it really helped and by the way I think you should make a instructable on binary

author
harrypotterrocks (author)2013-09-08

I don't get how 4=100

author

I think the problem might be that '100' looks like "one hundred', and that is probably confusing your brain.
It is better to think of it as "one, zero, zero'.

It might make it easier if we use different symbols.
Lets use Y (yes) for '1' and N (no) for zero.

so it would be 'YNN'

As I said in the previous comment, working from right to left, each column is double the one before it.
So these three columns are worth:
4  |  2  |  1
Y  | N  | N

So '100' is '4' because only the 4 column has a value in it.

To make sure it is clear, lets list all the combinations for 3 bits:

4  |  2  |  1
N  | N  | N   =  0
N  | N  | Y   =  1
N  | Y  | N   =  2
N  | Y  | Y   =  3
Y  | N  | N   =  4
Y  | N  | Y   =  5
Y  | Y  | N   =  6
Y  | Y  | Y   =  7

For each number, look where there is a 'Y', and add those values up.

So for 3 (NYY) you have 2 + 1 = 3
For 5 (YNY) you have 4 + 1 = 5

I hope that helps, I think I might make an instructable for understanding binary, because it is hard to get it at first.

author
harrypotterrocks (author)2013-09-07

Under the part of binary and bitwise ... I don't get how all the numbers mean 1 2 3 4

author

Ah ok, it is confusing when you don't know it (and easy to forget that it was hard once you know it!)

Let me try and explain it here, but if I don't make sense, please tell me, and I might try to put up a more detailed instructable about binary counting & binary maths.

So lets start with a number system we all know, which is base 10.
In base 10, we start with the number 0, and count up 1 at a time until we get to 9.

After 9, we don't have a symbol for ten, so instead we put a '1' down, and a '0' after it.

What this really means is that we have 1x10 + 0x1.

So 25 for example really means: 2x10 + 5x1 (2 lots of ten and 5 lots of 1).

Binary (base 2) follows the same pattern, but it doesn't go all the way to '9', it only goes to 1!

So the pattern goes like this:
0: 0
1: 1
2: 10
3: 11
4: 100
5: 101
6: 110
7: 111

Each column is a count of a certain value, lets call those columns x,y,z:

x :: y :: z
1   0    1

z represents how many 'ones' you have.
y is the number of 'twos', and x is the number of 'fours'.

In base 10, each column goes up by a factor of 10 (ones, tens, hundreds), each column is 10x bigger than the last.
Binary (base 10) only has 2 states though, so each column is only double the one before it.

So if we had 8 bits, working from right to left, you get:

128 64 32 16 8 4 2 1

So the biggest 8 bit number would be:
11111111 = 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

I hope that helps, but please tell me if it didn't, I would like to get better at explaining things, so telling me it doesn't make sense (and why) would help us both :)

author
harrypotterrocks (author)2013-09-06

I don't get the example of all the numbers will you please help

author

I'd be happy to help.

There are a lot of numbers in this instructable though, which part are you having trouble with?
Also, what is hard to understand, so I can give you a better answer :)

author
spacejes (author)2013-08-02

Thank you for this very good tutorial.

author
NathanWilliams (author)spacejes2013-08-02

Thank you, I really appreciate it :)

author
hardwarehank (author)2011-11-29

Fantastic. Thanks for writing this. I'm disappointed that the MAKE/arduino community isn't more supportive of arduino programmers growing into AVR programmers (but I guess they'd make less money if that happened). Arduino should be a gentle introduction to AVR, IMHO. Once you know how to write a sketch, the C program for AVR is basically exactly the same, and the AVR tools make the chips pretty easy to work with.

I bought a USBTinyISP to program my chips, and built it - that was good fun. Then, I learned a bit more about AVR, and built the VUSBTinyISP, which is an instructable. I highly recommend both of these, since they'll teach you all about burning chips.

author

Thanks!
I just reread what I wrote and I wish I had done better, but I had rushed it in for a deadline.

I like the Arduino and started there, but after playing with a few low level operations I quickly took to AVR GCC directly.
It was a lot more fun to code against such restrictions, when at work I am usually on a machine with between 8 & 32 GB of RAM, there is no pressure to try to do anything interesting.

The smallest I have done now though was an ATTiny13, and that was a fun set of restrictions, not even having hardware serial and having to do it directly in assembly was fun!

But to be far to Arduino users, that isn't everyone's definition of fun, and solving the problem by using it as a tool is all they need.

Maybe there is room for some instructables that takes a stock arduino and a USBTinyISP and ease the user into working without the Arduino libraries and boot loader.
That seems to me to be the easiest path if you want to convert them, they already have a stable hardware platform (power, USB->Serial etc) so all of their focus can be on the software differences.

author

Yeah I've messed with a ATTiny2313, ATTiny85s, and a few others. The 6-pin chips are challenging. What bothers me isn't arduino vs. AVR really; it's more the marketing push behind arduino. I applaud things like Freeduino, which take an arduino programmer and throw them into chip programming.

I was really turned off by the latest issue of MAKE. They had a 12-year-old kid write in about how he was doing really cool stuff with raw AVR in the Q&A section. Reading this, I was extremely happy, since obviously this is superior to arduino development. Their response? "Go find some arduino resources here and read our biased article about how arduino is better than everything else." That was the entire response. No commendation, just a quick "if you're thinking about doing any sort of microcontroller development, you better do it with arduino." That's anti-intellectual.

So, thanks again. Hopefully people will see the light once they've spent hundreds of dollars on about $10 worth of AVR chips.

author
todell-1 (author)2011-03-24

I could not find the referred appendix. Link?

author
NathanWilliams (author)todell-12011-03-26

Hi Todell,
Thanks again for reminding me about the missing appendix.
I just added it then if you want to look it over, but in the end I couldn't add much to the already excellent tutorial from ladyada.

author
todell-1 (author)2011-03-24

Where is the appendix mentioned in step 3?

author
NathanWilliams (author)todell-12011-03-24

Do'h!
I had this feeling in the back of my head that I had missed something.

I will do a proper write up this weekend (tomorrow), but until then you will do well to read ladyada's tutorial on the topic:
http://www.ladyada.net/learn/avr/

Thanks for pointing this out, I wrote this instructable in a rush and I just forgot.

About This Instructable

31,292views

143favorites

License:

Bio: Software engineer, using electronics and micro controllers as a hobby to keep me sane!
More by NathanWilliams:LED Gingerbread houseA complete starter guide to AVRsHydroponics Float Valve
Add instructable to: