
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__