You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

928 lines
28 KiB

#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);
}