Home Page

ATmega

Using avrdude to read the values of the fuse bits

The fuse bit settings of Atmel microcontrollers can be read using avrdude. The atutil script calls avrdude once for each fuse and saves the avrdude output in a file. The file is parsed to determine the fuse setting and then all of the fuse settings are output are output to the screen.

The __DATA__ section contains the fuse definitions for the ATmega168 fuses. It is not difficult to add new microcontrollers. The fuse definitions are usually listed in the Memory Programming section of the microcontroller datasheet.            

 

Program Listings

#!/usr/bin/perl

# Copyright (C) 2009 John C. Luciani Jr.

### calls avrdude to retrieve the fuse settings

use strict;
use warnings;
use Carp;

use Data::Dumper;

my %Fuses;
my $Atmega;
my $Fuse;
while (<DATA>) {
    s/\#.*//; # Remove comments
    s/^\s*//; # Remove leading spaces
    s/\s*$//; # Revove trailing spaces
    next unless length;  # Skip empty lines
    last if /^__END__$/; # Skip lines after the end marker
    if (s/\\\s*$//) {    # Remove the continuation backslash and 
	$_ .= <DATA>;    # append the next line to $_ then 
	redo unless eof; # restart the loop block after the conditional
    }
    $Atmega = $1, next if /^\[(.*)\]$/;
    next unless defined $Atmega;
    $Fuse = $1, next if /^fuse\s*=\s*(\S.*)/;
    next unless defined $Fuse;
    my @v = split /\s*\|\s*/;
    next unless $#v == 3;
    my %v = map { $_ => shift(@v) } qw(bit name desc default);
    $Fuses{$Atmega}{fuses}{$Fuse}{$v{bit}} = {map { $_ => $v{$_} } 
                                                  qw(name desc default)};
}


### need to read the signature and to get the ATmega model number

$Atmega = '__atmega168__';

my $Outfile = '__atutil__.tmp';
my @Fuses = keys % { $Fuses{$Atmega}{fuses} };

foreach my $fuse (@Fuses) {
    my $fuse_value;
    system("avrdude -c avrispmkII -p atmega168 -P usb -U $fuse:r:$Outfile:h");
    open(IN, "$Outfile") or 
	die "(atutil) Could not open $Outfile for input: $!";
    while (<IN>) {
	s/\#.*//; # Remove comments
	s/^\s*//; # Remove leading spaces
	s/\s*$//; # Revove trailing spaces
	next unless length; # Skip empty lines
	# only one line per file
	$Fuses{$Atmega}{fuses}{$fuse}{_value} = hex($_);
	last;
    }
    close(IN);
    system('rm __atutil__.tmp');
}

printf("\n\n");
foreach my $fuse (@Fuses) {
    my $fuse_value = $Fuses{$Atmega}{fuses}{$fuse}{_value};
    printf("fuse ... %s  ", $fuse);
    printf(" ???\n\n"), next unless defined $fuse_value;
    printf("(0x%0X)\n", $fuse_value);
    my $v = 128;
    foreach my $bit (7,6,5,4,3,2,1,0) {
	printf("   (%i) %-20s ... %s\n", 
	       $bit,
	       $Fuses{$Atmega}{fuses}{$fuse}{$bit}{name},
	       $fuse_value & $v ? 'unprogrammed' : 'programmed');
	$v = $v >> 1;
    }
}

# avrdude -c avrispmkII -p atmega168 -P usb -t

# terminal mode commands
#   part 
#   d efuse
#   d hfuse
#   d lfuse

__DATA__

#[signatures]
#atmega168p = 0x1E 0x94 0x0B __atmega168__
#atmega168  = 0x1E 0x94 0x06 __atmega168__

[__atmega168__]

# bit | name | description | default

# page 296

fuse=efuse

7 | EXT7      | Unused                  | 1
6 | EXT6      | Unused                  | 1
5 | EXT5      | Unused                  | 1
4 | EXT4      | Unused                  | 1
#
3 | EXT3      | Unused                  | 1
2 | BOOTSZ1   | Boot Size 1             | 1
1 | BOOTSZ0   | Boot Size 0             | 1
0 | BOOTRST   | Select Reset Vector     | 1

# page 295

fuse=lock

7 | LOCK7   | Unused            | 1
6 | LOCK6   | Unused            | 1
5 | BLB12   | Boot Lock Bit 12  | 1
4 | BLB11   | Boot Lock Bit 11  | 1
#
3 | BLB02   | Boot Lock Bit 02  | 1
2 | BLB01   | Boot Lock Bit 01  | 1
1 | LB2     | Memory Lock Bit 2 | 1
0 | LB1     | Memory Lock Bit 1 | 1

