embedded software boot camp

Checking the fuse bits in an Atmel AVR at run time

Friday, May 15th, 2009 by Nigel Jones

In general I try and post on topics that have broad appeal in the embedded world. Today I’m going to partially break with that tradition to show how to check the fuse bits in an Atmel AVR class processor. However, before I do so, I’d like to discuss my motivations for wanting to do this.

The AVR processor family, together with the PIC and other processor families contain fuse / configuration bits. These bits are settable only at program time and are used to configure the behavior of the processor at run time. Typical parameters that are configured are oscillator types, brown out voltage detect levels and memory partitioning. Now as I lamented in this post, there is no great way of communicating to the production staff how you want these fuse bits programmed. As a result I consider there to be a very high probability that a mistake will be made in production – and that all my efforts on crafting perfect code will thus be for naught. Thus while it is much better to prevent mistakes, if you can’t do so, then the next best thing to do is to detect them. As a result on one of the products that I am working on, I have as one of the startup tests a check to ensure that the fuse bits are indeed what they are supposed to be. While I recognize that if the fuse settings are dreadfully wrong it is unlikely that my code will run, I’m actually more concerned with the case where the fuse bits are set mostly correct – and thus that the code works most of the time.

So how do I do this on an AVR? Well if you are using an IAR compiler the work is mostly done for you. Here it is:

#include <intrinsics.h>

/* Macros to read the various fuse bytes */
#define _SPM_GET_LOW_FUSEBITS()  __AddrToZByteToSPMCR_LPM((void __flash*)0x0000U, 0x09U)
#define _SPM_GET_HIGH_FUSEBITS()  __AddrToZByteToSPMCR_LPM((void __flash*)0x0003U, 0x09U)
#define _SPM_GET_EXTENDED_FUSEBITS()  __AddrToZByteToSPMCR_LPM((void __flash*)0x0002U, 0x09U)

/* Structure to store the fuse bytes */
typedef struct{
uint8_t  fuse_low;      /* The low fuse setting */
uint8_t  fuse_high;     /* The high fuse setting */
uint8_t  fuse_extended; /* The extended fuse setting */
uint8_t  lockbits;      /* The lockbits */
} FUSE_SETTINGS;

/* Storage for the fuse settings will be in EEPROM */
static __eeprom __no_init FUSE_SETTINGS Fuse_Settings @ FUSE_VALUES; 

void fuses_Read(void)
{
 FUSE_SETTINGS value;

 value.fuse_low = _SPM_GET_LOW_FUSEBITS();
 value.fuse_high = _SPM_GET_HIGH_FUSEBITS();
 value.fuse_extended = _SPM_GET_EXTENDED_FUSEBITS();
 value.lockbits = _SPM_GET_LOCKBITS();
 __no_operation();

 Fuse_Settings = value;
}

The macro __AddrToZByteToSPMCR_LPM() is defined in intrinsics.h. Essentially it takes care of all the necessary finicky register usage required to read the fuse bits. You’ll also notice that I have used a macro _SPM_GET_LOCKBITS() to read the lockbits. This macro is also found in intrinsics.h. The really observant reader may wonder why there isn’t a macro in intrinsics.h for reading the fuse bits? Well there is – it’s just for reading the low fuse byte – which is all the early AVR processors had. I’ve pointed this out to IAR and they have promised to address this in the next release (thanks Steve!).

Before I leave this topic, I’ll also point out that I don’t read the fuse settings directly into EEPROM. Instead I read them into RAM and then copy the entire structure to EEPROM. I do this because writing to EEPROM messes with the same registers used for reading the fuse bits – and thus bad things happen. This also explains the __no_operation() statement before the data are copied to EEPROM.

Incidentally, I don’t know of a way to read the configuration bits of a PIC at run time. Chalk this up as one more reason why an AVR is superior to a PIC!

Home

Tags: , ,

8 Responses to “Checking the fuse bits in an Atmel AVR at run time”

  1. GregK says:

    Microchip deliver simply assembler code what is called from C code to read and write fuse bits. See bootloader aplication from Microchip:http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en530200

  2. Nigel Jones says:

    Greg: I took a look at the link you mentioned. I’ve never worked with the the PIC24 or dsPIC30 devices and so it wasn’t clear to me what’s going on. The application note block diagram seemed to indicate that the configuration bits could be read / written on these devices. However there was no mention of it in the text. I then looked at the supplied code. I have to say that the PIC application note code is almost as badly written as some of the atmel application notes. That is functions with no headers, parameters with no explanation, incorrect use of data types, failure to use const etc. As a result I wasn’t too much the wiser having looked at the code. When are the microcontroller companies going to start setting an example for the rest of the industry to follow?

  3. Alan Bowens says:

    A slight variation on your code sample is to reset the chip if the lock/fuse bits aren’t as expected during initialisation. If the chip runs at all, you then know that the bytes are right.

  4. snoop911 says:

    I’d like to try coding a PIC using IAR… how do you set the PIC configuration bits using the IAR compiler?

  5. cooldude says:

    I found a much simpler method of reading fuse values.
    In this case i’m displaying them on an 20×4 lcd display.
    The values are stored in the variables -high,low, lock,ext- respectively.
    Since the DataType of the variables is -int- , the values are casted to decimals.

    #include //header file for AVR Microcontroller
    #include

    #include //header file to generate time delay.
    #include”lcd20x4.h” //header file to program LCD

    #define GET_LOW_FUSE_BITS (0x0000)
    #define GET_HIGH_FUSE_BITS (0x0003)
    #define GET_LOCK_BITS (0x0001)
    #define GET_EXTENDED_FUSE_BITS (0x0002)

    int main(void)
    {
    int high,low, lock,ext;
    lcd_init(); //initialization of LCD

    { //Fuse values are saved in DECIMAL no. format
    _delay_ms(1); //DELAY to complete operation
    high=boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
    _delay_ms(1);
    low=boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
    _delay_ms(1);
    lock=boot_lock_fuse_bits_get(GET_LOCK_BITS);
    _delay_ms(1);
    ext=boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS);
    _delay_ms(1);
    }

    { //Display values
    lcd_string(“low =”);
    lcd_num(high);

    lcd_gotoxy(1,0);
    lcd_string(“high=”);
    lcd_num(low);

    lcd_gotoxy(0,20);
    lcd_string(“lock=”);
    lcd_num(lock);

    lcd_gotoxy(1,20);
    lcd_string(“ext=”);
    lcd_num(ext);
    }

    while(1);
    }

  6. cooldude says:

    EDIT:

    #include”avr/io.h” //header file for AVR Microcontroller
    #include “avr/boot.h”

    #include “util/delay.h” //header file to generate time delay.

  7. Morteza says:

    Hi
    Thanks for your great job

  8. Morteza says:

    Thanks for this port.

Leave a Reply