#!/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.


