diff --git a/Universal_Serial_Adapter/Project.h b/Universal_Serial_Adapter/Project.h index 3c68be8..65eaeb0 100644 --- a/Universal_Serial_Adapter/Project.h +++ b/Universal_Serial_Adapter/Project.h @@ -11,7 +11,7 @@ #ifndef Project_h #define Project_h -#define DEBUG false // For controlling debug output via serial +#define DEBUG true // For controlling debug output via serial // Buttons / Joystick / Input Hardware Pinouts #define okButtonPin 22 diff --git a/Universal_Serial_Adapter/RTClib.cpp b/Universal_Serial_Adapter/RTClib.cpp new file mode 100644 index 0000000..4516a57 --- /dev/null +++ b/Universal_Serial_Adapter/RTClib.cpp @@ -0,0 +1,240 @@ +// Code by JeeLabs http://news.jeelabs.org/code/ +// Released to the public domain! Enjoy! + +#include +#include +#include "RTClib.h" + +#define DS1307_ADDRESS 0x68 +#define SECONDS_PER_DAY 86400L + +#define SECONDS_FROM_1970_TO_2000 946684800 + +#if (ARDUINO >= 100) + #include // capital A so it is error prone on case-sensitive filesystems +#else + #include +#endif + +int i = 0; //The new wire library needs to take an int when you are sending for the zero register +//////////////////////////////////////////////////////////////////////////////// +// utility code, some of this could be exposed in the DateTime API if needed + +const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 }; //has to be const or compiler compaints + +// number of days since 2000/01/01, valid for 2001..2099 +static uint16_t date2days(uint16_t y, uint8_t m, uint8_t d) { + if (y >= 2000) + y -= 2000; + uint16_t days = d; + for (uint8_t i = 1; i < m; ++i) + days += pgm_read_byte(daysInMonth + i - 1); + if (m > 2 && y % 4 == 0) + ++days; + return days + 365 * y + (y + 3) / 4 - 1; +} + +static long time2long(uint16_t days, uint8_t h, uint8_t m, uint8_t s) { + return ((days * 24L + h) * 60 + m) * 60 + s; +} + +//////////////////////////////////////////////////////////////////////////////// +// DateTime implementation - ignores time zones and DST changes +// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second + +DateTime::DateTime (uint32_t t) { + t -= SECONDS_FROM_1970_TO_2000; // bring to 2000 timestamp from 1970 + + ss = t % 60; + t /= 60; + mm = t % 60; + t /= 60; + hh = t % 24; + uint16_t days = t / 24; + uint8_t leap; + for (yOff = 0; ; ++yOff) { + leap = yOff % 4 == 0; + if (days < 365 + leap) + break; + days -= 365 + leap; + } + for (m = 1; ; ++m) { + uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1); + if (leap && m == 2) + ++daysPerMonth; + if (days < daysPerMonth) + break; + days -= daysPerMonth; + } + d = days + 1; +} + +DateTime::DateTime (uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) { + if (year >= 2000) + year -= 2000; + yOff = year; + m = month; + d = day; + hh = hour; + mm = min; + ss = sec; +} + +static uint8_t conv2d(const char* p) { + uint8_t v = 0; + if ('0' <= *p && *p <= '9') + v = *p - '0'; + return 10 * v + *++p - '0'; +} + +// A convenient constructor for using "the compiler's time": +// DateTime now (__DATE__, __TIME__); +// NOTE: using PSTR would further reduce the RAM footprint +DateTime::DateTime (const char* date, const char* time) { + // sample input: date = "Dec 26 2009", time = "12:34:56" + yOff = conv2d(date + 9); + // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + switch (date[0]) { + case 'J': m = date[1] == 'a' ? 1 : m = date[2] == 'n' ? 6 : 7; break; + case 'F': m = 2; break; + case 'A': m = date[2] == 'r' ? 4 : 8; break; + case 'M': m = date[2] == 'r' ? 3 : 5; break; + case 'S': m = 9; break; + case 'O': m = 10; break; + case 'N': m = 11; break; + case 'D': m = 12; break; + } + d = conv2d(date + 4); + hh = conv2d(time); + mm = conv2d(time + 3); + ss = conv2d(time + 6); +} + +uint8_t DateTime::dayOfWeek() const { + uint16_t day = date2days(yOff, m, d); + return (day + 6) % 7; // Jan 1, 2000 is a Saturday, i.e. returns 6 +} + +uint32_t DateTime::unixtime(void) const { + uint32_t t; + uint16_t days = date2days(yOff, m, d); + t = time2long(days, hh, mm, ss); + t += SECONDS_FROM_1970_TO_2000; // seconds from 1970 to 2000 + + return t; +} + +//////////////////////////////////////////////////////////////////////////////// +// RTC_DS1307 implementation + +static uint8_t bcd2bin (uint8_t val) { return val - 6 * (val >> 4); } +static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); } + +uint8_t RTC_DS1307::begin(void) { + return 1; +} + + +#if (ARDUINO >= 100) + +uint8_t RTC_DS1307::isrunning(void) { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.write(i); + Wire.endTransmission(); + + Wire.requestFrom(DS1307_ADDRESS, 1); + uint8_t ss = Wire.read(); + return !(ss>>7); +} + +void RTC_DS1307::adjust(const DateTime& dt) { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.write(i); + Wire.write(bin2bcd(dt.second())); + Wire.write(bin2bcd(dt.minute())); + Wire.write(bin2bcd(dt.hour())); + Wire.write(bin2bcd(0)); + Wire.write(bin2bcd(dt.day())); + Wire.write(bin2bcd(dt.month())); + Wire.write(bin2bcd(dt.year() - 2000)); + Wire.write(i); + Wire.endTransmission(); +} + +DateTime RTC_DS1307::now() { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.write(i); + Wire.endTransmission(); + + Wire.requestFrom(DS1307_ADDRESS, 7); + uint8_t ss = bcd2bin(Wire.read() & 0x7F); + uint8_t mm = bcd2bin(Wire.read()); + uint8_t hh = bcd2bin(Wire.read()); + Wire.read(); + uint8_t d = bcd2bin(Wire.read()); + uint8_t m = bcd2bin(Wire.read()); + uint16_t y = bcd2bin(Wire.read()) + 2000; + + return DateTime (y, m, d, hh, mm, ss); +} + +#else + +uint8_t RTC_DS1307::isrunning(void) { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.send(i); + Wire.endTransmission(); + + Wire.requestFrom(DS1307_ADDRESS, 1); + uint8_t ss = Wire.receive(); + return !(ss>>7); +} + +void RTC_DS1307::adjust(const DateTime& dt) { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.send(i); + Wire.send(bin2bcd(dt.second())); + Wire.send(bin2bcd(dt.minute())); + Wire.send(bin2bcd(dt.hour())); + Wire.send(bin2bcd(0)); + Wire.send(bin2bcd(dt.day())); + Wire.send(bin2bcd(dt.month())); + Wire.send(bin2bcd(dt.year() - 2000)); + Wire.send(i); + Wire.endTransmission(); +} + +DateTime RTC_DS1307::now() { + Wire.beginTransmission(DS1307_ADDRESS); + Wire.send(i); + Wire.endTransmission(); + + Wire.requestFrom(DS1307_ADDRESS, 7); + uint8_t ss = bcd2bin(Wire.receive() & 0x7F); + uint8_t mm = bcd2bin(Wire.receive()); + uint8_t hh = bcd2bin(Wire.receive()); + Wire.receive(); + uint8_t d = bcd2bin(Wire.receive()); + uint8_t m = bcd2bin(Wire.receive()); + uint16_t y = bcd2bin(Wire.receive()) + 2000; + + return DateTime (y, m, d, hh, mm, ss); +} + +#endif + + +//////////////////////////////////////////////////////////////////////////////// +// RTC_Millis implementation + +long RTC_Millis::offset = 0; + +void RTC_Millis::adjust(const DateTime& dt) { + offset = dt.unixtime() - millis() / 1000; +} + +DateTime RTC_Millis::now() { + return (uint32_t)(offset + millis() / 1000); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/Universal_Serial_Adapter/RTClib.h b/Universal_Serial_Adapter/RTClib.h new file mode 100644 index 0000000..f5bf9d1 --- /dev/null +++ b/Universal_Serial_Adapter/RTClib.h @@ -0,0 +1,47 @@ +// Code by JeeLabs http://news.jeelabs.org/code/ +// Released to the public domain! Enjoy! + +// Simple general-purpose date/time class (no TZ / DST / leap second handling!) +class DateTime { +public: + DateTime (uint32_t t =0); + DateTime (uint16_t year, uint8_t month, uint8_t day, + uint8_t hour =0, uint8_t min =0, uint8_t sec =0); + DateTime (const char* date, const char* time); + uint16_t year() const { return 2000 + yOff; } + uint8_t month() const { return m; } + uint8_t day() const { return d; } + uint8_t hour() const { return hh; } + uint8_t minute() const { return mm; } + uint8_t second() const { return ss; } + uint8_t dayOfWeek() const; + + // 32-bit times as seconds since 1/1/2000 + long secondstime() const; + // 32-bit times as seconds since 1/1/1970 + uint32_t unixtime(void) const; + +protected: + uint8_t yOff, m, d, hh, mm, ss; +}; + +// RTC based on the DS1307 chip connected via I2C and the Wire library +class RTC_DS1307 { +public: + static uint8_t begin(void); + static void adjust(const DateTime& dt); + uint8_t isrunning(void); + static DateTime now(); +}; + +// RTC using the internal millis() clock, has to be initialized before use +// NOTE: this clock won't be correct once the millis() timer rolls over (>49d?) +class RTC_Millis { +public: + static void begin(const DateTime& dt) { adjust(dt); } + static void adjust(const DateTime& dt); + static DateTime now(); + +protected: + static long offset; +}; diff --git a/Universal_Serial_Adapter/Universal_Serial_Adapter.ino b/Universal_Serial_Adapter/Universal_Serial_Adapter.ino index 84dc949..f819040 100644 --- a/Universal_Serial_Adapter/Universal_Serial_Adapter.ino +++ b/Universal_Serial_Adapter/Universal_Serial_Adapter.ino @@ -8,7 +8,7 @@ attribute. */ -#include +#include #include "Project.h" #include "Config.h" @@ -26,6 +26,7 @@ UI* ui; Config* config; +RTC_DS1307 rtc; // helper for interrupt method call void processSerial() { @@ -33,11 +34,26 @@ void processSerial() { } void setup() { + // Setup various IO busses Serial.begin(115200); + Wire.begin(); + // Ensure RTC is set to run on battery + // clear /EOSC bit + // Sometimes necessary to ensure that the clock + // keeps running on just battery power. Once set, + // it shouldn't need to be reset but it's a good + // idea to make sure. + Wire.beginTransmission(0x68); // address DS3231 + Wire.write(0x0E); // select register + Wire.write(0b00011100); // write register bitmap, bit 7 is /EOSC + Wire.endTransmission(); + + rtc.begin(); + + // Stuff used for serial and UI config = new Config(); config->setDefaults(); - ui = new UI(); // Setup serial IO on an interrupt timer @@ -48,6 +64,22 @@ void setup() { } void loop() { + if (DEBUG) { + DateTime now = RTC.now(); + Serial.print(now.year(), DEC); + Serial.print('/'); + Serial.print(now.month(), DEC); + Serial.print('/'); + Serial.print(now.day(), DEC); + Serial.print(' '); + Serial.print(now.hour(), DEC); + Serial.print(':'); + Serial.print(now.minute(), DEC); + Serial.print(':'); + Serial.print(now.second(), DEC); + Serial.println(); + } + // Serial data is processed via an interrupt so isn't needed in the main loop ui->processInputEvents(); ui->processTimeoutEvents();