HOWTO get started with an AVR at minimal cost

This experience was conducted with:

overview

You will need:
  1. development tools
  2. a programmer
  3. a test circuit
  4. an example test program and an example Makefile

development tools

Install the tools like so:
you@there$ apt-get install gcc-avr binutils-avr avr-libc simulavr

the serial programmer

The hardware between the computer and the microcontroller is called the "programmer". The programmer could simply be a cable or it could be a PCB full of extra features. When the microcontroller may be programmed without removing it from its circuit they call it In-System Programming (ISP).

The UISP man page defines one of its supported cables (dasa) as: dasa (RESET=RTS SCK=DTR MOSI=TXD MISO=CTS), which seems like nothing but four wires and that is all it is.

Beware of incorrect serial cable pin-outs, which are available on the web. Such a pin-out is the reason behind the unusual colours of the wires in the photo. Below is the correct pin-out.

The following table shows how to connect a 9-pin serial port to an ATtiny26:

AVR pin   ATtiny26 pin   serial pin   DB9 pin   wrong colour
 MOSI          1            TxD          3         green
 MISO          2            CTS          8         orange
 SCK           3            DTR          4         white
 RESET        10            RTS          7          red
photo of completed work

No more is required than is shown on the left. The battery pack is connected to the black and red wires at the top of the image. Dip switch 1 connects power to all red buses on the breadboard. All light blue buses are ground. The upside-down potentiometer is a handy resistor with a long wire and is connected to switch 4 allowing it to pull the light blue wire (connected to RESET on the ATmega32) to ground. The ATtiny26 is grounded and otherwise unconnected on the right. The two LEDs and two resistors on the left are the ATmega32 test circuit and the ones on the right are the same for the ATtiny26. Bypass capacitors are visible either side of the ZIF socket.

If you're in New Zealand, components are available over the Internet from Dick Smith's Electronics and the South Island Component Centre.

Check that your programmer works like so:

you@there$ uisp -dserial=/dev/ttyS0 -dprog=dasa --rd_fuses
..which may tell you:
Probably the AVR MCU is not in the RESET state.
Check it out and run me again.
..but if the programmer is working then it will say:
Atmel AVR ATtiny26 is found.

Fuse Low Byte      = 0xe1
Fuse High Byte     = 0xf7
Fuse Extended Byte = 0xff
Calibration Byte   = 0xa6  --  Read Only
Lock Bits          = 0xff
    BLB12 -> 1
    BLB11 -> 1
    BLB02 -> 1
    BLB01 -> 1
      LB2 -> 1
      LB1 -> 1
..or for the ATmega32:
Atmel AVR ATmega32 is found.

Fuse Low Byte      = 0xe1
Fuse High Byte     = 0x99
Fuse Extended Byte = 0xff
Calibration Byte   = 0xb6  --  Read Only
Lock Bits          = 0xff
    BLB12 -> 1
    BLB11 -> 1
    BLB02 -> 1
    BLB01 -> 1
      LB2 -> 1
      LB1 -> 1

"dasa" worked for me roughly one time in ten. Manually pulling RESET low (with dip switch 4 in the photo) allowed me to program reliably with the remaining three wires. A second attempt to program the microcontroller without first allowing RESET up is prompted by:

Cannot identify device because it is locked.
Device similar to the ATmega103-old is found.

Device is locked.
Address out of memory range.

Allowing RESET up and then pulling it low again allows the microcontroller to be programmed again.

A typical session goes:

  1. switch 4 on (RESET)
  2. switch 1 on (power on)
  3. $ make erase
  4. switch 4 off
  5. switch 4 on (RESET)
  6. $ make upload
  7. switch 4 off
  8. watch the program run

the test circuit

test circuit

The pin-out of the microcontroller can be found in the "complete" document for your microcontroller model from Atmel (see references). The pin-out will be required to build the test circuit.

debugging

An int is 16 bits in size. A long contains 32 bits and libc will link in small routines for 32-bit arithmetic.

Start the program to be debugged like this:

you@there$ simulavr -d atmega16 -P simulavr-disp -g monitor.bin

