File syn_rtc.c¶
File List > drivers > syn_rtc.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_RTC) || SYN_USE_RTC
#include "../util/syn_assert.h"
#include "syn_rtc.h"
/* ── Days in each month (non-leap year) ─────────────────────────────────── */
static const uint8_t s_days_in_month[12] = {31u, 28u, 31u, 30u, 31u, 30u,
31u, 31u, 30u, 31u, 30u, 31u};
static bool rtc_is_leap(uint16_t year)
{
return ((year % 4u == 0u) && (year % 100u != 0u || year % 400u == 0u));
}
static uint8_t rtc_days_in_month(uint8_t month, uint16_t year)
{
uint8_t d = s_days_in_month[(uint8_t)(month - 1u)];
if (month == 2u && rtc_is_leap(year)) {
d++;
}
return d;
}
bool syn_rtc_is_valid(const SYN_RTC_DateTime *dt)
{
SYN_ASSERT(dt != NULL);
if (dt == NULL)
return false;
if (dt->year < 1970u)
return false;
if (dt->month < 1u || dt->month > 12u)
return false;
if (dt->day < 1u || dt->day > rtc_days_in_month(dt->month, dt->year))
return false;
if (dt->hour > 23u)
return false;
if (dt->minute > 59u)
return false;
if (dt->second > 59u)
return false;
return true;
}
static int32_t s_drift_ppm = 0;
SYN_Status syn_rtc_init(void)
{
s_drift_ppm = 0;
return syn_port_rtc_init();
}
void syn_rtc_set_drift_ppm(int32_t drift_ppm)
{
s_drift_ppm = drift_ppm;
/* Forward to port layer if port provides hardware calibration support */
#if defined(SYN_PORT_HAS_RTC_DRIFT)
syn_port_rtc_set_drift_ppm(drift_ppm);
#endif
}
SYN_Status syn_rtc_get(SYN_RTC_DateTime *dt)
{
SYN_ASSERT(dt != NULL);
SYN_Status status = syn_port_rtc_get(dt);
if (status != SYN_OK)
return status;
if (s_drift_ppm != 0) {
uint32_t epoch = syn_rtc_to_epoch(dt);
/* Apply cross-platform PPM scaling formula */
int64_t comp_s = (int64_t)epoch * 1000000LL / (1000000LL + (int64_t)s_drift_ppm);
syn_rtc_from_epoch((uint32_t)comp_s, dt);
}
return SYN_OK;
}
SYN_Status syn_rtc_set(const SYN_RTC_DateTime *dt)
{
SYN_ASSERT(dt != NULL);
if (!syn_rtc_is_valid(dt)) {
return SYN_INVALID_PARAM;
}
return syn_port_rtc_set(dt);
}
uint32_t syn_rtc_to_epoch(const SYN_RTC_DateTime *dt)
{
SYN_ASSERT(dt != NULL);
/* Count days from 1970-01-01 to the start of dt->year */
uint32_t days = 0u;
uint16_t y;
for (y = 1970u; y < dt->year; y++) {
days += rtc_is_leap(y) ? 366u : 365u;
}
/* Add days for elapsed months in the current year */
uint8_t m;
for (m = 1u; m < dt->month; m++) {
days += (uint32_t)rtc_days_in_month(m, dt->year);
}
/* Add elapsed days in the current month (day is 1-indexed) */
days += (uint32_t)dt->day - 1u;
return days * 86400u + (uint32_t)dt->hour * 3600u + (uint32_t)dt->minute * 60u +
(uint32_t)dt->second;
}
void syn_rtc_from_epoch(uint32_t epoch, SYN_RTC_DateTime *dt)
{
SYN_ASSERT(dt != NULL);
/* Extract time-of-day */
dt->second = (uint8_t)(epoch % 60u);
epoch /= 60u;
dt->minute = (uint8_t)(epoch % 60u);
epoch /= 60u;
dt->hour = (uint8_t)(epoch % 24u);
epoch /= 24u;
/* epoch now = days since 1970-01-01 */
uint32_t days = epoch;
/* Find year */
uint16_t year = 1970u;
for (;;) {
uint32_t days_in_year = rtc_is_leap(year) ? 366u : 365u;
if (days < days_in_year)
break;
days -= days_in_year;
year++;
}
dt->year = year;
/* Find month */
uint8_t month = 1u;
for (;;) {
uint32_t dim = (uint32_t)rtc_days_in_month(month, year);
if (days < dim)
break;
days -= dim;
month++;
}
dt->month = month;
dt->day = (uint8_t)(days + 1u);
}
SYN_Status syn_rtc_set_alarm(const SYN_RTC_DateTime *dt)
{
if (dt == NULL || !syn_rtc_is_valid(dt))
return SYN_INVALID_PARAM;
return syn_port_rtc_set_alarm(dt);
}
SYN_Status syn_rtc_schedule_alarm_seconds(uint32_t seconds_from_now, SYN_RTC_DateTime *alarm_dt)
{
SYN_RTC_DateTime now;
SYN_Status status = syn_rtc_get(&now);
if (status != SYN_OK)
return status;
uint32_t current_epoch = syn_rtc_to_epoch(&now);
uint32_t target_epoch = current_epoch + seconds_from_now;
SYN_RTC_DateTime target_dt;
syn_rtc_from_epoch(target_epoch, &target_dt);
if (alarm_dt != NULL) {
*alarm_dt = target_dt;
}
return syn_rtc_set_alarm(&target_dt);
}
#endif /* SYN_USE_RTC */