fuse=lfuse

# page 299

7 | CKDIV8   | Divide clock by 8                | 0 
6 | CKOUT    | Clock Output                     | 1 
5 | SUT1     | Select start-up time             | 1 
4 | SUT0     | Select start-up time             | 0 
#
3 | CKSEL3   | Select Clock source              | 0 
2 | CKSEL2   | Select Clock source              | 0 
1 | CKSEL1   | Select Clock source              | 0 
0 | CKSEL0   | Select Clock source              | 1 

fuse=hfuse

7 | RSTDISBL | Reset is disabled (I/O pin)                       | 1
6 | DWEN     | debugWIRE Enable                                  | 1
5 | SPIEN    | Enable Serial Program and Data Downloading        | 0
4 | WDTON    | WDT always on                                     | 1
#
3 | EESAVE   | EEPROM memory is preserved through the Chip Erase | 1
2 | BODLEVEL2 | Brown out detector trigger level | 1 (unprogrammed)
1 | BODLEVEL1 | Brown out detector trigger level | 1 (unprogrammed)
0 | BODLEVEL0 | Brown out detector trigger level | 1 (unprogrammed)


#[atmega328]

# fuse=hfuse
#2 | BOOTSZ1  | Select Boot Size     | 0 (programmed)
#1 | BOOTSZ0  | Select Boot Size     | 0 (programmed)
#0 | BOOTRST  | Select Reset Vector  | 1 (unprogrammed)


# Style (adapted from the Perl Cookbook, First Edition, Recipe 12.4)

# 1. Names of functions and local variables are all lowercase.
# 2. The program's persistent variables (either file lexicals
#    or package globals) are capitalized.
# 3. Identifiers with multiple words have each of these
#    separated by an underscore for readability.
# 4. Constants are all uppercase.
# 5. If the arrow operator (->) is followed by either a
#    method name or a variable containing a method name then
#    there is a space before and after the operator.


    

MSP430

Calculating USART Modulation Bits

The USART timing is determined by the baud rate clock, the division factor stored in UxBR0 and UxBR1 and the modulation bits stored in MCTL. The modulation bits are used to adjust the bit-to-bit timing of the USART. A set modulation bit increases the division factor by one and a cleared modulation bit causes no change.

The script br-calc calculates modulation bit values and the corresponding transmit and receive errors.            

 

Program Listings

#!/usr/bin/perl

# Copyright (C) 2007 John C. Luciani Jr. 

# Calculates the MCTL value for the MSP430x1xx USART using the
# equations on pages 272-276 of SLAU049E.pdf 

use strict;
use warnings;
use Carp;
use Data::Dumper;

my @M;        # Modulation Bits
my $Max_err; # The maximum error produced using the bits in @M


# test the example in SLAU049E.pdf

if (@ARGV) {
    while (@ARGV) {
	my ($br, $brclk, $num_bits, $mctl) = splice @ARGV, 0, 4;
	die "baud rate is not defined" unless defined $br;
	die "baud rate clock is not defined" unless defined $brclk;
	$num_bits = 11 unless defined $num_bits;
	&error_table($br, $brclk, $num_bits, $mctl);
    }
} else {
    #&error_table(2400, 32768, 11, 0x6B);
    &error_table(2400, 32768, 11);
    #&error_table(57600, 4000000, 11);                    
    #&error_table(2400, 32768, 11);                    
    #&error_table(9600, 32768, 11);                    
    #&error_table(9600, 32768, 11);
    #&error_table(38400, 1000000, 11);
    #&error_table(115200, 1000000,11);
}


# error_table (1) computes values for the modulation bits and 
# (2) prints the transmit and recieve bit errors. 

# parameters --

#   $br       desired baudrate
#   $brclk    the frequency (in Hz) of the baudrate clock
#   $num_bits the number of modulations bits to calculate
#   $mctl     optional value of the MCTL register
#             this is useful for debuging. If $mctl is not
#             specified then the modulation bits that produce
#             the smallest transmit error are calculated.

sub error_table ($$$;$) {
    my ($br, $brclk, $num_bits,$mctl) = @_;
    my @mctl = defined $mctl ? ($mctl) : (0..255);
    foreach (@mctl) {
	&calc_tx_error($br, $brclk, $num_bits, $_);
    }
    &print_error_table($br, $brclk, $num_bits);
}

sub sum {
    my ($mref, $j0, $jn) = @_;
    my $sum = 0;
    map { $sum += $mref->[$_ % 8]{bit} } ($j0..$jn);
    return $sum;
}

# From SLAU049E.pdf page 274

