Use ArduinoISP to pre-program new ATmega328P chips?

I've successfully built, tested, and used the breadboard config (with crystal) shown on http://arduino.cc/en/Tutorial/ArduinoISP. I've semi-permanently dedicated a Duemilanove board to my setup and used a zif socket to make it easy to put new chips into the programmer. Everything works well for adding optiboot to new AVR chips.

But I would also like to pre-load sketches into new chips (in addition to the bootloader) and I am struggling to understand how/if I can use my ArduinoISP setup to do this? I'd appreciate any suggestions or help in locating references.

Jim

Take the '328 out of the Duemilanove, connect Rx Tx lines to the progammer, boot from there & down from the PC.

http://arduino.cc/forum/index.php?topic=55245.0 http://arduino.cc/forum/index.php?topic=57143.0 http://arduino.cc/forum/index.php?topic=71018.0

http://www.google.com/search?q=00000001FF+site%3Aarduino.cc&aq=t

http://en.wikipedia.org/wiki/Intel_HEX

Don't forget to also set the fuses and lock bits.

Thanks. Looks like I've got some new stuff to research.

Jim

My current plan:

  1. Download latest optiboot_atmega328.hex from arduino github site (this seems to be the June 14, 2011 version by westfw, committed October 10, 2011 by damellis).

  2. Compile the sketch that I want pre-loaded into new chips, then grab the associated *.cpp.hex file from the arduino temporary build area.

  3. Use a text editor to strip off the terminating string (:00000001FF) from my sketch's hex file.

  4. Cut/paste my sketch's hex file contents and insert at the beginning of the optiboot_atmega328.hex file.

  5. Tools : Burn Bootloader : w/Arduino as ISP. I figure this will take care of fuses, right? Is there somewhere I need to tell avrdude the size of the hex file?

I am trying to avoid bricking any chips, so if I've got something screwed up please give me some more hints. Thanks.

Jim

  1. Tools : Burn Bootloader : w/Arduino as ISP. I figure this will take care of fuses, right?

Yes.

Is there somewhere I need to tell avrdude the size of the hex file?

No.

I am trying to avoid bricking any chips

So long as you have an ISP (like an Arduino running the ArduinoISP sketch) and are careful with the fuse settings, you will be fine.

The process I'm using is a little clumsy, but it works. Using ArduinoISP I was able to burn bootloader + sketch into a new chip, then put the chip into my Arduino compatible PCB and have the sketch fire up as soon as power was supplied. Cool.

Cutting and pasting the hex files isn't so bad, but there must be a cleaner way to invoke avrdude than by using the IDE to burn optiboot_atmega328.hex. This requires renaming files in the arduino distribution folder.

Something like "make optiboot44_plus_sketch042.hex" would be nice. Time to go hunting in github for the command sequence issued by the IDE when it's asked to burn a bootloader :P

Jim

Please report back with what you find.

So far, I’ve found this java file:
https://github.com/arduino/Arduino/blob/new-extension/app/src/processing/app/debug/AvrdudeUploader.java

package processing.app.debug;

import processing.app.Base;
import processing.app.Preferences;
import processing.app.Serial;
import processing.app.SerialException;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import gnu.io.*;


public class AvrdudeUploader extends Uploader {
  public AvrdudeUploader() {
  }

  public boolean uploadUsingPreferences(String buildPath, String className, boolean usingProgrammer)
  throws RunnerException, SerialException {
    this.verbose = verbose;
    Map<String, String> boardPreferences = Base.getBoardPreferences();

    // if no protocol is specified for this board, assume it lacks a
    // bootloader and upload using the selected programmer.
    if (usingProgrammer || boardPreferences.get("upload.protocol") == null) {
      String programmer = Preferences.get("programmer");
      Target target = Base.getTarget();

      if (programmer.indexOf(":") != -1) {
        target = Base.targetsTable.get(programmer.substring(0, programmer.indexOf(":")));
        programmer = programmer.substring(programmer.indexOf(":") + 1);
      }
      
      Collection params = getProgrammerCommands(target, programmer);
      params.add("-Uflash:w:" + buildPath + File.separator + className + ".hex:i");
      return avrdude(params);
    }

    return uploadViaBootloader(buildPath, className);
  }
  
  private boolean uploadViaBootloader(String buildPath, String className)
  throws RunnerException, SerialException {
    Map<String, String> boardPreferences = Base.getBoardPreferences();
    List commandDownloader = new ArrayList();
    String protocol = boardPreferences.get("upload.protocol");
    
    // avrdude wants "stk500v1" to distinguish it from stk500v2
    if (protocol.equals("stk500"))
      protocol = "stk500v1";
    commandDownloader.add("-c" + protocol);
    commandDownloader.add(
      "-P" + (Base.isWindows() ? "\\\\.\\" : "") + Preferences.get("serial.port"));
    commandDownloader.add(
      "-b" + Integer.parseInt(boardPreferences.get("upload.speed")));
    commandDownloader.add("-D"); // don't erase
    commandDownloader.add("-Uflash:w:" + buildPath + File.separator + className + ".hex:i");

    if (boardPreferences.get("upload.disable_flushing") == null ||
        boardPreferences.get("upload.disable_flushing").toLowerCase().equals("false")) {
      flushSerialBuffer();
    }

    return avrdude(commandDownloader);
  }
  
