File Coverage

blib/lib/Device/BusPirate/Chip/MPL3115A2.pm
Criterion Covered Total %
statement 30 97 30.9
branch 0 4 0.0
condition 0 3 0.0
subroutine 10 54 18.5
pod 1 21 4.7
total 41 179 22.9


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 -- leonerd@leonerd.org.uk
5              
6             package Device::BusPirate::Chip::MPL3115A2;
7              
8 1     1   613 use strict;
  1         1  
  1         37  
9 1     1   4 use warnings;
  1         1  
  1         31  
10 1     1   11 use base qw( Device::BusPirate::Chip );
  1         2  
  1         507  
11              
12             our $VERSION = '0.02';
13              
14 1     1   341 use Carp;
  1         2  
  1         65  
15              
16 1     1   4 use constant CHIP => "MPL3115A2";
  1         1  
  1         54  
17 1     1   4 use constant MODE => "I2C";
  1         1  
  1         32  
18              
19 1     1   551 use Future::Utils qw( repeat );
  1         1589  
  1         61  
20              
21 1     1   448 use Data::Bitfield qw( bitfield boolfield enumfield );
  1         840  
  1         64  
22              
23             =head1 NAME
24              
25             C - use a F chip with C
26              
27             =head1 DESCRIPTION
28              
29             This L subclass provides specific communication to a
30             F F chip attached to the F via
31             I2C.
32              
33             The reader is presumed to be familiar with the general operation of this chip;
34             the documentation here will not attempt to explain or define chip-specific
35             concepts or features, only the use of this module to access them.
36              
37             =cut
38              
39             # This device has a constant address
40             my $ADDR = 0x60;
41              
42 1     1   6 use constant WHO_AM_I_ID => 0xC4;
  1         2  
  1         84  
43              
44             use constant {
45 1         1967 REG_STATUS => 0x00,
46             REG_OUT_P_MSB => 0x01,
47             REG_OUT_P_CSB => 0x02,
48             REG_OUT_P_LSB => 0x03,
49             REG_OUT_T_MSB => 0x04,
50             REG_OUT_T_LSB => 0x05,
51             REG_DR_STATUS => 0x06,
52             REG_OUT_P_DELTA_MSB => 0x07,
53             REG_OUT_P_DELTA_CSB => 0x08,
54             REG_OUT_P_DELTA_LSB => 0x09,
55             REG_OUT_T_DELTA_MSB => 0x0A,
56             REG_OUT_T_DELTA_LSB => 0x0B,
57             REG_WHO_AM_I => 0x0C,
58             REG_F_STATUS => 0x0D,
59             REG_F_DATA => 0x0E,
60             REG_F_SETUP => 0x0F,
61             REG_TIME_DLY => 0x10,
62             REG_SYSMOD => 0x11,
63             REG_INT_SOURCE => 0x12,
64             REG_PT_DATA_CFG => 0x13,
65             REG_BAR_IN_MSB => 0x14,
66             REG_BAR_IN_LSB => 0x15,
67             REG_P_TGT_MSB => 0x16,
68             REG_P_TGT_LSB => 0x17,
69             REG_T_TGT => 0x18,
70             REG_P_WND_MSB => 0x19,
71             REG_P_WND_LSB => 0x1A,
72             REG_T_WND => 0x1B,
73             REG_P_MIN_MSB => 0x1C,
74             REG_P_MIN_CSB => 0x1D,
75             REG_P_MIN_LSB => 0x1E,
76             REG_T_MIN_MSB => 0x1F,
77             REG_T_MIN_LSB => 0x20,
78             REG_P_MAX_MSB => 0x21,
79             REG_P_MAX_CSB => 0x22,
80             REG_P_MAX_LSB => 0x23,
81             REG_T_MAX_MSB => 0x24,
82             REG_T_MAX_LSB => 0x25,
83             REG_CTRL_REG1 => 0x26,
84             REG_CTRL_REG2 => 0x27,
85             REG_CTRL_REG3 => 0x28,
86             REG_CTRL_REG4 => 0x29,
87             REG_CTRL_REG5 => 0x2A,
88             REG_OFF_P => 0x2B,
89             REG_OFF_T => 0x2C,
90             REG_OFF_H => 0x2D,
91 1     1   5 };
  1         1  
