File Coverage

blib/lib/Device/Chip/MCP23x17.pm
Criterion Covered Total %
statement 84 87 96.5
branch 6 8 75.0
condition n/a
subroutine 15 15 100.0
pod 8 8 100.0
total 113 118 95.7


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2015-2023 -- leonerd@leonerd.org.uk
5              
6 6     6   520 use v5.26;
  6         20  
7 6     6   30 use warnings;
  6         11  
  6         174  
8 6     6   582 use Object::Pad 0.800;
  6         9599  
  6         229  
9              
10             package Device::Chip::MCP23x17 0.07;
11             class Device::Chip::MCP23x17
12 1     1   602 :isa(Device::Chip);
  1         14664  
  1         43  
13              
14 6     6   1744 use Future::AsyncAwait;
  6         12  
  6         28  
15              
16             =head1 NAME
17              
18             C - chip driver for the F family
19              
20             =head1 SYNOPSIS
21              
22             use Device::Chip::MCP23S17;
23             use Future::AsyncAwait;
24              
25             use constant { HIGH => 0xFFFF, LOW => 0 };
26              
27             my $chip = Device::Chip::MCP23S17->new;
28             await $chip->mount( Device::Chip::Adapter::...->new );
29              
30             foreach my $bit ( 0 .. 15 ) {
31             await $chip->write_gpio( HIGH, 1 << $bit );
32             sleep 1;
33             await $chip->write_gpio( LOW, 1 << $bit );
34             }
35              
36             =head1 DESCRIPTION
37              
38             This L subclass provides specific communication to the
39             F F family of chips.
40              
41             This module itself is an abstract base; to talk to a specific chip see
42             one of the following subclasses:
43              
44             =over 4
45              
46             F over SPI - see L
47              
48             =back
49              
50             Aside from the method of communication with the actual chip hardware, these
51             modules all provide the same higher-level API to the containing application.
52              
53             This module currently only supports a chip running in the C
54             configuration.
55              
56             =cut
57              
58             ADJUST
59             {
60             $self->reset;
61             }
62              
63             =head1 MOUNT PARAMETERS
64              
65             =head2 reset
66              
67             The name of the GPIO line on the adapter that is connected to the C
68             pin of the chip, if there is one. This will be used by the L method.
69              
70             =cut
71              
72             field $_resetpin;
73              
74 5         9 async method mount ( $adapter, %params )
  5         11  
  5         9  
  5         8  
75 5         13 {
76 5         20 $_resetpin = delete $params{reset};
77              
78 5         40 return await $self->SUPER::mount( $adapter, %params );
79 5     5 1 591 }
80              
81             use constant {
82             # Register allocations when in IOCON.BANK=0 mode
83             # TODO: we don't yet support BANK=1 mode
84 6         9807 REG_IODIR => 0x00,
85             REG_IPOL => 0x02,
86             REG_GPINTEN => 0x04,
87             REG_DEFVAL => 0x06,
88             REG_INTCON => 0x08,
89             REG_IOCON => 0x0A,
90             REG_GPPU => 0x0C,
91             REG_INTF => 0x0E,
92             REG_INTCAP => 0x10,
93             REG_GPIO => 0x12,
94             REG_OLAT => 0x14,
95 6     6   2210 };
  6         12  
96              
97             field %_regcache;
98              
99 17         27 async method _cached_maskedwrite_u16 ( $name, $val, $mask )
  17         28  
  17         23  
  17         21  
  17         22  
100 17         38 {
101 17         44 my $want = ( $_regcache{$name} & ~$mask ) | ( $val & $mask );
102              
103 17 100       80 return if ( my $got = $_regcache{$name} ) == $want;
104 11         17 $_regcache{$name} = $want;
105              
106 11         92 my $reg = __PACKAGE__->can( "REG_\U$name" )->();
107              
108 11 100       49 if( ( $got & 0xFF00 ) == ( $want & 0xFF00 ) ) {
    50          
109             # low-byte write
110 9         63 await $self->write_reg( $reg, pack "C", $want & 0x00FF );
111             }
112             elsif( ( $got & 0x00FF ) == ( $want & 0x00FF ) ) {
113 2         11 await $self->write_reg( $reg+1, pack "C", ( $want & 0xFF00 ) >> 8 );
114             }
115             else {
116 0         0 await $self->write_reg( $reg, pack "S<", $want );
117             }
118 17     17   10922 }
119              
120             =head1 METHODS
121              
122             The following methods documented in an C expression return L
123             instances.
124              
125             Each method that takes a C<$mask> parameter uses it to select which IO pins
126             are affected. The mask is a 16-bit integer; selecting only those pins for
127             which bits are set. The lower 8 bits relate to the C pins, the higher 8
128             to the C pins. Pins that are not selected by the mask remain unaffected.
129              
130             =cut
131              
132             =head2 reset
133              
134             await $chip->reset;
135              
136             Resets the cached register values back to their power-up defaults.
137              
138             Additionally, if the C mount parameter is defined, pulses the C
139             pin of the chip.
140              
141             =cut
142              
143             async method reset
144 5         15 {
145             # Default registers
146 5         15 $_regcache{iodir} = 0xffff;
147 5         14 $_regcache{olat} = 0x0000;
148 5         10 $_regcache{ipol} = 0x0000;
149 5         11 $_regcache{gppu} = 0x0000;
150              
151 5 50       125 if( defined( my $reset = $_resetpin ) ) {
152 0         0 await $self->protocol->write_gpios( { $reset => 0 } );
153 0         0 await $self->protocol->write_gpios( { $reset => 1 } );
154             }
155 5     5 1 14 }
156              
157             =head2 write_gpio
158              
159             await $chip->write_gpio( $val, $mask );
160              
161             Sets the pins named in the C<$mask> to be outputs, and sets their values from
162             the bits in C<$val>. Both values are 16-bit integers.
163              
164             =cut
165              
166 6         11 async method write_gpio ( $val, $mask )
  6         10  
  6         12  
  6         8  
