====== ATmega328P Arduinos and custom fuse settings ======
Programming your device with the code you want to execute, whether with a bootloader or directly with a programmer, is one layer of programming your device. Another layer is the so-called //fuses// inside an ATmega microcontroller.
Fuses are programmable nonvolatile registers that are used to configure things like what the clock source and rate for the microcontroller is, whether brown-out detection is enabled, whether you're using a bootloader and if so how much flash will be reserved for a bootloader. Programming fuses can be scary because if you don't do it right, you can brick the micro. However, you can gain finer-grained control over your project by setting the micro's fuses yourself.
In this article, I will investigate using fuses to configure a 3.3V/8MHz Pro Mini to set a brown-out detection level that is different from what the standard 3.3V/8MHz Pro Mini uses because this happens to be the use case that has driven me to learn about this. However, what I cover should be adaptable to other Arduinos that use other ATmega processors. It may not be very relevant to other micro families.
===== The hard way or the hard way? =====
There are a couple ways we can go about setting the fuses on an ATmega328P Arduino. We can use a programmer along with the command-line ''avrdude'' utility to explicitly set fuse values, or we can create an additional Arduino board specification with the desired fuse settings, then burn a bootloader using the Arduino IDE, which will also set the fuses for us.
Both create different kinds of difficulties. To keep things as "Arduino" as possible, in what follows I am going to pursue the second approach. You can think of this process in terms of four questions, which I try to answer below. After that I walk through a case study.
===== Four general questions =====
==== 1. Where do I put new board specs? ====
Specs for Arduino boards, including fuse bit settings, are found in various ''board.txt'' files, themselves found in various ''hardware'' folders. This can get a little confusing as there are number of these folders and files that you'll find on your computer. I'll try to go through the differences below.
=== The install bundle ===
A ''board.txt'' file is included in the Arduino package you installed. You'll find it at
/path-where-you-installed-arduino/hardware/arduino/avr
This file has board descriptions written in a syntax the IDE understands for default boards Arduino supported at the time the package was released. This is //not// where you will add descriptions for your custom board or existing board with modified fuse settings, but it's good place to look to begin to grok the syntax.
When you open this ''boards.txt'' file in a text editor, look for the line:
uno.name=Arduino/Genuino Uno
Following that are a number of lines starting with ''uno.'' These are all parameters that describe the specifics of the Arduino/Genuino Uno board. Knowing this, you can probably make at least partial sense of some of the parameters. The lines that begin ''uno.bootloader.'' are where the fuse settings are given. You can see hexadecimal entries for the low bits, high bits, and extended bits along with the lock and unlock bits.
If you do some more searching, you'll find an entry for
pro.name=Arduino Pro or Pro Mini
The syntax here is a little more elaborate because the Pro and Pro Mini come in different flavors: they have been made with both ATmega328P and ATmega168 micros, and in 5V/16MHz and 3.3V/8MHz versions. Bootloader parameters that apply to all Pro Minis are prefixed with ''pro.bootloader.'' Those that apply to a specific version are prefixed with ''pro.menu.cpu..bootloader.''
=== Your profile ===
You will find another ''boards.txt'' file if you look in
/your-Arduino-profile-folder/packages/arduino/hardware/avr/
This is //not// your Arduino sketch folder. Rather it's where global application configuration information is stored. You can see the path to your profile folder towards the end of the Preferences dialog box. The Arduino profile folder on Linux systems is ''/home//.arduino15''.
The ''boards.txt'' here is one that the Aurdino IDE actually uses. It might be a copy of the ''boards.txt'' that shipped with the installation bundle, or it might have been subsequently updated by the IDE. If you have added additional boards using the IDE, you are likely to find additional folders for their architectures under ''/your-Arduino-profile-folder/packages'' or its subfolders.
It's my understanding that you can add your custom board descriptions in these areas, but the prevailing advice is not to. This area is designed to be managed by the IDE. If you make additions here, the IDE may overwrite them or you might corrupt the ability for the IDE to successfully add/remove/update boards. You //can// safely add new boards here if you go through a somewhat [[https://www.hackster.io/wallarug/arduino-ide-creating-custom-boards-89f7a6|cumbersome process]] involving a few additional files. But there seems to be an easier way.
=== Your sketch folder ===
The following method works on the version of Arduino I used at the time or writing this: 1.8.8. I don't know whether it can be relied on to hang around for a while or has been deprecated.
So, until I'm advised this is a horrible idea, the way I recommended you add your own custom board descriptions is to create a folder called ''hardware'' in the folder where your sketches are located and add the needed files there. I walk through the details in the case study that follows.
==== 2. How do I specify new board characteristics? ====
There is a //lot// of arcane knowledge that applies to writing custom Arduino ''board.txt'' entries, and I haven't found a source that documents it thoroughly. So my best advice is to study the ''boards.txt'' found at /path-where-you-installed-arduino/hardware/arduino/avr/
to get as familiar with the syntax. What interests us here are the entries for ''low_fuses'', ''high_fuses'', and ''extended_fuses'' (and to a certain extent ''unlock_bits'' and ''lock_bits''. The values for these entries will change depending on what features and behavior you want to enable or disable.
Another good resource to look through is the files found in the breadboard-1-6-x.zip archive found under "Minimal Circuit (Eliminating the External Clock)" at [[https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard|From Arduino to a Microcontroller on a Breadboard]]
==== 3. What specs do I want? ====
This article covers making a variant of an existing board where only the fuses have been changed. The relevant parameters in the ''boards.txt'' specs will have ''low_fuses'', ''high_fuses'', and ''extended_fuses'' in their names. The values associated with those parameters are hexadecimal.
Exactly which fuses do what and how is an article in and of itself. I haven't written one of those, so you might want to check out [[http://www.martyncurrey.com/arduino-atmega-328p-fuse-settings/|this article]] by Martyn Currey for a friendly description. You might also find online fuse calculators from [[https://eleccelerator.com/fusecalc/fusecalc.php?chip=atmega328p|Eleccelerator]] and [[http://www.engbedded.com/fusecalc/|Engbedded]] to be helpful. But be forewarned, there's no guarantee these calculators are bug-free. The ultimate reference is the [[http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|ATmega328P datasheet]].
When you're figuring out what your fuse values should be, don't be afraid to reference back to the original settings in whatever ''board.txt'' file entry you're basing things off.
There is one additional caveat here: You can only change fuses that are compatible with what the bootloader you plan to use will let you do. Again there isn't a lot of documentation here, so if you've done everything right but things still don't work, this might be the reason. The solution to this is to compile a custom bootloader, which is well beyond the scope of this piece.
==== 4. How do I burn a bootloader? ====
This too is a pretty big topic, but the TL;DR is: hook up a programmer to your board, select the right board and programmer, then do a //Tools > Burn Bootloader//. This [[https://learn.sparkfun.com/tutorials/installing-an-arduino-bootloader/all|Sparkfun tutorial]] is a good reference if you need more hand-holding.
With this covered, let's get busy.
===== Reading fuse settings =====
Reading an Atmega328P's fuse settings is a lot easier than writing them. So we start with this. We will also need this to confirm that everything had gone to plan when we burn the new settings.
With a USBtinyISP (or theoretically any supported programmer) connected to your Atmega328P target, use the following command to get a reading of how the fuses are configured.((Adapted from Shawn Hymel's [[https://shawnhymel.com/622/quick-tip-reading-fuse-bits-in-an-arduino/|Quick Tip: Reading Fuse Bits in an Arduino]].)) This assumes you are using a Linux-like CLI and do not have ''avrdude'' installed as a global command.
$ /path/to/avrdude -C /path/to/avrdude.conf -c usbtiny -p m328p -U lfuse:r:-:i -v
The ''/path/to/avrdude'' is typically:
/path-to-where-you-installed-arduino/hardware/tools/avr/bin/avrdude
The ''/path/to/avrdude.conf'' is typically:
/path-to-where-you-installed-arduino/hardware/tools/avr/etc/avrdude.conf
Newer versions of these might be found under
/your-Arduino-profile-folder/packages/arduino/tools/avrdude
The output of the above for an Arduino Uno looks like:
# preamble, preamble, preamble ...
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as DE
avrdude: safemode: efuse reads as FD
avrdude: safemode: Fuses OK (E:FD, H:DE, L:FF)
avrdude done. Thank you.
For a stock 3.3V/8MHz Pro Mini it's:
# preamble, preamble, preamble ...
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as DA
avrdude: safemode: efuse reads as FD
avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF)
avrdude done. Thank you.
For the nitty-gritty breakdown on the command see [[https://www.nongnu.org/avrdude/user-manual/avrdude_4.html|this page]].
----
===== Case study =====
In the section, I walk through the process of creating a custom version of a 3.3V/8MHz Pro Mini where the nominal brown-out detection (BOD) threshold is changed from the stock 2.7V to 1.8V.
If you reduce the BOD threshold or turn off BOD entirely, don't run programs uploaded by the bootloader at low voltages. See [[arduino:arduino_without_a_bootloader#cursed_is_the_bootloader|this]] for an explanation why.
==== Calculating new fuse settings ====
The only fuse settings I want to change are those that affect the BOD. These are defined in the lowest three bits of the extended fuse bits. The upper five bits are not used/reserved.
^ extended fuse bit ^ function ^
| 7 | not used/reserved |
| 6 | not used/reserved |
| 5 | not used/reserved |
| 4 | not used/reserved |
| 3 | not used/reserved |
| 2 | BODLEVEL2 |
| 1 | BODLEVEL1 |
| 0 | BODLEVEL0 |
^ BODLEVEL2, 1, 0 ^ VBOT ^
| 111 | BOD disabled |
| 110 | 1.8V((The ATmega328P datasheet indicates this state is "reserved", but several other sources indicate it sets the VBOT to 1.8V, including Microchip's own [[https://microchipdeveloper.com/8avr:bod|Developer Help]].)) |
| 101 | 2.7V |
| 100 | 4.3V |
| 0xx | not used/reserved |
Now is a good time to mention that the ATmega328P fuses are //active low//, meaning that a 0 sets ("programs") a bit, and a 1 unsets ("unprograms") it. This can sometimes lead to hilarious misunderstandings, so be careful.
So, to set the brown-out threshold to 1.8V I want the last three bits of the extended fuse byte to be ''b110'' and the entire byte to be ''b11111110'' or ''0xFE''. [[https://eleccelerator.com/fusecalc/fusecalc.php?chip=atmega328p&LOW=FF&HIGH=DA&EXTENDED=FE&LOCKBIT=FF|Online fuse calculators]] agree, so it's probably right.
==== Preparing the new board files ====
=== Folder structure ===
Inside my Arduino sketches folder (''/home//Arduino'' in Debian), I created the following folder structure:
/
+-- hardware/
+-- mfkcustom/
+-- avr/
+-- bootloaders/
+-- atmega/
+-- variants/
''mfkcustom'' is where I will put all my own custom board definitions. The remaining folder structure is needed by Arduino to make things work.
=== boards.txt ===
Inside the ''avr'' folder, create a text file called ''boards.txt'' for your board definition(s). I based the board definition here on the specs for the standard 3.3V Pro Mini and the format of ''boards.txt'' found in the breadboard-1-6-x.zip archive found under “Minimal Circuit (Eliminating the External Clock)” at [[https://www.arduino.cc/en/Tutorial/ArduinoToBreadboard|From Arduino to a Microcontroller on a Breadboard]]:
##############################################################
# Arduino Pro Mini 3.3V/8Mhz, 1.8V BOD (ATmega328P only)
mfkpro18bod.name=Pro Mini 3.3V/8Mhz, 1.8V BOD, ATmega328P
mfkpro18bod.upload.protocol=arduino
mfkpro18bod.upload.maximum_size=30720
mfkpro18bod.upload.maximum_data_size=2048
mfkpro18bod.upload.speed=57600
mfkpro18bod.bootloader.low_fuses=0xFF
mfkpro18bod.bootloader.high_fuses=0xDA
mfkpro18bod.bootloader.extended_fuses=0xFE
mfkpro18bod.bootloader.file=atmega/ATmegaBOOT_168_atmega328_pro_8MHz.hex
mfkpro18bod.bootloader.unlock_bits=0x3F
mfkpro18bod.bootloader.lock_bits=0x0F
mfkpro18bod.build.mcu=atmega328p
mfkpro18bod.build.f_cpu=8000000L
mfkpro18bod.build.core=arduino:arduino
mfkpro18bod.build.variant=arduino:eightanaloginputs
mfkpro18bod.build.board=AVR_PRO
mfkpro18bod.bootloader.tool=arduino:avrdude
mfkpro18bod.upload.tool=arduino:avrdude
##############################################################
I didn't create an option to use an ATmega168 instead of an ATmega328P because I wanted to keep the syntax as simple as possible.
Some things to note here:
* Your board's identifier (''mfkpro18bod'' here) needs to be unique throughout the installation.
* Some parameter values are prefaced with ''arduino:'' to indicate (I think) that global Arduino files and tools that define these should be used rather than a local ones.
* The parameter ''mfkpro18bod.bootloader.file'' refers to a hex file that's is in a local folder called ''atmega''. That's the folder at /hardware/mfkcustom/avr/bootloaders/atmega/
created above. We'll copy in that hex file next.
=== Bootloader ===
I will be using the same bootloader that is used for the standard 3.3V/8MHz Pro Mini. Standard bootloader files for ATmega-based Arduinos are found in:
/path-to-where-you-installed-arduino/packages/arduino/hardware/avr//bootloaders/atmega/
Copy ''ATmegaBOOT_168_atmega328_pro_8MHz.hex'' there to:
/hardware/mfkcustom/avr/bootloaders/atmega/
and make sure the name matches with what you specified in ''boards.txt''.
==== Burn the bootloader and test ====
Whether you plan to upload your sketches with a USB to serial converter (i.e., using the bootloader) or directly [[arduino:arduino_without_a_bootloader|using a programmer]], you still need to burn the bootloader to set the fuses. So let's burn and test it.
=== Burn it ===
* Create a new project consisting of a simple sketch (e.g. blink.)
* Under //Tools > Board// you should see an entry for "Pro Mini 3.3V/8Mhz, 1.8V BOD, ATmega328P". Select it.
* As a sanity check, Verify/Compile the project. No errors is good.
* Hook up a programmer to a 3.3V/8MHz Pro Mini and select the programmer you're using under //Tools >Programmer//.
* Plug the programmer into your computer
* Burn the bootloader by executing //Tools > Burn Bootloader//.
=== Test it ===
* With the programmer still plugged in, follow the instructions above to read the fuse settings. You should see:# preamble, preamble, preamble ...
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as DA
avrdude: safemode: efuse reads as FE
avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF)
avrdude done. Thank you.
Verify that the extended fuse setting has changed to ''0xFE''.
* Unplug the programmer and hook up a USB to serial converter.
* Upload your simple sketch to the Pro Mini to see if it works as expected.
If you reduce the BOD threshold or turn off BOD entirely, don't run programs uploaded by the bootloader at low voltages. To run a sketch at low voltage you should upload your sketch directly using a programmer. See [[arduino:arduino_without_a_bootloader#cursed_is_the_bootloader|this]] for an explanation why.
==== Upload with programmer and test ====
After burning the bootloader to set the fuses, [[https://mithatkonar.com/wiki/doku.php/arduino/arduino_without_a_bootloader#case_3pro_mini_33v8mhz_with_usbtinyisp|hook up your programmer]]. Be sure you have set both the board and programmer to the correct values under //Tools//. Then upload your simple test sketch with //Sketch > Upload Using Programmer//. Your sketch should upload and start running (possibly only after you have disconnected the programmer).
Follow the instructions above to read the fuse settings. You should see:
# preamble, preamble, preamble ...
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as DA
avrdude: safemode: efuse reads as FE
avrdude: safemode: Fuses OK (E:FD, H:DA, L:FF)
avrdude done. Thank you.
Confirm that the extended fuse is still ''0xFE''.
You can now connect the Pro Mini to a variable supply to see whether it works as expected below 2.7V. It may not work all the way down to 1.8V though, but because you're not using a bootloader, you won't run the risk of corrupting the program memory.