The Arduino toolchain does a wonderful job of hiding away unnecessary complexity that would otherwise prevent us from building up projects quickly. The compilation procedure with avr-g++, the uploading process with an ISP (in-system programmer), and the pre-configuration of the chip via its fuse bits are all hidden parts of this process. But what happens when you need to “go off the deep end” and build an Arduino-compatible circuit board from scratch? In these cases we need to dig down to the level of embedded systems engineers and understand how things work at the low level so that we can put together a working system.
In this post, I thought I’d get started introducing the Arduino toolchain and then drop down a few notes about the fuse bits.
What is AVR?
AVR refers to an entire family of 8-bit (and some 32-bit) microcontrollers produced by Atmel. Within this family is a collection of microcontrollers (let’s call ’em chips), like the
- atmega328p
- atmega32u4
- attiny85
- attiny2313
and many more….
Some of these chips we may have even been inadvertently working with all-along without even realizing it. For instance, the atmega328p is the microcontroller on the Arduino Uno. The atmega32u4 is the main chip on the Arduino Leonardo and the Teensy 2.0. These chips are the “main brains” on the development boards. Everything else on their respective boards is mostly additional circuitry that enables easy access to the pins and programming over USB.
What’s a Toolchain?
A software “toolchain” refers to a collection of software “tools” that, together, allow you to take C or C++ source code, turn it into an executable file that the microcontroller can understand, and upload it to the microcontroller’s onboard memory so that the microcontroller can run our program. In the case of Arduino, the toolchain is “all the complicated things that happen under the hood” when you press the upload button on the Arduino IDE up until our code starts running on the Arduino board.
For slightly longer-term original projects where we want to spin our own printed circuit board (PCB) populated with the atmega328p, in many cases it still makes sense to use many of the tools provided to us by the Arduino IDE. My reasoning for this judgement call is that I’d prefer not to write my own toolchain just to upload code to an Arduino board. In this sense, I’d like to program this custom Arduino-compatible PCB with code written in Arduino and compiled with the Arduino-provided toolchain.
Before we can take a home-made Arduino board and start uploading code to it through the same toolchain, though, we need to do some pre-configuration. First, we need to design and fab our original PCB that enables programming via the In-System-Programmer (ISP) pins on the chip. (I’ll cover how to design an Arduino-compatible PCB in another post.) Next, we need to properly configure the fuse bits.
What are these Fuse Bits and why do they matter?
Fuse bits are a concept hidden to us if we’re programming Arduino Boards through the IDE. If we’re making our own Arduino-compatible PCB, though, we need to know what they are and how they affect our board.
Fuse bits refer to programmable settings for the microcontroller that tell it information about its hardware configuration. While they’re not one-time-programmable it is possible to (more-or-less) brick your setup if you upload the wrong fuse bits. Put another way, since these fuse bits tell the microcontroller important things such as: “what is my clock source?” and “should I disable reprogramming?”–it is possible to tell the microcontroller that it’s configured in a way that doesn’t match its actual configuration, in which case it may or may not listen to you.
Rest assured, there are calculators, forums, and blog posts that can give us an idea of what fuse bits we want to set before we actually write them over.
Which fuse bits do I set?
Which fuse bits you’ll want to set depend on your configuration. These fuse bits give the chip information as to
- what clock source
- disable programming
- enable/disable clock prescaling for a slower processor speed
- enable/disable brownout detection to reset the device below certain input voltage supply thresholds
- set the starting address of program memory.
Which features to enable/disable are up to you and your configuration. Here’s some general guidelines:
- Disable brown-out detection unless you really want to reset your circuit below certain voltage thresholds.
- Keep the starting address of program memory at the default value.
- Enable programming (otherwise you wont be able to program the chip after setting these fuse bits.)
- Enable/Disable clock prescaling as you see fit. I generally put an external oscillator on my boards and disable prescaling unless I need to save power.
- Choose the clock source that you’re actually using, and give it 65 [ms] delay time unless you can be sure that the oscillator is stable and ready before that window when the system first boots up.
It’s worth double-checking that (a) programming is enabled and (b) the correct clock source is selected before writing over the fuse bits. If not, you may not be able to communicate with the chip and render it “unprogrammable” unless you change the current configuration to whatever you set it to via the most recent fuse bit settings.
Writing over the fuse bits
Before going any further, make sure you’ve got avrdude, the flash utility, installed. Luckily, if the Arduino IDE is installed, then you get avrdude with it for free. I’m using the command line on Linux for this one. (You can do this on Mac and Windows, also.) It’s a three-line incantation, one for each group of fuse bits. Assuming our desired lfuse, hfuse, and efuse are 0xe2, 0xd9, and 0xff respectively, our command-line input would look like this:
avrdude -c usbtiny -p atmega328p -U lfuse:w:0xe2:m
avrdude -c usbtiny -p atmega328p -U hfuse:w:0xd9:m
avrdude -c usbtiny -p atmega328p -U efuse:w:0xff:m
As each line is written, you’ll get a verification from avrdude. If you get something that says rc=-1, then avrdude can’t connect to your board for some reason. Either the power isn’t connected, the fuse bits are misconfigured to begin with, or there’s something wrong with the pcb design.
Keep in mind that these fuse bits are specific to the microcontroller. (i.e: An atmega32u4, the chip one on the Leonardo, will have different fuse bits from an atmega328p, the chip on the Uno.)
Atmega328p with Internal Oscillator:
For an 8-bit Arduino Clone using its internal oscillator, instead of a crystal (or external clock reference), the fuse bits are likely:
- lfuse: 0xe2
- hfuse: 0xd9
- efuse: 0xff
Specifically, the settings are:
- internal oscillator as clock source with 65 [ms] startup delay. (more info on the startup delay)
- no clock division
- default boot starting address
- enable programming over SPI
- disable brown-out detection
Atmega328p with External Crystal Oscillator:
For an 8-bit Arduino Clone using its internal oscillator, instead of a crystal (or external clock reference), the fuse bits are likely:
- lfuse: 0xff
- hfuse: 0xd9
- efuse: 0xff
Specifically, the settings are:
- external crystal oscillator as clock source with 65 [ms] startup delay (more info on the startup delay)
- no clock division
- default boot starting address
- enable programming over SPI
- disable brown-out detection
If you have need for brown-out detection, or any of the other settings for that matter, I’d recommend the AVR Fuse Calculator.