DS3231.h
#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 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();
}