#include <ds3231.h>
/*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();
}