How to set FUSES

I used avr/fuse.h as instruction manual:


    If FUSE_MEMORY_SIZE > 3, there is a single field: byte, which is an array
    of unsigned char with the size of the array being FUSE_MEMORY_SIZE.
    ……

    If you are compiling in C++, you cannot use the designated intializers so
    you must do:

    \code
    #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;
    }
    \endcode

I tested it:

// TestFuses.ino

FUSES = {
  0x00,  // WDT: off
  0x04,  // BOD: level 1.8V, active enabled
  0x01,  // OSCCFG: no lock, 16MHz
  0x00,  // reserved
  0x00,  // reserved
  0xC9,  // SYSCFG0: no CRC, pin = RESET, EESAVE
  0x00,  // SYSCFG1: no SetUpTime
  0x00,  // APPEND
  0x00,  // BOOTEND
};

    int main(void)
    {
        return 0;
    }

It compiled just fine, but uploading failed:

avrdude: jtagmkII_initialize(): Cannot locate "flash" and "boot" memories in description
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.15s

avrdude: Device signature = 0x1e9651 (probably m4809)
avrdude: erasing chip
avrdude: reading input file "C:\Users\xxxxx\AppData\Local\arduino\sketches\9A1467BDD7AD688E27928646F792363D/TestFuses.ino.hex"
avrdude: ERROR: address 0x820009 out of range at line 15 of C:\Users\xxxxx\AppData\Local\arduino\sketches\9A1467BDD7AD688E27928646F792363D/TestFuses.ino.hex
avrdude: read from file 'C:\Users\xxxxx\AppData\Local\arduino\sketches\9A1467BDD7AD688E27928646F792363D/TestFuses.ino.hex' failed

avrdude done.  Thank you.

Failed uploading: uploading error: exit status 1

I can recognize the fuse-bytes in my disassembler:

What did I do wrong?

Hi,

Example:
avrdude -c jtag2updi -P com9 -p m4809 -U fuse5:w:0xC9:m

Example read fuse:

avrdude -c jtag2updi -P com9 -p m4809 -U fuse0:r:FUSE_0.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse1:r:FUSE_1.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse2:r:FUSE_2.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse4:r:FUSE_4.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse5:r:FUSE_5.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse6:r:FUSE_6.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse7:r:FUSE_7.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U fuse8:r:FUSE_8.txt:h
avrdude -c jtag2updi -P com9 -p m4809 -U lock:r:FUSE_LOCK.txt:h

It's not about "flash" and "boot", it's known, avrdude looks for these, but Nano Every doesn't need them, so they are not in the compiled program.

I can find something similar in my output:

Sketch uses 194 bytes (0%) of program storage space. Maximum is 49152 bytes.
Global variables use 0 bytes (0%) of dynamic memory, leaving 6144 bytes for local variables. Maximum is 6144 bytes.
Performing 1200-bps touch reset on serial port COM4
"C:\Users\hanss\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/bin/avrdude" "-CC:\Users\hanss\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino17/etc/avrdude.conf" -v -V -patmega4809 -cjtag2updi -PCOM4  -b115200 -e -D "-Uflash:w:C:\Users\hanss\AppData\Local\arduino\sketches\9A1467BDD7AD688E27928646F792363D/TestFuses.ino.hex:i" "-Ufuse2:w:0x01:m" "-Ufuse5:w:0xC9:m" "-Ufuse8:w:0x00:m" {upload.extra_files}

But it's output. I apparently need something in the input that I don't know about yet.

Hi,

See post 3 or your output.
r ... read
w ... write
Please check the avrdude manual.

I've got an Arduino Nano Every. I've got the Arduino IDE 2.3.8. I don't have an avrdude manual.

Hi,

It would be nice if you took the initiative.
https://github.com/avrdudes/avrdude/
You can also find manuals for older versions.
You can update avrdude within the IDE or save the latest version somewhere else.

