avr_fuse(3)
π avr_fuse π
okay bestie, welcome to the avr-libc manual page for avr_fuse, and no you're not lost, this is exactly where you're supposed to be π«Ά
β¨ NAME β¨
avr_fuse β the whole entire fuse situation, explained by someone who actually loves you
π SYNOPSIS π
Detailed Description
πΈ DETAILED DESCRIPTION πΈ
π¦ Introduction π¦
So. The Fuse API lets you, the user, specify the fuse settings for whatever specific AVR device you're compiling for. Those fuse settings get parked in a special section inside the ELF output file, and that happens after linking. Iconic behavior honestly.
Now here's the slay part: programming tools can take advantage of that fuse info sitting all cozy in the ELF file β they extract it and figure out whether the fuses need to be programmed before the Flash and EEPROM memories get programmed. Which also means a single ELF file can carry literally everything you need to program an AVR. One file, main character energy. π
To use the Fuse API, include the <avr/io.h> header file. That one automatically pulls in the individual I/O header file and the <avr/fuse.h> file. Those other two files are the ones handing you everything you need to set the AVR fuses. Teamwork, fr fr.
π Fuse API π
Every I/O header file has to define the FUSE_MEMORY_SIZE macro, and it's defined as the number of fuse bytes that exist in the AVR device. That's the vibe check for how much fuse we're working with.
A new type, __fuse_t, is defined as a structure. How many fields that structure has? Determined by how many fuse bytes are in the FUSE_MEMORY_SIZE macro. So:
- If
FUSE_MEMORY_SIZE == 1, there is only a single field:byte, of typeunsigned char. - If
FUSE_MEMORY_SIZE == 2, there are two fields:low, andhigh, of typeunsigned char. - If
FUSE_MEMORY_SIZE == 3, there are three fields:low,high, andextended, of typeunsigned char. - If
FUSE_MEMORY_SIZE > 3, there is a single field:byte, which is an array ofunsigned charwith the size of the array beingFUSE_MEMORY_SIZE.
A convenience macro, FUSEMEM, is defined as a GCC attribute for a custom-named section of .fuse. Little cutie doing important work.
A convenience macro, FUSES, is defined that declares a variable, __fuse, of type __fuse_t with the attribute defined by FUSEMEM. This variable is what lets you, the end user, easily set the fuse data. So easy. So kind of the library tbh.
Note: If a device-specific I/O header file has previously definedFUSEMEM, thenFUSEMEMis not redefined. If a device-specific I/O header file has previously definedFUSES, thenFUSESis not redefined. Respect the first one to speak, periodt.
Each AVR device I/O header file has a set of defined macros that specify the actual fuse bits available on that device. And here's the twist that gets everybody π β the AVR fuses have inverted values: logical 1 for an unprogrammed (disabled) bit, and logical 0 for a programmed (enabled) bit. The defined macros for each individual fuse bit represent this in their definition by a bit-wise inversion of a mask. For example, the FUSE_EESAVE fuse in the ATmega128 is defined as:
#define FUSE_EESAVE ~_BV(3)
Note: The _BV macro creates a bit mask from a bit number. It is then inverted to represent logical values for a fuse memory byte.
To combine the fuse bits macros together to represent a whole fuse byte, use the bitwise AND operator, like so:
(FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN)
Each device I/O header file also defines macros that provide default values for each fuse byte that is available. LFUSE_DEFAULT is defined for a Low Fuse byte. HFUSE_DEFAULT is defined for a High Fuse byte. EFUSE_DEFAULT is defined for an Extended Fuse byte.
If FUSE_MEMORY_SIZE > 3, then the I/O header file defines macros that provide default values for each fuse byte like so: FUSE0_DEFAULT FUSE1_DEFAULT FUSE2_DEFAULT FUSE3_DEFAULT FUSE4_DEFAULT ....
π API Usage Example π
Putting all of this together is easy, bestie. Using C99's designated initializers:
#include <avr/io.h>
FUSES =
{
.low = LFUSE_DEFAULT,
.high = (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN),
.extended = EFUSE_DEFAULT,
};
int main(void)
{
return 0;
}
Or, using the variable directly instead of the FUSES macro:
#include <avr/io.h>
__fuse_t __fuse __attribute__((section (".fuse"))) =
{
.low = LFUSE_DEFAULT,
.high = (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN),
.extended = EFUSE_DEFAULT,
};
int main(void)
{
return 0;
}
If you are compiling in C++, you cannot use the designated intializers so you must do:
#include <avr/io.h>
FUSES =
{
LFUSE_DEFAULT, // .low
(FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN), // .high
EFUSE_DEFAULT, // .extended
};
int main(void)
{
return 0;
}
However there are a number of caveats that you need to be aware of to use this API properly. Listen up, this is the part where people get got π
Be sure to include <avr/io.h> to get all of the definitions for the API. The FUSES macro defines a global variable to store the fuse data. This variable is assigned to its own linker section. Assign the desired fuse values immediately in the variable initialization.
The .fuse section in the ELF file will get its values from the initial variable assignment ONLY. This means that you can NOT assign values to this variable in functions and the new values will not be put into the ELF .fuse section. Do not try it. It will not work. It's giving "I ignored the manual and now I'm confused" π
The global variable is declared in the FUSES macro has two leading underscores, which means that it is reserved for the 'implementation', meaning the library, so it will not conflict with a user-named variable. Nice of it, honestly.
You must initialize ALL fields in the __fuse_t structure. This is because the fuse bits in all bytes default to a logical 1, meaning unprogrammed. Normal uninitialized data defaults to all logical zeros. So it is vital that all fuse bytes are initialized, even with default data. If they are not, then the fuse bits may not programmed to the desired settings. That's the drama. Don't let it be you.
Be sure to have the -mmcu=device flag in your compile command line and your linker command line to have the correct device selected and to have the correct I/O header file included when you include <avr/io.h>. Both lines. Yes, both. I said what I said. β¨
You can print out the contents of the .fuse section in the ELF file by using this command line:
avr-objdump -s -j .fuse <ELF file>
The section contents shows the address on the left, then the data going from lower address to a higher address, left to right.
π«Ά Author π«Ά
Generated automatically by Doxygen for avr-libc from the source code.
and that's the whole page, bestie π huge thanks to avr-libc, Version 2.1.0, dated Sat Jan 29 2022 β she's a legend, go forth and initialize ALL your fuse bytes, no cap ππ π
π¦ where this page comes from
- fedora 43: avr-libc 2.2.1-3.fc43 Β· BSD-3-Clause
- fedora 44: avr-libc 2.2.1-5.fc44 Β· BSD-3-Clause
- opensuse-leap 16.0: avr-libc 2.1.0-bp160.1.33 Β· BSD-3-Clause
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms