DS3231.cpp
#incude <DS3231>
DS3231 Datsheet
/*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 )
{
}
/*H********************************************************************
* UTILITIES FROM JeeLabs/Ladyada
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 // NUMBER OF DAYS SINCE 2000/01/01, VALID FOR 2001..2099
daysInMonth[] PROGMEM = { 31,28,31,30,31,30,31,31,30,31,30,31 };
/*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
*****************************************/
/*******************************************************************************
* 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 7 BYTES FROM HERE: SECS REG, MINS REG, HRS, DAYS, MONS AND YRS
_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)
{
byte temp_buffer, hour;
_Wire.beginTransmission( CLOCK_ADDRESS ); // SOME KIND OF SYNC MECHANISM?
_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, amPm;
byte tmpHr;
_Wire.beginTransmission( CLOCK_ADDRESS );
_Wire.write( 0x02 ); // SEND REGISTER #
_Wire.endTransmission();
_Wire.requestFrom( CLOCK_ADDRESS, 1); // READ 1 BYTE FROM RTC
h12 = (_Wire.read() & 0b01000000); // READ 12/*24 FLAG BIT
if( h12 ) // IF H12 IS TRUE, IT'S 12H MODE; FALSE IS 24H
{ // 12 HOUR MODE
amPm = (Hour > 11);
tmpHr = Hour;
if( tmpHr > 11)
tmpHr = tmpHr - 12;
if( tmpHr == 0)
tmpHr = 12;
// BUILD 12HR SetVal: INCL AM/PM BIT and 12/24 HR BIT
tmpHr = decToBcd( tmpHr ) | ( amPm << 5) | 0b01000000;
}
else // 24 HOUR
tmpHr = decToBcd( Hour ) & 0b10111111; // BUILD 24 HR SET VAL
_Wire.beginTransmission( CLOCK_ADDRESS );
_Wire.write( 0x02 );
_Wire.write( tmpHr);
_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 )
temp_buffer = temp_buffer & 0b01111011; // SET ~EOSC & INTCN =0
else
temp_buffer = temp_buffer | 0b10000000; // SET ~EOSC = 1, IGNORE INTCN
frequency = frequency << 3; // SHFT FREQ TO 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)
{ // Oscillator Stop Flag (OSF) is set, so return false
result = false;
}
return( result );
}
/*H********************************************************************
* 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();
}