sub rx_error {
    my ($br, $brclk, $j, $mref) = @_;
    my $uxbr = int($brclk/$br);
    my $err = (($br/$brclk) * (2 * ($mref->[0]{bit} + int($uxbr/2)) 
			       + ($j * $uxbr + &sum($mref, 1, $j)))
	       -1-$j) * 100;
}

# From SLAU049E.pdf page 273

sub tx_error {
    my ($br, $brclk, $j, $mref) = @_;
    my $uxbr = int($brclk/$br);
    my $err = (($br/$brclk) * (($j+1) * $uxbr + &sum($mref, 0, $j)) 
	       -($j + 1)) * 100;
    return($err);
}

sub m_hex {
    my $mref = shift;
    my $m = 0;
    my $bv = 1;
    foreach (0..7) {
	$m += $bv if $mref->[$_]{bit};
	$bv *= 2;
    }
    return($m);
}

sub print_error_table {
    my ($br, $brclk, $num_bits) = @_;
    printf("\n\nBaud Rate = %i\n", $br);
    printf("BR Clock  = %i\n\n", $brclk);
    printf("MCTL      = 0x%X\n", &m_hex(\@M));
    printf("UxBR1     = 0x%02X\n", int($brclk/$br)>>8);
    printf("UxBR0     = 0x%02X\n\n", int($brclk/$br));
    printf("Transmit Errors\n\n",  );
    printf("      Min Error\n----- ------------\n");
    my $max_err;
    my $line_count = 0;
    my $i = 0;
    foreach (@M) {
	my $err = $_->{err}[$_->{bit}];
	printf("m%-2i=%i err = %7.2f%%   err0 = %7.2f%%  err1 = %7.2f%%\n", 
	       $i++,
	       $_->{bit}, 
	       $err, 
	       $_->{err}[0], 
	       $_->{err}[1]);
	$max_err = $err unless defined $max_err && abs($max_err) > abs($err);
	printf("----- -------------\n") if ++$line_count % 4 == 0;
    }
    printf("      max = %7.2f%%\n", $max_err);
    printf("\nReceive Errors\n\n",  &m_hex(\@M));
    printf("      Min Error\n----- ------------\n");
    $max_err = undef;
    $line_count = 0;
    foreach (0..$num_bits-1) {
	my $err = &rx_error($br, $brclk, $_, \@M);
	printf("m%-2i=%i err = %7.2f%%\n", $_, $M[$_]{bit}, $err);
	$max_err = $err unless defined $max_err && abs($max_err) > abs($err);
	printf("----- -------------\n") if ++$line_count % 4 == 0;
    }
    printf("      max = %7.2f%%\n", $max_err);

}

sub calc_tx_error {
    my ($br, $brclk, $num_bits, $mctl) = @_;
    my @m;
    my @bits;
    my $max_err;
    if (defined $mctl) {
	foreach (0..7) {
	    push @bits, $mctl & 2**$_ ? 1 : 0;
	}
    }
    foreach my $j (0..$num_bits - 1) {
	my @err;
	$m[$j]{bit} = 0;
	$err[0] = &tx_error($br, $brclk, $j, \@m);
	$m[$j]{bit} = 1;
	$err[1] = &tx_error($br, $brclk, $j, \@m);
	my $bit;

	# There are 8 modulation bits. If we are past the eighth bit
	# then wrap-around otherwise if a bit was passed in the
	# subroutine call then use it otherwise use the bit with the
	# lowest error.

	if ($j > 7) {
	    $bit = $m[$j % 8]{bit};
	} elsif (defined $bits[$j]) {
	    $bit = $bits[$j];
	} else {
	    $bit = abs($err[0]) < abs($err[1]) ? 0 : 1;
	}
	$max_err = abs($err[$bit]) 
	    unless defined $max_err && $max_err > abs($err[$bit]);
	return -1 if defined $Max_err && $Max_err < $max_err;
	$m[$j] = { bit => $bit,
		   err => [$err[0], $err[1]] }; 
    }
    printf("%s%02X", defined $Max_err ? ',' : 'Best MCTL ', $mctl);
    $Max_err = $max_err;
    @M = @m;
    return 0;
}


# Style (adapted from the Perl Cookbook, First Edition, Recipe 12.4)

# 1. Names of functions and local variables are all lowercase.
# 2. The program's persistent variables (either file lexicals
#    or package globals) are capitalized.
# 3. Identifiers with multiple words have each of these
#    separated by an underscore for readability.
# 4. Constants are all uppercase.
# 5. If the arrow operator (->) is followed by either a
#    method name or a variable containing a method name then
#    there is a space before and after the operator.