summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Schodet2010-07-18 22:04:30 +0200
committerNicolas Schodet2010-07-18 22:04:30 +0200
commit51f02daf45e5cc8215d9d81fdf1316783dc62721 (patch)
tree4d60ba1130cfab3282a57ac8cd81d640be9ce926
parent783e664888d39ef678e5d6bd3578ca07586afc89 (diff)
src/common/rtc: add RTC handling
-rw-r--r--src/common/rtc.c228
-rw-r--r--src/common/rtc.h59
2 files changed, 287 insertions, 0 deletions
diff --git a/src/common/rtc.c b/src/common/rtc.c
new file mode 100644
index 0000000..d3a1c9a
--- /dev/null
+++ b/src/common/rtc.c
@@ -0,0 +1,228 @@
+/* rtc.c */
+/* binwatch - Tiny binary wristwatch. {{{
+ *
+ * Copyright (C) 2010 Nicolas Schodet
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contact :
+ * Web: http://ni.fr.eu.org/
+ * Email: <nico at ni.fr.eu.org>
+ * }}} */
+#include "common.h"
+#include "rtc.h"
+#include "modules/twi/twi.h"
+
+#define RTC_STOP 0x20
+#define RTC_GO 0
+
+#define RTC_ADDR 0xa2
+
+#define RTC_RESET_YEAR 2008
+#define RTC_RESET_MONTH 5
+#define RTC_RESET_WEEKDAY 6
+#define RTC_RESET_DAY 10
+#define RTC_RESET_HOUR 15
+#define RTC_RESET_MINUTE 0
+#define RTC_RESET_SECOND 0
+#define RTC_RESET_GO 0
+
+/** RTC registers. */
+enum
+{
+ RTC_REG_CONTROL1,
+ RTC_REG_CONTROL2,
+ RTC_REG_SECOND,
+ RTC_REG_MINUTE,
+ RTC_REG_HOUR,
+ RTC_REG_DAY,
+ RTC_REG_WEEKDAY,
+ RTC_REG_MONTH_CENTURY,
+ RTC_REG_YEAR,
+ RTC_REG_ALARM_MINUTE,
+ RTC_REG_ALARM_HOUR,
+ RTC_REG_ALARM_DAY,
+ RTC_REG_ALARM_WEEKDAY,
+ RTC_REG_CLKOUT,
+ RTC_REG_TIMER_CONTROL,
+ RTC_REG_TIMER,
+};
+
+/** Convert from binary to BCD, overflow will return 0x99. */
+static uint8_t
+rtc_bin2bcd (uint8_t bin)
+{
+ if (bin > 99)
+ return 0x99;
+ else
+ {
+ uint8_t u = bin % 10;
+ uint8_t d = bin / 10;
+ return d << 4 | u;
+ }
+}
+
+/** Convert from BCD to binary. */
+static uint8_t
+rtc_bcd2bin (uint8_t bcd)
+{
+ uint8_t d = bcd >> 4;
+ uint8_t u = bcd & 0xf;
+ return d * 10 + u;
+}
+
+/** Prepare time buffer from natural time. */
+static void
+rtc_prepare_time (uint8_t *buffer, uint8_t hour, uint8_t minute,
+ uint8_t second)
+{
+ assert (hour < 24);
+ assert (minute < 60);
+ assert (second < 60);
+ buffer[0] = rtc_bin2bcd (second);
+ buffer[1] = rtc_bin2bcd (minute);
+ buffer[2] = rtc_bin2bcd (hour);
+}
+
+/** Prepare date buffer from natural date. */
+static void
+rtc_prepare_date (uint8_t *buffer, uint16_t year, uint8_t month,
+ uint8_t weekday, uint8_t day)
+{
+ /* 1900 could be coded, but the RTC will be wrong concerning the number of
+ * days in February. */
+ assert (year > 1900 && year < 2100);
+ assert (month > 0 && month <= 12);
+ assert (weekday < 7);
+ assert (day > 0 && day <= 31);
+ buffer[0] = rtc_bin2bcd (day);
+ buffer[1] = weekday;
+ buffer[2] = (year >= 2000 ? 0x80 : 0) | rtc_bin2bcd (month);
+ buffer[3] = rtc_bin2bcd (year % 100);
+}
+
+/** Reset RTC, return non zero on success. */
+static uint8_t
+rtc_reset (uint16_t year, uint8_t month, uint8_t weekday, uint8_t day,
+ uint8_t hour, uint8_t minute, uint8_t second, uint8_t go)
+{
+ uint8_t buffer[] = {
+ RTC_REG_CONTROL1,
+ RTC_STOP, 0,
+ 0, 0, 0,
+ 0, 0, 0, 0,
+ 0x80, 0x80, 0x80, 0x80,
+ 0, 0, 0,
+ go ? RTC_GO : RTC_STOP, 0
+ };
+ rtc_prepare_time (buffer + 1 + RTC_REG_SECOND, hour, minute, second);
+ rtc_prepare_date (buffer + 1 + RTC_REG_DAY, year, month, weekday, day);
+ twi_master_send (RTC_ADDR, buffer, sizeof (buffer));
+ return twi_master_wait () == sizeof (buffer);
+}
+
+/** Send register address for future read, return non zero on success. */
+static uint8_t
+rtc_send_addr (uint8_t reg)
+{
+ twi_master_send (RTC_ADDR, &reg, 1);
+ twi_master_wait ();
+ return 1;
+}
+
+uint8_t
+rtc_init (void)
+{
+ uint8_t buffer;
+ twi_init (0);
+ if (!rtc_send_addr (RTC_REG_SECOND))
+ return 0;
+ twi_master_recv (RTC_ADDR, &buffer, 1);
+ if (twi_master_wait () != 1)
+ return 0;
+ if (buffer & 0x80)
+ {
+ /* Low Voltage, reset. */
+ if (!rtc_reset (RTC_RESET_YEAR, RTC_RESET_MONTH, RTC_RESET_WEEKDAY,
+ RTC_RESET_DAY, RTC_RESET_HOUR, RTC_RESET_MINUTE,
+ RTC_RESET_SECOND, RTC_RESET_GO))
+ return 0;
+ return RTC_LOW_VOLTAGE;
+ }
+ else
+ return 1;
+}
+
+void
+rtc_get_time (uint8_t *hour, uint8_t *minute, uint8_t *second)
+{
+ uint8_t buffer[3];
+ rtc_send_addr (RTC_REG_SECOND);
+ twi_master_recv (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+ *second = rtc_bcd2bin (buffer[0] & 0x7f);
+ *minute = rtc_bcd2bin (buffer[1] & 0x7f);
+ *hour = rtc_bcd2bin (buffer[2] & 0x3f);
+}
+
+void
+rtc_get_date (uint16_t *year, uint8_t *month, uint8_t *weekday, uint8_t *day)
+{
+ uint8_t buffer[4];
+ rtc_send_addr (RTC_REG_DAY);
+ twi_master_recv (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+ *day = rtc_bcd2bin (buffer[0] & 0x3f);
+ *weekday = buffer[1] & 0x7;
+ *month = rtc_bcd2bin (buffer[2] & 0x1f);
+ *year = ((buffer[2] & 0x80) ? 2000 : 1900) + rtc_bcd2bin (buffer[3]);
+}
+
+void
+rtc_stop (void)
+{
+ uint8_t buffer[] = { RTC_REG_CONTROL1, RTC_STOP };
+ twi_master_send (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+}
+
+void
+rtc_go (void)
+{
+ uint8_t buffer[] = { RTC_REG_CONTROL1, RTC_GO };
+ twi_master_send (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+}
+
+void
+rtc_set_time (uint8_t hour, uint8_t minute, uint8_t second)
+{
+ uint8_t buffer[4];
+ buffer[0] = RTC_REG_SECOND;
+ rtc_prepare_time (buffer + 1, hour, minute, second);
+ twi_master_send (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+}
+
+void
+rtc_set_date (uint16_t year, uint8_t month, uint8_t weekday, uint8_t day)
+{
+ uint8_t buffer[5];
+ buffer[0] = RTC_REG_DAY;
+ rtc_prepare_date (buffer + 1, year, month, weekday, day);
+ twi_master_send (RTC_ADDR, buffer, sizeof (buffer));
+ twi_master_wait ();
+}
+
diff --git a/src/common/rtc.h b/src/common/rtc.h
new file mode 100644
index 0000000..e379370
--- /dev/null
+++ b/src/common/rtc.h
@@ -0,0 +1,59 @@
+#ifndef rtc_h
+#define rtc_h
+/* rtc.h */
+/* binwatch - Tiny binary wristwatch. {{{
+ *
+ * Copyright (C) 2010 Nicolas Schodet
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Contact :
+ * Web: http://ni.fr.eu.org/
+ * Email: <nico at ni.fr.eu.org>
+ * }}} */
+
+/** Value returned by rtc_init on low voltage detection. */
+#define RTC_LOW_VOLTAGE 2
+
+/** Initialise RTC, return 0 on failure, 1 on success, RTC_LOW_VOLTAGE on
+ * success with low voltage detected. */
+uint8_t
+rtc_init (void);
+
+/** Get current timer. */
+void
+rtc_get_time (uint8_t *hour, uint8_t *minute, uint8_t *second);
+
+/** Get current date. */
+void
+rtc_get_date (uint16_t *year, uint8_t *month, uint8_t *weekday, uint8_t *day);
+
+/** Stop RTC, needed before time or date is changed. */
+void
+rtc_stop (void);
+
+/** Resume RTC operation. */
+void
+rtc_go (void);
+
+/** Set current time. */
+void
+rtc_set_time (uint8_t hour, uint8_t minute, uint8_t second);
+
+/** Set current date. */
+void
+rtc_set_date (uint16_t year, uint8_t month, uint8_t weekday, uint8_t day);
+
+#endif /* rtc_h */