Subject:
Re: [gnupic] PICDEM FS USB and Linux tools
From:
"Xiaofan Chen" ####@####.####
Date:
22 Sep 2007 02:54:50 +0100
Message-Id: <a276da400709211854r51e53e41u2e0cb80d85a3aef8@mail.gmail.com>
On 9/21/07, Rick Bronson ####@####.#### wrote:
> Hi,
>
> If someone wanted to use Linux tools for uploading a program via USB
> to the PICDEM board what are the suggested tools? Two I've run across
> are:
>
> 1. picdem.pl (or picdem2.pl which is mentioned but I can't find it)
> 2. fsusb-0.1.11-2.tar.gz
>
> picdem.pl seems to requre USB.pm, which I can't seem to find for
> debian. Unless it's the one in Zaptel.
>
I have not used picdem.pl but I happen to have the archive posted
by Ben Dugan before. The author is Alessandro. I have not used it
since fsusb works fine for me.
USB.pm: is it this one?
http://search.cpan.org/~gwadej/Device-USB-0.21/lib/Device/USB.pm
picdem.pl: I notice that attachement does not with GNUPIC
archive. So I will try to posted it here. I tried to find it in
the thread I posted and found the post but the attachement was
gone.
http://www.linuxhacker.org/cgi-bin/ezmlm-cgi?1:aas:6514:laceapkmfbcildembmhb#b
#!/usr/bin/perl -w
use USB;
use IO::File;
#use Data::HexDump ();
#use Data::Dumper;
use constant READ_VERSION => 0x00;
use constant READ_FLASH => 0x01;
use constant WRITE_FLASH => 0x02;
use constant ERASE_FLASH => 0x03;
use constant READ_EEDATA => 0x04;
use constant WRITE_EEDATA => 0x05;
use constant READ_CONFIG => 0x06;
use constant WRITE_CONFIG => 0x07;
use constant UPDATE_LED => 0x32;
use constant RESET => 0xFF;
my $commands = {
'dump' => [ qw ( userid config eeprom program ) ],
'erase' => [ qw ( userid eeprom program ) ],
'compare' => 'file',
'reset' => undef,
'fill' => undef,
'write' => 'file',
};
print "picdem.pl 0.4 - 20050329\n";
print "Copyright (c) 2005 Tower Technologies, written by Alessandro Zummo,\n";
print "licensed under the GPL. Feedback to
####@####.####
# show usage when no args
help() unless scalar @ARGV;
# validate commands
my @cmds = @ARGV;
while (scalar @cmds)
{
my $cmd = shift @cmds;
# check command
help("Unknown command: $cmd\n")
unless grep { $cmd eq $_ } keys %$commands;
my $what = shift @cmds
if defined $commands->{$cmd};
# check command's option (if required)
help(
"Unknown option for $cmd: $what\n",
"Valid options are: ",
join(',', @{$commands->{$cmd}}), "\n"
)
if ref($commands->{$cmd}) eq 'ARRAY'
and not grep { $what eq $_ } @{$commands->{$cmd}};
help("Missing filename\n")
if defined $commands->{$cmd}
and $commands->{$cmd} eq 'file'
and not defined $what;
}
# the real job
# init USB
USB::usb_init();
USB::usb_find_busses();
USB::usb_find_devices();
# Search for 0x04D8/0x000B
my $device = picdem_find(0x04D8, 0x00B);
die "Couldn't find PICDEM FS USB\n"
unless defined $device;
print "PICDEM FS USB found\n";
# open and setup
my $handle = USB::usb_open($device);
USB::usb_set_configuration($handle, 1);
USB::usb_claim_interface($handle, 0);
# led on
picdem_update_led($handle, 4, 1);
# banner
my ($maj, $min) = picdem_version($handle);
print "bootloader version $maj.$min\n\n";
my $cmd = '';
while (scalar @ARGV)
{
$cmd = shift;
my $what = shift
if defined $commands->{$cmd};
job($handle, $cmd, $what);
}
# leds off, we have finished.
if ($cmd ne 'reset')
{
picdem_update_led($handle, 3, 0);
picdem_update_led($handle, 4, 0);
}
USB::usb_release_interface($handle, 0);
USB::usb_close($handle);
print "done.\n";
sub job
{
my ($handle, $cmd, $what) = @_;
# handle commands
if ($cmd eq 'dump')
{
print "dumping $what\n";
picdem_read_userid($handle) if $what eq 'userid';
picdem_read_config($handle) if $what eq 'config';
picdem_read_eeprom($handle) if $what eq 'eeprom';
picdem_read_flash($handle) if $what eq 'program';
}
if ($cmd eq 'erase')
{
print "erasing $what\n";
picdem_erase_eeprom($handle) if $what eq 'eeprom';
picdem_erase_flash($handle) if $what eq 'program';
picdem_erase_block($handle, 0x00200000, 1)
if $what eq 'userid';
}
if ($cmd eq 'write')
{
my $data = load_inhx32($what);
print "writing\n";
picdem_write_flash($handle, $data);
print "comparing\n";
picdem_compare_flash($handle, $data);
}
picdem_compare_flash($handle, load_inhx32($what))
if $cmd eq 'compare';
picdem_reset($handle) if $cmd eq 'reset';
picdem_fill($handle) if $cmd eq 'fill';
}
sub help
{
print @_;
print "\nUsage:\n\n";
foreach (sort keys %$commands)
{
print "$_\t[", join(',', @{$commands->{$_}}), "]\n"
if defined $commands->{$_} and ref($commands->{$_}) eq 'ARRAY';
print "$_\t<inhx32 file name>\n"
if defined $commands->{$_} and $commands->{$_} eq 'file';
print "$_\n"
if not defined $commands->{$_};
}
exit;
}
sub picdem_find
{
my ($vid, $pid) = @_;
my $bus = &USB::get_usb_busses();
do
{
my $device = &USB::get_usb_devices($bus);
do
{
my $desc = USB::get_usb_device_descriptor($device);
return $device
if $desc->{'idVendor'} == $vid
and $desc->{'idProduct'} == $pid;
}
while ($device = USB::get_usb_next_device($device));
}
while ($bus = USB::get_usb_next_bus($bus));
return undef;
}
sub do_cmd
{
my ($handle, $command, @data) = @_;
my ($out, $len) = encode($command, @data);
my $in = 0xFF x 64;
# print ">>> $len\n";
# dump_buffer($out);
my $num = USB::usb_bulk_write($handle, 0x01, $out, $len, 300);
die "usb_bulk_write: $!\n"
if $num < 0;
$num = USB::usb_bulk_read($handle, 0x81, $in, 64, 1000);
die "usb_bulk_read: $!\n"
if $num < 0;
# print "<<< $num\n";
# dump_buffer(substr($in, 0, $num));
@data = unpack('C*', substr($in, 0, $num));
return \@data;
}
sub encode
{
my ($command, @data) = @_;
my $raw = pack('C*', $command, @data);
return ($raw, length($raw));
}
sub dump_buffer
{
my ($buffer) = @_;
my ($cmd, $len, $addr1, $addr2, $addr3, $data) = unpack('C C C C C
a*', $buffer);
# printf "CMD: %02x\n", $cmd;
# printf "LEN: %02x\n", $len;
print Data::HexDump::HexDump($buffer);
print "\n";
}
sub picdem_version
{
my ($handle) = @_;
my $data = do_cmd($handle, READ_VERSION);
return ($data->[3], $data->[2]);
}
sub picdem_reset
{
my ($handle) = @_;
do_cmd($handle, RESET);
}
sub picdem_update_led
{
my ($handle, $led, $status) = @_;
my $data = do_cmd($handle, UPDATE_LED, $led, $status);
}
sub picdem_read_config
{
my ($handle) = @_;
my $cfg1 = do_cmd($handle, READ_CONFIG, 14, 0x00, 0x00, 0x30);
my $cfg2 = do_cmd($handle, READ_CONFIG, 2, 0xFE, 0xFF, 0x3F);
$cfg1 = join ' ', map { sprintf "%02X", $_ } splice @$cfg1, 5;
print "\n";
printf "300000: %s\n", $cfg1;
printf "3000FE: %02X\n", $cfg2->[5];
printf "3000FF: %02X\n", $cfg2->[6];
print "\n";
}
sub picdem_read_userid
{
my ($handle) = @_;
my $uid = do_cmd($handle, READ_CONFIG, 8, 0x00, 0x00, 0x20);
$uid = join ' ', map { sprintf "%02X", $_ } splice @$uid, 5;
print "\n";
print "200000: $uid\n";
print "\n";
}
sub picdem_read_eeprom
{
my ($handle) = @_;
print "\n";
for (my $i = 0; $i < 0xFF; $i += 16)
{
my $mem = do_cmd($handle, READ_EEDATA, 16, 0x00, 0x00, $i);
$mem = join ' ', map { sprintf "%02X", $_ } splice @$mem, 5;
printf "0000%02X: $mem\n", $i;
}
print "\n";
}
sub picdem_erase_eeprom
{
my ($handle) = @_;
for (my $i = 0; $i < 0xFF; $i += 16)
{
do_cmd($handle, WRITE_EEDATA, 16, 0x00, 0x00, $i, (0xFF) x 16);
}
}
sub mkpicaddr
{
my $addr = $_[0];
return (
($addr & 0x0000FF),
(($addr & 0x00FF00) >> 8),
(($addr & 0xFF0000) >> 16),
);
}
sub picdem_read_flash
{
my ($handle) = @_;
print "program:\n";
for (my $i = 0; $i < 0x007FFF; $i += 16)
{
my @addr = mkpicaddr($i);
my $mem = do_cmd($handle, READ_FLASH, 16, @addr);
$mem = join ' ', map { sprintf "%02X", $_ } splice @$mem, 5;
printf "%02X%02X%02X: $mem\n", $addr[2], $addr[1], $addr[0];
}
print "\n";
}
# erases $count 64-byte blocks starting @ $addr
sub picdem_erase_block
{
my ($handle, $addr, $count) = @_;
do_cmd($handle, ERASE_FLASH, $count, mkpicaddr($addr));
}
sub picdem_erase_flash
{
my ($handle) = @_;
# optimized but not working.. (??)
# picdem_erase_block($handle, 0x00000800, 0xE0);
# picdem_erase_block($handle, 0x00004000, 0xFF);
for (my $i = 0x800; $i < 0x7FFF; $i += 64)
{
picdem_erase_block($handle, $i, 1);
}
}
sub picdem_compare_flash
{
my ($handle, $data) = @_;
my $errors = 0;
foreach my $addr (sort keys %{$data->{'addr'}})
{
next unless $addr >= 0x800;
my @addr = mkpicaddr($addr);
my @data = unpack('C*', $data->{'addr'}{$addr}{'data'});
my $len = scalar @data;
my $mem = do_cmd($handle, READ_FLASH, scalar @data, @addr);
for (my $i = 0; $i < $len; $i++)
{
printf "mismatch #%d @ %08X: wanted %02X, read %02X\n",
++$errors,
$addr + $i,
$data[$i],
$mem->[5 + $i]
if $data[$i] != $mem->[5 + $i];
}
}
print "comparing succesful\n"
unless $errors > 0;
}
sub picdem_write_flash
{
my ($handle, $data) = @_;
my $i = 0;
foreach my $addr (sort keys %{$data->{'addr'}})
{
next unless $addr >= 0x800;
my @addr = mkpicaddr($addr);
my @data = unpack('C*', $data->{'addr'}{$addr}{'data'});
my $len = scalar @data;
die "picdem_write_flash: max 16 bytes\n"
if $len > 16;
# check data is at 16 bytes boundary
if ($addr & 0x0000000F)
{
my $block = $addr & 0xFFFFFFF0;
#printf "fixing boundary: %08X %08X %d\n", $addr, $block, $len;
@data = ((0xFF) x ($addr - $block), @data);
$len = scalar @data;
$addr = $block;
}
# picdem seems to only accept 16 bytes packets. We extend
# the packet with 0xFF. It will not hurt.
push(@data, (0xFF) x (16 - $len))
if $len < 16;
#printf "programming %08X\n", $addr;
do_cmd($handle, WRITE_FLASH,
16,
mkpicaddr($addr),
@data
);
$i++;
}
print "programmed $i locations\n";
}
sub picdem_fill
{
my ($handle) = @_;
print "filling program memory with 0x55AA\n";
for (my $i = 0x800; $i < 0x7FFF; $i += 16)
{
do_cmd($handle, WRITE_FLASH,
16,
mkpicaddr($i),
((0x55, 0xAA) x 8)
);
}
}
sub load_inhx32
{
my ($file) = @_;
my $fh = new IO::File;
$fh->open($file, '<:crlf')
or die "Couldn't open $file: $!\n";
print "loading $file\n";
my $high = 0x0000;
my %data = ();
while (<$fh>)
{
next unless /^:([[:xdigit:]]{2})([[:xdigit:]]{4})([[:xdigit:]]{2})([[:xdigit:]]{0,}?)([[:xdigit:]]{2})$/;
my ($count, $addr, $type, $data, $checksum) = ($1, $2, $3, $4, $5);
last if $type == 0x01; # EOF
# Extended linear address record
if ($type == 0x04)
{
die "Wrong address in type 0x04, must be 0x000, found $addr\n"
unless $addr eq '0000';
$high = (hex($data) << 16);
next;
}
# Extended segment address record
if ($type == 0x02)
{
die "Wrong address in type 0x02, must be 0x000, found $addr\n"
unless $addr eq '0000';
$high = (hex($data) << 4);
next;
}
# Data record
if ($type == 0x00)
{
$addr = $high || hex($addr);
# printf "$count %08X $type $data $checksum\n", $addr;
$data{'addr'}{$addr}{'data'} = pack('H*', $data);
$data{'addr'}{$addr}{'length'} = length($data{'addr'}{$addr}{'data'});
$data{'addr'}{$addr}{'checksum'} = pack('H', $checksum);
next;
}
die "I'm not able to handle INHX32 record type $type\n";
}
$fh->close;
# print Data::Dumper->Dump([\%data]);
return \%data;
}