92              
93             # Represent CTRL_REG1 to CTRL_REG3 as one three-byte field
94             bitfield CTRL_REG =>
95             # CTRL_REG1
96             SBYB => enumfield( 0, qw( STANDBY ACTIVE )),
97             OST => boolfield( 1 ),
98             RST => boolfield( 2 ),
99             OS => enumfield( 3, qw( 1 2 4 8 16 32 64 128 )),
100             RAW => boolfield( 6 ),
101             ALT => boolfield( 7 ),
102              
103             # CTRL_REG2
104             ST => enumfield( 8, map { 1 << $_ } 0 .. 15 ),
105             ALARM_SEL => boolfield( 13 ),
106             LOAD_OUTPUT => boolfield( 14 ),
107              
108             # CTRL_REG3
109             IPOL1 => boolfield( 16 ),
110             PP_OD1 => boolfield( 17 ),
111             IPOL2 => boolfield( 20 ),
112             PP_OD2 => boolfield( 21 );
113              
114             sub _mplread
115             {
116 0     0     my $self = shift;
117 0           my ( $reg, $len ) = @_;
118              
119 0           $self->mode->send_then_recv( $ADDR, pack( "C", $reg ), $len );
120             }
121              
122             sub _mplwrite
123             {
124 0     0     my $self = shift;
125 0           my ( $reg, $val ) = @_;
126              
127 0           $self->mode->send( $ADDR, pack( "C", $reg ) . $val );
128             }
129              
130             # Raw 8/16-bit integers
131             sub _mplread8 { $_[0]->_mplread( $_[1], 1 )
132 0     0     ->then( sub { Future->done( unpack "C", $_[0] ) } ) }
  0     0      
133 0     0     sub _mplwrite8 { $_[0]->_mplwrite( $_[1], pack "C", $_[2] ) }
134              
135             sub _mplread16 { $_[0]->_mplread( $_[1], 2 )
136 0     0     ->then( sub { Future->done( unpack "S>", $_[0] ) } ) }
  0     0      
137 0     0     sub _mplwrite16 { $_[0]->mplwrite( $_[1], pack "S>", $_[2] ) }
138              
139             # Converted pressure
140             sub _mplread_p { $_[0]->_mplread( $_[1], 3 )
141 0     0     ->then( sub { Future->done( unpack( "L>", "\0" . $_[0] ) / 64 ) } ) }
  0     0      
142              
143             # Converted altitude
144             sub _mplread_a { $_[0]->_mplread( $_[1], 3 )
145             ->then( sub {
146 0     0     my ( $msb, $lsb ) = unpack "s>C", $_[0];
147 0     0     Future->done( $msb + ( $lsb / 256 ) ); }) }
  0            
148              
149             # Converted temperature
150             sub _mplread_t { $_[0]->_mplread( $_[1], 2 )
151             ->then( sub {
152 0     0     my ( $msb, $lsb ) = unpack "cC", $_[0];
153 0     0     Future->done( $msb + ( $lsb / 256 ) ) }) }
  0            
