[TUT] Using FUSE bits from C code

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Being able to store all the information needed to program your AVR in single file, fuse bits included, is pretty convenient. The GCC toolchain supports declaring the fuse bits in the C source files, and stores this data in the generated ELF file. However the last step, generation of the .hex file and programming it with avrdude does not support this.

Hopefully ELF file support in avrdude will be ready soon, but in the meanwhile here's how to do it using a modified Makefile.

1) Declare the fuse bits in your C source file

You need to add the following code :

#include 

FUSES = 
{
    .low = (FUSE_CKSEL0 & FUSE_CKSEL1 & FUSE_CKSEL3 & FUSE_SUT0 & FUSE_CKDIV8),
    .high = HFUSE_DEFAULT,
    .extended = EFUSE_DEFAULT,
};

This declares 3 fuse bytes, lfuse, hfuse and efuse. hfuse and efuse have default values, while lfuse uses a custom configuration. Change this accordingly to your needs and the fuses supported by your microcontroller.
Complete documentation for fuses support in avr-libc can be found here : http://www.nongnu.org/avr-libc/u...
The list of all fuses constants can be found in the ioxxxx.h file corresponding to your microcontroller (look in [winavr install dir]/avr/include/avr).

2) Change the Makefile
Add a new FUSES variable to the Makefile :

FUSES = -U hfuse:w:hfuse.hex:i -U lfuse:w:lfuse.hex:i -U efuse:w:efuse.hex:i -u 

Add a new goal to extract the fuse data from the elf file and program it :

fuse: $(TARGET).elf
	avr-objcopy -j .fuse -O ihex $(TARGET).elf fuses.hex --change-section-lma .fuse=0
	srec_cat fuses.hex -Intel -crop 0x00 0x01 -offset  0x00 -O lfuse.hex -Intel
	srec_cat fuses.hex -Intel -crop 0x01 0x02 -offset -0x01 -O hfuse.hex -Intel
	srec_cat fuses.hex -Intel -crop 0x02 0x03 -offset -0x02 -O efuse.hex -Intel
	$(AVRDUDE) $(AVRDUDE_FLAGS) $(FUSES)

Look for the clean_list definition and add the intermediate hex files :

clean_list :
	@echo
	@echo $(MSG_CLEANING)
	$(REMOVE) $(TARGET).hex
	$(REMOVE) fuses.hex
	$(REMOVE) lfuse.hex
	$(REMOVE) hfuse.hex
	$(REMOVE) efuse.hex
	$(REMOVE) $(TARGET).eep
	$(REMOVE) $(TARGET).cof
	$(REMOVE) $(TARGET).elf
	$(REMOVE) $(TARGET).map
	$(REMOVE) $(TARGET).sym
	$(REMOVE) $(TARGET).lss
	$(REMOVE) $(SRC:%.c=$(OBJDIR)/%.o)
	$(REMOVE) $(SRC:%.c=$(OBJDIR)/%.lst)
	$(REMOVE) $(SRC:.c=.s)
	$(REMOVE) $(SRC:.c=.d)
	$(REMOVE) $(SRC:.c=.i)
	$(REMOVEDIR) .dep

The complete modified Makefile is attached to this post. Use it in place of the standard Makefile template. Again, if you do not use 3 fuse bytes, you'll need to modify the Makefile manually. There is probably a way to do this automatically in function of the MCU variable, but I'm not quite fluent enough in make-speak to do this. Ask if you need support.

3) Program the fuses :
The goal to program the fuses is 'fuse' :

make fuse

Or :

make program fuse

4) Done ! :-)

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting. I do appreciate the time and effort you put into this tutorial.

I'm of the opinion that burning fuses should not be done automatically as it makes "bricking" your uC easier. Burning fuses manually forces one to learn what the effects of their actions will be or suffer the consequences.

Still, for pre-packaged firmware on open source projects this does have a use.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, in my opinion the fuses are part of the firmware so it's only logical to package them with the program code. But maybe I've played with PICs for too long :-)

You're only burning fuses you've explicitly set in your C code. Seems safer to me than an hex word on the command line ... Anyways you can always activate avrdude's safe mode by removing the -u option from the FUSES variable in the Makefile.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While I think this is a well-written and engineered tutorial, I would not recommend putting FUSE settings in the code. Making small changes like crystal type or startup settings then requires code changes.

Since the fuse bits typically differ along with the device and certain settings outside the control of the firmware, I use a Makefile to do all the heavy lifting. The Makefile includes a config file, which contains things like the device, fuses, and certain things that need to be known to the Makefile.

This allows the fuses to be set via make, but does not require source code changes to make FUSE settings changes.

Jim Brain

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You will also need to remove the .fuse section from hex file creation.
In the %.hex creation rule of the Makefile modify from

