ds3231.cpp
#include <ds3231.h>
date2Days time2long DateTime unixtime
bcd2bin bin2bcd isleapYear now
getSecond getMinute getHour getDow
getDate get-month getYear setEpoch
setSecond setMinute setHour setDow
setDate setMonth setYear setClockMode
getTemperature getA1Time getA2Time setA1Time
setA2Time turnOnAlarm turnOffAlarm checkAlarmEnabled
checkIfAlarm enableOscillator enable32kHz oscillatorCheck
 /*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 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 setting of PM flag for 12:xx PM.  These address certain
 DS3231 errors in properly setting the AM/PM (bit 5) flag in 02h register when
passing from AM to PM and PM to AM.  David Merrifield 04/14/2020
Released into public domain.
********************************************************/
#include "DS3231.h"
// These included for DateTime class inclusion; will try to find a way to
// not need them in future...
#if defined(__AVR__)
#include <avr/pgmspace.h>
#elif defined(ESP8266)
#include <pgmspace.h>
#endif
// Changed following to work on 1.0
//#include "WProgram.h"
#include <Arduino.h>

//************************* DEFINES ************************************
#define CLOCK_ADDRESS 0x68
#define SECONDS_FROM_1970_TO_2000 946684800

/*F********************************************************************
* Constructor
**********************************************************************/
DS3231::DS3231():_Wire( Wire ) 
{
    // nothing to do for this constructor.
}
/*F********************************************************************
*
**********************************************************************/
DS3231::DS3231( TwoWire & w):_Wire(w) 
{
}

//             Utilities from JeeLabs/Ladyada

