Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support both struct timespec and struct tm variants of the aon timer APIs #2079

Merged
merged 7 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 38 additions & 16 deletions src/common/pico_util/datetime.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
#include "pico/util/datetime.h"

#include <stdio.h>
#if !PICO_ON_DEVICE && __APPLE__
// if we're compiling with LLVM on Apple, __weak does something else, but we don't care about overriding these anyway on host builds
#define __datetime_weak
#else
#define __datetime_weak __weak
#endif

__datetime_weak struct tm * pico_localtime_r(const time_t *time, struct tm *tm) {
kilograham marked this conversation as resolved.
Show resolved Hide resolved
return localtime_r(time, tm);
}

__datetime_weak time_t pico_mktime(struct tm *tm) {
return mktime(tm);
}

#if PICO_INCLUDE_RTC_DATETIME
#include <stdio.h>

static const char *DATETIME_MONTHS[12] = {
"January",
"February",
Expand Down Expand Up @@ -41,31 +56,38 @@ void datetime_to_str(char *buf, uint buf_size, const datetime_t *t) {
t->year);
};

void datetime_to_tm(const datetime_t *dt, struct tm *tm) {
tm->tm_year = dt->year - 1900;
tm->tm_mon = dt->month - 1;
tm->tm_mday = dt->day;
tm->tm_hour = dt->hour;
tm->tm_min = dt->min;
tm->tm_sec = dt->sec;
}

void tm_to_datetime(const struct tm *tm, datetime_t *dt) {
dt->year = (int16_t) (tm->tm_year + 1900); // 0..4095
dt->month = (int8_t) (tm->tm_mon + 1); // 1..12, 1 is January
dt->day = (int8_t) tm->tm_mday; // 1..28,29,30,31 depending on month
dt->dotw = (int8_t) tm->tm_wday; // 0..6, 0 is Sunday
dt->hour = (int8_t) tm->tm_hour; // 0..23
dt->min = (int8_t) tm->tm_min; // 0..59
dt->sec = (int8_t) tm->tm_sec; // 0..59
}

