Northern Widget
DS3231.cpp
/*******************************************************
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 AM/PM (bit 5) flag
in 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 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 constructor DateTime::DateTime (uint32_t t) to resolve a couple
of non-fatal compiler warnings.
David Sparks 08 Sept 2022 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
#elif defined(ESP8266)
#include
#endif
// Changed following to work on 1.0
//#include "WProgram.h"
#include
#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)
{
}
/*F********************************************************************
* Utilities
**********************************************************************/
// 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 };
/*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********************************************************************
* DateTime Info
**********************************************************************/
/*****************************************
Public Functions
*****************************************/
/*******************************************************************************
* TO GET ALL DATE/TIME INFORMATION AT ONCE AND AVOID 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
******************************************************************************/
////////////////////////////////////////////////////////////////////////////////
// DateTime implementation - ignores time zones and DST changes
// NOTE: also ignores leap seconds, see http://en.wikipedia.org/wiki/Leap_second
/*F********************************************************************
*
**********************************************************************/
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); }
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); }
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;
return( (y % 100 || y % 400 == 0)); // only check other, when first failed
}
/*F********************************************************************
* RTC Funcs
**********************************************************************/
/*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 epoch as parameter and feeds RTC
epoch = UnixTime and starts at 01.01.1970 00:00:00
HINT: => AVR time.h Lib is based on 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********************************************************************
*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();
byte temp_buffer = readControlByte( 1 ); // CLEAR OSF FLAG
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********************************************************************
* 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 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********************************************************************
* 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********************************************************************
* 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.
**********************************************************************/
void DS3231::
setClockMode( bool h12 )
{
byte temp_buffer;
_Wire.beginTransmission( CLOCK_ADDRESS ); // Start by reading byte 0x02
_Wire.write( 0x02 );
_Wire.endTransmission();
_Wire.requestFrom( CLOCK_ADDRESS, 1 );
temp_buffer = _Wire.read();
if( h12 ) // Set flag to requested value
temp_buffer = temp_buffer | 0b01000000;
else
temp_buffer = temp_buffer & 0b10111111;
_Wire.beginTransmission( CLOCK_ADDRESS );
_Wire.write( 0x02 ); // Write byte
_Wire.write( temp_buffer );
_Wire.endTransmission();
}
/*F********************************************************************
* Checks internal thermometer on DS3231 and returns the
temperature as a floating-point value.
Updated / modified a tiny bit from "Coding Badly" and "Tri-Again"
http://forum.arduino.cc/index.php/topic,22301.0.html
**********************************************************************/
float DS3231::
getTemperature()
{
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 upper byte, add lower
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;
A1h12 = temp_buffer &0b01000000;
if( A1h12 ) // DETERMINE A1 12/24 MODE
{
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********************************************************************
* 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);
_Wire.write( temp_buffer ); // A1 HOUR IS FIGURED OUT, SEND IT
// 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********************************************************************
* SETS ALARM-2 DATE AND TIME ON DS3231, USING A2* INFORMATION
**********************************************************************/
void DS3231::
setA2Time( byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy
, bool A2h12, bool A2PM)
{
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
{
if( A2Hour > 12 )
{ // START BY CONVERTING EXISTING TIME TO H12, IT WAS GIVEN IN 24H
A2Hour = A2Hour - 12; // OBVIOUSLY ISN'T A H12 TIME, IS IT?
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);
_Wire.write( temp_buffer ); // A2 HOUR IS FIGURED OUT, SEND IT
// 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) // MODIFY CONTROL BYTE
temp_buffer = temp_buffer | 0b00000101;
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 );
if( Alarm == 1 ) // MODIFY CONTROL BYTE
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.
tURNS FLAG OFF, ALSO. DEFAULTS TO CHECKING ALARM 2, UNLESS Alarm == 1.
**********************************************************************/
bool DS3231::
checkIfAlarm( byte Alarm )
{
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********************************************************************
* CHECKS WHETHER ALARM 1 OR ALARM 2 FLAG IS ON, RETURNS T/F ACCORDINGLY
CLEARS FLAG, IF CLEARFLAG IS SET DEFAULTS TO CHECKING ALARM 2, UNLESS Alarm == 1
**********************************************************************/
bool DS3231::
checkIfAlarm( byte Alarm, bool clearflag )
{
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 )
temp_buffer = temp_buffer &0b01111011;// SET ~EOSC TO 0 & INTCN TO ZRO
else
temp_buffer = temp_buffer | 0b10000000;// SET ~EOSC TO 1, LEAVE INTCN ALONE
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********************************************************************
* 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)
result = false; // OSCILLATOR sTOP FLAG (OSF) SET, SO RETURN 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
_Wire.write( 0x0e ); // FIRST CONTROL BYTE
}
_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();
}