File Coverage

blib/lib/Device/Chip/ADT7470.pm
Criterion Covered Total %
statement 72 72 100.0
branch 10 12 83.3
condition 1 2 50.0
subroutine 24 24 100.0
pod 7 8 87.5
total 114 118 96.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              
5             package Device::Chip::ADT7470;
6              
7 3     3   171119 use strict;
  3         53  
  3         91  
8 3     3   16 use warnings;
  3         7  
  3         76  
9 3     3   51 use 5.010;
  3         12  
10 3     3   16 use base qw( Device::Chip::Base::RegisteredI2C );
  3         6  
  3         1409  
11             Device::Chip::Base::RegisteredI2C->VERSION('0.10');
12              
13 3     3   16119 use constant REG_DATA_SIZE => 8;
  3         7  
  3         149  
14              
15 3     3   17 use utf8;
  3         6  
  3         12  
16              
17             our $VERSION = '0.03';
18              
19 3     3   112 use Carp;
  3         6  
  3         173  
20 3     3   1332 use Data::Bitfield qw( bitfield boolfield );
  3         5467  
  3         189  
21              
22 3     3   23 use constant { STALLED => 0xFFFF };
  3         8  
  3         600  
23              
24             =encoding UTF-8
25              
26             =head1 NAME
27              
28             C - chip driver for an F
29              
30             =head1 SYNOPSIS
31              
32             use Device::Chip::ADT7470;
33              
34             my $chip = Device::Chip::ADT7470->new;
35             $chip->mount( Device::Chip::Adapter::...->new )->get;
36              
37             printf "Current fan 1 speed is %d rpm\n", $chip->read_fan_rpm( 1 )->get;
38              
39             =head1 DESCRIPTION
40              
41             This L subclass provides specific communication to a
42             F F attached to a computer via an I²C adapter.
43              
44             Only a subset of the chip's capabilities are currently accessible through this driver.
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             =head1 MOUNT PARAMETERS
53              
54             =head2 addr
55              
56             The I²C address of the device. Can be specified in decimal, octal or hex with
57             leading C<0> or C<0x> prefixes.
58              
59             =cut
60              
61             sub I2C_options {
62 2     2 0 1794 my $self = shift;
63 2         8 my %params = @_;
64              
65 2   50     15 my $addr = delete $params{addr} // 0x40;
66 2 50       18 $addr = oct $addr if $addr =~ m/^0/;
67              
68             return (
69 2         16 %params, # this needs to fixed with resolution of 127570
70             addr => $addr,
71             max_bitrate => 400E3,
72             );
73             }
74              
75             =head1 METHODS
76              
77             The following methods documented with a trailing call to C<< ->get >> return
78             L instances.
79              
80             =cut
81              
82             use constant {
83 3         2627 REG_TACH => {
84             FAN1 => {
85             LOWBYTE => 0x2A,
86             HIGHBYTE => 0x2B
87             },
88             FAN2 => {
89             LOWBYTE => 0x2C,
90             HIGHBYTE => 0x2D
91             },
92             FAN3 => {
93             LOWBYTE => 0x2E,
94             HIGHBYTE => 0x2F
95             },
96             FAN4 => {
97             LOWBYTE => 0x30,
98             HIGHBYTE => 0x31
99             }
100             },
101             REG_DUTY => {
102             FAN1 => 0x32,
103             FAN2 => 0x33,
104             FAN3 => 0x34,
105             FAN4 => 0x35
106             },
107             REG_DEVICEID => 0x3D, # R
108             REG_COMPANYID => 0x3E, # R
109             REG_REVNUMBER => 0x3F, # R
110             REG_CONFIG1 => 0x40, # R/W
111 3     3   23 };
  3         7  
