DS3231.h
#include <DS3231.h>
daysInMonth date2Days time2Long
Public Functions
now() datetime() unixTime() bcd2Bin()
isLeapYear() getSecond() getMinute() getHour()
getDow() getDate() getMonth() getYear()
setEpoch() setSecond() setMinute() setHour()
setDow() setDate() setMonth() setYear()
setClockMode() getTemperature() getA1Time() getA2Time()
setA1Time setA2Time turnOnAlarm turnOffAlarm
checkAlarmEnabled checkIfAlarm checkIfAlarm enableOscillator
enable32khz oscillatorCheck
Private Functions
decToBcd bcdToDec readControlByte writeControlByte
/*H*******************************************************
DS3231.cpp: DS3231 Real-Time Clock library
Eric Ayars 4/1/11
Spliced in DateTime all-at-once reading (to avoid rollover) and unix
 time from Jean-Claude Wippler and Limor Fried
Andy Wickert 5/15/11
Fixed problem with SD processors(no function call) by replacing all 
occurences of the term PM, which
is defined as a macro on SAMD controllers by PM_time.
Simon Gassner 11/28/2017
Fixed setting 12-hour clock in setHour function so that 12:xx AM is not
 stored as 00:xx and corrected
the setting of the PM flag for 12:xx PM.  These address certain DS3231 
errors in properly setting the
AM/PM (bit 5) flag in the 02h register when passing from AM to PM and PM to AM.
David Merrifield 04/14/2020
Changed parameter to uint16_t in isleapYear() because the function performs
 16-bit arithmetic at (y % 400) and because date2days() calls it with a 
uint16_t parameter. Grouped and typecast certain parameters and intermediate
 results in the constructor DateTime::DateTime (uint32_t t) to resolve a 
couple of non-fatal compiler warnings.
David Sparks 08 Sept 2022
Released into the public domain.
********************************************************/
#include "DS3231.h"
#if defined(__AVR__)
#include 
#elif defined(ESP8266)
#include 
#endif
// Changed the following to work on 1.0
//#include "WProgram.h"
#include 


//************************* DEFINES ************************************

//************************* PROTOTYPES ************************************

//************************* VARIABLES ************************************
// These included for DateTime class inclusion; will try to find a way to
// not need them in future...

#define CLOCK_ADDRESS 0x68
#define SECONDS_FROM_1970_TO_2000 946684800

// Constructor
DS3231::DS3231() : _Wire( Wire ) 
{
	// nothing to do for this constructor.
}

DS3231::DS3231( TwoWire  & w ) : _Wire( w ) 
{ }

/*H********************************************************************
* Utilities from JeeLabs/Ladyada
utility code, SOME OF THIS COULD BE EXPOSED IN THE DateTime API IF NEEDED
DS3231 IS SMART ENOUGH TO KNOW THIS, BUT KEEPING IT FOR NOW SO i DON'T HAVE
TO REWRITE THEIR CODE. -ADW
**********************************************************************/



