File Coverage

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