  public boolean burnBootloader() throws RunnerException {
    String programmer = Preferences.get("programmer");
    Target target = Base.getTarget();
    if (programmer.indexOf(":") != -1) {
      target = Base.targetsTable.get(programmer.substring(0, programmer.indexOf(":")));
      programmer = programmer.substring(programmer.indexOf(":") + 1);
    }
    return burnBootloader(getProgrammerCommands(target, programmer));
  }
  
  private Collection getProgrammerCommands(Target target, String programmer) {
    Map<String, String> programmerPreferences = target.getProgrammers().get(programmer);
    List params = new ArrayList();
    params.add("-c" + programmerPreferences.get("protocol"));
    
    if ("usb".equals(programmerPreferences.get("communication"))) {
      params.add("-Pusb");
    } else if ("serial".equals(programmerPreferences.get("communication"))) {
      params.add("-P" + (Base.isWindows() ? "\\\\.\\" : "") + Preferences.get("serial.port"));
      if (programmerPreferences.get("speed") != null) {
params.add("-b" + Integer.parseInt(programmerPreferences.get("speed")));
      }
    }
    // XXX: add support for specifying the port address for parallel
    // programmers, although avrdude has a default that works in most cases.
    
    if (programmerPreferences.get("force") != null &&
        programmerPreferences.get("force").toLowerCase().equals("true"))
      params.add("-F");
    
    if (programmerPreferences.get("delay") != null)
      params.add("-i" + programmerPreferences.get("delay"));
    
    return params;
  }
  
  protected boolean burnBootloader(Collection params)
  throws RunnerException {
    Map<String, String> boardPreferences = Base.getBoardPreferences();
    List fuses = new ArrayList();
    fuses.add("-e"); // erase the chip
    if (boardPreferences.get("bootloader.unlock_bits") != null)
      fuses.add("-Ulock:w:" + boardPreferences.get("bootloader.unlock_bits") + ":m");
    if (boardPreferences.get("bootloader.extended_fuses") != null)
      fuses.add("-Uefuse:w:" + boardPreferences.get("bootloader.extended_fuses") + ":m");
    fuses.add("-Uhfuse:w:" + boardPreferences.get("bootloader.high_fuses") + ":m");
    fuses.add("-Ulfuse:w:" + boardPreferences.get("bootloader.low_fuses") + ":m");

    if (!avrdude(params, fuses))
      return false;

    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {}
    
    Target t;
    List bootloader = new ArrayList();
    String bootloaderPath = boardPreferences.get("bootloader.path");
    
    if (bootloaderPath != null) {
      if (bootloaderPath.indexOf(':') == -1) {
        t = Base.getTarget(); // the current target (associated with the board)
      } else {
        String targetName = bootloaderPath.substring(0, bootloaderPath.indexOf(':'));
        t = Base.targetsTable.get(targetName);
        bootloaderPath = bootloaderPath.substring(bootloaderPath.indexOf(':') + 1);
      }
      
      File bootloadersFile = new File(t.getFolder(), "bootloaders");
      File bootloaderFile = new File(bootloadersFile, bootloaderPath);
      bootloaderPath = bootloaderFile.getAbsolutePath();
      
      bootloader.add("-Uflash:w:" + bootloaderPath + File.separator +
                     boardPreferences.get("bootloader.file") + ":i");
    }
    if (boardPreferences.get("bootloader.lock_bits") != null)
      bootloader.add("-Ulock:w:" + boardPreferences.get("bootloader.lock_bits") + ":m");

    if (bootloader.size() > 0)
      return avrdude(params, bootloader);
    
    return true;
  }
  
  public boolean avrdude(Collection p1, Collection p2) throws RunnerException {
    ArrayList p = new ArrayList(p1);
    p.addAll(p2);
    return avrdude(p);
  }
  
