The second thing most people do when they start programming microcontrollers is to try and use an external crystal as a clock source. And many beginners start by setting the wrong fuse bits, so that the AVR expects an external clock signal instead of a crystal, thereby locking themselves out. This may be a problem, because the fuse-bits cannot be changed back without actually connecting the external clock source the microcontroller expects.
I am no exception, and started out by setting the wrong fuse bits. It was however very educational, and I will describe below how I created the problem, and how I corrected it.
The first thing to do, is to read the current fuse bytes:
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U hfuse:r:high.bin:b -U lfuse:r:low.bin:b
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading hfuse memory:
Reading | ################################################## | 100% 0.00s
avrdude: writing output file "high.bin"
avrdude: reading lfuse memory:
Reading | ################################################## | 100% 0.00s
avrdude: writing output file "low.bin"
avrdude: safemode: Fuses OK
avrdude done. Thank you.
louic@carbon ~/myavr $ cat high.bin low.bin
0b11011001
0b11100001 |
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U hfuse:r:high.bin:b -U lfuse:r:low.bin:b
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading hfuse memory:
Reading | ################################################## | 100% 0.00s
avrdude: writing output file "high.bin"
avrdude: reading lfuse memory:
Reading | ################################################## | 100% 0.00s
avrdude: writing output file "low.bin"
avrdude: safemode: Fuses OK
avrdude done. Thank you.
louic@carbon ~/myavr $ cat high.bin low.bin
0b11011001
0b11100001
In hex, these values are 0xD9 for the high fuse byte, and 0xE1 for the low byte, the factory settings of my brand new atmaga8. My programmer board has a 3.6864MHz quartz crystal that is connected to the XTAL inputs, and the atmega8 datasheet will tell us the appropriate fuse bit settings.
Now that I write this I do not see why I made a mistake setting the fuse bits, but a fact is that I told the AVR that I have an external clock source by setting the low byte to 0b11100111. The following error message was the result when I tried to upload a program to the microcontroller:
louic@carbon ~/myavr $ avrdude -p m8 -c myavr -P /dev/parport0 -U flash:w:prog.elf:r
avr-objcopy: --change-section-lma .eeprom=0x0000000000000000 never used
avrdude: AVR device not responding
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
avrdude done. Thank you. |
louic@carbon ~/myavr $ avrdude -p m8 -c myavr -P /dev/parport0 -U flash:w:prog.elf:r
avr-objcopy: --change-section-lma .eeprom=0x0000000000000000 never used
avrdude: AVR device not responding
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.
avrdude done. Thank you.
You're welcome. After a long search on the internet (and some experimenting) I succesfully used a second microcontroller as an external clock source, so that the controller with the wrong fuse-bits could be reprogrammed.
First, I removed the external crystal and the two capacitors. Then I connected one of the output pins of a second microcontroller to the XTAL1 pin of the "broken" atmega8. Nothing should be connected to XTAL2. Both of the microcontrollers have to be connected with their GND pin to the same ground, and to a power source of course. The working microcontroller should be programmed to send pulses with regular intervals on its output pin. The program shown below is fine:
#include <avr/io.h> //Required to use assembler commands
#define F_CPU 1000000UL // set the internal clock speed of 1 MHz
#include <util/delay.h>
int main()
{
DDRC = 0xFF; //Make Port C output values
PORTC = 0x00; //Turn all output pins on port c off
while(1) {
PORTC = 0x01;
_delay_ms(5); //This delay is probably not necessary
PORTC = 0x00;
_delay_ms(5); //This delay is probably not necessary either
}
} |
#include <avr/io.h> //Required to use assembler commands
#define F_CPU 1000000UL // set the internal clock speed of 1 MHz
#include <util/delay.h>
int main()
{
DDRC = 0xFF; //Make Port C output values
PORTC = 0x00; //Turn all output pins on port c off
while(1) {
PORTC = 0x01;
_delay_ms(5); //This delay is probably not necessary
PORTC = 0x00;
_delay_ms(5); //This delay is probably not necessary either
}
}
With this setup, the Atmega8 with the wrong fuse bits may be programmed as usual, because the other AVR now serves as external clock source. I started out by setting the default fusebits back:
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading input file "0xE1"
avrdude: writing lfuse (1 bytes):
Writing | | 0% 0.00s ***failed;
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE1:
avrdude: load data lfuse data from input file 0xE1:
avrdude: input file 0xE1 contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x0000
0xe1 != 0x00
avrdude: verification error; content mismatch
avrdude: safemode: lfuse changed! Was e1, and is now 0
Would you like this fuse to be changed back? [y/n] y
avrdude: safemode: and is now rescued
avrdude: safemode: hfuse changed! Was d9, and is now 0
Would you like this fuse to be changed back? [y/n] y
avrdude: safemode: and is now rescued
avrdude: safemode: Fuses OK
avrdude done. Thank you. |
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading input file "0xE1"
avrdude: writing lfuse (1 bytes):
Writing | | 0% 0.00s ***failed;
Writing | ################################################## | 100% 0.02s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE1:
avrdude: load data lfuse data from input file 0xE1:
avrdude: input file 0xE1 contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: verification error, first mismatch at byte 0x0000
0xe1 != 0x00
avrdude: verification error; content mismatch
avrdude: safemode: lfuse changed! Was e1, and is now 0
Would you like this fuse to be changed back? [y/n] y
avrdude: safemode: and is now rescued
avrdude: safemode: hfuse changed! Was d9, and is now 0
Would you like this fuse to be changed back? [y/n] y
avrdude: safemode: and is now rescued
avrdude: safemode: Fuses OK
avrdude done. Thank you.
Because of all the error messages, I tried again, just to check if the fuse-bits would be unchanged this time (they should be, because I just set them):
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading input file "0xE1"
avrdude: writing lfuse (1 bytes):
Writing | ################################################## | 100% 0.00s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE1:
avrdude: load data lfuse data from input file 0xE1:
avrdude: input file 0xE1 contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: reading input file "0xD9"
avrdude: writing hfuse (1 bytes):
Writing | ################################################## | 100% 0.00s
avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xD9:
avrdude: load data hfuse data from input file 0xD9:
avrdude: input file 0xD9 contains 1 bytes
avrdude: reading on-chip hfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: safemode: Fuses OK
avrdude done. Thank you. |
louic@carbon ~/myavr $ avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xE1:m -U hfuse:w:0xD9:m
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e9307
avrdude: reading input file "0xE1"
avrdude: writing lfuse (1 bytes):
Writing | ################################################## | 100% 0.00s
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xE1:
avrdude: load data lfuse data from input file 0xE1:
avrdude: input file 0xE1 contains 1 bytes
avrdude: reading on-chip lfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: reading input file "0xD9"
avrdude: writing hfuse (1 bytes):
Writing | ################################################## | 100% 0.00s
avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xD9:
avrdude: load data hfuse data from input file 0xD9:
avrdude: input file 0xD9 contains 1 bytes
avrdude: reading on-chip hfuse data:
Reading | ################################################## | 100% 0.00s
avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: safemode: Fuses OK
avrdude done. Thank you.
To set the correct fuse bits, I referred to the atmega8 datasheet again.
- For an external crystal, CKSEL3..0 should be set to a value between 1111 and 1010
- For a 3-8MHz crystal, CKSEL3..1 has to be set to 111
- And for any crystal, CKSEL0 = 1
- To make sure it would work, I chose the longest startup time (65ms): SUT1..0 = 11
- Because the crystal has a speed lower than 8MHz, CKOPT=0 and CKOPT=1 should both work, see the comments on this blog post. I chose to set CKOPT=1
Knowing this, one wonders what the values of the high and low fusebytes have to be. The answer is again found in the datasheet:
hfuse:
bit 7 6 5 4 3 2 1 0
name RSTDISBL WDTON SPIEN CKOPT EESAVE BOOTSZ1 BOOTSZ0 BOOTRST
lfuse:
bit 7 6 5 4 3 2 1 0
name BODLEVEL BODEN SUT1 SUT0 CKSEL3 CKSEL2 CKSEL1 CKSEL0 |
hfuse:
bit 7 6 5 4 3 2 1 0
name RSTDISBL WDTON SPIEN CKOPT EESAVE BOOTSZ1 BOOTSZ0 BOOTRST
lfuse:
bit 7 6 5 4 3 2 1 0
name BODLEVEL BODEN SUT1 SUT0 CKSEL3 CKSEL2 CKSEL1 CKSEL0
Combining this with the information above, we now know that we want to program the following bits:
hfuse: 0b11011001 (0xD9), so no change here
lfuse: 0b11111111 (0xFF) |
hfuse: 0b11011001 (0xD9), so no change here
lfuse: 0b11111111 (0xFF)
And the avrdude command becomes:
avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xFF:m |
avrdude -c myavr -p m8 -P /dev/parport0 -U lfuse:w:0xFF:m
After having put back the crystal and capacitors, I set the fuse-bits to their correct values, and the microcontroller worked perfectly again, now using the speed of the external crystal.