154              
155             =head1 ACCESSORS
156              
157             The following methods documented with a trailing call to C<< ->get >> return
158             L instances.
159              
160             =cut
161              
162             =head2 $config = $mpl->read_config->get
163              
164             Returns a C reference of the contents of control registers C
165             to C, using fields named from the data sheet.
166              
167             =head2 $mpl->change_config( %changes )->get
168              
169             Writes updates to the control registers C to C. This
170             will be performed as a read-modify-write operation, so any fields not given
171             as arguments to this method will retain their current values.
172              
173             Note that these two methods use a cache of configuration bytes to make
174             subsequent modifications more efficient. This cache will not respect the
175             "one-shot" nature of the C and C bits.
176              
177             =cut
178              
179             sub _cached_read_ctrlreg
180             {
181 0     0     my $self = shift;
182              
183 0 0         defined $self->{configbytes}
184             ? return Future->done( $self->{configbytes} )
185             : return $self->_mplread( REG_CTRL_REG1, 3 )
186             }
187              
188             sub read_config
189             {
190 0     0 0   my $self = shift;
191              
192             $self->_cached_read_ctrlreg->then( sub {
193 0     0     my ( $bytes ) = @_;
194 0           return Future->done( { unpack_CTRL_REG( unpack "L<", $bytes . "\0" ) } );
195 0           });
196             }
197              
198             sub change_config
199             {
200 0     0 1   my $self = shift;
201 0           my %changes = @_;
202              
203             $self->read_config->then( sub {
204 0     0     my ( $config ) = @_;
205 0           $config->{$_} = $changes{$_} for keys %changes;
206              
207 0           my $bytes = $self->{configbytes} =
208             substr pack( "L<", pack_CTRL_REG( %$config ) ), 0, 3;
209              
210 0           $self->_mplwrite( REG_CTRL_REG1, $bytes );
211 0           });
212             }
213              
214             =head2 $pressure = $mpl->get_sealevel_pressure->get
215              
216             =head2 $mpl->set_sealevel_pressure->get( $pressure )
217              
218             Read or write the barometric pressure calibration register which is used to
219             convert pressure to altitude when the chip is in altimeter mode, in Pascals.
220             The default value is 101,326 Pa.
221              
222             =cut
223              
224             sub get_sealevel_pressure { shift->_mplread16( REG_BAR_IN_MSB )
225 0     0 0   ->then( sub { Future->done( $_[0] * 2 ) }) }
  0     0      