// utility code, some of this could be exposed in 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
static const uint8_t daysInMonth [] PROGMEM = { 
    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// number of days since 2000/01/01, valid for 2001..2099



/*F******************************************************************** * **********************************************************************/ 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; } /***************************************** Public Functions *****************************************/
/******************************************************************************* * Three Parts: * 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( yOff ); 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; } /*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!!! **********************************************************************/ const uint32_t DateTime:: unixtime( void ) { 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******************************************************************** * **********************************************************************/ bool isleapYear( const uint8_t y ) { if( y & 3) // CHECK IF DIVISIBLE BY 4 return( false ); return( y % 100 || y % 400 == 0 );// ONLY CHECK OTHER, WHEN FIRST FAILED }
/*F******************************************************************** * **********************************************************************/ DateTime RTClib:: now( TwoWire & _Wire) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0 ); // This is first register address (Seconds) // 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 ) ); }
/*F******************************************************************** * ERIC'S ORIGINAL CODE FOLLOWS **********************************************************************/ 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) getHour( byte &h12, byte &PM_time) { byte temp_buffer, 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 passes epoch as parameter and feeds RTC epoch = UnixTime and starts at 01.01.1970 00:00:00 **********************************************************************/ void DS3231:: setEpoch( time_t epoch, bool flag_localtime) { 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 + 1); setDate( tmnow.tm_mday); setMonth( tmnow.tm_mon + 1); setYear( tmnow.tm_year - 100); }
/*F******************************************************************** * Sets seconds also resets Oscillator Stop Flag, which is set * whenever power is interrupted. **********************************************************************/ void DS3231:: setSecond( byte Second ) { _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******************************************************************** * Sets minutes **********************************************************************/ void DS3231:: setMinute( byte Minute ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x01 ); _Wire.write( decToBcd( Minute ) ); _Wire.endTransmission(); }
/*F******************************************************************** Sets hour, without changing 12/24h mode. hour must be in 24h format. * setHour revision by David Merrifield 4/14/2020 correcting handling of 12-hour clock **********************************************************************/ void DS3231:: setHour( byte Hour ) { bool h12; byte temp_hour; // Start by figuring out what 12/24 mode is _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x02 ); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1); h12 = (_Wire.read() & 0b01000000); if( h12 ) // if h12 is true, it's 12h mode; false is 24h. { // 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******************************************************************** * Sets Day of Week **********************************************************************/ void DS3231:: setDoW( byte DoW ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x03); _Wire.write( decToBcd( DoW ) ); _Wire.endTransmission(); }
/*F******************************************************************** * Sets Date **********************************************************************/ void DS3231:: setDate( byte Date ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x04 ); _Wire.write( decToBcd( Date ) ); _Wire.endTransmission(); }
/*F******************************************************************** * Sets month **********************************************************************/ void DS3231:: setMonth( byte Month ) { _Wire.beginTransmission( CLOCK_ADDRESS ); _Wire.write( 0x05); _Wire.write( decToBcd( Month ) ); _Wire.endTransmission(); }
/*F******************************************************************** * Sets year **********************************************************************/ void DS3231:: setYear( byte 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 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******************************************************************** * Checks internal thermometer on DS3231 and returns temperature as a floating-point value. **********************************************************************/ float DS3231:: getTemperature() { // 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 // SHIFT UPPER BYTE, ADD LOWER int16_t itemp = ( tMSB << 8 | (tLSB & 0xC0) ); 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 ) 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******************************************************************** * Sets alarm-1 date and time on DS3231, using A1* information **********************************************************************/ void DS3231:: setA1Time( 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); // 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 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 ); } // add in A2M3 bit temp_buffer = temp_buffer | ((AlarmBits & 0b00100000) << 2); // 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******************************************************************** * turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1. **********************************************************************/ void DS3231:: turnOnAlarm( byte Alarm) { byte temp_buffer = readControlByte( 0 ); if( Alarm == 1) temp_buffer = temp_buffer | 0b00000101; // modify control byte else temp_buffer = temp_buffer | 0b00000110; writeControlByte( temp_buffer, 0); }
/*F******************************************************************** * turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1. Leaves interrupt pin alone. **********************************************************************/ void DS3231:: turnOffAlarm( byte Alarm ) { byte temp_buffer = readControlByte( 0 ); // modify control byte if( Alarm == 1) temp_buffer = temp_buffer & 0b11111110; else temp_buffer = temp_buffer & 0b11111101; writeControlByte( temp_buffer, 0); }
/*F******************************************************************** * Checks whether given alarm is enabled. **********************************************************************/ bool DS3231:: checkAlarmEnabled( byte Alarm) { byte result = 0x0; byte temp_buffer = readControlByte( 0 ); if( Alarm == 1 ) result = temp_buffer & 0b00000001; else result = temp_buffer & 0b00000010; return( result ); }
/*F******************************************************************** * Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly. **********************************************************************/ bool DS3231:: checkIfAlarm( byte Alarm ) { // Turns flag off, also. // defaults to checking 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 { // Did alarm 2 go off? result = temp_buffer & 0b00000010; temp_buffer = temp_buffer & 0b11111101; // clear flag } writeControlByte( temp_buffer, 1); return( result ); }
/*F******************************************************************** * Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly **********************************************************************/ bool DS3231:: checkIfAlarm( byte Alarm, bool clearflag) { // Clears flag, if clearflag is set // 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 } if( clearflag ) writeControlByte( temp_buffer, 1); return( result ); }
/*F******************************************************************** * turns oscillator on or off. True is on, false is off. if battery is true, turns on even for battery-only operation, otherwise 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) **********************************************************************/ void DS3231:: enableOscillator( bool TF, bool battery, byte frequency) { 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 temp_buffer = temp_buffer & 0b10111111; // turn off BBSQW flag if( TF ) { // set ~EOSC to 0 and INTCN to zero temp_buffer = temp_buffer & 0b01111011; } else { // set ~EOSC = 1, leave INTCN alone temp_buffer = temp_buffer | 0b10000000; } // shift frequency into bits 3 and 4 and set frequency = frequency << 3; temp_buffer = temp_buffer | frequency; writeControlByte( temp_buffer, 0); // And write control bits }
/*F******************************************************************** * turn 32kHz pin on or off **********************************************************************/ void DS3231:: enable32kHz( bool TF) { byte temp_buffer = readControlByte( 1 ); if( TF ) { temp_buffer = temp_buffer | 0b00001000; // turn on 32kHz pin } else temp_buffer = temp_buffer & 0b11110111; // turn off 32kHz pin writeControlByte( temp_buffer, 1); }
/*F******************************************************************** * Returns false if oscillator has been off for some reason. If this is case, time is probably not correct. **********************************************************************/ bool DS3231:: oscillatorCheck() { byte temp_buffer = readControlByte( 1 ); bool result = true; if( temp_buffer & 0b10000000) { // Oscillator Stop Flag (OSF) is set, so return false result = false; } return( result ); } /*F******************************************************************** * **********************************************************************/ /***************************************** Private Functions *****************************************/
/*F******************************************************************** * Convert normal decimal numbers to binary coded decimal **********************************************************************/ byte DS3231:: decToBcd( byte val ) { return( ( (val/10 * 16) + (val%10) ); }
/*F******************************************************************** * Convert binary coded decimal to normal decimal numbers **********************************************************************/ byte DS3231:: bcdToDec(byte val) { return( (val/16 * 10) + (val%16) ); }
/*F******************************************************************** * Read selected control byte first byte (0) is 0x0e, second (1) is 0x0f **********************************************************************/ byte DS3231:: readControlByte( bool which) { _Wire.beginTransmission( CLOCK_ADDRESS ); if( which ) _Wire.write( 0x0f ); // second control byte else // first control byte _Wire.write( 0x0e); _Wire.endTransmission(); _Wire.requestFrom( CLOCK_ADDRESS, 1); return _Wire.read(); }
/*F******************************************************************** * Write selected control byte, which= false -> 0x0e, true->0x0f. **********************************************************************/ void DS3231:: writeControlByte( byte control, bool which) { _Wire.beginTransmission( CLOCK_ADDRESS ); if( which ) _Wire.write( 0x0f ); else _Wire.write( 0x0e ); _Wire.write( control ); _Wire.endTransmission(); }