Quote:

... -R .eeprom ...
to
Quote:
... -R .eeprom -R .fuse ...
or you receive error messages from Avrdude.

... the only thing you cannot unscramble is eggs...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello! I am trying to set the fuse bits by changing according to above tutorial in AVR Studio 4 but i am facing some errors when changes done.

The error screen shot has been attached. Can any one help me out.

error screen shot

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What version of the compiler are you using? Also what version of AVR-LibC ?

 

The fact is that since very early on the fuse.h in AVR-LibC has had a very long explanatory comment near the start so there is no active code on lines 142..151 as shown in your error messages there. Unless you have a VERY old version I suspect you have accidentally edited fuse.h so that part of the comment has become active code.

 

Also which model of AVr are you building for? If it does not recognise EFUSE_DFEAULT it could be that this is some older model of AVR that only had two fuse bytes (LOW and HIGH) but no EXTENDED.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using atmel avr studio - 4.18 and the AVR Libc version is 1.6.7

The microcontroller i am using is atmega 32 and sometimes atmega 16 but the thing is that what i read in the above discussions that i have made changes in fuse.h file which comes under avr/io.h file and that i included with my project but i am not getting why this kind of errors are coming. Can any one please help me out.

The screen shot of changes are pasted below.

changes has been donechanges  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why are you posting screenshots? Why not copy/paste the real program text?

 

Anyway since when have either mega16 or mega32 had ".extended" in their fuses? Of course that won't work.

 

Here is an example (where I had to retype what you posted as a screenshot)...

$ cat avr.c
#include <avr/io.h>

FUSES =
{
    .low = LFUSE_DEFAULT,
    .high = (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN)
};

int main(void) {
    while(1);
}
$ avr-gcc -Os -mmcu=atmega16 avr.c -o avr.elf
$ avr-objdump -s -j .fuse avr.elf

avr.elf:     file format elf32-avr

Contents of section .fuse:
 820000 e191                                 ..              

As you can see, omitting .extended, that compiled without error and the .fuse section in the ELF contains low=0xE1, high=0x91

 

My versions here are:

~$ avr-gcc -E -dM -Os -mmcu=atmega16 avr.c | grep VERSION
#define __AVR_LIBC_VERSION__ 10701UL
#define __AVR_LIBC_VERSION_STRING__ "1.7.1"
#define __GXX_ABI_VERSION 1002
#define __VERSION__ "4.5.3"
#define _AVR_VERSION_H_ 

So compiler is version 4.5.3 and LibC is version 1.7.1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi! Below I am pasteing content of fuse.h file of which file i send screenshot to you earlier.

Now i have one more confusion in red colored text that where to find elf file.

Please any one help me out.............. 

 

 

 

/* Copyright (c) 2007, Atmel Corporation
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.

   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

/* $Id: fuse.h,v 1.3.2.6 2009/07/14 17:24:31 arcanum Exp $ */

/* avr/fuse.h - Fuse API */

#ifndef _AVR_FUSE_H_
#define _AVR_FUSE_H_ 1

/* This file must be explicitly included by <avr/io.h>. */
#if !defined(_AVR_IO_H_)
#error "You must #include <avr/io.h> and not <avr/fuse.h> by itself."
#endif