112              
113             bitfield { format => "bytes-BE" }, CONFIG1 =>
114             STRT => boolfield(0),
115             TODIS => boolfield(3),
116             LOCK => boolfield(4),
117             FST_TCH => boolfield(5),
118             HF_LF => boolfield(6),
119             T05_STB => boolfield(7);
120              
121             =head2 read_config
122              
123             $config = $chip->read_config->get
124              
125             Returns a C reference of the contents of the user register.
126              
127             STRT => 0 | 1
128             TODIS => 0 | 1
129             LOCK => 0 | 1 (power cycle to unlock)
130             FST_TCH => 0 | 1
131             HF_LF => 0 | 1
132             T05_STB => 0 | 1
133              
134             =cut
135              
136             sub read_config {
137 1     1 1 150 my $self = shift;
138              
139             $self->cached_read_reg( REG_CONFIG1, 1 )->then(
140             sub {
141 1     1   228 my ($bytes) = @_;
142 1         6 Future->done( $self->{config} = { unpack_CONFIG1($bytes) } );
143             }
144 1         7 );
145             }
146              
147             =head2 change_config
148              
149             $chip->change_config( %config )->get
150              
151             Changes the configuration. Any field names not mentioned will be preserved.
152              
153             =cut
154              
155             sub change_config {
156 1     1 1 3706 my $self = shift;
157 1         4 my %changes = @_;
158              
159             (
160             defined $self->{config}
161             ? Future->done( $self->{config} )
162             : $self->read_config
163             )->then(
164             sub {
165 1     1   84 my %config = ( %{ $_[0] }, %changes );
  1         8  
166              
167 1         4 undef $self->{config}; # invalidate the cache
168 1         5 $self->write_reg( REG_CONFIG1, pack_CONFIG1(%config) );
169             }
170 1 50       6 );
171             }
172              
173             =head2 read_duty
174              
175             $duty = $chip->read_duty( $fan )->get
176              
177             Returns the pwm duty cycle for the specified fan (1-4).
178              
179             =cut
180              
181             sub read_duty {
182 2     2 1 162 my ( $self, $fan ) = @_;
183              
184 2         7 $fan = $self->_format_fan($fan);
185              
186             $self->read_reg( REG_DUTY->{"$fan"}, 1 )->then(
187             sub {
188 2     2   412 my ($duty) = unpack "C", $_[0];
189              
190 2         7 Future->done($duty);
191             }
192 2         13 );
193             }
194              
195             =head2 read_duty_percent
196              
197             $duty = $chip->read_duty_percent( $fan )->get
198              
199             Returns the pwm duty cycle as a percentage for the specified fan (1-4).
200              
201             =cut
202              
203             sub read_duty_percent {
204 1     1 1 3351 my ( $self, $fan ) = @_;
205              
206             $self->read_duty($fan)->then(
207             sub {
208 1     1   104 Future->done( int( $_[0] / 255 * 100 + 0.5) );
209             }
210 1         3 );
211             }
212              
213             =head2 write_duty
214              
215             $duty = $chip->write_duty( $fan, $duty )->get
216              
217             Writes the pwm duty cycle for the specified fan.
218              
219             =cut
220              
221             sub write_duty {
222 4     4 1 7906 my ( $self, $fan, $duty ) = @_;
223              
224 4         12 $fan = $self->_format_fan($fan);
225              
226 4 100       16 if ( $duty < 0 ) { $duty = 0 }
  1         3  
227 4 100       10 if ( $duty > 255 ) { $duty = 255 }
  1         2  
228              
229 4         28 $self->write_reg( REG_DUTY->{$fan}, pack "C", $duty );
230             }
231              
232             =head2 write_duty_percent
233              
234             $duty = $chip->write_duty_percent( $fan, $percent )->get
235              
236             Writes the pwm duty cycle as a percentage for the specified fan.
237              
238             =cut
239              
240             sub write_duty_percent {
241 1     1 1 2543 my ( $self, $fan, $percent ) = @_;
242              
243 1         7 $self->write_duty( $fan, $percent / 100 * 255 );
244             }
245              
246             =head2 read_fan_rpm
247              
248             $rpm = $chip->read_fan_rpm( $fan )->get
249              
250             Read the fan rpm for the specified fan.
251              
252             =cut
253              
254             sub read_fan_rpm {
255 2     2 1 5745 my ( $self, $fan ) = @_;
256              
257 2         5 $fan = $self->_format_fan($fan);
258              
259             Future->needs_all(
260             $self->read_reg( REG_TACH->{$fan}->{LOWBYTE}, 1 ),
261             $self->read_reg( REG_TACH->{$fan}->{HIGHBYTE}, 1 ),
262             )->then( sub {
263 2     2   729 my ( $lo, $hi ) = @_;
264 2         9 my $result = unpack "S<", $lo . $hi;
265              
266 2 100       8 my $rpm = ($result != STALLED) ? int((90000*60)/$result) : 0;
267              
268 2         7 Future->done($rpm);
269             }
270 2         10 );
271              
272             }
273              
274             sub _format_fan {
275 12     12   3213 my ( $self, $fan ) = @_;
276              
277 12 100       236 grep( /^$fan$/, qw(1 2 3 4) ) or croak 'Fan must be 1-4';
278              
279 9         51 return sprintf( 'FAN%d', $fan );
280             }
281              
282             0x55AA;