AVR Fuse Bits and Clock Sources: Setting CKSEL, CKDIV8 and BOD Safely

Fuse bits are a few bytes of special non-volatile configuration that the AVR reads at reset, before your program runs. They choose the clock source, the startup delay, the brown-out reset level, the bootloader size, and whether programming interfaces such as SPI programming or JTAG are enabled. Your C code cannot change them; they are written only by an external programmer.

Fuses confuse beginners for two reasons: a programmed fuse reads as 0, not 1, and a wrong setting can make the chip appear dead. This tutorial explains the ATmega328P fuses, how to change from the factory 1 MHz clock to a 16 MHz crystal, and how to avoid or recover from the common mistakes.

The Three Fuse Bytes of the ATmega328P

Remember the rule: 0 = programmed (active), 1 = unprogrammed. The factory values are low fuse 0x62, high fuse 0xD9 and extended fuse 0xFF.

Fuse byteBits (7 to 0)Factory value
Low (lfuse)CKDIV8, CKOUT, SUT1, SUT0, CKSEL3, CKSEL2, CKSEL1, CKSEL00x62: CKDIV8 programmed, internal 8 MHz RC
High (hfuse)RSTDISBL, DWEN, SPIEN, WDTON, EESAVE, BOOTSZ1, BOOTSZ0, BOOTRST0xD9: only SPIEN and BOOTSZ1/0 programmed
Extended (efuse)Bits 7..3 unused, BODLEVEL2, BODLEVEL1, BODLEVEL00xFF: brown-out detection disabled

With the factory low fuse, the internal 8 MHz RC oscillator is divided by 8 because CKDIV8 is programmed, so the CPU runs at 1 MHz. This is why F_CPU must be 1000000UL on a new chip.

Choosing a Clock Source

Clock sourceCKSEL3..0Typical use
External clock signal on XTAL10000Clock from another chip or oscillator module
Calibrated internal RC, 8 MHz0010No crystal needed; accuracy is a few percent unless calibrated
Internal 128 kHz RC0011Very low power, slow applications
Low frequency crystal (32.768 kHz)0100, 0101Watch crystal applications
Full swing crystal oscillator0110, 0111Crystal in noisy environments or driving other chips
Low power crystal oscillator1000 to 1111Normal choice for 8 to 16 MHz crystals

The internal RC oscillator saves two pins and two capacitors, but its frequency changes with voltage and temperature, which can push USART baud error out of tolerance. For reliable serial communication and accurate timing, use a crystal: connect a 16 MHz crystal between XTAL1 (pin 9) and XTAL2 (pin 10), with a 22 pF capacitor from each pin to ground.

Commonly used fuse values

Goallfusehfuseefuse
Factory default, 1 MHz internal0x620xD90xFF
8 MHz internal, CKDIV8 off0xE20xD90xFF
16 MHz external crystal, no bootloader0xFF0xD90xFD (BOD 2.7 V)
Arduino Uno style, 16 MHz with bootloader0xFF0xDE0xFD

Unused extended fuse bits may read back as 0 depending on avrdude version and settings, so 0xFD may be reported as 0x05. That is the same setting. Always verify fuse values with an online or offline AVR fuse calculator against the datasheet before writing.

Reading and Writing Fuses with avrdude

Always read the current fuses first and write them down. The :h format prints in hexadecimal, and - sends the output to the terminal.

Read all three fuses:
avrdude -c usbasp -p m328p -U lfuse:r:-:h -U hfuse:r:-:h -U efuse:r:-:h

Switch to a 16 MHz crystal (crystal and capacitors must be fitted first):
avrdude -c usbasp -p m328p -B 32 -U lfuse:w:0xFF:m

Enable brown-out detection at 2.7 V:
avrdude -c usbasp -p m328p -U efuse:w:0xFD:m

The -B 32 slows the programming clock, which is necessary while the chip is still running at 1 MHz. After the change to 16 MHz you can drop it. The :m suffix means the value is given immediately on the command line.

To confirm the new clock, flash a blink program built with F_CPU = 16000000UL and check that a 1 second delay really lasts one second, or use Timer0 in CTC mode with OC0A toggling and measure the frequency, as shown in the Timer0 tutorial. You can also program CKOUT (clear bit 6 of the low fuse) to output the system clock on PB0 for measurement with an oscilloscope.

Fuses That Can Lock You Out

  • CKSEL set to an external clock or crystal that is not connected: the chip has no clock and will not answer the programmer. Recovery: temporarily feed a clock signal of about 1 MHz or more into XTAL1, for example from another microcontroller's timer output, then rewrite the low fuse.
  • RSTDISBL programmed: the RESET pin becomes PC6, a normal I/O, and ISP programming is no longer possible. Only a high-voltage parallel programmer can restore it.
  • SPIEN unprogrammed: disables serial programming. The datasheet states that SPIEN is not accessible in serial programming mode, so a USBasp cannot change it, but a high-voltage programmer can, so take care there.
  • DWEN programmed: enables debugWIRE, which uses the RESET pin, so ISP stops working until debugWIRE is disabled with a debugger that supports it.
  • ATmega16/32 CKOPT: on these chips, crystals above 8 MHz need the CKOPT fuse programmed in the high fuse, and the factory-programmed JTAGEN fuse should be unprogrammed if you want to use PC2 to PC5 as I/O.

Other Useful Fuses

BODLEVEL enables a reset whenever VCC falls below a threshold, preventing erratic behaviour and EEPROM corruption during power dips. For a 5 V, 16 MHz system the 4.3 V level (efuse 0xFC) is the safer choice, since the datasheet speed grade requires a higher voltage at 16 MHz. EESAVE keeps EEPROM contents during chip erase. WDTON forces the watchdog permanently on. BOOTSZ and BOOTRST reserve a bootloader section and make the chip start from it, which is how the Arduino bootloader runs on reset.

Summary

AVR fuses are read at reset and select clock source, startup time, brown-out level, bootloader and programming options. A programmed fuse is 0. A new ATmega328P runs at 1 MHz because of CKDIV8; writing lfuse 0xFF selects a 16 MHz crystal, and 0xE2 gives 8 MHz internal. Read and record fuses before changing them, slow the programmer with -B for slow clocks, and never touch RSTDISBL, SPIEN or DWEN unless you understand the consequences.