226              
227 0     0 0   sub set_sealevel_pressure { $_[0]->_mplwrite16( REG_BAR_IN_MSB, $_[1] / 2 ) }
228              
229             =head2 $pressure = $mpl->read_pressure->get
230              
231             Returns the value of the C registers, suitably converted into
232             Pascals. (The chip must be in barometer mode and must I be in C mode
233             for the conversion to work).
234              
235             =cut
236              
237 0     0 0   sub read_pressure { shift->_mplread_p( REG_OUT_P_MSB ) }
238              
239             =head2 $altitude = $mpl->read_altitude->get
240              
241             Returns the value of the C registers, suitably converted into metres.
242             (The chip must be in altimeter mode and must I be in C mode for the
243             conversion to work).
244              
245             =cut
246              
247 0     0 0   sub read_altitude { shift->_mplread_a( REG_OUT_P_MSB ) }
248              
249             =head2 $temperature = $mpl->read_temperature->get
250              
251             Returns the value of the C registers, suitable converted into degrees
252             C. (The chip must I be in C mode for the conversion to work).
253              
254             =cut
255              
256 0     0 0   sub read_temperature { shift->_mplread_t( REG_OUT_T_MSB ) }
257              
258             =head2 $pressure = $mpl->read_min_pressure->get
259              
260             =head2 $pressure = $mpl->read_max_pressure->get
261              
262             Returns the values of the C and C registers, suitably converted
263             into Pascals.
264              
265             =head2 $mpl->clear_min_pressure->get
266              
267             =head2 $mpl->clear_max_pressure->get
268              
269             Clear the C or C registers, resetting them to start again from
270             the next measurement.
271              
272             =cut
273              
274 0     0 0   sub read_min_pressure { shift->_mplread_p( REG_P_MIN_MSB ) }
275 0     0 0   sub read_max_pressure { shift->_mplread_p( REG_P_MAX_MSB ) }
276              
277 0     0 0   sub clear_min_pressure { shift->_mplwrite( REG_P_MIN_MSB, "\x00\x00\x00" ) }
278 0     0 0   sub clear_max_pressure { shift->_mplwrite( REG_P_MAX_MSB, "\x00\x00\x00" ) }
279              
280             =head2 $altitude = $mpl->read_min_altitude->get
281              
282             =head2 $altitude = $mpl->read_max_altitude->get
283              
284             Returns the values of the C and C registers, suitably converted
285             into metres.
286              
287             =cut
288              
289             =head2 $mpl->clear_min_altitude->get
290              
291             =head2 $mpl->clear_max_altitude->get
292              
293             Clear the C or C registers, resetting them to start again from
294             the next measurement.
295              
296             =cut
297              
298 0     0 0   sub read_min_altitude { shift->_mplread_a( REG_P_MIN_MSB ) }
299 0     0 0   sub read_max_altitude { shift->_mplread_a( REG_P_MAX_MSB ) }
300              
301             *clear_min_altitude = \&clear_min_pressure;
302             *clear_max_altitude = \&clear_max_pressure;
303              
304             =head2 $temperature = $mpl->read_min_temperature->get
305              
306             =head2 $temperature = $mpl->read_max_temperature->get
307              
308             Returns the values of the C and C registers, suitably converted
309             into metres.
310              
311             =head2 $mpl->clear_min_temperature->get
312              
313             =head2 $mpl->clear_max_temperature->get
314              
315             Clear the C or C registers, resetting them to start again from
316             the next measurement.
317              
318             =cut
319              
320 0     0 0   sub read_min_temperature { shift->_mplread_t( REG_T_MIN_MSB ) }
321 0     0 0   sub read_max_temperature { shift->_mplread_t( REG_T_MAX_MSB ) }
322              
323 0     0 0   sub clear_min_temperature { shift->_mplwrite( REG_T_MIN_MSB, "\x00\x00" ) }
324 0     0 0   sub clear_max_temperature { shift->_mplwrite( REG_T_MAX_MSB, "\x00\x00" ) }
325              
326             =head1 METHODS
327              
328             =cut
329              
330             =head2 $mpl->check_id->get
331              
332             Reads the C register and checks for a valid ID result. The returned
333             future fails if the expected result is not received.
334              
335             =cut
336              
337             sub check_id
338             {
339 0     0 0   my $self = shift;
340              
341             $self->_mplread8( REG_WHO_AM_I )->then( sub {
342 0     0     my ( $id ) = @_;
343 0 0         $id == WHO_AM_I_ID or
344             die sprintf "Incorrect response from WHO_AM_I register (got %02X, expected %02X)\n",
345             $id, WHO_AM_I_ID;
346              
347 0           Future->done( $self );
348 0           });
349             }
350              
351             =head2 $mpl->start_oneshot->get
352              
353             Sets the C bit of C to start a one-shot measurement when in
354             standby mode. After calling this method you will need to use
355             C to wait for the measurement to finish, or rely somehow on
356             the interrupts.
357              
358             =cut
359              
360             sub start_oneshot
361             {
362 0     0 0   my $self = shift;
363              
364             $self->_cached_read_ctrlreg->then( sub {
365 0     0     my ( $bytes ) = @_;
366 0           my $ctrl_reg1 = substr( $bytes, 0, 1 ) | "\x02"; # Set OST bit
367 0           $self->_mplwrite( REG_CTRL_REG1, $ctrl_reg1 );
368 0           });
369             }
370              
371             =head2 $mpl->busywait_oneshot->get
372              
373             Repeatedly reads the C bit of C until it becomes clear.
374              
375             =cut
376              
377             sub busywait_oneshot
378             {
379 0     0 0   my $self = shift;
380              
381             repeat {
382             $self->_mplread( REG_CTRL_REG1, 1 )->then( sub {
383 0           Future->done( ord( $_[0] ) & 0x02 )
384 0     0     });
385 0   0 0     } until => sub { !$_[0]->failure and !$_[0]->get };
  0            
386             }
387              
388             =head2 $mpl->oneshot->get
389              
390             A convenient wrapper around C and C.
391              
392             =cut
393              
394             sub oneshot
395             {
396 0     0 0   my $self = shift;
397              
398             $self->start_oneshot->then( sub {
399 0     0     $self->busywait_oneshot
400 0           });
401             }
402              
403             =head1 AUTHOR
404              
405             Paul Evans
406              
407             =cut
408              
409             0x55AA;