File Coverage

blib/lib/Device/Chip/MPL3115A2.pm
Criterion Covered Total %
statement 120 181 66.3
branch 3 4 75.0
condition 4 6 66.6
subroutine 27 39 69.2
pod 21 22 95.4
total 175 252 69.4


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