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 )