Skip to content

Commit 3dce58f

Browse files
authored
support both struct timespec and struct tm variants of the aon timer APIs (#2079)
* support both struct timespec and struct tm variants of the aon timer APIs since use of one type on RP2040 and the other on RP2350 require pulling in C library code. Provide weak pico_ wrappers for localtime_r and mktime so that the user can override the conversion functions
1 parent 2692d9a commit 3dce58f

File tree

4 files changed

+306
-55
lines changed

4 files changed

+306
-55
lines changed

src/common/pico_util/datetime.c

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
11
#include "pico/util/datetime.h"
22

3-
#include <stdio.h>
3+
#if !PICO_ON_DEVICE && __APPLE__
4+
// if we're compiling with LLVM on Apple, __weak does something else, but we don't care about overriding these anyway on host builds
5+
#define __datetime_weak
6+
#else
7+
#define __datetime_weak __weak
8+
#endif
9+
10+
__datetime_weak struct tm * pico_localtime_r(const time_t *time, struct tm *tm) {
11+
return localtime_r(time, tm);
12+
}
13+
14+
__datetime_weak time_t pico_mktime(struct tm *tm) {
15+
return mktime(tm);
16+
}
417

518
#if PICO_INCLUDE_RTC_DATETIME
19+
#include <stdio.h>
20+
621
static const char *DATETIME_MONTHS[12] = {
722
"January",
823
"February",
@@ -41,31 +56,38 @@ void datetime_to_str(char *buf, uint buf_size, const datetime_t *t) {
4156
t->year);
4257
};
4358

59+
void datetime_to_tm(const datetime_t *dt, struct tm *tm) {
60+
tm->tm_year = dt->year - 1900;
61+
tm->tm_mon = dt->month - 1;
62+
tm->tm_mday = dt->day;
63+
tm->tm_hour = dt->hour;
64+
tm->tm_min = dt->min;
65+
tm->tm_sec = dt->sec;
66+
}
67+
68+
void tm_to_datetime(const struct tm *tm, datetime_t *dt) {
69+
dt->year = (int16_t) (tm->tm_year + 1900); // 0..4095
70+
dt->month = (int8_t) (tm->tm_mon + 1); // 1..12, 1 is January
71+
dt->day = (int8_t) tm->tm_mday; // 1..28,29,30,31 depending on month
72+
dt->dotw = (int8_t) tm->tm_wday; // 0..6, 0 is Sunday
73+
dt->hour = (int8_t) tm->tm_hour; // 0..23
74+
dt->min = (int8_t) tm->tm_min; // 0..59
75+
dt->sec = (int8_t) tm->tm_sec; // 0..59
76+
}
4477

4578
bool time_to_datetime(time_t time, datetime_t *dt) {
4679
struct tm local;
47-
if (localtime_r(&time, &local)) {
48-
dt->year = (int16_t) (local.tm_year + 1900); // 0..4095
49-
dt->month = (int8_t) (local.tm_mon + 1); // 1..12, 1 is January
50-
dt->day = (int8_t) local.tm_mday; // 1..28,29,30,31 depending on month
51-
dt->dotw = (int8_t) local.tm_wday; // 0..6, 0 is Sunday
52-
dt->hour = (int8_t) local.tm_hour; // 0..23
53-
dt->min = (int8_t) local.tm_min; // 0..59
54-
dt->sec = (int8_t) local.tm_sec; // 0..59
80+
if (pico_localtime_r(&time, &local)) {
81+
tm_to_datetime(&local, dt);
5582
return true;
5683
}
5784
return false;
5885
}
5986

6087
bool datetime_to_time(const datetime_t *dt, time_t *time) {
6188
struct tm local;
62-
local.tm_year = dt->year - 1900;
63-
local.tm_mon = dt->month - 1;
64-
local.tm_mday = dt->day;
65-
local.tm_hour = dt->hour;
66-
local.tm_min = dt->min;
67-
local.tm_sec = dt->sec;
68-
*time = mktime(&local);
89+
datetime_to_tm(dt, &local);
90+
*time = pico_mktime(&local);
6991
return *time >= 0;
7092
}
7193

src/common/pico_util/include/pico/util/datetime.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ extern "C" {
1919
* \ingroup pico_util
2020
*/
2121

22-
#if PICO_INCLUDE_RTC_DATETIME
2322
#include <time.h>
23+
#include <sys/time.h>
24+
25+
#if PICO_INCLUDE_RTC_DATETIME
2426

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

3436
bool time_to_datetime(time_t time, datetime_t *dt);
3537
bool datetime_to_time(const datetime_t *dt, time_t *time);
38+
39+
void datetime_to_tm(const datetime_t *dt, struct tm *tm);
40+
void tm_to_datetime(const struct tm *tm, datetime_t *dt);
41+
3642
#endif
3743

38-
#include <sys/time.h>
3944
uint64_t timespec_to_ms(const struct timespec *ts);
4045
uint64_t timespec_to_us(const struct timespec *ts);
4146
void ms_to_timespec(uint64_t ms, struct timespec *ts);
4247
void us_to_timespec(uint64_t ms, struct timespec *ts);
4348

49+
/*! \brief localtime_r implementation for use by the pico_util datetime functions
50+
* \ingroup util_datetime
51+
*
52+
* This method calls localtime_r from the C library by default,
53+
* but is declared as a weak implementation to allow user code to override it
54+
*/
55+
struct tm *pico_localtime_r(const time_t *time, struct tm *tm);
56+
57+
/*! \brief mktime implementation for use by the pico_util datetime functions
58+
* \ingroup util_datetime
59+
*
60+
* This method calls mktime from the C library by default,
61+
* but is declared as a weak implementation to allow user code to override it
62+
*/
63+
time_t pico_mktime(struct tm *tm);
64+
4465
#ifdef __cplusplus
4566
}
4667
#endif

src/rp2_common/pico_aon_timer/aon_timer.c

Lines changed: 108 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ static aon_timer_alarm_handler_t aon_timer_alarm_handler;
1313
#if HAS_RP2040_RTC
1414
#include "hardware/rtc.h"
1515
#include "pico/util/datetime.h"
16+
1617
#elif HAS_POWMAN_TIMER
1718
#include "hardware/powman.h"
1819

@@ -23,56 +24,92 @@ static void powman_timer_irq_handler(void) {
2324
irq_remove_handler(irq_num, powman_timer_irq_handler);
2425
if (aon_timer_alarm_handler) aon_timer_alarm_handler();
2526
}
27+
28+
static bool ts_to_tm(const struct timespec *ts, struct tm *tm) {
29+
return pico_localtime_r(&ts->tv_sec, tm) != NULL;
30+
}
2631
#endif
2732

28-
void aon_timer_set_time(const struct timespec *ts) {
33+
static bool tm_to_ts(const struct tm *tm, struct timespec *ts) {
34+
struct tm tm_clone = *tm;
35+
ts->tv_sec = pico_mktime(&tm_clone);
36+
ts->tv_nsec = 0;
37+
return ts->tv_sec != -1;
38+
}
39+
40+
bool aon_timer_set_time(const struct timespec *ts) {
2941
#if HAS_RP2040_RTC
30-
datetime_t dt;
31-
bool ok = time_to_datetime(ts->tv_sec, &dt);
32-
assert(ok);
33-
if (ok) rtc_set_datetime(&dt);
42+
struct tm tm;
43+
bool ok = pico_localtime_r(&ts->tv_sec, &tm);
44+
if (ok) aon_timer_set_time_calendar(&tm);
45+
return ok;
3446
#elif HAS_POWMAN_TIMER
3547
powman_timer_set_ms(timespec_to_ms(ts));
48+
return true;
3649
#else
3750
panic_unsupported();
3851
#endif
3952
}
4053

41-
void aon_timer_get_time(struct timespec *ts) {
54+
bool aon_timer_set_time_calendar(const struct tm *tm) {
4255
#if HAS_RP2040_RTC
4356
datetime_t dt;
44-
rtc_get_datetime(&dt);
45-
time_t t;
46-
bool ok = datetime_to_time(&dt, &t);
47-
assert(ok);
48-
ts->tv_nsec = 0;
49-
if (ok) {
50-
ts->tv_sec = t;
51-
} else {
52-
ts->tv_sec = -1;
57+
tm_to_datetime(tm, &dt);
58+
rtc_set_datetime(&dt);
59+
return true;
60+
#elif HAS_POWMAN_TIMER
61+
struct timespec ts;
62+
if (tm_to_ts(tm, &ts)) {
63+
return aon_timer_set_time(&ts);
5364
}
65+
return false;
66+
#else
67+
panic_unsupported();
68+
#endif
69+
}
70+
71+
bool aon_timer_get_time(struct timespec *ts) {
72+
#if HAS_RP2040_RTC
73+
struct tm tm;
74+
bool ok = aon_timer_get_time_calendar(&tm);
75+
return ok && tm_to_ts(&tm, ts);
5476
#elif HAS_POWMAN_TIMER
5577
ms_to_timespec(powman_timer_get_ms(), ts);
78+
return true;
5679
#else
5780
panic_unsupported();
5881
#endif
5982
}
6083

61-
aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_timer_alarm_handler_t handler, bool wakeup_from_low_power) {
62-
uint32_t save = save_and_disable_interrupts();
63-
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
64-
struct timespec ts_adjusted = *ts;
84+
bool aon_timer_get_time_calendar(struct tm *tm) {
6585
#if HAS_RP2040_RTC
66-
((void)wakeup_from_low_power); // don't have a choice
6786
datetime_t dt;
87+
rtc_get_datetime(&dt);
88+
datetime_to_tm(&dt, tm);
89+
return true;
90+
#elif HAS_POWMAN_TIMER
91+
struct timespec ts;
92+
aon_timer_get_time(&ts);
93+
return ts_to_tm(&ts, tm);
94+
#else
95+
panic_unsupported();
96+
#endif
97+
}
98+
99+
aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_timer_alarm_handler_t handler, bool wakeup_from_low_power) {
100+
#if HAS_RP2040_RTC
101+
struct tm tm;
68102
// adjust to after the target time
103+
struct timespec ts_adjusted = *ts;
69104
if (ts_adjusted.tv_nsec) ts_adjusted.tv_sec++;
70-
bool ok = time_to_datetime(ts_adjusted.tv_sec, &dt);
71-
assert(ok);
72-
if (ok) {
73-
rtc_set_alarm(&dt, handler);
105+
if (!pico_localtime_r(&ts_adjusted.tv_sec, &tm)) {
106+
return (aon_timer_alarm_handler_t)PICO_ERROR_INVALID_ARG;
74107
}
108+
return aon_timer_enable_alarm_calendar(&tm, handler, wakeup_from_low_power);
75109
#elif HAS_POWMAN_TIMER
110+
uint32_t save = save_and_disable_interrupts();
111+
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
112+
struct timespec ts_adjusted = *ts;
76113
uint irq_num = aon_timer_get_irq_num();
77114
powman_timer_disable_alarm();
78115
// adjust to after the target time
@@ -92,12 +129,34 @@ aon_timer_alarm_handler_t aon_timer_enable_alarm(const struct timespec *ts, aon_
92129
irq_set_exclusive_handler(irq_num, powman_timer_irq_handler);
93130
irq_set_enabled(irq_num, true);
94131
}
132+
aon_timer_alarm_handler = handler;
133+
restore_interrupts_from_disabled(save);
134+
return old_handler;
95135
#else
96136
panic_unsupported();
97137
#endif
138+
}
139+
140+
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) {
141+
#if HAS_RP2040_RTC
142+
((void)wakeup_from_low_power); // don't have a choice
143+
uint32_t save = save_and_disable_interrupts();
144+
aon_timer_alarm_handler_t old_handler = aon_timer_alarm_handler;
145+
datetime_t dt;
146+
tm_to_datetime(tm, &dt);
147+
rtc_set_alarm(&dt, handler);
98148
aon_timer_alarm_handler = handler;
99149
restore_interrupts_from_disabled(save);
100150
return old_handler;
151+
#elif HAS_POWMAN_TIMER
152+
struct timespec ts;
153+
if (!tm_to_ts(tm, &ts)) {
154+
return (aon_timer_alarm_handler_t)PICO_ERROR_INVALID_ARG;
155+
}
156+
return aon_timer_enable_alarm(&ts, handler, wakeup_from_low_power);
157+
#else
158+
panic_unsupported();
159+
#endif
101160
}
102161

103162
void aon_timer_disable_alarm(void) {
@@ -120,15 +179,36 @@ void aon_timer_start_with_timeofday(void) {
120179
aon_timer_start(&ts);
121180
}
122181

123-
void aon_timer_start(const struct timespec *ts) {
182+
bool aon_timer_start(const struct timespec *ts) {
124183
#if HAS_RP2040_RTC
125184
rtc_init();
126-
aon_timer_set_time(ts);
185+
return aon_timer_set_time(ts);
127186
#elif HAS_POWMAN_TIMER
128187
// todo how best to allow different configurations; this should just be the default
129188
powman_timer_set_1khz_tick_source_xosc();
130-
powman_timer_set_ms(timespec_to_ms(ts));
131-
powman_timer_start();
189+
bool ok = aon_timer_set_time(ts);
190+
if (ok) {
191+
powman_timer_set_ms(timespec_to_ms(ts));
192+
powman_timer_start();
193+
}
194+
return ok;
195+
#else
196+
panic_unsupported();
197+
#endif
198+
}
199+
200+
bool aon_timer_start_calendar(const struct tm *tm) {
201+
#if HAS_RP2040_RTC
202+
rtc_init();
203+
return aon_timer_set_time_calendar(tm);
204+
#elif HAS_POWMAN_TIMER
205+
// todo how best to allow different configurations; this should just be the default
206+
powman_timer_set_1khz_tick_source_xosc();
207+
bool ok = aon_timer_set_time_calendar(tm);
208+
if (ok) {
209+
powman_timer_start();
210+
}
211+
return ok;
132212
#else
133213
panic_unsupported();
134214
#endif

0 commit comments

Comments
 (0)