bool time_to_datetime(time_t time, datetime_t *dt) {
struct tm local;
if (localtime_r(&time, &local)) {
dt->year = (int16_t) (local.tm_year + 1900); // 0..4095
dt->month = (int8_t) (local.tm_mon + 1); // 1..12, 1 is January
dt->day = (int8_t) local.tm_mday; // 1..28,29,30,31 depending on month
dt->dotw = (int8_t) local.tm_wday; // 0..6, 0 is Sunday
dt->hour = (int8_t) local.tm_hour; // 0..23
dt->min = (int8_t) local.tm_min; // 0..59
dt->sec = (int8_t) local.tm_sec; // 0..59
if (pico_localtime_r(&time, &local)) {
tm_to_datetime(&local, dt);
return true;
}
return false;
}

bool datetime_to_time(const datetime_t *dt, time_t *time) {
struct tm local;
local.tm_year = dt->year - 1900;
local.tm_mon = dt->month - 1;
local.tm_mday = dt->day;
local.tm_hour = dt->hour;
local.tm_min = dt->min;
local.tm_sec = dt->sec;
*time = mktime(&local);
datetime_to_tm(dt, &local);
*time = pico_mktime(&local);
return *time >= 0;
}

Expand Down
25 changes: 23 additions & 2 deletions src/common/pico_util/include/pico/util/datetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ extern "C" {
* \ingroup pico_util
*/

#if PICO_INCLUDE_RTC_DATETIME
#include <time.h>
#include <sys/time.h>

#if PICO_INCLUDE_RTC_DATETIME

/*! \brief Convert a datetime_t structure to a string
* \ingroup util_datetime
Expand All @@ -33,14 +35,33 @@ void datetime_to_str(char *buf, uint buf_size, const datetime_t *t);

bool time_to_datetime(time_t time, datetime_t *dt);
bool datetime_to_time(const datetime_t *dt, time_t *time);

void datetime_to_tm(const datetime_t *dt, struct tm *tm);
void tm_to_datetime(const struct tm *tm, datetime_t *dt);

#endif

#include <sys/time.h>
uint64_t timespec_to_ms(const struct timespec *ts);
uint64_t timespec_to_us(const struct timespec *ts);
void ms_to_timespec(uint64_t ms, struct timespec *ts);
void us_to_timespec(uint64_t ms, struct timespec *ts);

/*! \brief localtime_r implementation for use by the pico_util datetime functions
* \ingroup util_datetime
*
* This method calls localtime_r from the C library by default,
* but is declared as a weak implementation to allow user code to override it
*/
struct tm *pico_localtime_r(const time_t *time, struct tm *tm);

/*! \brief mktime implementation for use by the pico_util datetime functions
* \ingroup util_datetime
*
* This method calls mktime from the C library by default,
* but is declared as a weak implementation to allow user code to override it
*/
time_t pico_mktime(struct tm *tm);

#ifdef __cplusplus
}
#endif
Expand Down
136 changes: 108 additions & 28 deletions src/rp2_common/pico_aon_timer/aon_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ static aon_timer_alarm_handler_t aon_timer_alarm_handler;
#if HAS_RP2040_RTC
#include "hardware/rtc.h"
#include "pico/util/datetime.h"

#elif HAS_POWMAN_TIMER
#include "hardware/powman.h"

Expand All @@ -23,56 +24,92 @@ static void powman_timer_irq_handler(void) {
irq_remove_handler(irq_num, powman_timer_irq_handler);
if (aon_timer_alarm_handler) aon_timer_alarm_handler();
}

static bool ts_to_tm(const struct timespec *ts, struct tm *tm) {
return pico_localtime_r(&ts->tv_sec, tm) != NULL;
}
#endif

void aon_timer_set_time(const struct timespec *ts) {
static bool tm_to_ts(const struct tm *tm, struct timespec *ts) {
struct tm tm_clone = *tm;
ts->tv_sec = pico_mktime(&tm_clone);
ts->tv_nsec = 0;
return ts->tv_sec != -1;
}

bool aon_timer_set_time(const struct timespec *ts) {
#if HAS_RP2040_RTC
datetime_t dt;
bool ok = time_to_datetime(ts->tv_sec, &dt);
assert(ok);
if (ok) rtc_set_datetime(&dt);
struct tm tm;
bool ok = pico_localtime_r(&ts->tv_sec, &tm);
if (ok) aon_timer_set_time_calendar(&tm);
return ok;
#elif HAS_POWMAN_TIMER
powman_timer_set_ms(timespec_to_ms(ts));
return true;
#else
panic_unsupported();
#endif
}

void aon_timer_get_time(struct timespec *ts) {
bool aon_timer_set_time_calendar(const struct tm *tm) {
#if HAS_RP2040_RTC
datetime_t dt;
rtc_get_datetime(&dt);
time_t t;
bool ok = datetime_to_time(&dt, &t);
assert(ok);
ts->tv_nsec = 0;
if (ok) {
ts->tv_sec = t;
} else {
ts->tv_sec = -1;
tm_to_datetime(tm, &dt);
rtc_set_datetime(&dt);
return true;
#elif HAS_POWMAN_TIMER
struct timespec ts;
if (tm_to_ts(tm, &ts)) {
return aon_timer_set_time(&ts);
}
return false;
#else
panic_unsupported();
#endif
}

bool aon_timer_get_time(struct timespec *ts) {
#if HAS_RP2040_RTC
struct tm tm;
bool ok = aon_timer_get_time_calendar(&tm);
return ok && tm_to_ts(&tm, ts);
#elif HAS_POWMAN_TIMER
ms_to_timespec(powman_timer_get_ms(), ts);
return true;
#else
panic_unsupported();
#endif
}

aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_timer_alarm_handler_t handler, bool wakeup_from_low_power) {
uint32_t save = save_and_disable_interrupts();
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
struct timespec ts_adjusted = *ts;
bool aon_timer_get_time_calendar(struct tm *tm) {
#if HAS_RP2040_RTC
((void)wakeup_from_low_power); // don't have a choice
datetime_t dt;
rtc_get_datetime(&dt);
datetime_to_tm(&dt, tm);
return true;
#elif HAS_POWMAN_TIMER
struct timespec ts;
aon_timer_get_time(&ts);
return ts_to_tm(&ts, tm);
#else
panic_unsupported();
#endif
}

aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_timer_alarm_handler_t handler, bool wakeup_from_low_power) {
#if HAS_RP2040_RTC
struct tm tm;
// adjust to after the target time
struct timespec ts_adjusted = *ts;
if (ts_adjusted.tv_nsec) ts_adjusted.tv_sec++;
bool ok = time_to_datetime(ts_adjusted.tv_sec, &dt);
assert(ok);
if (ok) {
rtc_set_alarm(&dt, handler);
if (!pico_localtime_r(&ts_adjusted.tv_sec, &tm)) {
return (aon_timer_alarm_handler_t)PICO_ERROR_INVALID_ARG;
}
return aon_timer_enable_alarm_calendar(&tm, handler, wakeup_from_low_power);
#elif HAS_POWMAN_TIMER
uint32_t save = save_and_disable_interrupts();
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
struct timespec ts_adjusted = *ts;
uint irq_num = aon_timer_get_irq_num();
powman_timer_disable_alarm();
// adjust to after the target time
Expand All @@ -92,12 +129,34 @@ aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_
irq_set_exclusive_handler(irq_num, powman_timer_irq_handler);
irq_set_enabled(irq_num, true);
}
aon_timer_alarm_handler = handler;
restore_interrupts_from_disabled(save);
return old_handler;
#else
panic_unsupported();
#endif
}

aon_timer_alarm_handler_t aon_timer_enable_alarm_calendar(const struct tm *tm, aon_timer_alarm_handler_t handler, bool wakeup_from_low_power) {
#if HAS_RP2040_RTC
((void)wakeup_from_low_power); // don't have a choice
uint32_t save = save_and_disable_interrupts();
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
datetime_t dt;
tm_to_datetime(tm, &dt);
rtc_set_alarm(&dt, handler);
aon_timer_alarm_handler = handler;
restore_interrupts_from_disabled(save);
return old_handler;
#elif HAS_POWMAN_TIMER
struct timespec ts;
if (!tm_to_ts(tm, &ts)) {
return (aon_timer_alarm_handler_t)PICO_ERROR_INVALID_ARG;
}
return aon_timer_enable_alarm(&ts, handler, wakeup_from_low_power);
#else
panic_unsupported();
#endif
}

void aon_timer_disable_alarm(void) {
Expand All @@ -120,15 +179,36 @@ void aon_timer_start_with_timeofday(void) {
aon_timer_start(&ts);
}

void aon_timer_start(const struct timespec *ts) {
bool aon_timer_start(const struct timespec *ts) {
#if HAS_RP2040_RTC
rtc_init();
aon_timer_set_time(ts);
return aon_timer_set_time(ts);
#elif HAS_POWMAN_TIMER
// todo how best to allow different configurations; this should just be the default
powman_timer_set_1khz_tick_source_xosc();
powman_timer_set_ms(timespec_to_ms(ts));
powman_timer_start();
bool ok = aon_timer_set_time(ts);
if (ok) {
powman_timer_set_ms(timespec_to_ms(ts));
powman_timer_start();
}
return ok;
#else
panic_unsupported();
#endif
}

bool aon_timer_start_calendar(const struct tm *tm) {
#if HAS_RP2040_RTC
rtc_init();
return aon_timer_set_time_calendar(tm);
#elif HAS_POWMAN_TIMER
// todo how best to allow different configurations; this should just be the default
powman_timer_set_1khz_tick_source_xosc();
bool ok = aon_timer_set_time_calendar(tm);
if (ok) {
powman_timer_start();
}
return ok;
#else
panic_unsupported();
#endif
Expand Down
Loading