File Coverage

blib/lib/Device/Chip/DS1307.pm
Criterion Covered Total %
statement 42 145 28.9
branch 1 4 25.0
condition 1 3 33.3
subroutine 15 33 45.4
pod 2 21 9.5
total 61 206 29.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-2023 -- leonerd@leonerd.org.uk
5              
6 3     3   507686 use v5.26;
  3         22  
7 3     3   16 use warnings;
  3         5  
  3         83  
8 3     3   538 use Object::Pad 0.800;
  3         9055  
  3         144  
9              
10             package Device::Chip::DS1307 0.08;
11             class Device::Chip::DS1307
12 3     3   1614 :isa(Device::Chip::Base::RegisteredI2C);
  3         23300  
  3         99  
13              
14 3     3   422 use utf8;
  3         6  
  3         11  
15              
16 3     3   76 use Carp;
  3         4  
  3         148  
17              
18 3     3   17 use Future::AsyncAwait;
  3         6  
  3         10  
19              
20 3     3   135 use constant DEFAULT_ADDR => 0x68;
  3         5  
  3         557  
21              
22             =encoding UTF-8
23              
24             =head1 NAME
25              
26             C - chip driver for a F
27              
28             =head1 DESCRIPTION
29              
30             This L subclass provides specific communication to a
31             F F chip attached to a computer via an I²C adapter.
32              
33             =cut
34              
35             field $_address :param = DEFAULT_ADDR;
36              
37             method I2C_options
38 2     2 0 700 {
39             return (
40 2         11 addr => $_address,
41             max_bitrate => 100E3,
42             );
43             }
44              
45             use constant {
46 3         373 REG_SECONDS => 0x00,
47             REG_MINUTES => 0x01,
48             REG_HOURS => 0x02,
49             REG_WDAY => 0x03,
50             REG_MDAY => 0x04,
51             REG_MONTH => 0x05,
52             REG_YEAR => 0x06,
53             REG_CONTROL => 0x07,
54 3     3   21 };
  3         4  
55              
56             use constant {
57             # REG_SECONDS
58 3         9096 MASK_CLOCKHALT => 1<<7,
59              
60             # REG_HOURS
61             MASK_12H => 1<<6,
62             MASK_PM => 1<<5,
63              
64             # REG_CONTROL
65             MASK_OUT => 1<<7,
66             MASK_SQWE => 1<<4,
67             MASK_RS => 3<<0,
68 3     3   43 };
  3         7  
69              
70 0         0 async method read_reg_u8 ( $reg )
  0         0  
  0         0  
71 0         0 {
72 0         0 return unpack "C", await $self->read_reg( $reg );
73 0     0 0 0 }
74              
75 0         0 async method write_reg_u8 ( $reg, $value )
  0         0  
  0         0  
  0         0  
76 0         0 {
77 0         0 await $self->write_reg( $reg, pack "C", $value );
78 0     0 0 0 }
79              
80 6     6   19 sub _unpack_bcd { ( $_[0] >> 4 )*10 + ( $_[0] % 16 ) }
81 7     7   29 sub _pack_bcd { int( $_[0] / 10 ) << 4 | ( $_[0] % 10 ) }
82              
83 0         0 async method read_reg_bcd ( $reg )
  0         0  
  0         0  
84 0         0 {
85 0         0 return _unpack_bcd unpack "C", await $self->read_reg( $reg );
86 0     0 0 0 }
87              
88 0         0 async method write_reg_bcd ( $reg, $value )
  0         0  
  0         0  
  0         0  
89 0         0 {
90 0         0 await $self->write_reg( $reg, pack "C", _pack_bcd( $value ) );
91 0     0 0 0 }
92              
93             =head1 METHODS
94              
95             The following methods documented in an C expression return L
96             instances.
97              
98             =cut
99              
100             =head2 read_FIELD
101              
102             $sec = await $ds->read_seconds;
103             $min = await $ds->read_minutes;
104             $hr = await $ds->read_hours;
105             $wday = await $ds->read_wday;
106             $mday = await $ds->read_mday;
107             $mon = await $ds->read_month;
108             $year = await $ds->read_year;
109              
110             Reads a timekeeping field and returns a decimal integer. The following fields
111             are recognised:
112              
113             The C field is always returned in 24-hour mode, even if the chip is in
114             12-hour ("AM/PM") mode.
115              
116             =cut
117              
118             # REG_SECONDS also contains the CLOCK HALTED flag
119 0         0 async method read_seconds ()
  0         0  
120 0         0 {
121 0         0 my $v = await $self->read_reg_u8( REG_SECONDS );
122 0         0 $v &= ~MASK_CLOCKHALT;
123 0         0 return _unpack_bcd $v;
124 0     0 0 0 }
125              
126 0     0 0 0 async method read_minutes () { return await $self->read_reg_bcd( REG_MINUTES ); }
  0         0  
  0         0  
  0         0  
  0         0  
127              
128             # REG_HOURS is either in 12 or 24-hour mode.
129 0         0 async method read_hours ()
  0         0  
