esp-idf-ds3231/source/driver.c

441 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 |
(alarm1 ? DS3231_CONTROL_A1IE : 0) |
(alarm2 ? DS3231_CONTROL_A2IE : 0);
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;
}
uint8_t rstatus;
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;
time->tm_year = 100 + BCD_TO_DEC(rtime[6]) + ((rtime[5] & (1 << 7)) ? 100 : 0);
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);
rtime[5] = DEC_TO_BCD(time->tm_mon + 1) + ((time->tm_year >= 200) ? (1 << 7) : 0);
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;
}
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;
}