  public boolean avrdude(Collection params) throws RunnerException {
    List commandDownloader = new ArrayList();
      
    if(Base.isLinux()) {
      if ((new File(Base.getHardwarePath() + "/tools/" + "avrdude")).exists()) {
        commandDownloader.add(Base.getHardwarePath() + "/tools/" + "avrdude");
        commandDownloader.add("-C" + Base.getHardwarePath() + "/tools/avrdude.conf");
      } else {
        commandDownloader.add("avrdude");
      }
    }
    else {
      commandDownloader.add(Base.getHardwarePath() + "/tools/avr/bin/" + "avrdude");
      commandDownloader.add("-C" + Base.getHardwarePath() + "/tools/avr/etc/avrdude.conf");
    }

    if (verbose || Preferences.getBoolean("upload.verbose")) {
      commandDownloader.add("-v");
      commandDownloader.add("-v");
      commandDownloader.add("-v");
      commandDownloader.add("-v");
    } else {
      commandDownloader.add("-q");
      commandDownloader.add("-q");
    }
    commandDownloader.add("-p" + Base.getBoardPreferences().get("build.mcu"));
    commandDownloader.addAll(params);

    return executeUploadCommand(commandDownloader);
  }
}

Not as easily deciphered as I’d hoped. The compiler and linker parameters were a lot easier to pick out from the github source.

Jim

you can use something like

#!/bin/bash

mcu="atmega8"
hfuse="cc"
lfuse="bf"
bootloader="optiboot_atmega8.hex"
sketch="blink.hex"

avrdude -c usbasp -p $mcu -P usb -e -u -Ulock:w:0x3f:m -Uhfuse:w:0x$hfuse:m -Ulfuse:w:0x$lfuse:m

avrdude -c usbasp -p $mcu -P usb -Uflash:w:$bootloader -Ulock:w:0x2f:m

avrdude -c usbasp -p $mcu -P usb -D -Uflash:w:$sketch:i

thats what i use if i only want to burn the bootloader and load the blink sketch :P (btw the code its written to be run on a unix machine - but im pretty sure you can run something similar for windows )

Thanks, putyn. That is helpful.

Do you happen to know the programmer name to put in place of “usbasp” when using ArduinoISP? I’ve read the avrdude documentation and can’t figure out which programmer is being emulated by ArduinoISP, at least as far as avrdude is concerned.

Jim

stk500

yeah like Coding Badly said its stk500 also if you want to use arduino as isp you need to use the -b to tell the upload speed - usually that upload speed its 19200 also ive had some problems with avrdude something like

avrdude -c stk500 -p atmega8 -b 19200 -P /dev/ttyUBS0 -e -u -Ulock:w:0x3f:m -Uhfuse:w:0x$hfuse:m -Ulfuse:w:0x$lfuse:m

would not work but if i would remove the space something like

avrdude -cstk500 -patmega8 -b19200  -P/dev/ttyUBS0 -e -u -Ulock:w:0x3f:m -Uhfuse:w:0x$hfuse:m -Ulfuse:w:0x$lfuse:m

not sure if this is a general problem or it was just my setup but if i didn't remove the space between it would gave me an error

Ok, a little slow in getting this solved. But here are a couple of batch files that work for me on Windows XP2. I am using ArduinoISP running on an Uno. Maybe someone else will get some use from these files.

To initialize a new chip and burn the bootloader:

REM Command line example
REM init com14 optiboot_atmega328.hex
avrdude -cavrisp -patmega328p -P%1 -b19200 -U lock:w:0x3F:m
avrdude -cavrisp -patmega328p -P%1 -b19200 -U hfuse:w:0xDE:m -U lfuse:w:0xFF:m -U efuse:w:0x05:m
avrdude -cavrisp -patmega328p -P%1 -b19200 -U lock:w:0x0F:m
avrdude -cavrisp -patmega328p -P%1 -b19200 -U flash:w:%2:a

To flash a hex file to the chip:

REM command line:
REM writehex com14 aBourbon230.hex
avrdude -cavrisp -pATMEGA328P -P%1 -b19200 -U flash:w:%2:a

BTW, the ISP shield from Evil Mad Science really makes this easy:
http://evilmadscience.com/productsmenu/tinykitlist/253

The process is slow, however, since ArduinoISP only runs at 19200 baud. Has anyone been successful speeding this up?

Jim

getting a dedicated programmer would speed things a lot there is a faster way to burn the bootloader using the optiloader provided by westfw https://github.com/WestfW/OptiLoader what this is its similar to the arduino isp but it only burns the bootloader (really fast) so you have one problem solved - you now need to figure out how to get the sketch you want after you burned the bootloader

Is there a reason why you want to burn a bootloader into the target as well as a sketch? Using ArduinoISP (or any programmer), you can burn a sketch directly into a target processor without using a bootloader.

dc42: Is there a reason why you want to burn a bootloader into the target as well as a sketch? Using ArduinoISP (or any programmer), you can burn a sketch directly into a target processor without using a bootloader.

In my case the reason is to preload an application on an Arduino-compatible board prior to shipping to customer. I included the bootloader so other applications could be loaded later by programmer-less individuals.

But you raise a good point that the bootloader isn't needed when you use ISP (except when an as-yet undiscovered bug in your program causes it to only work correctly when the bootloader runs first, but that's a different subject, sigh.....)

Jim