130 0         0 {
131 0         0 my $v = await $self->read_reg_u8( REG_HOURS );
132 0 0       0 if( $v & MASK_12H ) {
133 0         0 my $pm = $v & MASK_PM;
134 0         0 $v &= ~(MASK_12H|MASK_PM);
135 0         0 return _unpack_bcd( $v ) + 12*$pm;
136             }
137             else {
138 0         0 return _unpack_bcd $v;
139             }
140 0     0 0 0 }
141              
142 0     0 0 0 async method read_wday () { return await $self->read_reg_u8 ( REG_WDAY ); }
  0         0  
  0         0  
  0         0  
  0         0  
143 0     0 0 0 async method read_mday () { return await $self->read_reg_bcd( REG_MDAY ); }
  0         0  
  0         0  
  0         0  
  0         0  
144 0     0 0 0 async method read_month () { return await $self->read_reg_bcd( REG_MONTH ); }
  0         0  
  0         0  
  0         0  
  0         0  
145 0     0 0 0 async method read_year () { return await $self->read_reg_bcd( REG_YEAR ); }
  0         0  
  0         0  
  0         0  
  0         0  
146              
147             =head2 write_FIELD
148              
149             await $ds->write_seconds( $sec );
150             await $ds->write_minutes( $min );
151             await $ds->write_hours ( $hr );
152             await $ds->write_wday ( $wday );
153             await $ds->write_mday ( $mday );
154             await $ds->write_month ( $mon );
155             await $ds->write_year ( $year );
156              
157             Writes a timekeeping field as a decimal integer. The following fields are
158             recognised:
159              
160             The C field is always written back in 24-hour mode.
161              
162             =cut
163              
164 0     0 0 0 async method write_seconds () { await $self->write_reg_bcd( REG_SECONDS, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
165 0     0 0 0 async method write_minutes () { await $self->write_reg_bcd( REG_MINUTES, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
166 0     0 0 0 async method write_hours () { await $self->write_reg_bcd( REG_HOURS, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
167 0     0 0 0 async method write_wday () { await $self->write_reg_u8 ( REG_WDAY, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
168 0     0 0 0 async method write_mday () { await $self->write_reg_bcd( REG_MDAY, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
169 0     0 0 0 async method write_month () { await $self->write_reg_bcd( REG_MONTH, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
170 0     0 0 0 async method write_year () { await $self->write_reg_bcd( REG_YEAR, $_[1] ); }
  0         0  
  0         0  
  0         0  
  0         0  
171              
172             =head2 read_time
173              
174             @tm = await $ds->read_time;
175              
176             Returns a 7-element C-compatible list of values by reading the
177             timekeeping registers, suitable for passing to C, etc... Note
178             that the returned list does not contain the C or C fields.
179              
180             Because the F only stores a 2-digit year number, the year is presumed
181             to be in the range C<2000>-C<2099>.
182              
183             This method presumes C-compatible semantics for the C field
184             stored on the chip; i.e. that 0 is Sunday.
185              
186             This method performs an atomic reading of all the timekeeping registers as a
187             single I²C transaction, so is preferrable to invoking multiple calls to
188             individual read methods.
189              
190             =cut
191              
192             async method read_time
193 1         11 {
194 1         9 my ( $bcd_sec, $bcd_min, $bcd_hour, $wday, $bcd_mday, $bcd_mon, $bcd_year ) =
195             unpack "C7", await $self->read_reg( REG_SECONDS, 7 );
196              
197             return (
198 1         7861 _unpack_bcd( $bcd_sec ),
199             _unpack_bcd( $bcd_min ),
200             _unpack_bcd( $bcd_hour ),
201             _unpack_bcd( $bcd_mday ),
202             _unpack_bcd( $bcd_mon ) - 1,
203             _unpack_bcd( $bcd_year ) + 100,
204             $wday,
205             );
206 1     1 1 263 }
207              
208             =head2 write_time
209              
210             await $ds->write_time( @tm );
211              
212             Writes the timekeeping registers from a 7-element C-compatible list
213             of values. This method ignores the C and C fields, if present.
214              
215             Because the F only stores a 2-digit year number, the year must be in
216             the range C<2000>-C<2099> (i.e. numerical values of C<100> to C<199>).
217              
218             This method performs an atomic writing of all the timekeeping registers as a
219             single I²C transaction, so is preferrable to invoking multiple calls to
220             individual write methods.
221              
222             =cut
223              
224             async method write_time
225 1         3 {
226 1         4 my ( $sec, $min, $hour, $mday, $mon, $year, $wday ) = @_;
227              
228 1 50 33     8 $year >= 100 and $year <= 199 or croak "Invalid year ($year)";
229              
230 1         5 await $self->write_reg( REG_SECONDS, pack "C7",
231             _pack_bcd( $sec ),
232             _pack_bcd( $min ),
233             _pack_bcd( $hour ),
234             _pack_bcd( $wday ),
235             _pack_bcd( $mday ),
236             _pack_bcd( $mon + 1 ),
237             _pack_bcd( $year - 100 ),
238             );
239 1     1 1 269 }
240              
241             =head1 AUTHOR
242              
243             Paul Evans
244              
245             =cut
246              
247             0x55AA;