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.
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/
Incredibly useful, even nowadays. Thank you.
Still useful, even ten years on, for using a Duemilanove as an ISP 🙂