From 51f02daf45e5cc8215d9d81fdf1316783dc62721 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sun, 18 Jul 2010 22:04:30 +0200 Subject: src/common/rtc: add RTC handling --- src/common/rtc.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/common/rtc.h | 59 ++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 src/common/rtc.c create mode 100644 src/common/rtc.h 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: + * }}} */ +#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, ®, 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: + * }}} */ + +/** 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 */ -- cgit v1.2.3