I have been using Atmel AVR
microcontrollers since Fall 2001 and I think it's a very good
architecture. There are lots of flash microcontrollers around these
days, but I was first attracted to AVRs because the free GNU C compiler
(gcc) can compile programs into AVR code. And because you can reprogram
them without disconnecting them from the circuit; and the newer ones
can even reprogram themselves without resetting. Anyway, enough of that.
I spend as much of my computing time as possible in Linux, so of course
I have sought out the Linux AVR tools. The most critical tools are
a programmer (hardware and software), an assembler, a C compiler, and some
libraries. I'm going to refer to other web sites on how to build a programmer
circuit, since there are many. My first programmer was a parallel port
contraption based on the AVR direct parallel dummy programmer described here
(local copy). You can also buy a
programmer and get started a little quicker. Here is the software
that I use for normal development:
- UISP programmer software, written by Uros Platise and maintained by Marek Michalkiewicz.
- avr-gcc, which is the GNU C compiler set up to produce AVR code. (configure with --target=avr --enable-languages=c)
- binutils 2.11 for assembling, linking, making libraries, etc. (configure with --target=avr)
- avr-libc is a C code library, required when using avr-gcc
- tavrasm, Tom Mortensen's AVR
Assembler. This is an open source assembler that compiles code written for
Atmel's DOS assembly. I don't write assembly language unless I'm forced to,
but I use tavarasm if I get assembly code from other sources.
Example Code in C
Here are some short example programs for AVR, written in C. These are
known to compile in avr-gcc. I've compiled binaries for 8515, and if there are
any requests I'll compile for other chips too.
- pwm1.c does pulse width modulation using simple loops. It could control several motors, but they would all have the same amount of power. (8515 binary)
- pwm2.c does PWM with two channels. You can control two motors and set their power levels separately. (8515 binary)
- (not ready yet) pwm3.c does two channel PWM using the 16-bit hardware timer #1. While the hardware timer is counting, the AVR is free to do other things.
I've used several types of programmer circuits that work, but eventually
decided to stick with the serial port programmer described in Atmel
application note 910 (AVR910). The programmer consists of a serial
port connection and an AVR (usually AT90S1200) that runs the AVR910 code.
Since it's sort of a standard, nearly all programming software supports it.
Also, I wanted to use the AVR109 boot loader, which follows the same protocol.
Here are links and patches related to the AVR910 programmer.
- Download PDF file from Atmel
- Download AVR910 source code from Atmel
- Alternative AVR910 source code from the uisp ftp site. Look for *.asm. For a while this was much more current than the Atmel site, but now the Atmel site has been updated.
- tavrasm, Tom Mortensen's open source AVR Assembler. I use this assembler to compile the AVR910 code.
- patch.for-8535 allows AVR910 to work on an 8535. Since the code was written for a 1200, they didn't need to initialize the stack pointer. But on most recent chips, you must init the stack pointer or every subroutine call will be a disaster.
- patch.for-2313 allows AVR910 to work on a 2313. This is almost the same as the 8535, except that a 2313 doesn't have a SPH register.
- patch.hwversion-chars fixes a very minor problem with reporting the hardware version.
AVR Boot Loader
To reprogram a robot across an infrared link, I have been using the
self-programming feature of the ATmega163. I couldn't find any
solutions to this problem on the web, so I went back to the basics.
Atmel application note #109 talks about boot loaders and includes code
for an AVR boot loader, so this was a good starting point. It was written
for a commercial C compiler called IAR, which I don't have, so I ported
it to avr-gcc and fixed some bugs. Then it wasn't as fast as I would
like, so I made some optimizations to improve programming speed.
This schematic shows one way to set up an ATmega to
use a boot loader. The top of the diagram is a standard AVR serial programmer,
which you need in order to program the fuse bits and the boot loader itself.
At the bottom is an example application circuit with an ATmega. Once the boot
loader is working, you don't need the AVR programmer anymore. You can
reprogram the device with just the serial connection.
Now that it works for me, I've cleaned up the code and created patches so that
other people can try it too. Here are links to the originals, and my latest
Since an IR link is not reliable, I wrapped up the programmer commands with a
very simple packet structure with a sequence number and checksum. I'll post
this eventually. Let me know if it would be useful.
- Application note 109: Self Programming
- Download PDF file from Atmel
- Download AVR109 source code from Atmel. (This link breaks periodically. What you need is the code for AVR application note 109 on their web site.)
- patch.port-to-gcc. Apply to the
original avr109 code from atmel site, and it will compile in avr-gcc
environment. See the readme inside the patch for gory details. (updated March 4, 2002)
- patch.version-num. Return software
version 2.3 instead of 1.0 so that programmer software knows it can use
autoincrement feature. Also return 1.0 for hardware version instead of
- patch.mword-write. Add 'M' command for
multiword writes, up to 64 sequential 16-bit words in a burst. Of
course it doesn't do much good unless your programmer software takes advantage
of this new command, so I added code to uisp to buffer up consecutive writes so
that many words can be written in one command. (updated March 1, 2002)
- patch.stray-APP_END. When compiled
for ATmega163, the app note code has a bug which causes chip erase to only
erase addresses 0-0x3ff, instead of continuing to just below the boot loader.
This patch removes one line from defines.h to fix the problem.
- Intel Hex Binaries of AVR109 with all the above patches: for ATmega161 and ATmega163. Baud rate fixed at 19200 with 3.69MHz crystal.
- patch.avr109-start-cmd-Z. As written, there's no way to start the application after you've entered the
boot loader. The only way is to power down, move a jumper or something
to change the value on PD4, and power up again. This is ridiculous. So I added a 'Z' command which jumps to the application code. (Updated June 12, 2002)
- UISP programmer software
- Download official uisp source code
- patch.uisp-mword-write. Add support for multiword writes to uisp. Also checks synchronization before
any commands are sent, to recover from any leftover state from a previous operation. (updated March 1, 2002)
- patch.increase-serial-timeout. Uisp has a default timeout of 1 second on all serial port transactions, but the erase command can take longer than a second when you are self-programming, and it depends on the clock speed of the AVR. This patch simply changes the timeout to 5 seconds. (updated June 12, 2002)
- patch.add-start-cmd-Z. Adds uisp support for the Start Application command (code 'Z') that I added to the AVR109 Boot Loader. With the Start Application command, you can reprogram the AVR application and then start it without powering down, changing PD4, and powering up again. (updated June 12, 2002)
- patch.add-serial-logging. Minor improvement of the serial logging code I wrote that is in the 2002-05-24 version of uisp. (updated June 12, 2002)
Performance of uisp with multiword writes
To see how much multiword writes were helping, I tested with a sample 8K
program. The table shows how long it took to download 8K using uisp
with various baud rates and download styles.
||AVR109 boot loader
||AVR109 boot loader|
with multiword patch
|2400 ||? ||258.7 sec ||46.8 sec ||5.5x
|4800 ||? ||171.6 sec ||24.4 sec ||7.0x
|9600 ||? ||86.3 sec ||12.6 sec ||6.8x
|19200 ||88.3 sec ||86.3 sec ||8.0 sec ||10.8x
For a while, I tried to hack multiword writes into AVR910 to see if it would
help there too. But then I realized that because the AVR910 code does both the
UART code and SPI transactions in software, it cannot keep up with a long
stream of writes. You could try to buffer all the writes until the end, and
then do all the SPI accesses at once. But I'm doing this for fun, right?
Please let me know if you have any suggestions for these pages. I'm not a
robotics expert, but I want to share what I have learned.