Then do this to gdb (note that you give monitor.elf to gdb while giving monitor.bin to simulavr).:

you@there$ gdb monitor.elf
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-linux"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) target remote localhost:1212
Remote debugging using localhost:1212
0x00000000 in __vectors ()
(gdb) cont
Continuing.
Reply contains invalid hex digit 59
(gdb) 
There is automatically a breakpoint in main, which is why gdb stopped. The simulavr-disp window will show the CPU stopped at the address of main. To see where in the source it has stopped, do this:
you@there$ avr-objdump -m avr -Sl monitor.elf | less

The "AVR instruction set" from Atmel (see references) will help you see what the compiler has done to your source code.

references

serial pin-out

DB-9  DB-25  Signal Direction Desc

  1     8     DCD   <<--      Data Carrier Detect (Modem connected to another)
  2     3     RxD   <<--      Receives bytes into PC
  3     2     TxD   -->>      Transmits bytes out of PC
  4     20    DTR   -->>      Data Terminal Ready (I'm ready to communicate)
  5     7     GND   n/a       Signal Ground
  6     6     DSR   <<--      Data Set Ready (I'm ready to communicate)
  7     4     RTS   -->>      Request to Send (flow control)
  8     5     CTS   <<--      Clear to Send (flow control)
  9     22    RI    <<--      Ring Indicator (telephone line is ringing)
(taken from UISP 20050207)

example test program


#include <avr/io.h>


#define true  1


// delay for a minimum of <millis>
// with a 1Mhz clock, the resolution is 1 ms
void delay_ms( unsigned int millis )
{
    // Note: this function is faulty, see avrm8ledtest-0.2.tar.gz for
    //       updated code.
    unsigned int  outer1;
    unsigned int  outer2;

    for ( outer1 = 50;  outer1;  outer1 -- )
    {
        for ( outer2 = 1000;  outer2;  outer2 -- )
        {
            while ( millis ) { millis --; }
        }
    }
}


int main( void )
{
    /* INITIALIZE */
    /* enable PA0 as output */
    DDRA |= _BV(PA0);

    /* BLINK, BLINK ... */
    while ( true )
    {
        /* switch the LED off */
        PORTA &= ~_BV(PA0);
        delay_ms( 500 );

        /* switch the LED on */
        PORTA |= _BV(PA0);
        delay_ms( 500 );
    }
}

example Makefile


# Compiler flags, paths for include files and libraries

PROJECT = blink
TARGET = attiny26
#TARGET = atmega32
CC = avr-gcc
OBJCOPY = avr-objcopy
CFLAGS = -mmcu=$(TARGET) -Os -Wall $(DEBUG)
INCLUDES = -I.
SRC = $(PROJECT).c
OBJ = $(SRC:.c=.o)
UISP_OPTS = -dserial=/dev/ttyS0 -dprog=dasa

# "-c" means compile but don't link
.c.o:
	$(RM) $@
	$(CC) -c $(CFLAGS) $(INCLUDES) $*.c


# Targets...

all: $(OBJ)

$(PROJECT).hex: $(PROJECT).elf
	$(OBJCOPY) -j .text -O ihex $(PROJECT).elf $(PROJECT).hex

$(PROJECT).bin: $(PROJECT).elf
	$(OBJCOPY) -j .text -O binary $(PROJECT).elf $(PROJECT).bin

$(PROJECT).elf: $(OBJ)
	$(CC) $(CFLAGS) -o $(PROJECT).elf -Wl,-Map,$(PROJECT).map $(OBJ)

simulate: $(PROJECT).bin
	simulavr -d atmega16 -P simulavr-disp $(PROJECT).bin

identify:
	uisp $(UISP_OPTS) --rd_fuses

erase:
	uisp $(UISP_OPTS) --erase

upload: $(PROJECT).hex
	uisp $(UISP_OPTS) --upload if=$(PROJECT).hex

clean:
	rm -f *.o *.elf *.map *.hex *.bin $(PROJECT)
$Revision: 1.3 $, $Date: 2006/04/28 07:37:49 $
Valid HTML 4.01 Transitional