PINE64::MCP23008 Module on CPAN

pinout ot the MCP23008 GPIO extender chip
pinout ot the MCP23008 GPIO extender chip

My latest module to upload to CPAN is a perl-based driver for the MCP23008 GPIO extender. This chip is very handy when you have ran out of GPIO pins on your single board computer and gives you 8 more digital I/O’s. This has come in handy for me on several occasions when I used an LCD for a project that took too many of my digital pins.

The driver works as you would expect: you can make any combination of inputs or outputs, and read in the state of the inputs. This is an i2c device that defaults to an address of 0x20. You can adjust the address by putting high / low values on the address lines A0 – A2 for a possible 8 different chips on a single i2c bus.

Below is a simple implementation of the module configured as an input and then as an output from the synopsis on CPAN ->

The methods basically use Device::I2C to manipulate the chip’s internal registers. Register 0x00 is the I/O direction register. This is an 8-bit register with each bit position representing a digital pin. Valid values are 0 – 255 where all pins default to outputs. So, to make pin 6 an input, you would call the set_direction() method like so:

set_direction(64);

The MCP23008 has internal 100K Ohm pull up resistors when pins are configured as inputs. This can save you room on your board by not having to build external pull up resistors yourself. This is configured in the GPPU (GPIO Pull-up resistor register) 0x06. The code below enables all 8 of the internal pull up resistors.

enable_pullup(255);

I also implemented the I/O polarity feature. With this enabled, the chip will give you the opposite polarity of the current state of the pin. The function call below reverses the polarity of pin 5.

set_polarity(32);

The main methods of the module are read_pin() and write_pin(). They work as you would expect. It reads / writes values to the GPIO port register 0x09.

$gpext->read_pin(4);
$gpext->write_pin(7, 1);

Here is a description of the GPIO port register straight out of the datasheet:

I didn’t implement all of the features of the MCP23008, but enough to make them useful for most projects. Here is the complete source code for the module:

     1	#!/usr/bin/perl 
     2	use strict;
     3	use Device::I2C;
     4	use IO::Handle;
     5	use Fcntl;
       
     6	package PINE64::MCP23008;
       
     7	our $VERSION = '0.9';
       
     8	#global vars
     9	my ($i2cbus, $addr, $gpext);
    10	my $gpregval = 0; 	#init gpio register value to 0
       
    11	my @pin_nums = (1,2,4,8,16,32,64,128);
       
    12	sub new{
    13		my $class = shift;
    14		my $self = bless {}, $class;
       
    15		#first arg is device address
    16		$addr = $_[0];
       
    17		#second arg i2c bus; optional
    18		$i2cbus = $_[1];
    19		
    20		if($i2cbus eq ''){
    21			$i2cbus = '/dev/i2c-0';
    22		}#end if
    23		
    24		$gpext = Device::I2C->new($i2cbus, "r+"); 
       
    25		#init i2c device
    26		$gpext->checkDevice($addr);
    27		$gpext->selectDevice($addr);
       
    28		#init gp register val to all off
    29		$gpregval = 0; 
       
    30		return $self;
    31	}#end new
       
    32	sub set_direction{
    33		#sets value of the IO direction register
    34		#ie 255 makes all input; 0 makes all output
       
    35		my $direction = $_[1];
    36		$gpext->writeByteData(0x00, $direction);	
    37	}#end set_direction
       
    38	sub enable_pullup{
    39		#when a pin is configured as an input
    40		#you can enable internal 100K pull-up
    41		#resistors by writing to the GPPU register
    42		#0-255 are valid values: 0 - all disabled
    43		#255 - all enabled
    44		
    45		my $en_gppu = $_[1];
    46		$gpext->writeByteData(0x06, $en_gppu);
    47	}#end enable_pullup
       
    48	sub set_polarity{
    49		#sets the polarity of the gpio pins; 
    50		#0 is normal polarity
    51		#255 is all pins reversed
    52		
    53		my $io_pol = $_[1];
    54		$gpext->writeByteData(0x01, $io_pol);
    55	}#end set_polarity
       
    56	sub write_pin{
    57		my $ind = $_[1];
    58		my $iox = $pin_nums[$ind];
       
    59		#1 or 0
    60		my $val = $_[2];
       
    61		if($val == 1){
    62			$gpregval+=$iox; 
    63		}#end if
    64		if($val == 0){
    65			$gpregval-=$iox;
    66		}#end if
       
    67		$gpext->writeByteData(0x09, $gpregval);
    68	}#end write_pin
       
    69	sub read_pin{
    70		my $ind = $_[1];
    71		my $iox = $pin_nums[$ind];
    72		my $pinval = 0; 
       
    73		#read GPIO register
    74		my $regval = $gpext->readByteData(0x09); 
       
    75		#ensure 8 binary places are displayed
    76		my $binout = sprintf("%08b", $regval);
       
    77		#parse eight binary digits into an array
    78		my @pinvals = split(//, $binout);
    79		
    80		#reverse array to match pin #'s
    81		@pinvals = reverse(@pinvals);
       
    82		#value of pin is index of $pinvals
    83		$pinval = $pinvals[$ind]; 
       
    84		return $pinval; 
    85	}#end read_pin
       
    86	1;
    87	__END__

Leave a Reply

Your email address will not be published. Required fields are marked *