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 byte | Bits (7 to 0) | Factory value |
|---|---|---|
| Low (lfuse) | CKDIV8, CKOUT, SUT1, SUT0, CKSEL3, CKSEL2, CKSEL1, CKSEL0 | 0x62: CKDIV8 programmed, internal 8 MHz RC |
| High (hfuse) | RSTDISBL, DWEN, SPIEN, WDTON, EESAVE, BOOTSZ1, BOOTSZ0, BOOTRST | 0xD9: only SPIEN and BOOTSZ1/0 programmed |
| Extended (efuse) | Bits 7..3 unused, BODLEVEL2, BODLEVEL1, BODLEVEL0 | 0xFF: 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 source | CKSEL3..0 | Typical use |
|---|---|---|
| External clock signal on XTAL1 | 0000 | Clock from another chip or oscillator module |
| Calibrated internal RC, 8 MHz | 0010 | No crystal needed; accuracy is a few percent unless calibrated |
| Internal 128 kHz RC | 0011 | Very low power, slow applications |
| Low frequency crystal (32.768 kHz) | 0100, 0101 | Watch crystal applications |
| Full swing crystal oscillator | 0110, 0111 | Crystal in noisy environments or driving other chips |
| Low power crystal oscillator | 1000 to 1111 | Normal 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
| Goal | lfuse | hfuse | efuse |
|---|---|---|---|
| Factory default, 1 MHz internal | 0x62 | 0xD9 | 0xFF |
| 8 MHz internal, CKDIV8 off | 0xE2 | 0xD9 | 0xFF |
| 16 MHz external crystal, no bootloader | 0xFF | 0xD9 | 0xFD (BOD 2.7 V) |
| Arduino Uno style, 16 MHz with bootloader | 0xFF | 0xDE | 0xFD |
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.