/** \file */
/** \defgroup avr_fuse <avr/fuse.h>: Fuse Support

    \par Introduction

    The Fuse API allows a user to specify the fuse settings for the specific
    AVR device they are compiling for. These fuse settings will be placed
    in a special section in the ELF output file, after linking.

    Programming tools can take advantage of the fuse information embedded in
    the ELF file, by extracting this information and determining if the fuses
    need to be programmed before programming the Flash and EEPROM memories.
    This also allows a single ELF file to contain all the
    information needed to program an AVR. 

    To use the Fuse API, include the <avr/io.h> header file, which in turn
    automatically includes the individual I/O header file and the <avr/fuse.h>
    file. These other two files provides everything necessary to set the AVR
    fuses.
    
    \par Fuse API
    
    Each I/O header file must define the FUSE_MEMORY_SIZE macro which is
    defined to the number of fuse bytes that exist in the AVR device.
    
    A new type, __fuse_t, is defined as a structure. The number of fields in 
    this structure are determined by the number of fuse bytes in the 
    FUSE_MEMORY_SIZE macro.
    
    If FUSE_MEMORY_SIZE == 1, there is 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 is a single field: byte, which is an array
    of unsigned char with the size of the array being FUSE_MEMORY_SIZE.
    
    A convenience macro, FUSEMEM, is defined as a GCC attribute for a 
    custom-named section of ".fuse".
    
    A convenience macro, FUSES, is defined that declares a variable, __fuse, of
    type __fuse_t with the attribute defined by FUSEMEM. This variable
    allows the end user to easily set the fuse data.

    \note If a device-specific I/O header file has previously defined FUSEMEM,
    then FUSEMEM is not redefined. If a device-specific I/O header file has
    previously defined FUSES, then FUSES is not redefined.

    Each AVR device I/O header file has a set of defined macros which specify the
    actual fuse bits available on that device. The AVR fuses have inverted
    values, logical 1 for an unprogrammed (disabled) bit and logical 0 for a
    programmed (enabled) bit. The defined macros for each individual fuse
    bit represent this in their definition by a bit-wise inversion of a mask.
    For example, the FUSE_EESAVE fuse in the ATmega128 is defined as:
    \code
    */
    #define FUSE_EESAVE      ~_BV(3)
   /*
    \endcode
    \note The _BV macro creates a bit mask from a bit number. It is then 
    inverted to represent logical values for a fuse memory byte.
    
    To combine the fuse bits macros together to represent a whole fuse byte,
    use the bitwise AND operator, like so:
    \code
    
    (FUSE_BOOTSZ0 & FUSE_BOOTSZ1 & FUSE_EESAVE & FUSE_SPIEN & FUSE_JTAGEN)

    \endcode
    
    Each device I/O header file also defines macros that provide default values
    for each fuse byte that is available. 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 that
    provide default values for each fuse byte like so:
    FUSE0_DEFAULT
    FUSE1_DEFAULT
    FUSE2_DEFAULT
    FUSE3_DEFAULT
    FUSE4_DEFAULT
    ....
    
    \par API Usage Example
    
    Putting all of this together is easy. Using C99's designated initializers:
    
    \code
    */
    #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;
    }
    \endcode
    /*
    Or, using the variable directly instead of the FUSES macro,
    
    \code
    #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;
    }
    \endcode
    
    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
    
    
    However there are a number of caveats that 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 will get its values from the initial 
    variable assignment ONLY. This means that you can NOT assign values to 
    this variable in functions and the new values will not be put into the
    ELF .fuse section.
    
    The global variable is declared in the FUSES macro has two leading 
    underscores, which means that it is 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. This is because
    the fuse bits in all bytes default to a logical 1, meaning unprogrammed. 
    Normal uninitialized data defaults to all locgial zeros. So it is vital that
    all fuse bytes are initialized, even with default data. If they are not,
    then the fuse bits may not programmed to the desired settings.
    
    Be sure to have the -mmcu=<em>device</em> flag in your compile command line and
    your linker command line to have the correct device selected and to have 
    the correct I/O header file 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:

    \code
    */
    avr-objdump -s -j .fuse <ELF file>
    /*
    \endcode
    The section contents shows the address on the left, then the data going from
    lower address to a higher address, left to right.

*/

#ifndef __ASSEMBLER__

#ifndef FUSEMEM
#define FUSEMEM  __attribute__((section (".fuse")))
#endif

#if FUSE_MEMORY_SIZE > 3

typedef struct
{
    unsigned char byte[FUSE_MEMORY_SIZE];
} __fuse_t;

#elif FUSE_MEMORY_SIZE == 3

typedef struct
{
    unsigned char low;
    unsigned char high;
    unsigned char extended;
} __fuse_t;

#elif FUSE_MEMORY_SIZE == 2

typedef struct
{
    unsigned char low;
    unsigned char high;
} __fuse_t;

#elif FUSE_MEMORY_SIZE == 1

typedef struct
{
    unsigned char byte;
} __fuse_t;

#endif

#ifndef FUSES
#define FUSES __fuse_t __fuse FUSEMEM
#endif

#endif /* !__ASSEMBLER__ */

#endif /* _AVR_FUSE_H_ */

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Indu Barai wrote:
Now i have one more confusion in red colored text that where to find elf file.
Did you not see my example above?:

$ avr-gcc -Os -mmcu=atmega16 avr.c -o avr.elf
$ avr-objdump -s -j .fuse avr.elf

As you can see - when you build code the ultimate output is the .elf file (in my case I called it avr.elf). I then passed this as input to avr-objdump with the -s and -j .fuse parameters to have it dump out the contents of the .fuse section. As you saw, in my case that looked like:

820000 e191

So my two fuse bytes are 0xE1 and 0x91.

 

As for your copy of fuse.h that you posted it IS corrupted. Something seems to have edited it. When I compare it to the copy you should have I see this:

Some how /* and */ have been added to the file so that some of the comment sections have become active code.

 

I suggest you get a clean copy. You should be able to get it here:

 

http://svn.savannah.nongnu.org/v...

 

(and NEVER, EVER edited any of the system headers - they should all be treated as "read only").