|
|
#include "DS3231.h"
|
|
|
#include "i2cMaster.h"
|
|
|
|
|
|
|
|
|
static uint8_t decToBcd(uint8_t);
|
|
|
static uint8_t bcdToDec(uint8_t);
|
|
|
/*
|
|
|
sets up i2c bus and resets any necessary flags. MUST be called before using
|
|
|
the ds3231
|
|
|
*/
|
|
|
void initDS3231(void)
|
|
|
{
|
|
|
initI2C();
|
|
|
|
|
|
// clear any alarms
|
|
|
//ds3231RemoveAlarm(ALARM_1);
|
|
|
//ds3231RemoveAlarm(ALARM_2);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
utility function to set the register pointer on
|
|
|
the ds3231
|
|
|
Param: reg -> the register to be pointed at by the register pointer
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the register provided was invalid
|
|
|
*/
|
|
|
uint8_t setRegisterPointer(uint8_t reg)
|
|
|
{
|
|
|
if(reg < DS3231_REGISTER_SECONDS || reg > DS3231_REGISTER_TEMPERATURE_LSB)
|
|
|
return 1;
|
|
|
|
|
|
i2cStart(DS3231_ADDRESS_WRITE);
|
|
|
i2cWrite(reg);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
utility function to get a registers value (single byte)
|
|
|
Param: reg -> the register to read
|
|
|
Returns: the value of the register (1 byte)
|
|
|
*/
|
|
|
uint8_t getRegisterValue(uint8_t reg)
|
|
|
{
|
|
|
setRegisterPointer(reg);
|
|
|
i2cRepeatStart(DS3231_ADDRESS_READ);
|
|
|
uint8_t registerValue = i2cReadNak();
|
|
|
i2cStop();
|
|
|
|
|
|
return registerValue;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
writes the value to the provided register of the
|
|
|
ds3231
|
|
|
Param: value -> the value to write to the register
|
|
|
reg -> the register to write the value to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the register provided was out of range
|
|
|
*/
|
|
|
uint8_t writeValueThenStop(uint8_t value, uint8_t reg)
|
|
|
{
|
|
|
if(reg < DS3231_REGISTER_SECONDS || reg > DS3231_REGISTER_TEMPERATURE_LSB)
|
|
|
return 1;
|
|
|
|
|
|
setRegisterPointer(reg);
|
|
|
i2cWrite(value);
|
|
|
i2cStop();
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows for alarms to be cleared/removed/deleted from the ds3231. This function clears
|
|
|
the appropriate alarms registers, clears the alarm flag and clears the alarm enable bit.
|
|
|
Removing an alarm permantely deletes the alarm, unlike the `ds3231ClearAlarmFlag` function
|
|
|
which just clears the alarm triggered flag but keeps the alarm
|
|
|
Param: alarm -> the alarm number to clear, e.g. ALARM_2
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) if everything was ok
|
|
|
1 if an invalid alarm number was provided
|
|
|
*/
|
|
|
uint8_t ds3231RemoveAlarm(alarm_number_t alarm)
|
|
|
{
|
|
|
if(alarm < 0 || alarm >= ALARM_NUMBER_T_MAX)
|
|
|
return 1;
|
|
|
|
|
|
// assume alarm 1, if alarm 2 this data gets changed
|
|
|
uint8_t minutesReg = DS3231_REGISTER_ALARM1_MINUTES;
|
|
|
uint8_t hoursReg = DS3231_REGISTER_ALARM1_HOURS;
|
|
|
uint8_t dayDateReg = DS3231_REGISTER_ALARM1_DAY_DATE;
|
|
|
uint8_t enableInterruptFlag = DS3231_CONTROL_A1IE_BIT;
|
|
|
uint8_t alarmFlag = DS3231_STATUS_A1F_BIT;
|
|
|
|
|
|
if(alarm == ALARM_2)
|
|
|
{
|
|
|
minutesReg = DS3231_REGISTER_ALARM2_MINUTES;
|
|
|
hoursReg = DS3231_REGISTER_ALARM2_HOURS;
|
|
|
dayDateReg = DS3231_REGISTER_ALARM2_DAY_DATE;
|
|
|
enableInterruptFlag = DS3231_CONTROL_A2IE_BIT;
|
|
|
alarmFlag = DS3231_STATUS_A2F_BIT;
|
|
|
}
|
|
|
|
|
|
if(alarm == ALARM_1) // only alarm1 has seconds register
|
|
|
writeValueThenStop(0, DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
|
|
|
// clear mins, hours and day/date alarm registers
|
|
|
writeValueThenStop(0, minutesReg);
|
|
|
writeValueThenStop(0, hoursReg);
|
|
|
writeValueThenStop(0, dayDateReg);
|
|
|
|
|
|
// disable interrupts for alarm
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
writeValueThenStop(controlReg & ~enableInterruptFlag, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
// clear alarm flag
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
writeValueThenStop(statusReg & ~alarmFlag, DS3231_REGISTER_STATUS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
sets the global hour mode for the ds3231. The ds3231 offers 2 modes
|
|
|
for storing the hours value in the timekeeping registers and alarm registers,
|
|
|
these are AM/PM mode (uses 12 hours and a AM/PM indicator bit) and 24 hour mode.
|
|
|
|
|
|
This should be called before any other if needing to change the mode to AM/PM, as
|
|
|
24 hour mode is selected by default
|
|
|
*/
|
|
|
void ds3231Use12HourMode(bool use12HourMode)
|
|
|
{
|
|
|
is24HourMode = !use12HourMode;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
ensures the alarm_t struct passed to set an alarm contains valid combinations of values
|
|
|
Param: alarm -> pointer to the alarm the user supplied to be passed to the ds3231
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) if the alarm was valid
|
|
|
1 if an invalid alarmNumber was used
|
|
|
2 if an invalid trigger was used
|
|
|
3 if an invalid second value was used with ALARM1 and A1_SEC_MATCH
|
|
|
4 if an invalid second or minute value was used with ALARM1 and A1_MIN_SEC_MATCH
|
|
|
5 if an invalid second or minute or hour value was used with ALARM1 and A1_HOUR_MIN_SEC_MATCH
|
|
|
6 if an invalid second or minute or hour or day/date value was used with ALARM1 and A1_DAY_DATE_HOUR_MIN_SEC_MATCH
|
|
|
7 if an invalid minute was used with ALARM2 and A2_MIN_MATCH
|
|
|
8 if an invalid minute or hour was used with ALARM2 and A2_HOUR_MIN_MATCH
|
|
|
9 if an invalid minute or hour or day/date was used with ALARM2 and A2_DAY_DATE_HOUR_MIN_MATCH
|
|
|
10 unknown error occurred processing alarm 1
|
|
|
11 unknown error occurred processing alarm 2
|
|
|
12 unknown error occurred after handling alarm 1 or 2
|
|
|
*/
|
|
|
static uint8_t validateAlarm(const alarm_t *alarm)
|
|
|
{
|
|
|
// invalid alarm number
|
|
|
if(alarm->alarmNumber < 0 || alarm->alarmNumber >= ALARM_NUMBER_T_MAX)
|
|
|
return 1;
|
|
|
// invalid trigger
|
|
|
if(alarm->trigger < 0 || alarm->trigger >= ALARM_TRIGGER_T_MAX)
|
|
|
return 2;
|
|
|
|
|
|
|
|
|
if(alarm->alarmNumber == ALARM_1)
|
|
|
{
|
|
|
switch(alarm->trigger)
|
|
|
{
|
|
|
case A1_EVERY_SEC: // no values are needed in this case
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
|
|
|
case A1_SEC_MATCH:
|
|
|
if(alarm->second < 60)
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
return 3;
|
|
|
|
|
|
case A1_MIN_SEC_MATCH:
|
|
|
if(alarm->second < 60 && alarm->minute < 60)
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
return 4;
|
|
|
|
|
|
case A1_HOUR_MIN_SEC_MATCH:
|
|
|
if(alarm->second < 60 && alarm->minute < 60)
|
|
|
if((is24HourMode && alarm->hour < 24) || (!is24HourMode && alarm->hour < 13))
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
return 5;
|
|
|
|
|
|
case A1_DAY_DATE_HOUR_MIN_SEC_MATCH:
|
|
|
if(alarm->second < 60 && alarm->minute < 60)
|
|
|
{
|
|
|
if((is24HourMode && alarm->hour < 24) || (!is24HourMode && alarm->hour < 13))
|
|
|
{
|
|
|
if(alarm->useDay && (alarm->dayDate > 0 && alarm->dayDate < DAY_T_MAX))
|
|
|
{
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
else if(!alarm->useDay && (alarm->dayDate < 32))
|
|
|
{
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return 6;
|
|
|
|
|
|
default:
|
|
|
return 10;
|
|
|
}
|
|
|
}
|
|
|
else // check alarm 2 combinations
|
|
|
{
|
|
|
switch(alarm->trigger)
|
|
|
{
|
|
|
case A2_EVERY_MIN: // no values needed in this case
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
|
|
|
case A2_MIN_MATCH:
|
|
|
if(alarm->minute < 60)
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
return 7;
|
|
|
|
|
|
case A2_HOUR_MIN_MATCH:
|
|
|
if(alarm->minute < 60)
|
|
|
if((is24HourMode && alarm->hour < 24) || (!is24HourMode && alarm->hour < 13))
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
return 8;
|
|
|
|
|
|
case A2_DAY_DATE_HOUR_MIN_MATCH:
|
|
|
if(alarm->minute < 60)
|
|
|
{
|
|
|
if((is24HourMode && alarm->hour < 24) || (!is24HourMode && alarm->hour < 13))
|
|
|
{
|
|
|
if(alarm->useDay && (alarm->dayDate > 0 && alarm->dayDate < DAY_T_MAX))
|
|
|
{
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
else if(!alarm->useDay && (alarm->dayDate < 32))
|
|
|
{
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return 9;
|
|
|
|
|
|
default:
|
|
|
return 11;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return 12;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
sets an alarm on the ds3231. Also ensures INTCN and A1IE / A2IE is set so alarms will function
|
|
|
Param: alarm -> pointer to the alarm struct that contains all the info needed to
|
|
|
set the alarm
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) if the alarm was valid
|
|
|
1 if an invalid alarmNumber was used
|
|
|
2 if an invalid trigger was used
|
|
|
3 if an invalid second value was used with ALARM1 and A1_SEC_MATCH
|
|
|
4 if an invalid second or minute value was used with ALARM1 and A1_MIN_SEC_MATCH
|
|
|
5 if an invalid second or minute or hour value was used with ALARM1 and A1_HOUR_MIN_SEC_MATCH
|
|
|
6 if an invalid second or minute or hour or day/date value was used with ALARM1 and A1_DAY_DATE_HOUR_MIN_SEC_MATCH
|
|
|
7 if an invalid minute was used with ALARM2 and A2_MIN_MATCH
|
|
|
8 if an invalid minute or hour was used with ALARM2 and A2_HOUR_MIN_MATCH
|
|
|
9 if an invalid minute or hour or day/date was used with ALARM2 and A2_DAY_DATE_HOUR_MIN_MATCH
|
|
|
10 unknown error occurred processing alarm 1
|
|
|
11 unknown error occurred processing alarm 2
|
|
|
12 unknown error occurred after handling alarm 1 or 2
|
|
|
*/
|
|
|
uint8_t ds3231SetAlarm(const alarm_t *alarm)
|
|
|
{
|
|
|
uint8_t error = validateAlarm(alarm);
|
|
|
if(error)
|
|
|
return error;
|
|
|
|
|
|
// enable alarm interrupts
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
// ensure INTCN is set for alarms to trigger an interrupt on INTCN/SQW pin
|
|
|
// ensure A1IE / A2IE is enabled for alarm1/2 interrupts
|
|
|
controlReg |= DS3231_CONTROL_INTCN_BIT;
|
|
|
if(alarm->alarmNumber == ALARM_1)
|
|
|
controlReg |= DS3231_CONTROL_A1IE_BIT;
|
|
|
else
|
|
|
controlReg |= DS3231_CONTROL_A2IE_BIT;
|
|
|
writeValueThenStop(controlReg, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
if(alarm->alarmNumber == ALARM_1)
|
|
|
{
|
|
|
switch(alarm->trigger)
|
|
|
{
|
|
|
case A1_EVERY_SEC: // needs a1m1, a1m2, a1m3 and a1m4 all set
|
|
|
// set a1m1
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M1_BIT), DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
// set a1m2
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M2_BIT), DS3231_REGISTER_ALARM1_MINUTES);
|
|
|
// set a1m3
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M3_BIT), DS3231_REGISTER_ALARM1_HOURS);
|
|
|
// set a1m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M4_BIT), DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
break;
|
|
|
|
|
|
case A1_SEC_MATCH: // needs a1m2, a1m3 and a1m4 set
|
|
|
// set seconds
|
|
|
writeValueThenStop(decToBcd(alarm->second), DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
// set a1m2
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M2_BIT), DS3231_REGISTER_ALARM1_MINUTES);
|
|
|
// set a1m3
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M3_BIT), DS3231_REGISTER_ALARM1_HOURS);
|
|
|
// set a1m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M4_BIT), DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
break;
|
|
|
|
|
|
case A1_MIN_SEC_MATCH: // needs a1m3 and a1m4 set
|
|
|
// set seconds
|
|
|
writeValueThenStop(decToBcd(alarm->second), DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
// set mins
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM1_MINUTES);
|
|
|
// set a1m3
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M3_BIT), DS3231_REGISTER_ALARM1_HOURS);
|
|
|
// set a1m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M4_BIT), DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
break;
|
|
|
|
|
|
case A1_HOUR_MIN_SEC_MATCH: // needs a1m4 set
|
|
|
// set seconds
|
|
|
writeValueThenStop(decToBcd(alarm->second), DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
// set mins
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM1_MINUTES);
|
|
|
// set hours
|
|
|
writeValueThenStop(decToBcd(alarm->hour), DS3231_REGISTER_ALARM1_HOURS);
|
|
|
// set a1m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM1_A1M4_BIT), DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
break;
|
|
|
|
|
|
case A1_DAY_DATE_HOUR_MIN_SEC_MATCH: // no a1m* bits needed
|
|
|
// set seconds
|
|
|
writeValueThenStop(decToBcd(alarm->second), DS3231_REGISTER_ALARM1_SECONDS);
|
|
|
// set mins
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM1_MINUTES);
|
|
|
// set hours
|
|
|
writeValueThenStop(decToBcd(alarm->hour), DS3231_REGISTER_ALARM1_HOURS);
|
|
|
|
|
|
// set day/date
|
|
|
if(alarm->useDay)
|
|
|
writeValueThenStop(alarm->dayDate | DS3231_ALARM_DAY_BIT, DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
else
|
|
|
writeValueThenStop(decToBcd(alarm->dayDate), DS3231_REGISTER_ALARM1_DAY_DATE);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: // this should never be reached, here to stop compiler warnings
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else // alarm 2
|
|
|
{
|
|
|
switch(alarm->trigger)
|
|
|
{
|
|
|
case A2_EVERY_MIN: // needs a2m2, a2m3 and a2m4 set
|
|
|
// set a2m2
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M2_BIT), DS3231_REGISTER_ALARM2_MINUTES);
|
|
|
// set a2m3
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M3_BIT), DS3231_REGISTER_ALARM2_HOURS);
|
|
|
// set a2m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M4_BIT), DS3231_REGISTER_ALARM2_DAY_DATE);
|
|
|
break;
|
|
|
|
|
|
case A2_MIN_MATCH: // a2m3 and a2m4 set
|
|
|
// set a2m3
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M3_BIT), DS3231_REGISTER_ALARM2_HOURS);
|
|
|
// set a2m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M4_BIT), DS3231_REGISTER_ALARM2_DAY_DATE);
|
|
|
// set minutes
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM2_MINUTES);
|
|
|
break;
|
|
|
|
|
|
case A2_HOUR_MIN_MATCH: // a2m4 set
|
|
|
// set a2m4
|
|
|
writeValueThenStop(decToBcd(DS3231_ALARM2_A2M4_BIT), DS3231_REGISTER_ALARM2_DAY_DATE);
|
|
|
// set minutes
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM2_MINUTES);
|
|
|
// set hours
|
|
|
writeValueThenStop(decToBcd(alarm->hour), DS3231_REGISTER_ALARM2_HOURS);
|
|
|
break;
|
|
|
|
|
|
case A2_DAY_DATE_HOUR_MIN_MATCH: // no a2m* bits needed, does need day/date bit set
|
|
|
// set minutes
|
|
|
writeValueThenStop(decToBcd(alarm->minute), DS3231_REGISTER_ALARM2_MINUTES);
|
|
|
// set hours
|
|
|
writeValueThenStop(decToBcd(alarm->hour), DS3231_REGISTER_ALARM2_HOURS);
|
|
|
|
|
|
if(alarm->useDay)
|
|
|
writeValueThenStop(alarm->dayDate | DS3231_ALARM_DAY_BIT, DS3231_REGISTER_ALARM2_DAY_DATE);
|
|
|
else
|
|
|
writeValueThenStop(decToBcd(alarm->dayDate), DS3231_REGISTER_ALARM2_DAY_DATE);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: // this should never be reached, here to stop compiler warnings
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ds3231ClearAlarmFlag(alarm->alarmNumber);
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
used to reset an alarms flag (that indicates the alarm was triggered). This function does
|
|
|
NOT delete the alarm, to delete an alarm see the `ds3231RemoveAlarm` function
|
|
|
Param: alarm -> the alarm number flag to be reset, e.g. ALARM_1
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231ClearAlarmFlag(alarm_number_t alarm)
|
|
|
{
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
|
|
|
if(alarm == ALARM_1)
|
|
|
writeValueThenStop(statusReg & ~DS3231_STATUS_A1F_BIT, DS3231_REGISTER_STATUS);
|
|
|
else
|
|
|
writeValueThenStop(statusReg & ~DS3231_STATUS_A2F_BIT, DS3231_REGISTER_STATUS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
convenience function to set the ds3231 time using a single function
|
|
|
Param: hour -> the hour to set the ds3231 to
|
|
|
minute -> the minute to set the ds3231 to
|
|
|
second -> the second to set the ds3231 to
|
|
|
isPM -> used to indicate whether the hours value should be treated as a PM value
|
|
|
this should only be true if 12 hour AM/PM mode is activated and it is a PM value.
|
|
|
By default 12 hour AM/PM mode is NOT enabled
|
|
|
*/
|
|
|
uint8_t ds3231SetTime(uint8_t hour, uint8_t minute, uint8_t second, bool isPM)
|
|
|
{
|
|
|
uint8_t error = DS3231_OPERATION_SUCCESS;
|
|
|
error |= ds3231SetHour(hour, isPM);
|
|
|
error |= ds3231SetMinute(minute);
|
|
|
error |= ds3231SetSecond(second);
|
|
|
|
|
|
return error == DS3231_OPERATION_SUCCESS ? DS3231_OPERATION_SUCCESS : error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the day, date, month, year and century to be set with a single function call
|
|
|
Param: day -> the desired day the DS3231 will be set to
|
|
|
date -> the desired date the DS3231 will be set to
|
|
|
month -> the desired month the DS3231 will be set to
|
|
|
year -> the desired year the DS3231 will be set to
|
|
|
century -> the desired century the DS3231 will be set to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) if everything worked, otherwise a non-zero error
|
|
|
*/
|
|
|
uint8_t ds3231SetFullDate(day_t day, uint8_t date, month_t month, uint8_t year, uint8_t century)
|
|
|
{
|
|
|
uint8_t error = DS3231_OPERATION_SUCCESS;
|
|
|
error |= ds3231SetDay(day);
|
|
|
error |= ds3231SetDate(date);
|
|
|
error |= ds3231SetMonth(month);
|
|
|
error |= ds3231SetYear(year);
|
|
|
ds3231SetCentury(century);
|
|
|
|
|
|
return error == DS3231_OPERATION_SUCCESS ? DS3231_OPERATION_SUCCESS : error;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
checks to see if the CENTURY_BIT bit is set in the MONTH register. If it is then a new century has been entered so the currentCentury counter is incremented.
|
|
|
This function should be called at the start / end of every other function that interacts with the DS3231, otherwise turning a century will be missed. However if it is unlikely that the DS3231 will experience a change in century, this function can be ignored and removed from the rest of the library code
|
|
|
*/
|
|
|
static void checkCentury(void)
|
|
|
{
|
|
|
uint8_t month = getRegisterValue(DS3231_REGISTER_MONTH_CENTURY);
|
|
|
if(month & DS3231_CENTURY_BIT) // entered a new century
|
|
|
{
|
|
|
century++;
|
|
|
month &= ~(DS3231_CENTURY_BIT); // this forces the century bit clear whilst keeping the correct month
|
|
|
ds3231SetMonth((month_t) month);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
Returns: the current century of the DS3231, e.g.
|
|
|
21 = 20xx, 22 = 21xx etc.
|
|
|
*/
|
|
|
uint8_t ds3231GetCentury(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
return century;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
Sets the starting century for the DS3231. The DS3231 doesn't store the century
|
|
|
itself, so the library handles it
|
|
|
*/
|
|
|
void ds3231SetCentury(uint8_t cent)
|
|
|
{
|
|
|
century = cent;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the year to be set on the ds3231
|
|
|
Param: the year to set on the ds3231
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the year is invalid (> 99)
|
|
|
*/
|
|
|
uint8_t ds3231SetYear(uint8_t year)
|
|
|
{
|
|
|
if(year > 99)
|
|
|
return 1;
|
|
|
|
|
|
checkCentury();
|
|
|
writeValueThenStop(decToBcd(year), DS3231_REGISTER_YEAR);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the retreival of the year held by the ds3231
|
|
|
Returns: the year held by the ds3231
|
|
|
*/
|
|
|
uint8_t ds3231GetYear(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t year = getRegisterValue(DS3231_REGISTER_YEAR);
|
|
|
|
|
|
return bcdToDec(year);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
sets the month on the ds3231
|
|
|
Param: month -> the month to set the ds3231 to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the month provided was out of range
|
|
|
*/
|
|
|
uint8_t ds3231SetMonth(month_t month)
|
|
|
{
|
|
|
if(month < 0 || month >= MONTH_T_MAX)
|
|
|
return 1;
|
|
|
|
|
|
writeValueThenStop(decToBcd((uint8_t) month), DS3231_REGISTER_MONTH_CENTURY);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the retreival of the current date held by the ds3231
|
|
|
Returns: the month held by the ds3231
|
|
|
*/
|
|
|
month_t ds3231GetMonth(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t month = getRegisterValue(DS3231_REGISTER_MONTH_CENTURY);
|
|
|
|
|
|
return (month_t) bcdToDec(month);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the date to be set on the ds3231
|
|
|
Param: date -> the date to set the ds3231 to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the date provided was out of range (> 31)
|
|
|
*/
|
|
|
uint8_t ds3231SetDate(uint8_t date)
|
|
|
{
|
|
|
if(date > 31)
|
|
|
return 1;
|
|
|
|
|
|
checkCentury();
|
|
|
writeValueThenStop(decToBcd(date), DS3231_REGISTER_DATE);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the date to be retreived from the ds3231
|
|
|
Returns: the date held by the ds3231
|
|
|
*/
|
|
|
uint8_t ds3231GetDate(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t date = getRegisterValue(DS3231_REGISTER_DATE);
|
|
|
|
|
|
return bcdToDec(date);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the day value to be set for the ds3231
|
|
|
Param: day -> the day to set the ds3231 to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the day provided was invalid
|
|
|
*/
|
|
|
uint8_t ds3231SetDay(day_t day)
|
|
|
{
|
|
|
if(day < 0 || day >= DAY_T_MAX)
|
|
|
return 1;
|
|
|
|
|
|
checkCentury();
|
|
|
writeValueThenStop(decToBcd((uint8_t) day), DS3231_REGISTER_DAY);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the ds3231 day value to be retreived
|
|
|
Returns: the current day value of the ds3231
|
|
|
*/
|
|
|
day_t ds3231GetDay(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t day = getRegisterValue(DS3231_REGISTER_DAY);
|
|
|
|
|
|
return (day_t) bcdToDec(day);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
sets the hours register on the ds3231
|
|
|
Param: hours -> the hours value to set the ds3231 to
|
|
|
isPM -> if using 12 hour mode isPM states whether the hours value
|
|
|
represents a PM time (if true) or AM time (if false).
|
|
|
isPM is ignored if 24 hour mode is being used
|
|
|
Return: DS3231_OPERATION_SUCCESS (0) if setting the hours worked
|
|
|
1 if an invalid hours value was supplied for 24 hour mode
|
|
|
2 if an invalid hours value was supplied for 12 hour mode
|
|
|
*/
|
|
|
uint8_t ds3231SetHour(uint8_t hours, bool isPM)
|
|
|
{
|
|
|
if(is24HourMode && hours > 23)
|
|
|
return 1;
|
|
|
if(!is24HourMode && hours > 12)
|
|
|
return 2;
|
|
|
|
|
|
checkCentury();
|
|
|
uint8_t hoursValue = 0; // used to build the byte to send
|
|
|
if(!is24HourMode) // set special bits for 12 hr mode
|
|
|
{
|
|
|
hoursValue |= DS3231_HOUR_MODE_12_BIT; // bit 6 indicates the mode
|
|
|
if(isPM)
|
|
|
hoursValue |= DS3231_PM_BIT;
|
|
|
}
|
|
|
|
|
|
hoursValue |= decToBcd(hours);
|
|
|
writeValueThenStop(hoursValue, DS3231_REGISTER_HOURS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the ds3231 hour value to be retreived
|
|
|
Returns: the hours value the ds3231 has currently stored
|
|
|
*/
|
|
|
uint8_t ds3231GetHour(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t hours = getRegisterValue(DS3231_REGISTER_HOURS);
|
|
|
|
|
|
return bcdToDec(hours);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the minutes value of the ds3231 to be set
|
|
|
Param: minutes -> the minutes value to set on the ds3231
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the minutes value was invalid (> 59)
|
|
|
*/
|
|
|
uint8_t ds3231SetMinute(uint8_t minutes)
|
|
|
{
|
|
|
if(minutes > 59) // invalid condition
|
|
|
return 1;
|
|
|
|
|
|
checkCentury();
|
|
|
writeValueThenStop(decToBcd(minutes), DS3231_REGISTER_MINUTES);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the minutes value of the ds3231 to be returned
|
|
|
Returns: the minutes value held by the ds3231
|
|
|
*/
|
|
|
uint8_t ds3231GetMinute(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t minutes = getRegisterValue(DS3231_REGISTER_MINUTES);
|
|
|
|
|
|
return bcdToDec(minutes);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the seconds value of the ds3231 to be set
|
|
|
Param: seconds -> the seconds value to pass to the ds3231
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) on success
|
|
|
1 if the seconds value was invalid (> 59)
|
|
|
*/
|
|
|
uint8_t ds3231SetSecond(uint8_t seconds)
|
|
|
{
|
|
|
if(seconds > 59)
|
|
|
return 1; // invalid condition
|
|
|
|
|
|
checkCentury();
|
|
|
writeValueThenStop(decToBcd(seconds), DS3231_REGISTER_SECONDS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the seconds value of the ds3231 to be retreived
|
|
|
Returns: the seconds value the ds3231 is at currently
|
|
|
*/
|
|
|
uint8_t ds3231GetSecond(void)
|
|
|
{
|
|
|
checkCentury();
|
|
|
uint8_t seconds = getRegisterValue(DS3231_REGISTER_SECONDS);
|
|
|
|
|
|
return bcdToDec(seconds);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
sets the oscillator enable bit in the control register to 1, which indicates that when the
|
|
|
ds3231 switches to the battery power supply that the oscillator should stop (therefore
|
|
|
saving the power). This does mean however that no new data is put into the time registers,
|
|
|
essentially disables the time functions of the ds3231
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231DisableOscillatorOnBattery(void)
|
|
|
{
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
writeValueThenStop(controlReg | DS3231_CONTROL_EOSC_BIT, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
enables the oscillator to run when the ds3231 switches to battery mode. By default this is already the case and therefore there is no need to call this function if "ds3231DisableOscillatorOnBattery(void)" has not been called
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231EnableOscillatorOnBattery(void)
|
|
|
{
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
writeValueThenStop(controlReg & ~DS3231_CONTROL_EOSC_BIT, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
enables the battery backed square wave output. Enabling this clears the INTCN bit and
|
|
|
therefore means alarms will not trigger
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0) if everything was ok
|
|
|
1 if an invalid frequency was provided
|
|
|
*/
|
|
|
uint8_t ds3231EnableBBSQW(bbsqw_frequency_t freq)
|
|
|
{
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
controlReg &= ~(DS3231_CONTROL_INTCN_BIT); // clear intc otherwise bbsqw will not work
|
|
|
controlReg |= DS3231_CONTROL_BBQSW_BIT;
|
|
|
switch(freq)
|
|
|
{
|
|
|
case HZ_1: // rs1 and rs2 both clear
|
|
|
controlReg &= ~(DS3231_CONTROL_RS1_BIT);
|
|
|
controlReg &= ~(DS3231_CONTROL_RS2_BIT);
|
|
|
break;
|
|
|
case KHZ_1_024: // rs1 set, rs2 clear
|
|
|
controlReg |= DS3231_CONTROL_RS1_BIT;
|
|
|
controlReg &= ~(DS3231_CONTROL_RS2_BIT);
|
|
|
break;
|
|
|
case KHZ_4_096: // rs1 clear, rs2 set
|
|
|
controlReg |= DS3231_CONTROL_RS2_BIT;
|
|
|
controlReg &= ~(DS3231_CONTROL_RS1_BIT);
|
|
|
break;
|
|
|
case KHZ_8_192: // rs1 and rs2 set
|
|
|
controlReg |= (DS3231_CONTROL_RS1_BIT | DS3231_CONTROL_RS2_BIT);
|
|
|
break;
|
|
|
default:
|
|
|
i2cStop();
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
writeValueThenStop(controlReg, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
the ds3231 updates the temperature values every 64 seconds, however a user can force
|
|
|
a new temperature reading by setting the CONV bit in the CONTROL register
|
|
|
*/
|
|
|
void ds3231ForceTemperatureUpdate(void)
|
|
|
{
|
|
|
bool busy = true;
|
|
|
do // loop until BSY is clear (we can start our conversion)
|
|
|
{
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
|
|
|
if(!(statusReg & DS3231_STATUS_BSY_BIT))
|
|
|
busy = false;
|
|
|
} while(busy);
|
|
|
|
|
|
// set the CONV bit to start a new conversion
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
writeValueThenStop(controlReg | DS3231_CONTROL_CONV_BIT, DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
busy = true;
|
|
|
do // loop until CONV becomes clear (conversion complete)
|
|
|
{
|
|
|
uint8_t controlReg = getRegisterValue(DS3231_REGISTER_CONTROL);
|
|
|
|
|
|
if(!(controlReg & DS3231_CONTROL_CONV_BIT))
|
|
|
busy = false;
|
|
|
} while(busy);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
reads the temperature sensor of the ds3231. The temperature is encoded in a uint16_t with
|
|
|
bits 15 to 8 (0 indexed) representing a SIGNED integer temperature and bits 7 to 6
|
|
|
representing the decimal part of the temperature. For example, if the function
|
|
|
returned...
|
|
|
6464 which is 0001100101000000
|
|
|
Then the top 8 bits (00011001) are the SIGNED integer representation of the temperature,
|
|
|
which is +25
|
|
|
And the following 2 bits (01) are the fractional part of the temperature, which is 0.25
|
|
|
So the tempreature read was +25.25
|
|
|
NOTE: the ds3231 has a valid temperature reading at around 2 seconds after first powering on,
|
|
|
reading the temperature before this will likely lead to an incorrect result
|
|
|
Returns: encoded 10 bit temperature value
|
|
|
*/
|
|
|
static int16_t ds3231_raw_to_temperature(uint8_t temp_msb, uint8_t temp_lsb) {
|
|
|
// ????????? ????? ????? ?? ?????? (??????? ????)
|
|
|
// ???????? ????? ?? 8 ???, ????? ???????? 16-?????? ?????
|
|
|
int16_t raw = (int16_t)((temp_msb << 8) | temp_lsb);
|
|
|
|
|
|
// ???????? ?????? ?? 6 ??? (??????? ??????? ??????? ????)
|
|
|
// ? ???????? ?? 25, ????? ???????? ????? ???? ???????
|
|
|
// (0.25<EFBFBD>C * 100 = 25)
|
|
|
return (raw >> 6) * 25;
|
|
|
}
|
|
|
int16_t ds3231GetTemperature(void)
|
|
|
{
|
|
|
uint8_t temperatureUpper = getRegisterValue(DS3231_REGISTER_TEMPERATURE_MSB);
|
|
|
uint8_t temperatureLower = getRegisterValue(DS3231_REGISTER_TEMPERATURE_LSB);
|
|
|
|
|
|
return ds3231_raw_to_temperature(temperatureUpper, temperatureLower);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
checks if the OSCILLATOR STOPPED FLAG (OSF) is set, if so the oscillator was stopped at some point, therefore the validity of the data held in the ds3231's registers may be at risk.
|
|
|
Also, clears the OSF flag if it was set
|
|
|
Returns: true if the oscillator has stopped at some point, false if it hasn't
|
|
|
*/
|
|
|
bool ds3231HasOscillatorStopped(void)
|
|
|
{
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
|
|
|
bool didStop = false;
|
|
|
if(statusReg & DS3231_STATUS_OSF_BIT) // oscillator stopped flag set
|
|
|
{
|
|
|
didStop = true;
|
|
|
// now reset the flag
|
|
|
writeValueThenStop(statusReg & ~DS3231_STATUS_OSF_BIT, DS3231_REGISTER_STATUS);
|
|
|
}
|
|
|
|
|
|
return didStop;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
enables the 32KHz square wave output signal. The oscillator must be running for this
|
|
|
wave to be output
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231Enable32KHzOutput(void)
|
|
|
{
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
if(statusReg & ~DS3231_STATUS_EN32KHZ_BIT) // wasn't enabled, so enable it
|
|
|
writeValueThenStop(statusReg | DS3231_STATUS_EN32KHZ_BIT, DS3231_REGISTER_STATUS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
disables the 32KHz square wave output signal
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231Disable32KhzOutput(void)
|
|
|
{
|
|
|
uint8_t statusReg = getRegisterValue(DS3231_REGISTER_STATUS);
|
|
|
if(statusReg & DS3231_STATUS_EN32KHZ_BIT) // is enabled, so disable it
|
|
|
writeValueThenStop(statusReg & ~DS3231_STATUS_EN32KHZ_BIT, DS3231_REGISTER_STATUS);
|
|
|
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the aging offset register value to be set
|
|
|
Param: offset -> the value to set the aging offset register to
|
|
|
Returns: DS3231_OPERATION_SUCCESS (0)
|
|
|
*/
|
|
|
uint8_t ds3231SetAgingOffset(int8_t offset)
|
|
|
{
|
|
|
writeValueThenStop(offset, DS3231_REGISTER_AGING_OFFSET);
|
|
|
return DS3231_OPERATION_SUCCESS;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
allows the aging offset register to be read
|
|
|
Returns: the signed value stored in the aging offset register
|
|
|
*/
|
|
|
int8_t ds3231GetAgingOffset(void)
|
|
|
{
|
|
|
return getRegisterValue(DS3231_REGISTER_AGING_OFFSET);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
used to convert normal decimal numbers to BCD numbers
|
|
|
Param: val -> the decimal value
|
|
|
Returns: the BCD representation of val
|
|
|
*/
|
|
|
static uint8_t decToBcd(uint8_t val)
|
|
|
{
|
|
|
return (val / 10 * 16) + (val % 10);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
used to convery binary coded decimal to standard
|
|
|
decimal numbers
|
|
|
Param: val -> the BCD value
|
|
|
Returns: the standard decimal representation of val
|
|
|
*/
|
|
|
static uint8_t bcdToDec(uint8_t val)
|
|
|
{
|
|
|
return (val / 16 * 10) + (val % 16);
|
|
|
}
|