Getting an AVR to blink might seem like an incredibly difficult task compared to the usual Arduino blink, but it really isn’t! In this post we will be uploading a basic blink example to an ATtiny2313. This is perfect for projects where using an Arduino would be over the top. So let’s get started!
You will need:
- ATtiny2313 kit or a minimalist target board!
- USBTinyISP
- Soldering iron & solder
- LED & resistor
We will be working on a Mac for this example. There are options available for Windows and Linux as well, the gist is the same as described in this post.
Get your ATtiny2313 dev board kit and let’s open it up!
The ATtiny2313 is the main chip in this kit. It is worthwhile to flip through the datasheet and get an overview of how it works. Look at the datasheet here.
Solder in all of the pieces. Be sure to match up the DIP socket with the notch.
Time to add in an LED and a resistor. For this example, we are going to go with pin PB3. This pin is special because it has a 16 bit timer! This makes it quite delightful to use for fading.
Although the pin numbers are “weird” on this board, there are many advantages to this that you will find as you make more projects with AVRs.
If you haven’t assembled your USBTinyISP, now is a good time to do so. You can follow a tutorial here. The arrow on the wire attacher matches with the arrow on the board.
Time for the software part! There is a very handy package available that installs everything together called Crosspack. Go and download the latest one here and install it.
To get the code from the computer onto the chip, the procedure is to compile and then upload the code. We can handle this using a makefile. With a short terminal command, it will do everything that we detail it to do. Here is the makefile we used for this project.
The main parts of the makefile that have to change (if you were using a different chip or programmer) are the device, clock, programmer, objects, and fuses.
code
MAKE SURE THE FUSES ARE CORRECT! The values in the makefile we are using are set for the ATtiny2313. You can actually brick your avr if you mess up the fuses. This is one thing that Arduino protects you from. However if you exercise diligence with regards to the fuses, it should not be an issue. Check out the AVR fuse calculator here.
If you are using a different programmer from the USBTinyISP, then you will have to change the PROGRAMMER variable.
Now let’s test the makefile by opening Terminal (/Applications/Utilities/Terminal.app
) and entering the command make all
. You should see text as seen in the screenshot below. If you do not, be sure to fix this before continuing by checking the installation of Crosspack.
Finally for the interesting part, writing the code to be uploaded to the chip! You can look at our code here.
The _BV()
is an avr macro to do a left bitshift. To turn on and off the LED, we set PORTB
to 8 or 0 respectively. The delay_ms
function is from here, and the 17500
number comes from the computational time that it takes the loop to run. Cool eh!
Enter the make all
command into Terminal again since the code has changed since we last compiled. Be sure to fix any errors before proceeding. Plug in the USBtinyISP, and now lets upload the code! Enter the command make install
. You should see some long text appear. The below screenshot shows the end part of the code.
If all was good, you now have a blinking LED using an avr! Totally cool, congrats! If something is not working, go back and check the makefile to see that it is okay.
Now challenge yourself to go further and play around more! There is a most excellent list of resources available here. What will YOU make with your avr? Let us know on Google+ and Twitter, or come show it off on the Robot Party! Happy hacking!
IIRC, the compiler knows how to do "PORTB |= _BV(3)" and "PORTB &= ~_BV(3)" as operations that just affect those bits of the register (that is, even if an interrupt hits right in the middle and the handler changes the other bits of PORTB, the code won’t revert them). Also (IIRC), PB3 is _BV(3), (Or is it 3? Something like that.) so you could say "DDRB = LED" and "PORTB |= LED". Of course, that doesn’t help if you changed the LED to a PORTD pin. Also, I’ve had different versions of gcc optimize my delay loop differently; it’s worth getting a bit of assembly for the time-wasting core of your delay loop.
> the compiler knows how to do "PORTB |= _BV(3)" and "PORTB &= ~_BV(3)" as operations that just affect those bits of the register (that is, even if an interrupt hits right in the middle and the handler changes the other bits of PORTB, the code won’t revert them).
The "bit value" macro is defined as follows:
#define _BV(bit) (1 << (bit))
The intelligence of the compiler is an entirely separate issue. And, it appears that your observation is indeed the case for those specific examples:
The command PORTB |= _BV(3) resolves to "sbi 0x18, 3." This sets bit 3 of register 18, where 0x18 is the I/O address of PORTB. This is a single, uninterruptible statement.
Similarly PORTB &= ~_BV(3) just clears a single bit.
For other uses of _BV(), one may not be as lucky. :)
> Also (IIRC), PB3 is _BV(3), (Or is it 3? Something like that.)
PB3 is defined as follows:
#define PB3 3
So, you can use _BV(PB3) or _BV(3), or even just "8,"
Windell H. Oskay
drwho(at)evilmadscientist.com
http://www.evilmadscientist.com/
Ah, right. We always used a #define LED PB3 and PORTB |= _BV(LED), but I guess that was entirely so that, when the next revision of the board moved the LED to PB2, we wouldn’t be trying to figure out which 8s need to change to 4s, which 3s need to change to 2s, and so forth.
Is there a reason why there is a custom delay funktion instead of using _delay_ms()?
CrossPack-AVR is great! I recently upgraded to the 20120217 version (Feb 17, 2012 release), from the previous 20100115, which brought avr-gcc from version 4.3.3 to 4.5.1. I’d love to find a list of all the interesting changes between these two compiler versions.
Minor nit: Terminal.app is normally in /Applications/Utilities (no "~" on the beginning — that would be inside your home folder).
Thanks for the great target boards and the tutorial. For those who want to use the avrgcc function _delay_ms(), double check the value of the low fuse in the makefile as well as the value passed to avrgcc for F_CPU (defined as CLOCK in the makefile). For the 2313/4314, a low fuse value of 0x64 indicates CKDIV8 is programmed, resulting in an effective F_CPU value of 1MHz, not 8MHz. This will throw off the avrgcc delay functions. Leaving CKDIV8 unprogrammed (1) will result in a low fuse value of 0xE4.