2021-07-25 18:14:45 +02:00
|
|
|
|
#include "private.h"
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#define F_INIT (1 << 0)
|
|
|
|
|
|
|
|
|
|
struct ds3231_t
|
|
|
|
|
{
|
|
|
|
|
int flags;
|
|
|
|
|
i2c_port_t port;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define ASSERT_DRV() do { \
|
|
|
|
|
if (driver == NULL || !(driver->flags & F_INIT)) { \
|
|
|
|
|
errno = EINVAL; \
|
|
|
|
|
return -1; \
|
|
|
|
|
} \
|
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
DS3231_API ds3231_t ds3231_create(i2c_port_t port)
|
|
|
|
|
{
|
|
|
|
|
if (port > I2C_NUM_MAX)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds3231_t driver = malloc(sizeof(struct ds3231_t));
|
|
|
|
|
if (driver)
|
|
|
|
|
{
|
|
|
|
|
driver->flags = 0;
|
|
|
|
|
driver->port = port;
|
|
|
|
|
}
|
|
|
|
|
return driver;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API void ds3231_destroy(ds3231_t driver)
|
|
|
|
|
{
|
|
|
|
|
free(driver);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_initialize(ds3231_t driver, int *opt_out_osf)
|
|
|
|
|
{
|
|
|
|
|
if (driver == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read status regıister
|
|
|
|
|
uint8_t status;
|
|
|
|
|
if (ds3231_io_read(driver->port, DS3231_REGISTER_STATUS, &status, sizeof status))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opt_out_osf)
|
|
|
|
|
{
|
|
|
|
|
*opt_out_osf = !!(status & DS3231_STATUS_OSF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
driver->flags |= F_INIT;
|
|
|
|
|
|
|
|
|
|
int err;
|
|
|
|
|
{
|
|
|
|
|
// Wait until busy flag clears.
|
|
|
|
|
ds3231_status_t status;
|
|
|
|
|
while(!(err = ds3231_getStatus(driver, &status)) && (status & DS3231_STATUS_BSY))
|
|
|
|
|
{
|
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setSquareWaveOutput(ds3231_t driver, ds3231_rate_t rate, int battery_backed)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
uint8_t control = battery_backed ? DS3231_CONTROL_BBSQW : 0;
|
|
|
|
|
switch (rate)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
case DS3231_RATE_1HZ:
|
|
|
|
|
control |= DS3231_CONTROL_RS_1HZ;
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_RATE_1024HZ:
|
|
|
|
|
control |= DS3231_CONTROL_RS_1024HZ;
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_RATE_4096HZ:
|
|
|
|
|
control |= DS3231_CONTROL_RS_4096HZ;
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_RATE_8192HZ:
|
|
|
|
|
control |= DS3231_CONTROL_RS_8192HZ;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_CONTROL, &control, sizeof control))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setInterrupt(ds3231_t driver, int alarm1, int alarm2)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
uint8_t control =
|
|
|
|
|
DS3231_CONTROL_INTCN |
|
2021-08-21 22:49:58 +02:00
|
|
|
|
(alarm1 ? DS3231_CONTROL_A1IE : 0) |
|
|
|
|
|
(alarm2 ? DS3231_CONTROL_A2IE : 0);
|
2021-07-25 18:14:45 +02:00
|
|
|
|
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_CONTROL, &control, sizeof control))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_getStatus(ds3231_t driver, ds3231_status_t *out_status)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (out_status == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-22 11:30:57 +02:00
|
|
|
|
uint8_t rstatus;
|
2021-07-25 18:14:45 +02:00
|
|
|
|
if (ds3231_io_read(driver->port, DS3231_REGISTER_STATUS, &rstatus, sizeof rstatus))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out_status = (ds3231_status_t)rstatus;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_getAgingOffset(ds3231_t driver, int8_t *out_offset)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (out_offset == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_read(driver->port, DS3231_REGISTER_OFFSET, out_offset, sizeof *out_offset))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setAgingOffset(ds3231_t driver, int8_t offset)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_OFFSET, &offset, sizeof offset))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline static void ds3231_fill_yday(struct tm *time)
|
|
|
|
|
{
|
|
|
|
|
time->tm_yday = time->tm_mday;
|
|
|
|
|
switch(time->tm_mon)
|
|
|
|
|
{
|
|
|
|
|
case 0: // Jan
|
|
|
|
|
break;
|
|
|
|
|
case 1: // Feb
|
|
|
|
|
time->tm_yday += 31; break;
|
|
|
|
|
case 2: // Mar
|
|
|
|
|
time->tm_yday += 59; break;
|
|
|
|
|
case 3: // Apr
|
|
|
|
|
time->tm_yday += 90; break;
|
|
|
|
|
case 4: // May
|
|
|
|
|
time->tm_yday += 120; break;
|
|
|
|
|
case 5: // Jun
|
|
|
|
|
time->tm_yday += 151; break;
|
|
|
|
|
case 6: // Jul
|
|
|
|
|
time->tm_yday += 181; break;
|
|
|
|
|
case 7: // Aug
|
|
|
|
|
time->tm_yday += 212; break;
|
|
|
|
|
case 8: // Sep
|
|
|
|
|
time->tm_yday += 243; break;
|
|
|
|
|
case 9: // Oct
|
|
|
|
|
time->tm_yday += 273; break;
|
|
|
|
|
case 10: // Nov
|
|
|
|
|
time->tm_yday += 304; break;
|
|
|
|
|
case 11: // Dec
|
|
|
|
|
time->tm_yday += 334; break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((time->tm_year % 4) || (time->tm_year == 2100))
|
|
|
|
|
{
|
|
|
|
|
time->tm_yday -= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_getTime(ds3231_t driver, struct tm *time)
|
|
|
|
|
{
|
|
|
|
|
uint8_t rtime[7];
|
|
|
|
|
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (time == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_read(driver->port, DS3231_REGISTER_SECONDS, rtime, sizeof rtime))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time->tm_sec = BCD_TO_DEC(rtime[0]);
|
|
|
|
|
time->tm_min = BCD_TO_DEC(rtime[1]);
|
|
|
|
|
if (rtime[2] & (1 << 6))
|
|
|
|
|
{
|
|
|
|
|
uint8_t bcd = DS3231_12_TO_24(rtime[2]);
|
|
|
|
|
time->tm_hour = BCD_TO_DEC(bcd);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
time->tm_hour = BCD_TO_DEC(rtime[2]);
|
|
|
|
|
}
|
|
|
|
|
time->tm_wday = rtime[3];
|
|
|
|
|
time->tm_mday = BCD_TO_DEC(rtime[4]);
|
|
|
|
|
time->tm_mon = BCD_TO_DEC((rtime[5] & 0x1F)) - 1;
|
2021-07-31 19:08:31 +02:00
|
|
|
|
time->tm_year = 100 + BCD_TO_DEC(rtime[6]) + ((rtime[5] & (1 << 7)) ? 100 : 0);
|
2021-07-25 18:14:45 +02:00
|
|
|
|
ds3231_fill_yday(time);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setTime(ds3231_t driver, const struct tm *time)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (time == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t rtime[7];
|
|
|
|
|
rtime[0] = DEC_TO_BCD(time->tm_sec);
|
|
|
|
|
rtime[1] = DEC_TO_BCD(time->tm_min);
|
|
|
|
|
rtime[2] = DEC_TO_BCD(time->tm_hour);
|
|
|
|
|
rtime[3] = time->tm_wday;
|
|
|
|
|
rtime[4] = DEC_TO_BCD(time->tm_mday);
|
2021-07-31 19:08:31 +02:00
|
|
|
|
rtime[5] = DEC_TO_BCD(time->tm_mon + 1) + ((time->tm_year >= 200) ? (1 << 7) : 0);
|
2021-07-25 18:14:45 +02:00
|
|
|
|
rtime[6] = DEC_TO_BCD(time->tm_year % 100);
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_SECONDS, rtime, sizeof rtime))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setAlarm1(ds3231_t driver, ds3231_alarmType_t type, const struct tm *time)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (type > DS3231_ALARM1_SMHWD || time == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t rtime[4];
|
|
|
|
|
rtime[0] = DEC_TO_BCD(time->tm_sec);
|
|
|
|
|
rtime[1] = DEC_TO_BCD(time->tm_min);
|
|
|
|
|
rtime[2] = DEC_TO_BCD(time->tm_hour);
|
|
|
|
|
switch(type)
|
|
|
|
|
{
|
|
|
|
|
case DS3231_ALARM1_PERSEC:
|
|
|
|
|
rtime[0] |= (1 << 7);
|
|
|
|
|
case DS3231_ALARM1_S:
|
|
|
|
|
rtime[1] |= (1 << 7);
|
|
|
|
|
case DS3231_ALARM1_SM:
|
|
|
|
|
rtime[2] |= (1 << 7);
|
|
|
|
|
case DS3231_ALARM1_SMH:
|
|
|
|
|
rtime[3] = (1 << 7);
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_ALARM1_SMHWD: // Week day.
|
|
|
|
|
rtime[3] = (1 << 6) | DEC_TO_BCD(time->tm_wday);
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_ALARM1_SMHMD: // Month day.
|
|
|
|
|
rtime[3] = DEC_TO_BCD(time->tm_mday);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_ALARM1_SECONDS, rtime, sizeof rtime))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_setAlarm2(ds3231_t driver, ds3231_alarmType_t type, const struct tm *time)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (type > DS3231_ALARM2_MHWD || time == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t rtime[3];
|
|
|
|
|
rtime[0] = DEC_TO_BCD(time->tm_min);
|
|
|
|
|
rtime[1] = DEC_TO_BCD(time->tm_hour);
|
|
|
|
|
switch(type)
|
|
|
|
|
{
|
|
|
|
|
case DS3231_ALARM2_PREMIN:
|
|
|
|
|
rtime[0] |= (1 << 7);
|
|
|
|
|
case DS3231_ALARM2_M:
|
|
|
|
|
rtime[1] |= (1 << 7);
|
|
|
|
|
case DS3231_ALARM2_MH:
|
|
|
|
|
rtime[2] = (1 << 7);
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_ALARM2_MHMD:
|
|
|
|
|
rtime[2] = DEC_TO_BCD(time->tm_mday);
|
|
|
|
|
break;
|
|
|
|
|
case DS3231_ALARM2_MHWD:
|
|
|
|
|
rtime[2] = (1 << 6) | DEC_TO_BCD(time->tm_wday);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
errno = EINVAL;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_ALARM2_MINUTES, rtime, sizeof rtime))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_beginTemperature(ds3231_t driver)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
uint8_t control;
|
|
|
|
|
if (ds3231_io_read(driver->port, DS3231_REGISTER_CONTROL, &control, sizeof control))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
control |= DS3231_CONTROL_CONV;
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_CONTROL, &control, sizeof control))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DS3231_API int ds3231_endTemperature(ds3231_t driver, int16_t *out_temperature)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
if (out_temperature == NULL)
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t status = 0xFF;
|
|
|
|
|
uint8_t tempr[2];
|
|
|
|
|
|
|
|
|
|
while (
|
|
|
|
|
!ds3231_io_read(driver->port, DS3231_REGISTER_CONTROL, &status, sizeof status) &&
|
|
|
|
|
(status & DS3231_RSTATUS_BSY)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
|
|
|
status = 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
(status & DS3231_RSTATUS_BSY) ||
|
|
|
|
|
ds3231_io_read(driver->port, DS3231_REGISTER_TEMP_H, tempr, sizeof tempr)
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out_temperature = (int16_t)((tempr[0] << 8) | (tempr[1]));
|
|
|
|
|
*out_temperature >>= 6;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-08-22 11:30:57 +02:00
|
|
|
|
|
|
|
|
|
int ds3231_clearInt(ds3231_t driver)
|
|
|
|
|
{
|
|
|
|
|
ASSERT_DRV();
|
|
|
|
|
|
|
|
|
|
uint8_t status = (1 << 3);
|
|
|
|
|
if (ds3231_io_write(driver->port, DS3231_REGISTER_STATUS, &status, sizeof status))
|
|
|
|
|
{
|
|
|
|
|
errno = EIO;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|