HiPi
Perl Modules for Raspberry Pi
Version 0.92 - released 28 March 2024

HiPi::Interface::MCP23S17

This module provides an interface to the MCP23S17 GPIO extender with SPI interface.

An interface to the I2C version of this IC is provided by HiPi::Interface::MCP23017.

It uses HiPi::Device::SPI as a backend.

Methods

Create a new instance of the module class.

use HiPi qw( :rpi :mcp23s17 );
use HiPi::Interface::MCP23S17;
my $mcp = HiPi::Interface::MCP23S17->new(
    devicename => '/dev/spidev0.0',
    address    => 0,
);

The address corresponds to the device hardware address as controlled by the 3 address pins and the IOCON.HAEN configuration bit on the MCP23S17. There are 8 possible hardware addresses from 0 to 7. The value is set on the MCP23S17 by the high / low bias of the 3 address pins.

AD2 AD1 AD0   HiPi Address
 0   0   1         0
 0   1   0         2
 ...................
 1   1   1         7

Note that unless you set the IOCON.HAEN bit, then the device address is always the default 0 ( zero ) regardless of the bias on the address pins.

Read an array of bit values ( 0 or 1 ) from the specified register.

$numbytes is the number of bytes to read - defaults to 1
You can usefully read 2 bytes if the controller is in
default sequential mode.

# 1 byte ( 8 bits )
bits are populated according to bit numbers as described in
the MCP23017 / MCP23S17 documentation

$bits[0] is populated from register bit 0 from the first byte
$bits[7] is populated from register bit 7 from the first byte

# 2 bytes ( 16 bits )
$bits[8] is populated from register bit 0 from the second byte
$bits[15] is populated from register bit 7 from the second byte

$register is a string containing the register name.

Valid values are:
'IODIRA', 'IPOLA', 'GPINTENA', 'DEFVALA', 'INTCONA',
'IOCON',  'GPPUA', 'INTFA',    'INTCAPA', 'GPIOA',
'OLATA',  'IODIRB','IPOLB',    'GPINTENB','DEFVALB', 
'INTCONB','GPPUB', 'INTFB',    'INTCAPB', 'GPIOB',
'OLATB'

Examples:

use HiPi qw( :mcp23s17 );
use HiPi::Interface::MCP23S17;
my $mcp = HiPi::Interface::MCP23S17->new(
    devicename => '/dev/i2c-1',
    address    => 0x20,
);<

# get the value ( 1 or 0 ) for pin A0

my @bits = $mcp->read_register_bits('GPIOA');
my $a0value = $bits[0];

# get the value ( 1 or 0 ) for pin B6

my @bits = $mcp->read_register_bits('GPIOB');
my $b6value = $bits[6];

# get the values for all 16 pins when registers are sequential
# i.e. $mcp->iocon_bank() == 1; ( the default )

my @bits = $mcp->read_register_bits('GPIOA', 2);

# $bits[3]  will contain value for pin A3
# $bits[10] will contain value for pin B2

# note that you can return all the values from
# the entire MCP23S17 register assuming
# default sequential read mode with

my @bits = $mcp->read_register_bits('IODIRA', 22 );

# which values are in which bits will depend
# on the current IOCON.BANK value

# i.e. $mcp->iocon_bank()
# consult the MCP23S17 data sheet

Write an array of bit values ( 0 or 1 ) to the specified register.

$bits[0] is written to register bit 0
$bits[7] is written to register bit 7

You can usefully write 8 bits ( or 16 bits if the controller is in
default sequential mode).

@bits can contain between 1 x 8 and 22 x 8 values
but writing the correct values for 22 * 8 bits 
at once seems an unlikely thing to want to do.

$register is a string containing the register name.
Valid values are:

'IODIRA', 'IPOLA', 'GPINTENA', 'DEFVALA', 'INTCONA',
'IOCON',  'GPPUA', 'INTFA',    'INTCAPA', 'GPIOA',
'OLATA',  'IODIRB','IPOLB',    'GPINTENB','DEFVALB', 
'INTCONB','GPPUB', 'INTFB',    'INTCAPB', 'GPIOB',
'OLATB'

Examples :

use HiPi qw( :mcp23s17 );
....   
# starting in default power on mode, set pin B3 as an
# output and set its value high.
# note that when writing single values, each operation
# is essentially a read / write

# first set B3 as output

my @bits = $mcp->read_register_bits( 'IODIRB' );
$bits[3] = MCP23S17_OUTPUT;
$mcp->write_register_bits( 'IODIRB', @bits );

# then set its value high

@bits = $mcp->read_register_bits( 'GPIOB' );
$bits[3] = MCP23S17_HIGH;
$mcp->write_register_bits( 'OLATB', @bits );

# switch mode to IOCON.BANK=1 - segregated registers

my @bits = $mcp->read_register_bits( 'IOCON' );
$bits[MCP23S17_BANK] = 1;
$mcp->write_register_bits( 'IOCON', @bits );

