Device-AVR-UPDI
view release on metacpan or search on metacpan
bin/avr-updi view on Meta::CPAN
Optional. Provides the USB device where the UPDI adapter is connected. If
absent a default of F</dev/ttyUSB0> will apply.
=head2 --part, -p PART
Required. Gives the name of the ATmega or ATtiny chip that is expected. Parts
may be specified in the following ways:
ATmega4809
atmega4809
m4809
ATtiny814
attiny814
t814
Specifically, these are the same forms as recognised by F<avr-gcc>'s C<-mmcu>
option and F<avrdude>'s C<-p> option, for convenience in Makefiles and build
scripts.
=head2 --baud, -B BAUD
Optional. Sets a different baud rate for communications. If not supplied, will
default to 115200.
If communications are unreliable, try setting a slower speed.
=head2 --erase, -e
Send the CHIPERASE key as well as the NVMPROG key when enabling programming
for the C<write-fuses> operation. Normally this flag is not required when
writing fuses to a newly-programmed chip, but may be necessary to recover from
a bad checksum or bad fuse value.
=head2 --binary
Files read from or written to will be in raw binary format, instead of Intel
hex.
=cut
STDOUT->autoflush;
my %configvars;
if( -e ( my $config = "$ENV{HOME}/.avrupdirc" ) ) {
open my $fh, "<", $config or die "Cannot read $config - $!\n";
while( <$fh> ) {
next if m/^\s*#/;
chomp;
m/^\s*(\w+)\s*=\s*(.*)\s*$/ and
$configvars{$1} = $2;
}
}
our $PORT
:GlobalOption("port|P=",
"Path to the device where the UPDI adapter is connected")
= join ",", grep { defined } $configvars{port}, "/dev/ttyUSB0";
if( $PORT =~ m/,/ ) {
my $chosen_port = first { -e $_ } map { glob $_ } split m/,/, $PORT;
defined $chosen_port or
die "Cannot find a UPDI port out of $PORT\n";
$PORT = $chosen_port;
}
our $PART
:GlobalOption("part|p=",
"Name of the ATmega or ATtiny chip that is expected");
our $BAUD
:GlobalOption("baud|B=u",
"Sets a different baud rate for communications")
= $configvars{baud} // 115200;
our $ERASE
:GlobalOption("erase|e",
"Send the CHIPERASE key as well as the NVMPROG key");
our $BINARY
:GlobalOption("binary",
"Files read from or written to will be in raw binary format");
my $updi;
sub make_updi
{
$updi = Device::AVR::UPDI->new(
dev => $PORT,
part => $PART,
baud => $BAUD,
);
$updi->init_link->get;
}
sub check_signature
{
unless( $updi->read_asi_sys_status->get & (1<<3) ) {
$updi->enable_nvmprog->get;
}
my $sig = $updi->read_signature->get;
if( $updi->partinfo->signature ne $sig ) {
printf STDERR "Signature %v02X does not match expected %v02X\n",
$sig, $updi->partinfo->signature;
exit 1;
}
}
sub open_input_file
{
my ( $file ) = @_;
if( $BINARY ) {
return main::Fileformat::Binary->open_in( $file );
}
else {
return main::Fileformat::IntelHex->open_in( $file );
}
}
bin/avr-updi view on Meta::CPAN
my @FUSES = @{ $updi->partinfo->fusenames };
my $fuseinfo = $updi->fuseinfo;
foreach my $idx ( 0 .. $#FUSES ) {
my $regname = $FUSES[$idx];
next if !defined $regname;
my $regvalue = $updi->read_fuse( $idx )->get;
printf "%-7s: %02X\n", $regname, $regvalue;
foreach my $fusename ( @{ $fuseinfo->{fuses} } ) {
my $fuse = $fuseinfo->{fusemap}{$fusename};
next unless $fuse->{reg} eq $regname;
my $val = $regvalue & $fuse->{mask};
if( $fuse->{values} ) {
my $chosen = first { $_->{value} == $val } $fuse->{values}->@*;
printf " %s.%-10s: %s\n", $regname, $fusename, $chosen->{name};
}
else {
printf " %s.%-10s: %d\n", $regname, $fusename, $val ? 1 : 0;
}
}
}
}
=head2 write-fuses
Writes fuse values.
$ avr-updi write-fuses -p t814 BODCFG=02 SYSCFG0.RSTPINCFG=GPIO
Fuses may be specified as numerical values for entire registers, or symbolic
names for individual fields.
=cut
sub command_write_fuses
:Command_description("Writes fuse values")
:Command_arg("fuses...", "the fuses to set (as NAME=value ...)")
{
my ( $fuseargs ) = @_;
make_updi;
if( $ERASE ) {
print STDERR "Erasing chip...\n";
$updi->erase_chip( no_reset => 1 )->get;
}
check_signature;
my @FUSES = @{ $updi->partinfo->fusenames };
my $fuseinfo = $updi->fuseinfo;
my @values;
while( @$fuseargs ) {
my ( $name, $value ) = split m/=/, $fuseargs->[0] or last;
shift @$fuseargs;
if( $name =~ m/\./ ) {
my ( $regname, $fusename ) = $name =~ m/^(.*?)\.(.*)$/;
my $idx = first { defined $FUSES[$_] and $FUSES[$_] eq $regname } 0 .. $#FUSES;
defined $idx or die "Unrecognised fuse register $regname";
my $fuse = $fuseinfo->{fusemap}{$fusename} or
die "Unrecognised fuse $fusename";
$fuse->{reg} eq $regname or
die "Fuse $fusename does not live in register $regname";
my $mask = 0+$fuse->{mask};
if( $fuse->{values} ) {
my $chosen = first { $_->{name} eq $value } $fuse->{values}->@* or
die "Fuse $fusename does not have a value $value";
$value = $chosen->{value};
}
else {
$value = $value ? $mask : 0;
}
$values[$idx] //= $updi->read_fuse( $idx )->get;
$values[$idx] &= ~$mask;
$values[$idx] |= $value;
}
else {
my $idx = first { defined $FUSES[$_] and $FUSES[$_] eq $name } 0 .. $#FUSES;
defined $idx or die "Unrecognised fuse";
$value = hex $value if $value =~ m/^0x/;
$values[$idx] = $value;
}
}
foreach my $idx ( 0 .. $#values ) {
next unless defined( my $value = $values[$idx] );
printf "Setting %s to 0x%02X\n", $FUSES[$idx], $value;
$updi->write_fuse( $idx, $value )->get;
}
$RESET_REQUIRED++;
}
# IO formats
package main::Fileformat {
use base 'IO::Handle';
sub open_out {
my $class = shift;
my $fh;
if( $_[0] eq "-" ) {
$fh = IO::Handle->new_from_fd( STDOUT->fileno, "w" );
}
else {
open $fh, ">", $_[0] or die "Cannot write $_[0] - $!\n";
}
( run in 1.237 second using v1.01-cache-2.11-cpan-71847e10f99 )