167 6         19 {
168             # Write the values before the direction, so as not to cause glitches
169 6         21 await $self->_cached_maskedwrite_u16( olat => $val, $mask ),
170             await $self->_cached_maskedwrite_u16( iodir => 0x0000, $mask ),
171 6     6 1 21974 }
172              
173             =head2 read_gpio
174              
175             $val = await $chip->read_gpio( $mask );
176              
177             Sets the pins named in the C<$mask> to be inputs, and reads the current pin
178             values of them. The mask and the return value are 16-bit integers.
179              
180             =cut
181              
182 2         3 async method read_gpio ( $mask )
  2         4  
  2         4  
183 2         6 {
184 2         11 await $self->tris_gpio( $mask );
185              
186 2         1454 my $val = unpack "S<", await $self->read_reg( REG_GPIO, 2 );
187 2         144 return $val & $mask;
188 2     2 1 431 }
189              
190             =head2 tris_gpio
191              
192             await $chip->tris_gpio( $mask );
193              
194             Sets the pins named in the C<$mask> to be inputs ("tristate"). The mask is a
195             16-bit integer.
196              
197             =cut
198              
199 3         4 async method tris_gpio ( $mask )
  3         6  
  3         5  
200 3         8 {
201 3         11 await $self->_cached_maskedwrite_u16( iodir => 0xFFFF, $mask );
202 3     3 1 1688 }
203              
204             =head2 set_input_polarity
205              
206             await $chip->set_input_polarity( $pol, $mask );
207              
208             Sets the input polarity of the pins given by C<$mask> to be the values given
209             in C<$pol>. Pins associated with bits set in C<$pol> will read with an
210             inverted sense. Both values are 16-bit integers.
211              
212             =cut
213              
214 1         3 async method set_input_polarity ( $pol, $mask )
  1         1  
  1         2  
  1         2  
215 1         3 {
216 1         6 await $self->_cached_maskedwrite_u16( ipol => $pol, $mask );
217 1     1 1 350 }
218              
219             =head2 set_input_pullup
220              
221             await $chip->set_input_pullup( $pullup, $mask );
222              
223             Enables or disables the input pullup resistors on the pins given by C<$mask>
224             as per the values given by C<$pullup>. Both values are 16-bit integers.
225              
226             =cut
227              
228 1         2 async method set_input_pullup ( $pullup, $mask )
  1         2  
  1         2  
  1         2  
229 1         5 {
230 1         5 await $self->_cached_maskedwrite_u16( gppu => $pullup, $mask );
231 1     1 1 12146 }
232              
233             =head2 as_adapter
234              
235             $adapter = $chip->as_adapter;
236              
237             Returns an instance implementing the L interface,
238             allowing access to the GPIO pins via the standard adapter API. See also
239             L.
240              
241             =cut
242              
243             method as_adapter
244 1     1 1 274 {
245 1         405 require Device::Chip::MCP23x17::Adapter;
246 1         11 return Device::Chip::MCP23x17::Adapter->new( chip => $self );
247             }
248              
249             =head1 TODO
250              
251             =over 4
252              
253             =item *
254              
255             Wrap the interrupt-related registers - C, C, C,
256             C, C. Support the interrupt-related bits in C -
257             C, C, C.
258              
259             =item *
260              
261             Support the general configuration bits in the C register - C,
262             C.
263              
264             =item *
265              
266             Consider how easy/hard or indeed how useful it might be to support
267             C configuration.
268              
269             =back
270              
271             =head1 AUTHOR
272              
273             Paul Evans
274              
275             =cut
276              
277             0x55AA;