#########################################################################################
# Package       HiPi::Interface::MCP23S17
# Description:  Control MCP23S17 Port Extender via SPI
# Created       Sun Dec 02 01:42:27 2012
# SVN Id        $Id: MCP23S17.pm 3 2015-03-12 01:07:32Z Mark Dootson $
# Copyright:    Copyright (c) 2016 Mark Dootson
# Licence:      This work is free software; you can redistribute it and/or modify it 
#               under the terms of the GNU General Public License as published by the 
#               Free Software Foundation; either version 3 of the License, or any later 
#               version.
#########################################################################################

package HiPi::Interface::MCP23S17;

#########################################################################################

use strict;
use warnings;
use parent qw( HiPi::Interface::MCP23X17 );
use HiPi::Constant qw( :raspberry );
use Carp;
use HiPi::Device::SPI qw( :spi );

our $VERSION = '0.25';

our @EXPORT = ();
our @EXPORT_OK = ();
our %EXPORT_TAGS = ( all => \@EXPORT_OK );

use constant $HiPi::Interface::MCP23X17::mcp23S17const;
use constant $HiPi::Interface::MCP23X17::mcp23017const;
use constant $HiPi::Interface::MCP23X17::mcpPinConst;

{
    # allow use of constants with both naming conventions
    my @const23S17 = ( keys %$HiPi::Interface::MCP23X17::mcp23S17const );
    push( @EXPORT_OK, @const23S17 );
    $EXPORT_TAGS{mcp23S17} = \@const23S17;
    
    my @const23017 = ( keys %$HiPi::Interface::MCP23X17::mcp23017const );
    push( @EXPORT_OK, @const23017 );
    $EXPORT_TAGS{mcp23017} = \@const23017;
    
    my @constMCPpin = ( keys %$HiPi::Interface::MCP23X17::mcpPinConst );
    push( @EXPORT_OK, @constMCPpin );
    $EXPORT_TAGS{mcppin} = \@constMCPpin;
}

use constant {
    SPI_READ        => 0x41,
    SPI_WRITE       => 0x40,
};

sub new {
    my ($class, %userparams) = @_;
    
   my %params = (
        devicename   => '/dev/spidev0.0',
        speed        => SPI_SPEED_MHZ_1,
        bitsperword  => 8,
        delay        => 0,
        device       => undef,
        address      => 0,
    );
    
    foreach my $key (sort keys(%userparams)) {
        $params{$key} = $userparams{$key};
    }
        
    unless( defined($params{device}) ) {
        my $dev = HiPi::Device::SPI->new(
            speed        => $params{speed},
            bitsperword  => $params{bitsperword},
            delay        => $params{delay},
            devicename   => $params{devicename},
        );
        
        $params{device} = $dev;
    }
    
    my $self = $class->SUPER::new(%params);
    
    # get current register address config so correct settings are loaded
    $self->read_register_bytes('IOCON');
    return $self;
}

sub do_write_register_bytes {
    my($self, $regaddress, @bytes) = @_;
    my $devaddr = SPI_WRITE + ( $self->address << 1 );
    $self->device->transfer( pack('C*', ( $devaddr, $regaddress, @bytes ) ) );
    return 1;
}

sub do_read_register_bytes {
    my($self, $regaddress, $numbytes) = @_;
    my @bufferbytes = ( (1) x $numbytes );
    my $packbytes = $numbytes + 2;
    my $format = 'C' . $packbytes;
    my $devaddr = SPI_READ + ( $self->address << 1 );
    my @vals = unpack($format, $self->device->transfer( pack($format, ( $devaddr, $regaddress, @bufferbytes )) ));
    # first 2 vals in return buffer are not part of returned data
    shift @vals; shift @vals;
    return @vals;
}


1;

__END__