/*F******************************************************************** * **********************************************************************/ static const uint8_t daysInMonth [] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
/*F******************************************************************** * 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 & & isleapYear( y ) ) ++days; return days + 365 * y + (y + 3) / 4 - 1; }
/*F******************************************************************** * **********************************************************************/ static long time2long( uint16_t days, uint8_t h, uint8_t m, uint8_t s) { return(( days * 24L + h) * 60 + m) * 60 + s; }
/*F******************************************************************** * Public Functions **********************************************************************/ /********************************************************************* * TO GET ALL DATE/TIME INFORMATION AT ONCE AND AVOID THE CHANCE OF ROLLOVER * DateTime implementation spliced in here from Jean-Claude Wippler's (JeeLabs) * RTClib, as modified by Limor Fried (Ladyada); source code at: * https://github.com/adafruit/RTClib ********************************************************************/
/*F******************************************************************** * 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 = isleapYear( (uint16_t) yOff ); if( days < (uint16_t)(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; }
/*F******************************************************************** * **********************************************************************/ 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; }
/*F******************************************************************** * SUPPORTED FORMATS ARE DATE "mMM DD YYYY" AND TIME "HH:MM:SS" * (SAME AS __DATE__ AND __TIME__) **********************************************************************/ DateTime::DateTime( const char* date, const char* time) { static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char buff[4] = {'0','0','0','0'}; int y; sscanf( date, "%s %hhu %d", buff, &d, &y); yOff = y >= 2000 ? y - 2000 : y; m = ( strstr( month_names, buff ) - month_names ) / 3 + 1; sscanf(time, "%hhu:%hhu:%hhu", &hh, &mm, &ss); }
/*F******************************************************************** * UNIX time: IS CORRECT ONLY WHEN SET TO UTC!!! **********************************************************************/ 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; }
/*F******************************************************************** * SLIGHTLY MODIFIED FROM JeeLabs / Ladyada GET ALL DATE/TIME AT ONCE TO * AVOID ROLLOVER (E.G., MINUTE/SECOND DON'T MATCH) **********************************************************************/ static uint8_t bcd2bin( uint8_t val ) { return val - 6 * (val >> 4); } /*F******************************************************************** * COMMENTED TO AVOID COMPILER WARNINGS, BUT KEEPING IN CASE WE WANT THIS // EVENTUALLY **********************************************************************/ // static uint8_t bin2bcd( uint8_t val) // { return val + 6 * (val / 10); }
/*F******************************************************************** * Sept 2022 changed parameter to uint16_t from uint8_t **********************************************************************/ bool isleapYear( const uint16_t y ) { if( y & 3 ) // CHECK IF DIVISIBLE BY 4 return( false ); //only check other, when first failed return( y % 100 || y % 400 == 0 ); }
/*F******************************************************************** * **********************************************************************/ DateTime RTClib:: now( TwoWire & _Wire) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0 ); // THIS IS FIRST REGISTER ADDRESS ( sECONDS ) // We'll READ FROM HERE ON FOR 7 BYTES: SECS REG, MINUTES REG, HOURS, DAYs // , MONTHS AND YEARS. _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 7); uint16_t ss = bcd2bin( _Wire.read() & 0x7F ); uint16_t mm = bcd2bin( _Wire.read() ); uint16_t hh = bcd2bin( _Wire.read() ); _Wire.read(); uint16_t d = bcd2bin( _Wire.read()); uint16_t m = bcd2bin( _Wire.read()); uint16_t y = bcd2bin( _Wire.read()) + 2000; return( DateTime( y, m, d, hh, mm, ss) ); } ///// ERIC'S ORIGINAL CODE FOLLOWS /////
/*F******************************************************************** * **********************************************************************/ byte DS3231::getSecond() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x00 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return bcdToDec( _Wire.read() ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getMinute() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x01 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return bcdToDec( _Wire.read() ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getHour( bool & h12, bool & PM_time ) { byte temp_buffer; byte hour; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); temp_buffer = _Wire.read(); h12 = temp_buffer & 0b01000000; if( h12 ) { PM_time = temp_buffer & 0b00100000; hour = bcdToDec( temp_buffer & 0b00011111); } else hour = bcdToDec( temp_buffer & 0b00111111); return( hour ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getDoW() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x03 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return bcdToDec( _Wire.read() ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getDate() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x04 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return bcdToDec( _Wire.read()); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getMonth( bool & Century) { byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x05 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); temp_buffer = _Wire.read(); Century = temp_buffer & 0b10000000; return( bcdToDec( temp_buffer & 0b01111111)) ; }
/*F******************************************************************** * **********************************************************************/ byte DS3231::getYear() { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x06 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return bcdToDec(_Wire.read()); }
/*F******************************************************************** * setEpoch function gives the epoch as parameter and feeds RTC epoch = UnixTime and starts at 01.01.1970 00:00:00 HINT: => the AVR time.h Lib is based on the year 2000 **********************************************************************/ void DS3231::setEpoch( time_t epoch, bool flag_localtime) { #if defined (__AVR__) epoch -= SECONDS_FROM_1970_TO_2000; #endif struct tm tmnow; if( flag_localtime) localtime_r( &epoch, &tmnow); else gmtime_r( &epoch, &tmnow ); setSecond( tmnow.tm_sec ); setMinute( tmnow.tm_min ); setHour( tmnow.tm_hour ); setDoW( tmnow.tm_wday + 1U ); setDate( tmnow.tm_mday ); setMonth( tmnow.tm_mon + 1U ); setYear( tmnow.tm_year - 100U ); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setSecond( byte Second ) { // Sets the seconds // resets Oscillator Stop Flag, which is set whenever power interrupted _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x00 ); _Wire.write(decToBcd( Second ) ); _Wire.endTransmission(); // Clear OSF flag byte temp_buffer = readControlByte( 1 ); writeControlByte( (temp_buffer &0b01111111), 1 ); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setMinute( byte Minute ) { // Sets the minutes _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x01 ); _Wire.write(decToBcd( Minute ) ); _Wire.endTransmission(); }
/*F******************************************************************** * Following setHour revision by David Merrifield 4/14/2020 correcting handling of 12-hour clock **********************************************************************/ void DS3231::setHour( byte Hour ) { // Sets hour, without changing 12/24h mode. hour must be in 24h format bool h12; byte temp_hour; // Start by figuring out what the 12/24 mode is _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); h12 = ( _Wire.read() & 0b01000000 ); // if h12 is true, it's 12h mode; false is 24h. if( h12 ) { // 12 hour bool am_pm = (Hour > 11); temp_hour = Hour; if( temp_hour > 11 ) temp_hour = temp_hour - 12; if( temp_hour == 0 ) temp_hour = 12; temp_hour = decToBcd( temp_hour ) | (am_pm << 5) | 0b01000000; } else { // 24 hour temp_hour = decToBcd( Hour ) & 0b10111111; } _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.write( temp_hour ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setDoW( byte DoW) { // Sets Day of Week _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x03 ); _Wire.write( decToBcd( DoW ) ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setDate( byte Date ) { // Sets Date _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x04 ); _Wire.write( decToBcd( Date ) ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setMonth( byte Month) { // SETS MONTH _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x05 ); _Wire.write( decToBcd( Month ) ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setYear( byte Year) { // SETS YEAR _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x06 ); _Wire.write( decToBcd( Year ) ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setClockMode( bool h12 ) { // SETS MODE TO 12-HOUR (TRUE) OR 24-HOUR (FALSE). ONE THING THAT BOTHERS // ME ABOUT HOW i'VE WRITTEN THIS IS THAT IF READ AND RIGHT HAPPEN AT RIGHT // HOURLY MILLISECND, 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 HOUR //, SINCE setHour() FUNCTION DOESN'T CHANGE THIS MODE. byte temp_buffer; // Start by reading byte 0x02. _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); temp_buffer = _Wire.read(); // Set flag to requested value: if( h12 ) temp_buffer = temp_buffer | 0b01000000; else temp_buffer = temp_buffer & 0b10111111; // Write byte _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.write( temp_buffer ); _Wire.endTransmission(); }
/*F******************************************************************** * **********************************************************************/ float DS3231::getTemperature() { // Checks internal thermometer on the DS3231 and returns temperature as // floating-point value. Updated / modified a tiny bit from "Coding Badly" // and "Tri-Again" http://forum.arduino.cc/index.php/topic,22301.0.html byte tMSB, tLSB; float temp3231; // temp registers (11h-12h) get updated automatically every 64s _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x11 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 2 ); // Should I do more "if available" checks here? if(_Wire.available()) { tMSB = _Wire.read(); //2's complement int portion tLSB = _Wire.read(); //fraction portion int16_t itemp = ( tMSB << 8 | (tLSB & 0xC0));// Shift hi byte, add lo temp3231 = ( (float)itemp / 256.0 ); // Scale and return } else temp3231 = -9999; // Impossible temperature; error value return( temp3231 ); }
/*F******************************************************************** * **********************************************************************/ void DS3231::getA1Time( byte &A1Day, byte &A1Hour, byte &A1Minute , byte &A1Second, byte &AlarmBits, bool &A1Dy, bool &A1h12, bool &A1PM) { byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x07 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 4 ); temp_buffer = _Wire.read(); // Get A1M1 and A1 Seconds A1Second = bcdToDec( temp_buffer & 0b01111111 ); // put A1M1 bit in position 0 of DS3231_AlarmBits. AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >> 7; temp_buffer = _Wire.read(); // Get A1M2 and A1 minutes A1Minute = bcdToDec( temp_buffer & 0b01111111); // put A1M2 bit in position 1 of DS3231_AlarmBits. AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >> 6; temp_buffer = _Wire.read(); // Get A1M3 and A1 Hour // put A1M3 bit in position 2 of DS3231_AlarmBits. AlarmBits = AlarmBits | (temp_buffer &0b10000000) >> 5; // determine A1 12/24 mode A1h12 = temp_buffer &0b01000000; if( A1h12 ) { A1PM = temp_buffer &0b00100000; // DETERMINE AM/PM A1Hour = bcdToDec( temp_buffer &0b00011111 ); // 12-HOUR } else A1Hour = bcdToDec( temp_buffer &0b00111111); // 24-HOUR temp_buffer = _Wire.read(); // GET A1M4 AND A1 DAY/DATE // put A1M3 bit in position 3 of DS3231_AlarmBits. AlarmBits = AlarmBits | ( temp_buffer &0b10000000 ) >> 4; // determine A1 day or date flag A1Dy = (temp_buffer &0b01000000) >> 6; if( A1Dy ) // ALARM IS BY DAY OF WEEK, NOT DATE A1Day = bcdToDec( temp_buffer & 0b00001111 ); else // ALARM IS BY DATE, NOT DAY OF WEEK A1Day = bcdToDec(temp_buffer & 0b00111111); }
/*F******************************************************************** * **********************************************************************/ void DS3231::getA1Time( byte &A1Day, byte &A1Hour, byte &A1Minute , byte &A1Second, byte &AlarmBits, bool &A1Dy, bool &A1h12 , bool &A1PM, bool clearAlarmBits) { if( clearAlarmBits ) AlarmBits = 0x0; getA1Time( A1Day, A1Hour, A1Minute, A1Second, AlarmBits, A1Dy, A1h12 , A1PM); }
/*F******************************************************************** * **********************************************************************/ void DS3231::getA2Time( byte & A2Day, byte &A2Hour, byte &A2Minute , byte &AlarmBits, bool &A2Dy, bool &A2h12, bool &A2PM) { byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x0b ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 3 ); temp_buffer = _Wire.read(); // GET A2M2 AND A2 MINUTES A2Minute = bcdToDec( temp_buffer &0b01111111 ); // PUT A2M2 BIT IN POSITION 4 OF DS3231_AlarmBits AlarmBits = AlarmBits | (temp_buffer &0b10000000)>>3; temp_buffer = _Wire.read(); // GET A2M3 AND A2 HOUR // PUT A2M3 BIT IN POSITION 5 OF DS3231_AlarmBits AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>2; // DETERMINE A2 12/24 MODE A2h12 = temp_buffer & 0b01000000; if( A2h12 ) { A2PM = temp_buffer & 0b00100000; // DETERMINE AM/PM A2Hour = bcdToDec( temp_buffer & 0b00011111 ); // 12-HOUR } else A2Hour = bcdToDec( temp_buffer & 0b00111111 ); // 24-HOUR temp_buffer = _Wire.read(); // GET A2M4 AND A1 DAY/DATE // PUT A2M4 BIT IN POSITION 6 OF DS3231_AlarmBits AlarmBits = AlarmBits | ( temp_buffer & 0b10000000 ) >> 1; // DETERMINE A2 DAY OR DATE FLAG A2Dy = (temp_buffer & 0b01000000) >> 6; if( A2Dy ) // ALARM IS BY DAY OF WEEK, NOT DATE A2Day = bcdToDec(temp_buffer & 0b00001111); else // ALARM IS BY DATE, NOT DAY OF WEEK A2Day = bcdToDec( temp_buffer & 0b00111111 ); } }
/*F******************************************************************** * **********************************************************************/ void DS3231::getA2Time( byte &A2Day, byte &A2Hour, byte &A2Minute , byte &AlarmBits, bool &A2Dy, bool &A2h12, bool &A2PM, bool clearAlarmBits) { if( clearAlarmBits ) AlarmBits = 0x0; getA2Time( A2Day, A2Hour, A2Minute, AlarmBits, A2Dy, A2h12, A2PM ); }
/*F******************************************************************** * **********************************************************************/ void DS3231::setA1Time( byte A1Day, byte A1Hour, byte A1Minute, byte A1Second , byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) { // SETS ALARM-1 DATE AND TIME ON DS3231, USING A1* INFORMATION byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x07 ); // A1 STARTS AT 07h // SEND A1 SECOND AND A1M1 _Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7)); // SEND A1 MINUTE AND A1M2 _Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6)); // FIGURE OUT A1 HOUR if( A1h12 ) { // START BY CONVERTING EXISTING TIME TO H12 IF IT WAS GIVEN IN 24H if( A1Hour > 12 ) { // WELL, THEN, THIS OBVIOUSLY ISN'T A h12 TIME, IS IT? A1Hour = A1Hour - 12; A1PM = true; } if( A1PM ) { // AFTERNOON // CONVERT THE HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd(A1Hour) | 0b01100000; } else { // MORNING // CONVERT HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd(A1Hour) | 0b01000000; } } else // NOW FOR 24H temp_buffer = decToBcd( A1Hour ); temp_buffer = temp_buffer | (( AlarmBits & 0b00000100 ) << 5 ); // A1 HOUR IS FIGURED OUT, SEND IT _Wire.write( temp_buffer ); // FIGURE OUT A1 DAY/DATE AND A1M4 temp_buffer = ((AlarmBits & 0b00001000 ) << 4) | decToBcd( A1Day ); if( A1Dy ) // SET A1 DAY/DATE FLAg (OTHERWISE IT'S ZERO) temp_buffer = temp_buffer | 0b01000000; _Wire.write( temp_buffer ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * **********************************************************************/ void DS3231::setA2Time( byte A2Day, byte A2Hour, byte A2Minute , byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) { // SETS Alarm-2 DATE AND TIME ON DS3231, USING A2* INFORMATION byte temp_buffer; _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x0b ); // A1 STARTS AT 0bh // SEND A2 MINUTE AND A2M2 _Wire.write( decToBcd( A2Minute ) | ((AlarmBits & 0b00010000) << 3)); if( A2h12 ) // FIGURE OUT A2 HOUR { // START BY CONVERTING EXISTING TIME TO H12 IF IT WAS GIVEN IN 24H if( A2Hour > 12 ) { // WELL, THEN, THIS OBVIOUSLY ISN'T A H12 TIME, IS IT? A2Hour = A2Hour - 12; A2PM = true; } if( A2PM ) // AFTERNOON { // CONVERT HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd(A2Hour) | 0b01100000; } else // MORNING { // CONVERT HOUR TO BCD AND ADD APPROPRIATE FLAGS temp_buffer = decToBcd(A2Hour) | 0b01000000; } } else // NOW FOR 24h temp_buffer = decToBcd( A2Hour ); temp_buffer = temp_buffer | (( AlarmBits & 0b00100000 ) << 2); // ADD IN A2M3 BIT // A2 HOUR IS FIGURED OUT, SEND IT _Wire.write( temp_buffer ); // FIGURE OUT A2 DAY/DATE AND A2M4 temp_buffer = (( AlarmBits & 0b01000000) << 1) | decToBcd( A2Day ); if( A2Dy ) // SET A2 DAY/DATE FLAG (oTHERWISE IT'S ZERO temp_buffer = temp_buffer | 0b01000000; _Wire.write( temp_buffer ); _Wire.endTransmission(); // ALL DONE! }
/*F******************************************************************** * **********************************************************************/ void DS3231::turnOnAlarm( byte Alarm ) { // TURNS ON ALARM NUMBER "Alarm". DEFAULTS TO 2 IF ALARM IS NOT 1 byte temp_buffer = readControlByte( 0 ); if( Alarm == 1) // MODIFY CONTROL BYTE temp_buffer = temp_buffer | 0b00000101; else temp_buffer = temp_buffer | 0b00000110; writeControlByte( temp_buffer, 0 ); }
/*F******************************************************************** * **********************************************************************/ void DS3231::turnOffAlarm( byte Alarm ) { // TURNS OFF ALARM NUMBER "Alarm". DEFAULTS TO 2 IF aLARM IS NOT 1 // LEAVES INTERRUPT PIN ALONE byte temp_buffer = readControlByte( 0 ); if( Alarm == 1) // MODIFY CONTROL BYTE temp_buffer = temp_buffer & 0b11111110; else temp_buffer = temp_buffer & 0b11111101; writeControlByte( temp_buffer, 0 ); }
/*F******************************************************************** * **********************************************************************/ bool DS3231::checkAlarmEnabled( byte Alarm ) { // CHECKS WHETHER THE GIVEN ALARM IS ENABLED byte result = 0x0; byte temp_buffer = readControlByte( 0 ); if( Alarm == 1) result = temp_buffer & 0b00000001; else result = temp_buffer & 0b00000010; return( result ); }
/*F******************************************************************** * **********************************************************************/ bool DS3231::checkIfAlarm( byte Alarm ) { // CHECKS WHETHER ALARM 1 OR ALARM 2 FLAG IS ON, RETURNS T/F ACCORDINGLY // TURNS FLAG OFF, ALSO DEFAULTS TO CHECKING ALARM 2, UNLESS aLARM == 1 byte result; byte temp_buffer = readControlByte( 1 ); if( Alarm == 1 ) { // DID ALARM 1 GO OFF? result = temp_buffer & 0b00000001; temp_buffer = temp_buffer &0b11111110; // CLEAR FLAG } else { // DID ALARM 2 GO OFF? result = temp_buffer & 0b00000010; temp_buffer = temp_buffer & 0b11111101; // CLEAR FLAG } writeControlByte( temp_buffer, 1 ); return( result ); }
/*F******************************************************************** * **********************************************************************/ bool DS3231::checkIfAlarm( byte Alarm, bool clearflag ) { // CHECKS WHETHER ALARM 1 OR ALARM 2 FLAG IS ON, RETURNS T/F ACCORDINGLY. // CLEARS FLAG, IF CLEARFLAG SET, DFLTS TO CHKNG ALARM 2, UNLESS aLARM == 1. byte result; byte temp_buffer = readControlByte( 1 ); if( Alarm == 1 ) { result = temp_buffer & 0b00000001; // DID ALARM 1 GO OFF? temp_buffer = temp_buffer & 0b11111110; // CLEAR FLAG } else { result = temp_buffer & 0b00000010; // DID ALARM 2 GO OFF? temp_buffer = temp_buffer & 0b11111101; // CLEAR FLAG } if( clearflag ) writeControlByte( temp_buffer, 1 ); return result; }
/*F******************************************************************** * **********************************************************************/ void DS3231::enableOscillator( bool TF, bool battery, byte frequency ) { // TURNS OSCILLATOR ON OR OFF. TRUE IS ON, FALSE IS OFF. IF BATTERY TRUE, // TURNS ON EVEN FOR BATTERY-ONLY OPERATION, ELSE TURNS OFF IF vCC IS OFF // frequency must be 0, 1, 2, or 3. // 0 = 1 Hz // 1 = 1.024 kHz // 2 = 4.096 kHz // 3 = 8.192 kHz (Default if frequency byte is out of range) if( frequency > 3) frequency = 3; // READ CONTROL BYTE IN, BUT ZERO OUT CURRENT STATE OF RS2 AND RS1. byte temp_buffer = readControlByte( 0 ) & 0b11100111; if( battery ) temp_buffer = temp_buffer | 0b01000000; // TURN ON BBSQW FLAG else // TURN OFF BBSQW FLAG temp_buffer = temp_buffer & 0b10111111; if( TF ) temp_buffer = temp_buffer & 0b01111011; // SET ~EOSC = 0 & INTCN = 0 else // SET ~EOSC TO 1, LEAVE INTCN AS IS temp_buffer = temp_buffer | 0b10000000; frequency = frequency << 3; // SHIFT FREQUENCY INTO BITS 3 AND 4 AND SET temp_buffer = temp_buffer | frequency; writeControlByte( temp_buffer, 0 ); // AND WRITE CONTROL BITS }
/*F******************************************************************** * **********************************************************************/ void DS3231::enable32kHz( bool TF) { // turn 32kHz pin on or off byte temp_buffer = readControlByte(1); if( TF ) // TURN ON 32kHz PIN temp_buffer = temp_buffer | 0b00001000; else // TURN OFF 32kHz PIN temp_buffer = temp_buffer & 0b11110111; writeControlByte( temp_buffer, 1 ); }
/*F******************************************************************** * **********************************************************************/ bool DS3231::oscillatorCheck() { // Returns false if oscillator has been off for some reason. If this is // case, the time is probably not correct. byte temp_buffer = readControlByte( 1 ); bool result = true; if( temp_buffer &0b10000000) result = false; // OSCILLATOR STOP FLAG (OSF) IS SET, SO RETURN FALSE return result; }
/*F******************************************************************** * Private Functions **********************************************************************/
/*F******************************************************************** * **********************************************************************/ byte DS3231::decToBcd( byte val ) { // CONVERT NORMAL DECIMAL NUMBERS TO BINARY CODED DECIMAL return( (val/10*16) + (val%10) ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::bcdToDec( byte val) { // CONVERT BINARY CODED DECIMAL TO NORMAL DECIMAL NUMBERS return ( (val/16*10) + (val%16) ); }
/*F******************************************************************** * **********************************************************************/ byte DS3231::readControlByte( bool which) { // READ SELECTED CONTROL BYTE FIRST BYTE (0) IS 0X0E, SECOND (1) IS 0X0f _Wire.beginTransmission( CLOCK_ADDRESS ); if( which ) // SECOND CONTROL BYTE _Wire.write( 0x0f ); else // FIRST CONTROL BYTE _Wire.write( 0x0e ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1 ); return _Wire.read(); }
/*F******************************************************************** * **********************************************************************/ void DS3231::writeControlByte( byte control, bool which) { // WRITE SELECTED CONTROL BYTE. WHICH=FALSE -> 0X0E, TRUE->0X0F _Wire.beginTransmission( CLOCK_ADDRESS ); if( which ) _Wire.write( 0x0f ); else _Wire.write( 0x0e ); _Wire.write( control ); _Wire.endTransmission(); }