Device-FTDI
view release on metacpan or search on metacpan
lib/Device/FTDI/MPSSE.pm view on Meta::CPAN
);
use constant {
MSBFIRST => 0,
LSBFIRST => CMD_LSBFIRST,
};
sub set_bit_order
{
my $self = shift;
my ( $lsbfirst ) = @_;
( $self->{mpsse_setup} &= ~CMD_LSBFIRST )
|= ( $lsbfirst & CMD_LSBFIRST );
# TODO: Consider a token-effort Future->done for completeness?
}
=head2 set_clock_edges
$mpsse->set_clock_edges( $rdclock, $wrclock )
Configures the clocking sense of subsequent read or write operations.
Each value should be one of the following constants
CLOCK_FALLING, CLOCK_RISING
=cut
push @EXPORT_OK, qw(
CLOCK_FALLING CLOCK_RISING
);
use constant {
CLOCK_FALLING => 1,
CLOCK_RISING => 0,
};
sub set_clock_edges
{
my $self = shift;
my ( $rdclock, $wrclock ) = @_;
$self->{mpsse_cmd_rd} = CMD_READ | ( $rdclock ? CMD_CLK_ON_READ : 0 );
$self->{mpsse_cmd_wr} = CMD_WRITE | ( $wrclock ? CMD_CLK_ON_WRITE : 0 );
$self->{mpsse_cmd_rdwr} = $self->{mpsse_cmd_wr} | $self->{mpsse_cmd_rd};
}
=head2 write_bytes
=head2 read_bytes
=head2 readwrite_bytes
$mpsse->write_bytes( $data_out )->get
$data_in = $mpsse->read_bytes( $len )->get
$data_in = $mpsse->readwrite_bytes( $data_out )->get
Perform a bytewise clocked serial transfer. These are the "main" methods of
the class; they invoke the main core of the MPSSE.
In each case, the C<CLK> pin will count the specified length of bytes of
transfer. For the C<write_> and C<readwrite_> methods this count is implied by
the length of the inbound buffer; during the operation the specified bytes
will be sent out of the C<DO> pin.
For the C<read_> and C<readwrite_> methods, the returned future will yield the
bytes that were received in the C<DI> pin during this time.
=cut
sub _readwrite_bytes
{
my $self = shift;
my ( $cmd, $len, $data ) = @_;
defined $cmd or croak "clock edge sense has not yet been set";
$cmd |= $self->{mpsse_setup};
$data = substr( $data, 0, $len );
$data .= "\0" x ( $len - length $data );
my $f = $self->_send_bytes( pack( "C v", $cmd, $len - 1 ) .
( $cmd & CMD_WRITE ? $data : "" ) );
$f = $self->_recv_bytes( $len ) if $cmd & CMD_READ;
return $f;
}
sub write_bytes
{
my $self = shift;
$self->_readwrite_bytes( $self->{mpsse_cmd_wr}, length $_[0], $_[0] );
}
sub read_bytes
{
my $self = shift;
$self->_readwrite_bytes( $self->{mpsse_cmd_rd}, $_[0], "" );
}
sub readwrite_bytes
{
my $self = shift;
$self->_readwrite_bytes( $self->{mpsse_cmd_rdwr}, length $_[0], $_[0] );
}
=head2 write_bits
=head2 read_bits
=head2 readwrite_bits
$mpsse->write_bits( $bitlen, $bits_out )->get
$bits_in = $mpsse->read_bits( $bitlen )->get
$bits_in = $mpsse->readwrite_bits( $bitlen, $bits_out )->get
Performs a bitwise clocked serial transfer of between 1 and 8 single bits.
In each case, the C<CLK> pin will count the specified length of bits of
transfer.
For the C<write_> and C<readwrite_> methods individal bits of the given byte
will be clocked out of the C<DO> pin. In C<MSBFIRST> mode, this will start
from the highest bit of the byte; in C<LSBFIRST> mode, this will start from
the lowest. The remaining bits will be ignored.
For the C<read_> and C<readwrite_> methods, the returned future will yield
the bits that were received in the C<DI> pin during this time. In C<MSBFIRST>
mode, the bits returned by the chip will start from the highest bit of the
byte; in C<LSBFIRST> they will start from the lowest. The other bits will be
set to zero.
=cut
sub _readwrite_bits
{
my $self = shift;
my ( $cmd, $len, $data ) = @_;
defined $cmd or croak "clock edge sense has not yet been set";
$cmd |= $self->{mpsse_setup} | CMD_BITMODE;
$data = substr( $data, 0, 1 );
$data = "\0" if !length $data;
my $f = $self->_send_bytes( pack( "C C", $cmd, $len - 1 ) .
( $cmd & CMD_WRITE ? $data : "" ) );
if( $cmd & CMD_READ ) {
$f = $self->_recv_bytes( 1 )
->transform( done => sub {
my $bits = ord shift;
# The FTDI chip's shift register partial-byte reads come in at
# the "wrong" end of the byte. By shifting and masking the result
# we'll ensure the caller sees them where they expect, and doesn't
# get any extra junk bits
if( $self->{mpsse_setup} & CMD_LSBFIRST ) {
$bits >>= ( 8 - $len );
}
else {
$bits <<= ( 8 - $len );
$bits &= 0xff;
}
return chr $bits;
});
}
return $f;
}
sub write_bits
{
my $self = shift;
$self->_readwrite_bits( $self->{mpsse_cmd_wr}, $_[0], $_[1] );
}
sub read_bits
{
my $self = shift;
$self->_readwrite_bits( $self->{mpsse_cmd_rd}, $_[0], "" );
lib/Device/FTDI/MPSSE.pm view on Meta::CPAN
If enabled, loopback mode bypasses the actual IO pins from the chip and
connects the chip's internal output to its own input. This can be useful for
testing whether the chip is mostly functioning correctly.
=cut
sub set_loopback
{
my $self = shift;
my ( $on ) = @_;
$self->_send_bytes( pack "C", $on ? CMD_LOOPBACK_ON : CMD_LOOPBACK_OFF );
}
=head2 set_clock_divisor
$mpsse->set_clock_divisor( $div )->get
Sets the divider the chip uses to determine the output clock frequency. The
eventual frequency will be
$freq_Hz = 12E6 / (( 1 + $div ) * 2 )
=cut
sub set_clock_divisor
{
my $self = shift;
my ( $div ) = @_;
$self->_send_bytes( pack "C v", CMD_SET_CLOCK_DIVISOR, $div );
}
=head2 set_clkdiv5
$mpsse->set_clkdiv5( $on )->get
Disables or enables the divide-by-5 clock prescaler.
Some I<FTDI> chips are capable of faster clock speeds. These chips use a base
frequency of 60MHz rather than 12MHz, but divide it down by 5 by default to
remain compatible with code unaware of this. To access the higher speeds
available on these chips, disable the divider by using this method. The clock
rate implied by C<set_clock_divisor> will then be 5 times faster.
=cut
sub set_clkdiv5
{
my $self = shift;
my ( $on ) = @_;
$self->_send_bytes( pack "C", $on ? CMD_CLKDIV5_ON : CMD_CLKDIV5_OFF );
}
=head2 set_3phase_clock
$mpsse->set_3phase_clock( $on )->get
If enabled, data is clocked in/out using a 3-phase strategy compatible with
the I2C protocol. If this is set, the effective clock rate becomes 2/3 that
implied by the clock divider.
=cut
sub set_3phase_clock
{
my $self = shift;
my ( $on ) = @_;
$self->_send_bytes( pack "C", $on ? CMD_3PHASECLK_ON : CMD_3PHASECLK_OFF );
}
=head2 set_adaptive_clock
$mpsse->set_adaptive_clock( $on )->get
If enabled, the chip waits for acknowledgement of a clock signal on the
C<GPIOL3> pin before continuing for every bit transferred. This may be used by
I<ARM> processors.
=cut
sub set_adaptive_clock
{
my $self = shift;
my ( $on ) = @_;
$self->_send_bytes( pack "C", $on ? CMD_ADAPTIVE_CLOCK_ON : CMD_ADAPTIVE_CLOCK_OFF );
}
=head2 set_open_collector
$mpsse->set_open_collector( $dbus, $cbus )->get
I<Only on FT232H chips>.
Enables open-collector mode on the output pins given by the bitmasks. This
mode is useful to avoid bus drive contention, especially when implementing
I2C.
=cut
sub set_open_collector
{
my $self = shift;
my ( $dbus, $cbus ) = @_;
$self->_send_bytes( pack "C C C", CMD_SET_OPEN_COLLECTOR, $dbus, $cbus );
}
# Future/buffering support
sub _send_bytes
{
my $self = shift;
my ( $bytes ) = @_;
my $buf = $self->{mpsse_buffers}[-1] ||
( $self->{mpsse_buffers}[0] = [ "", [] ] );
( run in 0.897 second using v1.01-cache-2.11-cpan-5837b0d9d2c )