File Coverage

blib/lib/Device/Chip/MCP23x17.pm
Criterion Covered Total %
statement 81 84 96.4
branch 6 8 75.0
condition n/a
subroutine 14 14 100.0
pod 8 8 100.0
total 109 114 95.6


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