diff --git a/README.md b/README.md new file mode 100644 index 00000000..6e4b9231 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +RTClib +====== + +A fork of Jeelab's fantastic RTC library. + +Forked from adafruit to maniacbug to NorthernWidget, where it is being modifed by awickert to include alarm functions for the DS3234 SPI high-accuracy real-time clock. diff --git a/RTC_DS3234.cpp b/RTC_DS3234.cpp index bee37017..7d4a00d6 100644 --- a/RTC_DS3234.cpp +++ b/RTC_DS3234.cpp @@ -1,6 +1,9 @@ // Code by JeeLabs http://news.jeelabs.org/code/ // Released to the public domain! Enjoy! +// SPI_MODE1 --> SPI_Mode3 by ADW; fails in my test if another SPI device is +// attached and it is in SPI_MODE1, even if that devices CSpin is OUTPUT, HIGH + #if ARDUINO < 100 #include #else @@ -27,6 +30,18 @@ const int SECONDS_W = 0x80; const int EOSC = 7; const int OSF = 7; +// Alarm enable -- mostly commented out becaause 1 and 2 are alarm numbers, so can be written in functions in an intuitive way in decimal +//const int A1IE = 0x01; // bit 0: Alarm1 interrupt enable (1 to enable) +//const int A2IE = 0x02; // bit 1: Alarm2 interrupt enable (1 to enable) +const int INTCN = 0x04; // bit 2: Interrupt control (1 for use of the alarms and to disable square wave) + +// Alarm status +//const int A1F = 0x01; // bit 0: Alarm 1 Flag - (1 if alarm 1 was triggered) +//const int A2F = 0x02; +//const int DS3234_OSF = 0x80; // Not using oscillator stop flag + + + uint8_t RTC_DS3234::begin(void) { pinMode(cs_pin,OUTPUT); @@ -35,7 +50,7 @@ uint8_t RTC_DS3234::begin(void) //Ugh! In order to get this to interop with other SPI devices, //This has to be done in cs() - SPI.setDataMode(SPI_MODE1); + SPI.setDataMode(SPI_MODE3); //Enable oscillator, disable square wave, alarms cs(LOW); @@ -56,7 +71,7 @@ uint8_t RTC_DS3234::begin(void) void RTC_DS3234::cs(int _value) { - SPI.setDataMode(SPI_MODE1); + SPI.setDataMode(SPI_MODE3); digitalWrite(cs_pin,_value); } @@ -84,6 +99,272 @@ void RTC_DS3234::adjust(const DateTime& dt) } +// Alarms from DS3234 library by Petre Rodan, with enabling functions by Andy +// Wickert so the user doesn't have to know which register to use and the +// end code looks cleaner (even if it requires more functions up here) +// CS pin setting removed, since this is part of RTClib, in which these are +// defined already + +void RTC_DS3234::set_alarm_1(const uint8_t s, const uint8_t mi, const uint8_t h, const uint8_t d, const uint8_t * flags) +// flags are: A1M1 (seconds), A1M2 (minutes), A1M3 (hour), +// A1M4 (day) 0 to enable, 1 to disable, DY/DT (dayofweek == 1/dayofmonth == 0) +// +// flags define what calendar component to be checked against the current time in order +// to trigger the alarm - see datasheet +// A1M1 (seconds) (0 to enable, 1 to disable) +// A1M2 (minutes) (0 to enable, 1 to disable) +// A1M3 (hour) (0 to enable, 1 to disable) +// A1M4 (day) (0 to enable, 1 to disable) +// DY/DT (dayofweek == 1/dayofmonth == 0) +// +// So in short, if the mask bits are "1", the alarm will always go off +// regardless of this factor; if the mask bits are 0, they will check your +// value for this. So every minute on the "30 s" requires, e.g., +// set_alarm_1(alarm_pin, 30, 0, 0, 0, {0, 1, 1, 1, 1}) +{ + bool t[4] = { s, mi, h, d }; + uint8_t i; + + for (i = 0; i <= 3; i++) { + cs(LOW); + SPI.transfer(i + 0x87); + if (i == 3) { + SPI.transfer(decToBcd(t[3]) | (flags[3] << 7) | (flags[4] << 6)); + } else + SPI.transfer(decToBcd(t[i]) | (flags[i] << 7)); + cs(HIGH); + } +} + +void RTC_DS3234::set_alarm_2(const uint8_t mi, const uint8_t h, const uint8_t d, + const uint8_t * flags) +// flags are: A2M2 (minutes), A2M3 (hour), A2M4 (day) 0 to enable, 1 to disable, DY/DT (dayofweek == 1/dayofmonth == 0) - +// +// flags define what calendar component to be checked against the current time in order +// to trigger the alarm +// A2M2 (minutes) (0 to enable, 1 to disable) +// A2M3 (hour) (0 to enable, 1 to disable) +// A2M4 (day) (0 to enable, 1 to disable) +// DY/DT (dayofweek == 1/dayofmonth == 0) +// +// See notes from Alarm 1 +{ + uint8_t t[3] = { mi, h, d }; + uint8_t i; + + for (i = 0; i <= 2; i++) { + digitalWrite(cs_pin, LOW); + cs(LOW); + if (i == 2) { + SPI.transfer(decToBcd(t[2]) | (flags[2] << 7) | (flags[3] << 6)); + } else + SPI.transfer(decToBcd(t[i]) | (flags[i] << 7)); + cs(HIGH); + } +} + +void RTC_DS3234::enable_alarm(const uint8_t alarm_number) +// Written like this thanks to the fact that these A1IE and A2IE are binary 1 +// and 2, which are also decimal 1 and 2: so just pass the number of that alarm +// INTCN is the interrupt enable bit +{ + cs(LOW); + set_addr(0x8E, INTCN | alarm_number); + cs(HIGH); +} + +// when the alarm flag is cleared the pulldown on INT is also released +void RTC_DS3234::clear_alarm_flag(const uint8_t alarm_number) +{ + uint8_t reg_val; + cs(LOW); + uint8_t addr_in = get_addr(0x0F); + reg_val = addr_in & ~alarm_number; + set_addr(0x8F, 0); + cs(HIGH); +} + +/* +// Old one in case I did something wrong here +void Logger::DS3234_clear_a1f() +{ + uint8_t reg_val; + + DS3234_A1F = 0x1; + reg_val = DS3234_get_sreg(CSpinRTC) & ~DS3234_A1F; + DS3234_set_sreg(pin, reg_val); +} + +uint8_t Logger::DS3234_get_sreg(const uint8_t pin) +{ + uint8_t rv; + rv = DS3234_get_addr(pin, 0x0f); + return rv; +} + +void DS3234_set_sreg(const uint8_t pin, const uint8_t sreg) +{ + DS3234_set_addr(pin, 0x8F, sreg); +} +*/ + + + +// Temperature +float RTC_DS3234::get_temperature_degC() +{ + float temp_degC; + uint8_t temp_msb, temp_lsb; + int8_t nint; + + cs(LOW); + temp_msb = get_addr(0x11); + temp_lsb = get_addr(0x12) >> 6; + cs(HIGH); + if ((temp_msb & 0x80) != 0) + nint = temp_msb | ~((1 << 8) - 1); // if negative get two's complement + else + nint = temp_msb; + + temp_degC = 0.25 * temp_lsb + nint; + + return temp_degC; +} + +// Addressing + +uint8_t RTC_DS3234::get_addr(const uint8_t addr) +{ + uint8_t rv; + cs(LOW); // Somehow this is still needed in spite of the fact that this works within other functions that already do this... strange + SPI.transfer(addr); + rv = SPI.transfer(0x00); + cs(HIGH); + return rv; +} + +void RTC_DS3234::set_addr(const uint8_t addr, const uint8_t val) +{ + cs(LOW); // Somehow this is still needed in spite of the fact that this works within other functions that already do this... strange + SPI.transfer(addr); + SPI.transfer(val); + cs(HIGH); +} + +// helpers modified from the DS3234 library by Petre Rodan and the DS3231 +// library by Eric Ayars -- just 1 now for setting the alarms + +uint8_t RTC_DS3234::decToBcd(const uint8_t val) +{ + return ((val / 10 * 16) + (val % 10)); +} + +/* +Uncomment if I decide to include the "get" functions. +uint8_t RTC_DS3234::bcdtodec(const uint8_t val) +{ + return ((val / 16 * 10) + (val % 16)); +} +*/ + +// end helpers + +// Clock-setting-related functions, based on Eric Ayars' DS3231 library +// and Andy Wickert's work with it for the ALog Bottle Logger + +void RTC_DS3234::setClockMode(bool h12) { + // sets the mode to 12-hour (true) or 24-hour (false). + // One thing that bothers me about how I've written this is that + // if the read and right happen at the right hourly millisecnd, + // the clock will be set back an hour. Not sure how to do it better, + // though, and as long as one doesn't set the mode frequently it's + // a very minimal risk. + // It's zero risk if you call this BEFORE setting the hour, since + // the setHour() function doesn't change this mode. + + uint8_t temp_buffer; + + cs(LOW); + // Start by reading byte 0x02. + temp_buffer = get_addr(0x02); + // Set the flag to the requested value: + if (h12) { + temp_buffer = temp_buffer | 0x01000000; + } + else { + temp_buffer = temp_buffer & 0x10111111; + } + // Write the byte + set_addr(0x02, temp_buffer); + cs(HIGH); + +} + +void RTC_DS3234::setSecond(byte Second) { + // Sets the seconds + // This function also resets the Oscillator Stop Flag, which is set + // whenever power is interrupted. + set_addr(0x80, decToBcd(Second)); + // Clear oscillator stop flag + byte temp_buffer = get_addr(0x0F); // read the control byte + set_addr(0x8F, temp_buffer & 0b01111111); +} + +void RTC_DS3234::setMinute(uint8_t Minute) { + // Sets the minutes + set_addr(0x81, decToBcd(Minute)); +} + +void RTC_DS3234::setHour(uint8_t Hour) { + // Sets the hour, without changing 12/24h mode. + // The hour must be in 24h format. + + // Start by figuring out what the 12/24 mode is + bool h12 = get_addr(0x02) & 0b01000000; + // if h12 is true, it's 12h mode; false is 24h. + + if (h12) { + // 12 hour + if (Hour > 12) + { + Hour = decToBcd(Hour-12) | 0b01100000; + } + else + { + Hour = decToBcd(Hour) & 0b11011111; + } + } + else + { + // 24 hour + Hour = decToBcd(Hour) & 0b10111111; + } + + set_addr(0x82, Hour); +} + +void RTC_DS3234::setDoW(uint8_t DoW) { + // Sets the Day of Week + set_addr(0x83, decToBcd(DoW)); +} + +void RTC_DS3234::setDate(uint8_t Date) { + // Sets the Date + set_addr(0x84, decToBcd(Date)); +} + +void RTC_DS3234::setMonth(uint8_t Month) { + // Sets the month + set_addr(0x85, decToBcd(Month)); +} + +void RTC_DS3234::setYear(uint8_t Year) { + // Sets the year + set_addr(0x86, decToBcd(Year)); +} + +// End clock-setting functions based on Eric Ayars' DS3231 library + DateTime RTC_DS3234::now() { cs(LOW); diff --git a/RTC_DS3234.h b/RTC_DS3234.h index 00328005..b12f6ae6 100644 --- a/RTC_DS3234.h +++ b/RTC_DS3234.h @@ -16,11 +16,64 @@ class RTC_DS3234 uint8_t isrunning(void); DateTime now(); + // temperature register + float get_temperature_degC(); + + // alarms + // 1 + void set_alarm_1(const uint8_t s, const uint8_t mi, const uint8_t h, const uint8_t d, const uint8_t * flags); + void enable_alarm_1(); + //void get_alarm_1(const uint8_t pin, char *buf, const uint8_t len); + //void clear_a1f(const uint8_t pin); + //uint8_t triggered_a1(const uint8_t pin); + // 2 + void set_alarm_2(const uint8_t mi, const uint8_t h, const uint8_t d, const uint8_t * flags); + void enable_alarm_2(); + //void get_alarm_2(const uint8_t pin, char *buf, const uint8_t len); + //void clear_a2f(const uint8_t pin); + //uint8_t triggered_a2(const uint8_t pin); + // Both + void enable_alarm(const uint8_t alarm_number); // Sets the bits to enable that alarm's interrupt; may inadvertently disable other alarm...? + void clear_alarm_flag(const uint8_t alarm_number); // Clears alarm flags; release pulldown on INTERRUPT pin + + // Time-setting functions + // Note that none of these check for sensibility: You can set the + // date to July 42nd and strange things will probably result. + + void setSecond(uint8_t Second); + // In addition to setting the seconds, this clears the + // "Oscillator Stop Flag". + void setMinute(uint8_t Minute); + // Sets the minute + void setHour(uint8_t Hour); + // Sets the hour + void setDoW(uint8_t DoW); + // Sets the Day of the Week (1-7); + void setDate(uint8_t Date); + // Sets the Date of the Month + void setMonth(uint8_t Month); + // Sets the Month of the year + void setYear(uint8_t Year); + // Last two digits of the year + void setClockMode(bool h12); + // Set 12/24h mode. True is 12-h, false is 24-hour. + + + protected: void cs(int _value); + // control/status register -- might just break out into separate functions though + // void DS3234_set_creg(const uint8_t pin, const uint8_t val); + private: int cs_pin; + // Control register -- get and set bits/bytes + uint8_t get_addr(const uint8_t addr); + void set_addr(const uint8_t addr, const uint8_t val); + // Helpers + uint8_t decToBcd(const uint8_t); + //uint8_t bcdtodec(const uint8_t); // currently unused }; #endif // __RTC_DS3234_H__ diff --git a/examples/datecalc/datecalc.pde b/examples/datecalc/datecalc.ino similarity index 100% rename from examples/datecalc/datecalc.pde rename to examples/datecalc/datecalc.ino diff --git a/examples/ds1307/ds1307.pde b/examples/ds1307/ds1307.ino similarity index 100% rename from examples/ds1307/ds1307.pde rename to examples/ds1307/ds1307.ino diff --git a/examples/ds3234/ds3234.pde b/examples/ds3234/ds3234.ino similarity index 100% rename from examples/ds3234/ds3234.pde rename to examples/ds3234/ds3234.ino diff --git a/examples/set_echo/set_echo.ino b/examples/set_echo/set_echo.ino new file mode 100644 index 00000000..86bcbe7c --- /dev/null +++ b/examples/set_echo/set_echo.ino @@ -0,0 +1,146 @@ +/* + +Sets the time and prints back time stamps for 5 seconds + +Based on DS3231_set.pde +by Eric Ayars +4/11 + +Added printing back of time stamps and increased baud rate +Andy Wickert +5/15/2011 + +Ported to the DS3234, using my modification of RTClib +Contribs in backwards order through time: +Andy Wickert, maniacbug, Petre Rodan, adafruit, jeelabs +5/16/2014 -- whoa, 3 years already! + +*/ + +#include +#include +#include +#include + +RTC_DS3234 Clock(47); + +byte Year; +byte Month; +byte Date; +byte DoW; +byte Hour; +byte Minute; +byte Second; + +bool Century=false; +bool h12; +bool PM; + +void GetDateStuff(byte& Year, byte& Month, byte& Day, byte& DoW, + byte& Hour, byte& Minute, byte& Second) { + // Call this if you notice something coming in on + // the serial port. The stuff coming in should be in + // the order YYMMDDwHHMMSS, with an 'x' at the end. + boolean GotString = false; + char InChar; + byte Temp1, Temp2; + char InString[20]; + + byte j=0; + while (!GotString) { + if (Serial.available()) { + InChar = Serial.read(); + InString[j] = InChar; + j += 1; + if (InChar == 'x') { + GotString = true; + } + } + } + Serial.println(InString); + // Read Year first + Temp1 = (byte)InString[0] -48; + Temp2 = (byte)InString[1] -48; + Year = Temp1*10 + Temp2; + // now month + Temp1 = (byte)InString[2] -48; + Temp2 = (byte)InString[3] -48; + Month = Temp1*10 + Temp2; + // now date + Temp1 = (byte)InString[4] -48; + Temp2 = (byte)InString[5] -48; + Day = Temp1*10 + Temp2; + // now Day of Week + DoW = (byte)InString[6] - 48; + // now Hour + Temp1 = (byte)InString[7] -48; + Temp2 = (byte)InString[8] -48; + Hour = Temp1*10 + Temp2; + // now Minute + Temp1 = (byte)InString[9] -48; + Temp2 = (byte)InString[10] -48; + Minute = Temp1*10 + Temp2; + // now Second + Temp1 = (byte)InString[11] -48; + Temp2 = (byte)InString[12] -48; + Second = Temp1*10 + Temp2; +} + +DateTime now; +const int len = 32; +static char buf[len]; + +void setup() { + // Start the serial port + Serial.begin(57600); + + // Start the I2C interface + SPI.begin(); + Clock.begin(); +} + +void loop() { + + // If something is coming in on the serial line, it's + // a time correction so set the clock accordingly. + if (Serial.available()) { + GetDateStuff(Year, Month, Date, DoW, Hour, Minute, Second); + + Serial.println(); + Serial.println("Transferred time from computer to board:"); + Serial.print(Year); + Serial.print(F("-")); + Serial.print(Month); + Serial.print(F("-")); + Serial.print(Date); + Serial.print(F(" ")); + Serial.print(Hour); //24-hr + Serial.print(F(":")); + Serial.print(Minute); + Serial.print(F(":")); + Serial.println(Second); + Serial.println(); + + Clock.setClockMode(false); // set to 24h + //setClockMode(true); // set to 12h + + Clock.setYear(Year); + Clock.setMonth(Month); + Clock.setDate(Date); + Clock.setDoW(DoW); + Clock.setHour(Hour); + Clock.setMinute(Minute); + Clock.setSecond(Second); + + // Give time at next five seconds + for (int i=0; i<5; i++){ + delay(1000); + now = Clock.now(); + Serial.println(now.unixtime()); + Serial.println(now.toString(buf,len)); + Serial.println(); + } + + } + delay(1000); +} diff --git a/examples/softrtc/softrtc.pde b/examples/softrtc/softrtc.ino similarity index 100% rename from examples/softrtc/softrtc.pde rename to examples/softrtc/softrtc.ino