# ensure A5 is an input and apply pull up resistor

my @bits = $mcp->read_register_bits( 'IODIRA' );
$bits[5] = MCP23S17_INPUT;
$mcp->write_register_bits( 'IODIRA', @bits );
@bits = $mcp->read_register_bits( 'GPPUA' );
$bits[5] = 1;
$mcp->write_register_bits( 'GPPUA', @bits );

# Note there are convenience functions for setting individual
# pin values, settings and IOCON register bits.

Read an array of bytes starting at the specified register.

$numbytes is the number of bytes to read - defaults to 1

$register is a string containing the register name.
Valid values are:

'IODIRA', 'IPOLA', 'GPINTENA', 'DEFVALA', 'INTCONA',
'IOCON',  'GPPUA', 'INTFA',    'INTCAPA', 'GPIOA',
'OLATA',  'IODIRB','IPOLB',    'GPINTENB','DEFVALB', 
'INTCONB','GPPUB', 'INTFB',    'INTCAPB', 'GPIOB',
'OLATB'

It is often more convenient to use read_register_bits which calls read_register_bytes internally and separates the returned values into ordered bit values.

my @bytes = $mcp->read_register_bytes( 'GPIOA', 2);

Write an array of bytes starting at the specified register.

@bytes is the data to write

$register is a string containing the register name.
Valid values are:

'IODIRA', 'IPOLA', 'GPINTENA', 'DEFVALA', 'INTCONA',
'IOCON',  'GPPUA', 'INTFA',    'INTCAPA', 'GPIOA',
'OLATA',  'IODIRB','IPOLB',    'GPINTENB','DEFVALB', 
'INTCONB','GPPUB', 'INTFB',    'INTCAPB', 'GPIOB',
'OLATB'

It is often more convenient to use write_register_bits which calls write_register_bytes internally.

$mcp->write_register_bytes( 'OLATA', @bytes );

A convenience method to set and get the BANK value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_bank( 1 );
my $val = $mcp->iocon_bank;

A convenience method to set and get the MIRROR value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_mirror( 1 );
my $val = $mcp->iocon_mirror;

A convenience method to set and get the SEQOP value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_seqop( 1 );
my $val = $mcp->iocon_seqop;

A convenience method to set and get the DISSLW value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_disslw( 1 );
my $val = $mcp->iocon_disslw;

A convenience method to set and get the HAEN value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_haen( 1 );
my $val = $mcp->iocon_haen;

A convenience method to set and get the ODR value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_odr( 1 );
my $val = $mcp->iocon_odr;

A convenience method to set and get the INTPOL value from the IOCON register. If the optional $bool is provided ( 0 or 1 ) the IOCON register bit is set to that value. The method returns the value of the IOCON bit.

$mcp->iocon_intpol( 1 );
my $val = $mcp->iocon_intpol;

Read the value of the pin named in $pinname from the GPIO register. If the optional $bool is provided the value will be written to the appropriate bit in the OLAT register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_value( MCP_PIN_A5 );
$mcp->pin_value( MCP_PIN_A5, 1 );
# without constant
$val = $mcp->pin_value( 'A5' );
$mcp->pin_value( 'A5', 1 );

Read the value for the pin named in $pinname from the IODIR register. If the optional $bool is provided the value will be written to the IODIR register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_mode( MCP_PIN_A5 );
$mcp->pin_mode( MCP_PIN_A5, MCP23S17_INPUT );
...
$mcp->pin_mode( MCP_PIN_A5, MCP23S17_OUTPUT );

Read the value for the pin named in $pinname from the GPPU register. If the optional $bool is provided the value will be written to the GPPU register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_pull_up( MCP_PIN_A5 );
$mcp->pin_pull_up( MCP_PIN_A5, 1 );

Read the value for the pin named in $pinname from the IPOL register. If the optional $bool is provided the value will be written to the IPOL register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_polarity( MCP_PIN_A5 );
$mcp->pin_polarity( MCP_PIN_A5, 1 );

Read the value for the pin named in $pinname from the GPINTEN register. If the optional $bool is provided the value will be written to the GPINTEN register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_interrupt_enable( MCP_PIN_A5 );
$mcp->pin_interrupt_enable( MCP_PIN_A5, 1 );

Read the value for the pin named in $pinname from the DEFVAL register. If the optional $bool is provided the value will be written to the DEFVAL register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_interrupt_default( MCP_PIN_A5 );
$mcp->pin_interrupt_default( MCP_PIN_A5, 1 );

Read the value for the pin named in $pinname from the INTCON register. If the optional $bool is provided the value will be written to the INTCON register.

use HiPi qw( :mcp23s17 );
....
my $val = $mcp->pin_interrupt_control( MCP_PIN_A5 );
$mcp->pin_interrupt_control( MCP_PIN_A5, 1 );