File Coverage

DS3231.xs
Criterion Covered Total %
statement 4 178 2.2
branch 0 76 0.0
condition n/a
subroutine n/a
pod n/a
total 4 254 1.5


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #include
5             #include
6             #include
7             #include
8             #include
9             #include
10             #include
11             #include
12             #include
13             #include
14             #include "rtc.h"
15             #include "bit.h"
16              
17             /* top-level register */
18              
19             #define RTC_REG_DT 0x00
20              
21             /* sub-level registers */
22              
23             #define RTC_SEC 0x00 // 0-59
24             #define RTC_MIN 0x01 // 0-59
25             #define RTC_HOUR 0x02 // 1-12 (12-hr clock), 0-23 (24-hr clock)
26             #define RTC_WDAY 0x03 // day of week (1-7)
27             #define RTC_MDAY 0x04 // day of month (1-31)
28             #define RTC_MONTH 0x05 // 1-12
29             #define RTC_YEAR 0x06 // 1-99
30              
31             #define RTC_TEMP_MSB 0x11
32             #define RTC_TEMP_LSB 0x12
33              
34             /* sub-level register bits */
35              
36             // RTC_HOUR sub-level register bits
37              
38             #define RTC_AM_PM 0x05
39             #define RTC_12_24 0x06
40              
41             /* ---------- BCD field helpers (single source of BCD policy) ----------
42             * DS3231 time/date registers hold BCD values, some sharing a register with
43             * flag bits (Month 0x05 bit7 = Century; Hours 0x02 bits6-5 = 12/24 + AM/PM).
44             * Every BCD read/write routes through these two, so encoding can never be
45             * applied inconsistently (the bug that produced raw-not-BCD month/hour writes).
46             * setBcdField: preserve the `keep` bits, BCD-encode `value` into the rest.
47             * getBcdField: read `mask` bits, BCD-decode.
48             * Masks are per the DS3231 datasheet (Rev 2; 6/05, Figure 1).
49             */
50 0           int setBcdField (int fd, int reg, int value, int keep, char* name){
51 0           int preserved = getRegister(fd, reg) & keep;
52 0           return setRegister(fd, reg, preserved | dec2bcd(value), name);
53             }
54              
55 0           int getBcdField (int fd, int reg, int mask){
56 0           return bcd2dec(getRegister(fd, reg) & mask);
57             }
58              
59 0           int getSeconds (int fd){
60 0           return getBcdField(fd, RTC_SEC, 0x7F);
61             }
62              
63 0           void setSeconds (int fd, int value){
64 0 0         if (value < 0 || value > 59){
    0          
65 0           croak("seconds parameter out of bounds. Must be between 0-59\n");
66             }
67 0           setBcdField(fd, RTC_SEC, value, 0x00, "seconds");
68 0           }
69              
70 0           int getMinutes (int fd){
71 0           return getBcdField(fd, RTC_MIN, 0x7F);
72             }
73              
74 0           void setMinutes (int fd, int value){
75 0 0         if (value < 0 || value > 59){
    0          
76 0           croak("minutes parameter out of bounds. Must be between 0-59\n");
77             }
78 0           setBcdField(fd, RTC_MIN, value, 0x00, "minutes");
79 0           }
80              
81 0           int getHour (int fd){
82            
83             int hour;
84              
85 0 0         if ((getRegisterBit(fd, RTC_HOUR, RTC_12_24)) == 0){
86             // 24 hr clock
87 0           hour = getRegister(fd, RTC_HOUR);
88             }
89             else {
90             // 12 hr clock
91 0           hour = getRegisterBits(fd, RTC_HOUR, 4, 0);
92             }
93 0           return bcd2dec(hour);
94             }
95              
96 0           void setHour (int fd, int value){
97              
98 0 0         if ((getRegisterBit(fd, RTC_HOUR, RTC_12_24)) != 0){
99             // 12 hour clock
100              
101 0 0         if (value > 12 || value < 1){
    0          
102 0           char* error =
103             "hour (%d) is out of bounds when in 12-hour clock " \
104             "mode. Valid values are 1-12";
105              
106 0           croak(error);
107             }
108             /* 12-h: keep bit6 (12/24 select) + bit5 (AM/PM); BCD hour into bits0-4 */
109 0           setBcdField(fd, RTC_HOUR, value, 0x60, "hour");
110             }
111             else {
112             // 24 hour clock
113              
114 0 0         if (value > 23 || value < 0){
    0          
115 0           char* error =
116             "hour (%d) is out of bounds when in 24-hour clock " \
117             "mode. Valid values are 0-23";
118              
119 0           croak(error);
120             }
121             /* 24-h: full BCD (bit6 = 0 selects 24-hour) */
122 0           setBcdField(fd, RTC_HOUR, value, 0x00, "hour");
123             }
124 0           }
125              
126 0           const char* getDayOfWeek (int fd){
127 0           int dow = bcd2dec(getRegister(fd, RTC_WDAY));
128 0           return dayOfWeek[dow - 1];
129             }
130              
131 0           void setDayOfWeek (int fd, int value){
132              
133 0 0         if (value > 7 || value < 1){
    0          
134 0           croak("Day of week (%d) out of bounds. Must be 1-7 (Mon-Sun)\n", value);
135             }
136              
137 0           setBcdField(fd, RTC_WDAY, value, 0x00, "wday");
138 0           }
139              
140 0           int getDayOfMonth (int fd){
141 0           return getBcdField(fd, RTC_MDAY, 0x3F);
142             }
143              
144 0           void setDayOfMonth (int fd, int value){
145              
146 0 0         if (value < 1 || value > 31){
    0          
147 0           croak("Month day (%d) out of range. Must be between 1-31\n", value);
148             }
149              
150 0           setBcdField(fd, RTC_MDAY, value, 0x00, "dayofmonth");
151 0           }
152              
153 0           int getMonth (int fd){
154 0           return getBcdField(fd, RTC_MONTH, 0x1F);
155             }
156              
157 0           void setMonth (int fd, int value){
158              
159 0 0         if (value < 1 || value > 12){
    0          
160 0           croak("Month (%d) out of range. Must be between 1-12\n", value);
161             }
162              
163             /* keep bit7 (Century); BCD month into bits0-4 */
164 0           setBcdField(fd, RTC_MONTH, value, 0x80, "month");
165 0           }
166              
167 0           int getYear (int fd){
168 0           return getBcdField(fd, RTC_YEAR, 0xFF) + 2000;
169             }
170              
171 0           void setYear (int fd, int value){
172              
173 0 0         if (value < 2000 || value > 2099){
    0          
174 0           croak("Year (%d) out of range. Must be between 2000-2099\n", value);
175             }
176              
177 0           int year = value - 2000;
178              
179 0           setBcdField(fd, RTC_YEAR, year, 0x00, "year");
180 0           }
181              
182 0           float getTemp (int fd){
183              
184 0           int msb = getRegister(fd, RTC_TEMP_MSB);
185 0           int lsb = getRegister(fd, RTC_TEMP_LSB);
186              
187             /* 0x11 is a signed 8-bit two's-complement integer (deg C); 0x12 bits 7-6
188             are the 0.25-deg fraction. Read the MSB as int8_t so sub-zero temps
189             sign-extend correctly (DS3231 Rev 2 temperature registers). */
190 0           float celcius = (int8_t)msb + (lsb >> 6) * 0.25;
191              
192 0           return celcius;
193             }
194              
195 0           int getMeridien (int fd){
196              
197 0 0         if ((getRegisterBit(fd, RTC_HOUR, RTC_12_24)) == 0){
198 0           croak(
199             "AM/PM functionality not available when in 24-hour clock mode\n"
200             );
201             }
202 0           return getRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
203             }
204              
205 0           void setMeridien (int fd, int value){
206              
207 0 0         if ((getRegisterBit(fd, RTC_HOUR, RTC_12_24)) == 0){
208 0           croak(
209             "AM/PM can not be set when in 24-hour clock mode\n"
210             );
211             }
212              
213 0 0         if (value == 1){
214 0           enableRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
215             }
216 0 0         else if (value == 0){
217 0           disableRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
218             }
219             else {
220 0           croak(
221             "AM/PM value (%d) out of bounds. Send 1 for enable, 0 for disable",
222             value
223             );
224             }
225 0           }
226              
227 0           int getMilitary (int fd){
228 0           return getRegisterBit(fd, RTC_HOUR, RTC_12_24);
229             }
230              
231 0           void setMilitary (int fd, int value){
232              
233 0           int militaryTime = getMilitary(fd);
234 0           int hour = getHour(fd);
235              
236 0 0         if (militaryTime == value){
237             // nothing to do
238 0           return;
239             }
240              
241 0 0         if (value == 1){
242             // enable 12 hr clock
243 0 0         if (hour == 0){
244             // AM, at hour zero
245 0           setHour(fd, 12);
246 0           disableRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
247             }
248 0 0         else if (getHour(fd) <= 12){
249             // AM
250 0           setHour(fd, hour);
251 0           disableRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
252             }
253             else {
254             // PM
255 0           setHour(fd, hour - 12);
256 0           enableRegisterBit(fd, RTC_HOUR, RTC_AM_PM);
257             }
258              
259 0           enableRegisterBit(fd, RTC_HOUR, RTC_12_24);
260             }
261             else {
262             // enable 24 hr clock
263 0           int meridien = getMeridien(fd);
264 0           disableRegisterBit(fd, RTC_HOUR, RTC_12_24);
265              
266 0 0         if (meridien == 0){
267             // AM
268 0 0         if (hour == 12){
269 0           setHour(fd, 0);
270             }
271             else {
272 0           setHour(fd, hour);
273             }
274             }
275             else {
276             // PM
277 0 0         if (hour < 12){
278 0           setHour(fd, hour + 12);
279             }
280             else {
281 0           setHour(fd, hour);
282             }
283             }
284             }
285             }
286              
287 0           int getFh (int rtcAddr){
288              
289             int fd;
290              
291 0 0         if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {
292 0           close(fd);
293 0           croak("Couldn't open the device: %s\n", strerror(errno));
294             }
295              
296 0 0         if (ioctl(fd, I2C_SLAVE_FORCE, rtcAddr) < 0) {
297 0           close(fd);
298 0           croak(
299             "Couldn't find device at addr %d: %s\n",
300             rtcAddr,
301             strerror(errno)
302             );
303             }
304              
305 0           _establishI2C(fd);
306              
307 0           return fd;
308             }
309              
310 0           void disableRegisterBit (int fd, int reg, int bit){
311 0           int data = bitOff(getRegister(fd, reg), bit);
312 0           setRegister(fd, reg, data, "disabling bit");
313 0           }
314              
315 0           void enableRegisterBit (int fd, int reg, int bit){
316 0           int data = bitOn(getRegister(fd, reg), bit);
317 0           setRegister(fd, reg, data, "enabling bit");
318 0           }
319              
320 0           int getRegister (int fd, int reg){
321              
322             char buf[1];
323 0           buf[0] = reg;
324              
325 0 0         if ((write(fd, buf, 1)) != 1){
326 0           close(fd);
327 0           croak(
328             "Could not write register pointer %d: %s\n",
329             reg,
330             strerror(errno)
331             );
332             }
333              
334 0 0         if ((read(fd, buf, 1)) != 1){
335 0           close(fd);
336 0           croak("Could not read register %d: %s\n", reg, strerror(errno));
337             }
338              
339 0           return buf[0];
340             }
341              
342 0           int getRegisterBit (int fd, int reg, int bit){
343 0           int regData = getRegister(fd, reg);
344 0           return bitGet(regData, bit, bit);
345             }
346              
347 0           int getRegisterBits (int fd, int reg, int msb, int lsb){
348 0           return bitGet(getRegister(fd, reg), msb, lsb);
349             }
350              
351 0           int setRegister(int fd, int reg, int value, char* name){
352             /*
353             always call dec2bcd(value) before sending
354             in the value to this function
355             */
356              
357 0           char buf[2] = {reg, value};
358              
359 0 0         if ((write(fd, buf, sizeof(buf))) != 2){
360 0           close(fd);
361 0           croak(
362             "Could not write to the %s register: %s\n",
363             name,
364             strerror(errno)
365             );
366             }
367              
368 0           return 0;
369             }
370              
371 0           int setRegisterBits(int fd, int reg, int lsb, int nbits, int value, char* name){
372             /*
373             never call dec2bcd(value) before sending
374             in the value to this function
375             */
376              
377 0           int data = getRegister(fd, reg);
378              
379 0           data = bitSet(data, lsb, nbits, value);
380              
381 0           char buf[2] = {reg, data};
382              
383 0 0         if ((write(fd, buf, sizeof(buf))) != 2){
384 0           croak(
385             "Could not write to the %s register: %s\n",
386             name,
387             strerror(errno)
388             );
389             }
390              
391 0           return 0;
392             }
393              
394 3           int bcd2dec (int num){
395 3           return (((num & 0xF0) >> 4) * 10) + (num & 0x0F);
396             }
397              
398 6           int dec2bcd(int num){
399 6           return((num/10) * 16 + (num%10));
400             }
401              
402 0           void _establishI2C (int fd){
403              
404 0           int buf[1] = { 0x00 };
405              
406 0 0         if (write(fd, buf, 1) != 1){
407 0           close(fd);
408 0           croak("Error: Received no ACK bit, couldn't establish connection!");
409             }
410 0           }
411              
412 0           void _close (int fd){
413 0           close(fd);
414 0           }
415              
416             MODULE = RPi::RTC::DS3231 PACKAGE = RPi::RTC::DS3231
417              
418             PROTOTYPES: DISABLE
419              
420             void
421             setSeconds (fd, value)
422             int fd
423             int value
424              
425             void
426             setMinutes (fd, value)
427             int fd
428             int value
429              
430             void
431             setMilitary (fd, value)
432             int fd
433             int value
434              
435             int
436             getMilitary (fd)
437             int fd
438              
439             void
440             setMeridien (fd, value)
441             int fd
442             int value
443              
444             int
445             getMeridien (fd)
446             int fd
447              
448             int
449             getHour (fd)
450             int fd
451              
452             int
453             getSeconds (fd)
454             int fd
455              
456             int
457             getMinutes (fd)
458             int fd
459              
460             void
461             setHour (fd, value)
462             int fd
463             int value
464              
465             const char*
466             getDayOfWeek (fd)
467             int fd
468              
469             void
470             setDayOfWeek (fd, value)
471             int fd
472             int value
473              
474             int
475             getDayOfMonth (fd)
476             int fd
477              
478             void
479             setDayOfMonth (fd, value)
480             int fd
481             int value
482              
483             int getMonth (fd)
484             int fd
485              
486             void setMonth (fd, value)
487             int fd
488             int value
489              
490             int getYear (fd)
491             int fd
492              
493             void setYear (fd, value)
494             int fd
495             int value
496              
497             float getTemp (fd)
498             int fd
499              
500             int
501             getFh (rtcAddr)
502             int rtcAddr
503              
504             void
505             disableRegisterBit (fd, reg, bit)
506             int fd
507             int reg
508             int bit
509             PREINIT:
510             I32* temp;
511             PPCODE:
512 0           temp = PL_markstack_ptr++;
513 0           disableRegisterBit(fd, reg, bit);
514 0 0         if (PL_markstack_ptr != temp) {
515 0           PL_markstack_ptr = temp;
516 0           XSRETURN_EMPTY;
517             }
518 0           return;
519              
520             void
521             enableRegisterBit (fd, reg, bit)
522             int fd
523             int reg
524             int bit
525             PREINIT:
526             I32* temp;
527             PPCODE:
528 0           temp = PL_markstack_ptr++;
529 0           enableRegisterBit(fd, reg, bit);
530 0 0         if (PL_markstack_ptr != temp) {
531 0           PL_markstack_ptr = temp;
532 0           XSRETURN_EMPTY;
533             }
534 0           return;
535              
536             int
537             getRegister (fd, reg)
538             int fd
539             int reg
540              
541             int
542             getRegisterBit (fd, reg, bit)
543             int fd
544             int reg
545             int bit
546              
547             int
548             getRegisterBits (fd, reg, msb, lsb)
549             int fd
550             int reg
551             int msb
552             int lsb
553              
554             int
555             setRegister (fd, reg, value, name)
556             int fd
557             int reg
558             int value
559             char* name
560              
561             int
562             setRegisterBits(fd, reg, lsb, nbits, value, name)
563             int fd
564             int reg
565             int lsb
566             int nbits
567             int value
568             char* name
569              
570             int
571             bcd2dec (num)
572             int num
573              
574             int
575             dec2bcd (num)
576             int num
577              
578             void
579             _establishI2C (fd)
580             int fd
581              
582             void
583             _close (fd)
584             int fd