According to the official Arduino site, it isn’t currently possible to use the Arduino Uno as an ISP. Having investigated this a bit, it seems that the problem is because the Optiboot loader resets the ATMega328 when the serial port is opened. When avrdude is started up to program the slave chip it firsts opens the serial port then tries immediately to write to it. The initial comms fail because the chip has reset and, whilst in the bootloader, isn’t actually responding to the STK500 protocol.

This was a bit annoying, having recently bought an Arduino Uno, as I’d like to use it to program another ATMega328, and I’m too cheap to buy an FTDI cable unless I really have to. I’m also new to this game, so hacking this sort of thing is officially considered “fun”.

There are reports that you can cut the reset-en link or use a 120Ω resistor to hold the reset pin high to get it to work, but I didn’t really want to cut the link. Using the resistor, as well as sounding a bit temperamental, turned out not to work for me anyway.

I put the new chip on the breadboard, and connected up following the official instructions. That much is pretty easy, as is uploading the ArduinoISP sketch. Powering up I then had the LED on the Uno flashing – which I realised was because I’d previously uploaded the blink sketch to the new chip (came with the Duemilanove bootloader, I was trying to upgrade), and digital pin 13 is also the SCK pin used in programming, so the slave ship was blinking the Uno! At least I knew both chips were functioning.

So the first thing to try was the standard ‘Burn Bootloader w/ Arduino as ISP’. As expected, errors ensued:

avrdude: stk500_getparm(): (a) protocol error, expect=0x14, resp=0x14

avrdude: stk500_getparm(): (a) protocol error, expect=0x14, resp=0x01
avrdude: stk500_initialize(): (a) protocol error, expect=0x14, resp=0x10
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.

avrdude: stk500_disable(): unknown response=0x12

This was expected – the LEDs indicated that the Arduino was resetting. So, was the ArduinoISP actually working? I fired up my favourite serial terminal (minicom), set to 19200 8N1, and waited for the LEDs to settle down after it reset again. Looking at the source code, it’s a nice easy protocol to test, so sending “1 ” and the response comes back “AVR ISP” – great. Send “P ” and the programming LED comes on; “Q ” turns it off again. (Note each command is followed by a space.)

Next was to try avrdude directly. Sparkfun have a good tutorial on how to do this, so I basically copied their commands:

avrdude -P /dev/ttyACM0 -b 19200 -c stk500v1 -p m328p -v -e -U efuse:w:0x05:m -U hfuse:w:0xD6:m -U lfuse:w:0xFF:m

Apart from lots of useful information, this came up with exactly the same errors as above. It would, of course, as that’s all the IDE is calling anyway.

I didn’t feel like hacking the source code for avrdude, but wondered if a delay after opening the serial port would suffice. This led to the thought that maybe if avrdude was run inside a debugger, a breakpoint could be created to stop execution at the right place. Enter gdb.

Running gdb is easy, so the above command to set the fuses is just prefixed with “gdb –args”. This leaves the (gdb) prompt waiting to know what to do. Let’s set a breakpoint at the open function, which will probably get called at some point:

(gdb) break open

Breakpoint 1 at 0x4026a0
(gdb)

All good so far – now start it off:

(gdb) run
Starting program: /usr/bin/avrdude -P /dev/ttyACM0 -b 19200 -c stk500v1 -p m328p -v -e -U flash:w:optiboot_atmega328.hex -U lock:w:0x0F:m

avrdude: Version 5.10, compiled on Jun 29 2010 at 21:09:48
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "/etc/avrdude.conf"

Breakpoint 1, 0x00007ffff720afe0 in open64 () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)

That looks sane – the first thing avrdude is doing is reading its configuration file. Let’s keep going.

(gdb) cont
Continuing.
         User configuration file is "/home/matthew/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyACM0
         Using Programmer              : stk500v1
         Overriding Baud Rate          : 19200

Breakpoint 1, 0x00007ffff720afe0 in open64 () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)

Ah – now we’re probably about to open the serial port. Let’s allow that open to run, and pause afterwards:

(gdb) next
Single stepping until exit from function open64,
which has no line number information.
0x0000000000421781 in ?? ()
(gdb)

Yes – the Arduino reset. Now that’s over with we might as well get rid of the breakpoint, let it run, and see what happens.

(gdb) delete breakpoints 1
(gdb) cont
Continuing.
         AVR Part                      : ATMEGA328P
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PC2
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65     5     4    0 no       1024    4      0  3600  3600 0xff 0xff
           flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
           lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : STK500
         Description     : Atmel STK500 Version 1.x firmware
         Hardware Version: 2
         Firmware Version: 1.18
         Topcard         : Unknown
         Vtarget         : 0.0 V
         Varef           : 0.0 V
         Oscillator      : Off
         SCK period      : 0.1 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.12s

avrdude: Device signature = 0x1e950f
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as D6
avrdude: safemode: efuse reads as 5
avrdude: erasing chip
avrdude: reading input file "optiboot_atmega328.hex"
avrdude: input file optiboot_atmega328.hex auto detected as Intel Hex
avrdude: writing flash (32748 bytes):

Writing | ################################################## | 100% 0.57s

avrdude: 32748 bytes of flash written
avrdude: verifying flash memory against optiboot_atmega328.hex:
avrdude: load data flash data from input file optiboot_atmega328.hex:
avrdude: input file optiboot_atmega328.hex auto detected as Intel Hex
avrdude: input file optiboot_atmega328.hex contains 32748 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 37.75s

avrdude: verifying ...
avrdude: 32748 bytes of flash verified
avrdude: reading input file "0x0F"
avrdude: writing lock (1 bytes):

Writing | ################################################## | 100% 0.12s

avrdude: 1 bytes of lock written
avrdude: verifying lock memory against 0x0F:
avrdude: load data lock data from input file 0x0F:
avrdude: input file 0x0F contains 1 bytes
avrdude: reading on-chip lock data:

Reading | ################################################## | 100% 0.05s

avrdude: verifying ...
avrdude: 1 bytes of lock verified

avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as D6
avrdude: safemode: efuse reads as 5
avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Program exited normally.
(gdb)

Excellent! All worked out. So we just finish:

(gdb) quit

The above example is for actually programming the bootloader – the same method works for setting the flags (which should actually be done first) using the following command instead:

gdb --args avrdude  -P /dev/ttyACM0 -b 19200 -c stk500v1 -p m328p -v -e -U efuse:w:0x05:m -U hfuse:w:0xD6:m -U lfuse:w:0xFF:m

The commands in gdb are exactly the same – so:

break open
run
cont
next
(wait here for the Ardiuno to reset)
delete breakpoints 1
cont
quit

Does it work? Swapping out the ATMega328 on the Uno for the newly programmed chip, setting board type now to ‘Arduino Uno’ rather than ‘Arduino Duemilanove or Nano w/ ATmega328’, and upload blink. We have a flashing LED!

This was all done in Linux. I guess it should be possible to use gdb or another debugger in a similar way in other operating systems, but I have no intention of trying it.

4 Thoughts on “Using the Arduino Uno as an AVR ISP”

  • Very useful! I don’t really know why we must put a delay in open call, I should check the protocol and the isp source code.

    I’m writing a simple wrapper around open with a 2 seconds delay in a dynamic library an using LD_PRELOAD to load this symbol.

  • i think you are the one to help me with this problem

    but can you please tell me how to enter gdb or the command promte ??

    i tried to open command promt in hardware/tools/avr/bin/

Leave a Reply

Your email address will not be published. Required fields are marked *