avr_fuse(3avr)

πŸ’– NAME πŸ’–

avr_fuse β€” bestie, this is the whole vibe of <avr/fuse.h>: Fuse Support, no cap πŸ’…


✨ SYNOPSIS ✨

okay so buckle up because this one is all about the ~Detailed Description~ and it's giving very much "read the whole thing or your chip gets the wrong fuses" energy 🫢

🌸 Detailed Description 🌸

πŸ¦‹ Introduction πŸ¦‹

The Fuse API is literally how you tell the compiler which fuse settings you want for the specific AVR device you're building for. Those settings get parked in a special section inside the ELF output file β€” and that happens after linking, just so you know the timeline bestie ✨

Programming tools can eat this up: they pull the fuse info out of the ELF file and figure out whether the fuses even need to be programmed before the Flash and EEPROM memories get programmed. Which is iconic, because it means one single ELF file can hold every single thing you need to program an AVR πŸ˜­πŸ‘‘

To actually use the Fuse API, you include <avr/io.h>, and that header is a girlboss and automatically includes both the individual I/O header file and the <avr/fuse.h> file. Those two other files? They provide everything you need to set the AVR fuses. obsessed. πŸŽ€

πŸ’œ Fuse API πŸ’œ

Every I/O header file must define the FUSE_MEMORY_SIZE macro, and it's set to the number of fuse bytes that exist in that AVR device. simple as that ✨

There's a new type, __fuse_t, defined as a structure. How many fields it has? Depends entirely on how many fuse bytes FUSE_MEMORY_SIZE says there are:

  • If FUSE_MEMORY_SIZE == 1, there's only a single field: byte, of type unsigned char.
  • If FUSE_MEMORY_SIZE == 2, there are two fields: low and high, of type unsigned char.
  • If FUSE_MEMORY_SIZE == 3, there are three fields: low, high, and extended, of type unsigned char.
  • If FUSE_MEMORY_SIZE > 3, there's a single field: byte, which is an array of unsigned char, and the size of that array is FUSE_MEMORY_SIZE.

Then there's a convenience macro, FUSEMEM β€” it's defined as a GCC attribute for a custom-named section of ".fuse". πŸ’…

And another convenience macro, FUSES β€” it declares a variable, __fuse, of type __fuse_t with the attribute that FUSEMEM defines. This variable is the whole point: it lets the end user set the fuse data without breaking a sweat 🧚

Note πŸ“Œ If a device-specific I/O header file has previously defined FUSEMEM, then FUSEMEM is not redefined. Same deal with FUSES: if a device-specific I/O header file already defined it, FUSES is not redefined.

Every AVR device I/O header file has a whole set of defined macros that spell out the actual fuse bits available on that device. And here's the twist, bestie β€” AVR fuses have inverted values: logical 1 means an unprogrammed (disabled) bit, and logical 0 means a programmed (enabled) bit. The macros for each individual fuse bit show this by doing 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. Then it gets inverted to represent logical values for a fuse memory byte.

To combine those fuse bit macros into one whole fuse byte, you 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 giving default values for each fuse byte that exists. 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 providing 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, fr fr. 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're compiling in C++, you cannot use the designated initializers, 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 β€” and listen, this part is important β€” there are a number of caveats you need to be aware of to use this API properly 😀

  • 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 gets its values from the initial variable assignment ONLY. This means you can NOT assign values to this variable inside functions β€” and the new values will not be put into the ELF .fuse section. πŸ’€
  • The global variable declared in the FUSES macro has two leading underscores, which means it's reserved for the "implementation" β€” meaning the library β€” so it will not conflict with a user-named variable. πŸ‘
  • You must initialize ALL fields in the __fuse_t structure. Why? Because the fuse bits in all bytes default to a logical 1 β€” that means unprogrammed β€” while normal uninitialized data defaults to all logical zeros. So it is vital that all fuse bytes are initialized, even with default data. If they aren't, then the fuse bits may not be programmed to the desired settings. 😭 this is the one that gets people, don't be that girl.
  • Be sure to have the -mmcu=device flag in your compile command line and your linker command line, so the correct device is selected and the correct I/O header file is included when you include <avr/io.h>.
  • 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 a lower address to a higher address, left to right. simple 😌


πŸ‘‘ Author πŸ‘‘

Generated automatically by Doxygen for AVR-LibC from the source code. (not a human author bestie, the robots did it πŸ€–βœ¨)


okay that's the whole page 🫢 this one came straight from AVR-LibC, Version 2.2.1, and it's filed under avr_fuse(3avr). go set your fuses correctly, ily, periodt πŸ’…πŸ’–πŸŽ€

πŸ“¦ where this page comes from