ATtiny85 Fuses Explained
2023-11-20
In the world of microcontrollers, a fuse is not only a hardware device that protects circuitry from electrical overload, it's also a concept of low-level configuration settings stored in non-volatile memory. These fuses are typically used to set hardware configuration parameters like clock sources and boot sequences, things that are essential when initializing the microcontroller.
Once set, these fuses remain set until explicitly changed, even if new firmware is flashed into the program memory.
Fuses in the ATtiny85
The ATtiny85 works exactly like this. It has a number of fuses to control essential hardware configuration. The parameters are stored in configuration bits grouped into fuse bytes. Instead of talking about the various bits being "set", the word "programmed" is often used. The reason for this is that a set bit typically refers to a value or 1, while a programmed bit actually has a value of 0. This is slightly unintuitive, but has to do with the fact that the type of memory used is filled with ones when erased, so that the default, "empty" state of a bit is 1. With that out of the way, let's go through the different fuse bytes and their configuration bits.
Low fuse byte
The low fuse byte determines basic settings mostly related to timing. The different bits represent the following parameters:
Bit(s) | Name | Function |
---|---|---|
0-3 | CKSEL | Clock source select. These bits define the clock source to be used by the microcontroller. |
4-5 | SUT | Startup time. These two bits define the startup time of the microcontroller. A short startup time is great under optimal circumstances, but with a shaky power supply or an external clock source requiring time to stabilize, it might be necessary to take it easy. The exact timing of the startup depends on the clock source selected. |
6 | CKOUT | Clock out. If programmed, the system clock signal will be output on the CLKO pin, which is PB2 on the ATtiny85. This overrides any other use of that pin. |
7 | CKDIV8 | Clock divide. If programmed, a clock divider will be activated, dividing the speed of the system clock by 8. To read more about the clock speed, please see my post Setting the ATtiny85 clock speed. |
High fuse byte
The high fuse byte contains more advanced configuration parameters than the low one. The parameters are:
Bit(s) | Name | Function |
---|---|---|
0-2 | BODLEVEL | Brown-out detection level. These bits control the voltage level threshold for the brown-out reset. |
3 | EESAVE | If programmed, the EEPROM will remain intact when the chip erase command is run, and only wiping out the Flash (program) memory and lock bits (see Lock bits below). |
4 | WDTON | Watchdog timer always on. If programmed the watchdog timer will be running at all times. If not programmed, the watchdog timer will be disabled on startup and can be enabled, configured, and disabled by the firmware at runtime. |
5 | SPIEN | Serial programming and data downloading enable. When programmed, the SPI interface for programming the ATtiny is enabled. Luckily this is the default state. Unprogramming the bit disables SPI, making it impossible to flash new code into the ATtiny. |
6 | DWEN | Debug wire enable. If programmed, it enables the DebugWIRE interface for debugging, if you're lucky enough to have access to the hardware and software needed to do so. |
7 | RSTDISBL | External reset disable. If programmed, the external reset functionality is disabled, making the reset pin available for other uses. On an eight-pin microcontroller that normally has five pins available for GPIO, getting an extra one can be very tempting. Before tossing the reset functionality out the window, please note that it's used during SPI programming, which means new code can't be flashed into the ATtiny once this bit is programmed. The only way to make it receptive to new code again is using a high voltage programmer. |
Extended fuse byte
In addition to the low and the high fuse bytes there's the extended fuse byte, which only defines a single parameter:
Bit(s) | Name | Function |
---|---|---|
0 | SELFPRGEN | Self-programming enable. When programmed, the flash (program) memory will be writable by the firmware in the ATtiny during operation, offering endless opportunities to shoot yourself in the foot. |
Lock bits
The lock bits aren't strictly fuses, but share some similarities. They are used to enable security features in the ATtiny85. There are two lock bits that can represent one of three lock bit protection modes together:
- No lock functionality enabled
- Disable writing to the program memory and EEPROM in both serial and high-voltage programming modes, and prevent the fuses from being modified.
- Like mode 2, but also prevents reading the program memory and EEPROM in both serial and high-voltage programming modes.
Since the lock bits can only be reset using the chip erase command, which also erases the program memory and (depending on the setting of EESAVE, see above) the EEPROM, this effectively protects the code and data in the ATtiny85 from ending up in the wrong hands even if they have access to the chip.
Writing fuses
The fuses related to clock speed (CKSEL and CKDIV8) can be set using the Arduino IDE. Doing so utilizes the avrdude command line tool under the hood, and to set the other fuses you have to use this tool manually.
To calculate the values for the different fuse arguments to avrdude you can use a fuse calculator. They don't just calculate the byte values for you, but also present them as full arguments ready to be passed to avrdude. Here's an example of one such set of arguments, which represents the default ATtiny85 configuration:
-U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
The -U
command, which is the memory operation specification, is included once for each fuse byte (low, high and extended) and once for the lock byte. The other parts of the argument consists of w
for write, the value to be written, and m
for immediate mode, which means the value to be written is specified in the argument itself.
The only additional arguments needed are the ones specifying programmer and chip settings. A nice little shortcut is to peek at the exact avrdude command issued by the Arduino IDE. That way you know you're getting programmer parameters and a chip configuration that works. To do this, go into Preferences and enable verbose output for the compile command, then set the clock speed and look for the avrdude command in the rather lengthy output. In my case this looked like this:
"/Users/martin/Library/Arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/bin/avrdude" "-C/Users/martin/Library/Arduino15/packages/arduino/tools/avrdude/6.3.0-arduino17/etc/avrdude.conf" -v -v -v -v -pattiny85 -cusbtiny
CMD: \[50 08 00 00] \[00 50 08 ff]
We can now combine the relevant parts from the two sources into a full command that does what we want:
"[...]/avrdude" "-C[...]/avrdude.conf" -v -pattiny85 -cusbtiny -U lfuse:w:0x62:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m -U lock:w:0xFF:m
Run it, and hopefully you're good to go.
Reading fuses
If you just want to check the configuration of an ATtiny you can remove all the write instructions from the command above and run something like this:
[...]/avrdude -C[...]/avrdude.conf" -pattiny85 -cusbtiny
Which will spit out a nice little overview:
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e930b (probably t85)
avrdude: safemode: Fuses OK (E:FF, H:DF, L:62)
avrdude done. Thank you.
Here you see both the device signature as well as the values for the low, high and extended fuse bytes.
Getting out of trouble
As mentioned repeatedly above, some fuse settings puts the ATtiny85 in a state where it can't be re-programmed and/or reached again without a high-voltage programmer. This is sort of a back door built into the ATtiny, whereby applying 12V to the reset pin with precise timing and then sending serial commands through a few other pins chips can be brought back from the dead. I'll write more about it in the future, but for now I'll refer to this excellent blog post by Eric Draken.
Happy tinkering!