⭅ Previous (Emulating Program ROM) | Next (Desoldering ICs) ⭆ |
In this article I’ll talk about how I managed to program (aka flash) an Arduino bootloader onto my custom atmega based circuit board. This enables me to use the Arduino IDE with my custom board, making it easier for myself and others to contribute to firmware projects.
Some details are a bit finicky and poorly documented online, particularly involving the fuse bits. I’ll share how I flashed the bootloader onto my custom board, as well as an Arduino nano clone that shipped with an out of date bootloader.
Last time I got a prototype of the chiplab working on a breadboard. While it works, the breadboard setup is very delicate and hard to reproduce. Many times I transfered the breadboard to my bench for testing, only to find that a wire had come loose. Here’s what the breadboard prototype looked like.
By transfering this design to a printed circuit board, the connections would be more robust against unplugging. This would also make it easier to reproduce this setup, should others be interested in building their own labs at home. Here’s the custom board:
To ensure the custom board remains easy to program, I’d like to support it in the Arduino IDE. For this the microcontroller needs an Arduino compatible bootloader, which is not included from the factory.
Typically in order to flash an atmega chip, three things are needed:
Programmers can be expensive and specialized devices. But we can actually program an Arduino board to act as a programmer, with the knowledge of the programmer protocol and the ability to program an atmega chip.
This “Arduino as ISP” aka in-system programmer sketch comes out of the box with the Arduino IDE, however I needed to make some modifications in order to get it to work. Part of the programming process involves resetting the chip. This can normally be accomplished by hitting a reset button on the board. The ISP sketch can automatically reset, but only by programming at a lower frequency than is provided by default.
Additionally, I modified the sketch to use the classic programming pins which are shared across most arduino and atmega boards.
I’ve uploaded my modifications to Github here.
You should be able to use the Arduino IDE to flash this sketch onto the Arduino board you want to use as the programmer. Make sure the board settings in the IDE match this board you’re using as the programmer.
With the programmer prepared, we need to connect it to the target board. You’ll want to find the pins based both on the programmer board, and the target board.
4 data pins are required:
The official Arduino docs are quite good here. In my case, I’m using an Arduino Nano, which uses the same atmega as the Leonardo. If you cant find which pins you need, you can look up a datasheet for the chip used in your programmer board, and just search for MOSI / MISO / SCK.
https://docs.arduino.cc/built-in-examples/arduino-isp/ArduinoISP/
MOSI on the programmer connects to MOSI on the target. Likewise for MISO and SCK. The reset pin goes from pin 10 on the programmer, to the reset pin on the target. This can also be found in the datasheet if necessary.
Then standard power:
Now that we have our programmer ready, we need to prepare our computer to speak to the programmer via USB. AVRDUDE is the software which translates the bytes we wish to program into the appropriate protocol for the programmer, and handles variations across target chips.
The Arduino IDE uses AVRDUDE under the hood. Unfortunately the IDE doesnt have a way to specify using a lower-speed programmer, so we’ll need to call AVRDUDE directly.
In order to flash the chip, the programmer needs to first set a few “fuse” bits on the microcontroller. This bits enable the bootloader to be rewritten, and can also be thought of as a check that you’re programming the correct board. Each atmega chip typically varies in the specific values for these fuse bits.
A trick I found helpful for determining the correct values of these bits is to first try programming with the Arduino IDE. The command will fail due to using the wrong speed, but we can reuse much of the command.
Before we attempt to program, we need to find a compatible Arduino board for our microcontroller. Essentially this involves finding an Arduino board that uses the same chip. Here are a few for reference:
Chip name | Arduino Boards using this chip |
---|---|
Atmega328 | Nano |
Atmega328p | Uno |
Atmega32u4 | Micro, Leonardo |
My board uses at atmega32u4, so I select Arduino Micro.
Then the following in the Arduino IDE:
Then attempt burning the bootloader under Tools->Burn bootloader.
You should see a lot of error messages, but at the top, some line that looks like this. I’ve added backslashes to split the long line.
.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/bin/avrdude \
-C.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf \
-v -patmega32u4 -cstk500v1 -P/dev/ttyUSB0 -b19200 -e -Ulock:w:0x3F:m \
-Uefuse:w:0xcb:m -Uhfuse:w:0xd8:m -Ulfuse:w:0xff:m
The key pieces here are the -p line, which should correctly identify the chip you want to flash. All the -U lines control various fuse bits which we need.
Copy your output here for later.
The Arduino IDE ships with many bootloaders. To find one compatible with your chip, you can use the chip-to-board table from earlier, then try a bootloader from that board. For my atmega32u4, I found the caterina firmware designed for the micro:
arduino-1.8.18/hardware/arduino/avr/bootloaders/caterina/Micro-prod-firmware-2012-12-10.hex
At last, we’re ready to combine the parts and program the bootloader with avrdude.
Update the command you saved from step 3, changing:
-b19200
to -b1200
,-U lock:w:0x0F:m
at the end, to lock the bootloader after
programming. This seems to be the same based on the several boards I tested.For my micro based board, it looks like this:
/home/alex/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/bin/avrdude \
-C/home/alex/.arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf \
-v -patmega32u4 -cstk500v1 -P/dev/ttyUSB0 -b1200 -e -Ulock:w:0x3F:m \
-Uefuse:w:0xcb:m -Uhfuse:w:0xd8:m -Ulfuse:w:0xff:m -U \
flash:w:/home/user/arduino/arduino-1.8.18/hardware/arduino/avr/bootloaders/caterina/Micro-prod-firmware-2012-12-10.hex \
-U lock:w:0x0F:m
If all goes well you should see this at the end of your output (fuse bits vary of course):
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.23s
avrdude: Device signature = 0x1e9587 (probably m32u4)
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as D8
avrdude: safemode: efuse reads as CB
avrdude: erasing chip
avrdude: reading input file "0x3"
avrdude: error opening 0x3: No such file or directory
avrdude: can't determine file format for 0x3, specify explicitly
avrdude: read from file '0x3' failed
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as D8
avrdude: safemode: efuse reads as CB
avrdude: safemode: Fuses OK (E:CB, H:D8, L:FF)
avrdude done. Thank you.
With the bootloader flashed, you should now be able to upload sketches from the Arduino IDE.
Now connect your computer directly to the new board over usb, no longer using the Arduino-as-ISP system. Make sure the relevant port is selected in the IDE after replugging.
I used this small serial-echo script, which works without any other peripherals on the board:
void setup() {
Serial.begin(9600);
}
void loop() {
String data = Serial.readString();
if (data.length() > 0) {
Serial.print(data);
}
}
⭅ Previous (Emulating Program ROM) | Next (Desoldering ICs) ⭆ |