To see it simple: I bought an original Arduino board. Arduino is supposed to support it. As for the fuses, Arduino offers support in the form of avr/fuses.h (#included automatically) probably thinking that it's sufficient.

The numbers for the fuses appear in the .ino.hex file, apparently the compiler processes them. The transfer of that file to the board is handled by the Arduino Integrated Development Environment, I should have no direct business with the third party that is invoked by the IDE, but that third party is where things go wrong.

Even then, the description in avr/fuses.h is clear. If that's not enough, something should be added.

Having said that, I will dive into the mentioned document, see what I can find.

Hi,

Actually, that should be in the Boards.txt file. Make sure to create a backup first. To this day, I still don't know what fuses_4809.bin does.

C:\Users\xyz\AppData\Local\Arduino15\packages\arduino\hardware\megaavr\1.8.8...

What exactly do you want to change in the fuses?

Indeed the fuses are specified in the boards.txt file.

The Nano Every is best used with the MegaCoreX core file (available through the boards manager) and not the Arduino megaAVR core. It has more features and is better maintained.

There is good access to the fuse settings in the Tools menu. You don't need to work with boards.txt or get into avrdude.

Tried MegaCorex:

// TestFuses_MegaCorex

FUSES = {
  0x00,  // WDT: off
  0x04,  // BOD: level 1.8V, active enabled (selected in Tools: 2.6V)
  0x01,  // OSCCFG: no lock, 16MHz (selected in Tools: internal 16 MHz)
  0x00,  // reserved
  0x00,  // reserved
  0xC9,  // SYSCFG0: no CRC, pin = RESET, EESAVE
  0x00,  // SYSCFG1: no SetUpTime
  0x00,  // APPEND
  0x00,  // BOOTEND
};

int main(void) {
  CCP = CCP_IOREG_gc;
  CLKCTRL.MCLKCTRLB = 0;
  Serial.begin(9600);
  Serial.println("FUSES");
  register8_t* ptr = &FUSE.WDTCFG;
  for (uint8_t i = 0; i < 9; i++) {  // 9 registers
    uint8_t reg = *(ptr + i);
    if (reg < 16) Serial.print("0x0");
    else Serial.print("0x");
    Serial.println(reg, HEX);
  }
  Serial.flush();
  return 0;
}

And this is what I got in Serial Monitor:

FUSES
0x00
0x54
0x01
0xFF
0x00
0xC9
0x06
0x00
0x00

Apparently MegaCoreX sets the fuses to what's selected in Tools, and to some defaults, not what is given in the program.

This is in fuses_4809.bin:

But I guess it's not what is used.

As to what I want: see post #1

Also in general: why can't I pick and choose the fuses I want? By the way: I do appreciate defaults usually.

Hi,

I'm familiar with the content. However, I can't correctly map the values to any fuses. The only recognizable matches are 0x01, 0xFF, and 0x00. The rest is unclear to me.
Why do you want to change the fuses?

Apparently MegaCoreX sets the fuses to what's selected in Tools, and to some defaults, not what is given in the program.

I think setting the fuses at runtime in code uses the _PROTECTED_WRITE macro because
Fuse registers are protected by the Configuration Change Protection (CCP) mechanism. There needs to be a reset after the fuse change.

For example

// Write fuse2 (OSCCFG) to select 16 MHz
_PROTECTED_WRITE(FUSE.OSCCFG, 0x01);
// trigger software reset
_PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm); 

Claude AI tells me this with more detail

Writing Fuses from C++

The fuse registers are accessible via the FUSE struct defined in the AVR headers:

#include <avr/io.h>

// Write fuse2 (OSCCFG) to select 16 MHz
_PROTECTED_WRITE(FUSE.OSCCFG, 0x01);

The critical detail is _PROTECTED_WRITE(). Fuse registers are protected by the Configuration Change Protection (CCP) mechanism — you can't just assign to them directly. The macro handles the required timed write sequence: it writes the unlock signature 0xD8 to the CCP register, which opens a 4-cycle window during which the protected register can be written.


The Full FUSE Struct

The named members map directly to the hardware registers:

FUSE.WDTCFG    // fuse0 - watchdog
FUSE.BODCFG    // fuse1 - brown-out detector
FUSE.OSCCFG    // fuse2 - oscillator/clock
FUSE.SYSCFG0   // fuse5 - UPDI pin, CRC, reset
FUSE.SYSCFG1   // fuse6 - startup time
FUSE.APPEND    // fuse7 - app code end
FUSE.BOOTEND   // fuse8 - boot section end

Important Caveats

Changes take effect after reset. Fuse writes don't take effect immediately — the new values are latched on the next power-on or reset. So writing FUSE.OSCCFG in your code and expecting the clock to change on the next line won't work. You'd need to trigger a software reset afterward:

_PROTECTED_WRITE(FUSE.OSCCFG, 0x01);
_PROTECTED_WRITE(RSTCTRL.SWRR, RSTCTRL_SWRE_bm); // trigger software reset

SYSCFG0 (fuse5) is still dangerous. The same warning applies as with avrdude — if you accidentally set the UPDI pin to GPIO mode from running code, you'll lock yourself out. Be very careful with this one.

The data sheet indicate that the fuses cannot be set via code:

Fuses in the 4809 can't be set at runtime. Don't listen to AI.

I found something remarkable in TestFuses.ino.map: it states addresses for the memory sections.

I recognize fuse 0x00820000 as possibly something that ended up in the upload file.

It narrows the location of the error somewhat down.

I skimmed through the avrdude document and I have some understanding of how it works. Now I have 2 ideas for a workaround:

. The line where the IDE calls avrdude has {upload.extra_files} at the end. Questions: Where to put those extra files? How to name them so they can be found? If the same fuse is assigned again, will NVMCTRL perform an and on them? (Or is this the next dead end?)

. Use the IDE to compile the program and upload outside the IDE. Questions: where do I find avrdude's commandline? Any interference from the Serial Monitor?

Which fuse settings are you wanted to change that are not available in the menus of MegaCoreX? Or are you trying to have different fuse settings for different sketches and don't want to have to manually select them each time?