From b0bcc2c3f20d7326bd5c4f820909fc2787c9ed5b Mon Sep 17 00:00:00 2001 From: steffen Date: Wed, 20 Jun 2018 12:40:53 +0200 Subject: [PATCH 1/5] cpu/atmega256rfr2/rtc.c: Initial RTC Implementation Implements the rtc using a 32,768kHz watch crystal connected to the asynchronous Timer2 Lightly tweaked version of the Application Note "AVR134: Real Time Clock using the Asynchronous Timer" --- cpu/atmega256rfr2/periph/rtc.c | 143 +++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 cpu/atmega256rfr2/periph/rtc.c diff --git a/cpu/atmega256rfr2/periph/rtc.c b/cpu/atmega256rfr2/periph/rtc.c new file mode 100644 index 000000000000..7e367b1e05c8 --- /dev/null +++ b/cpu/atmega256rfr2/periph/rtc.c @@ -0,0 +1,143 @@ +#include +#include +#include "periph/rtc.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define YEAR_OFFSET (1900) + +static volatile struct tm no_alarm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static volatile struct tm current_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static volatile struct tm alarm_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static volatile rtc_alarm_cb_t alarm_cb; +static void *alarm_arg; + + + +void rtc_init(void) +{ + DDRG &= ~((1 << PG3) | (1 << PG4)); + rtc_poweron(); + TIMSK2 &= ~((1 << TOIE2) | (1 << OCIE2A) | (1 << OCIE2B)); + ASSR |= (1 << AS2); + TCNT2 = 0; + TCCR2A = 0; + TCCR2B = (1 << CS22) | (1 << CS20); + while (ASSR & ((1 << TCN2UB) | (1 << OCR2AUB) | (1 << OCR2BUB) | (1 << TCR2AUB) | (1 << TCR2BUB))) {} + TIMSK2 |= (1 << TOIE2); + sei(); +} + +int rtc_set_time(struct tm *time) +{ + current_time = *time; + return 0; +} + +int rtc_get_time(struct tm *time) +{ + *(time) = current_time; + return 0; +} + +int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) +{ + if (time->tm_sec < 60 && time->tm_min < 60 && time->tm_hour < 24 && time->tm_mday < 32 \ + && time->tm_wday < 7 && time->tm_mon < 12 && time != NULL) { + //time ok set alarm + alarm_time = *time; + alarm_cb = cb; + alarm_arg = arg; + return 0; + } + return -2; +} + +int rtc_get_alarm(struct tm *time) +{ + *time = alarm_time; + return 0; +} + +void rtc_clear_alarm(void) +{ + alarm_time = no_alarm; +} + +void rtc_poweroff(void) +{ + PRR0 |= (1 << PRTIM2); +} + +void rtc_poweron(void) +{ + PRR0 &= ~(1 << PRTIM2); +} + +uint8_t __not_leap(void) +{ + if (!((current_time.tm_year + YEAR_OFFSET) % 100)) { + return (current_time.tm_year + YEAR_OFFSET) % 400; + } + else { + return (current_time.tm_year + YEAR_OFFSET) % 4; + } +} + +uint8_t __compare_time(volatile struct tm *time1, volatile struct tm *time2) +{ + if (time1->tm_hour == time2->tm_hour && time1->tm_mday == time2->tm_mday \ + && time1->tm_min == time2->tm_min && time1->tm_mon == time2->tm_mon \ + && time1->tm_sec == time2->tm_sec && time1->tm_year == time2->tm_year) { + return 1; + } + return 0; +} + +ISR(TIMER2_OVF_vect){ + current_time.tm_sec++; + if (current_time.tm_sec == 60) { + current_time.tm_sec = 0; + current_time.tm_min++; + if (current_time.tm_min == 60) { + current_time.tm_min = 0; + current_time.tm_hour++; + if (current_time.tm_hour == 24) { + current_time.tm_hour = 0; + current_time.tm_mday++; + current_time.tm_yday++; + if (current_time.tm_mday == 32) { + current_time.tm_mon++; + current_time.tm_mday = 1; + } + else if (current_time.tm_mday == 31) { + if (current_time.tm_mon == 4 || current_time.tm_mon == 6 || current_time.tm_mon == 9 || current_time.tm_mon == 11) { + current_time.tm_mon++; + current_time.tm_mday = 1; + } + } + else if (current_time.tm_mday == 30) { + if (current_time.tm_mon == 2) { + current_time.tm_mon++; + current_time.tm_mday = 1; + } + } + else if (current_time.tm_mday == 29) { + if (current_time.tm_mon == 2 && __not_leap()) { + current_time.tm_mon++; + current_time.tm_mday = 1; + } + } + if (current_time.tm_mon == 13) { + current_time.tm_year++; + current_time.tm_mon = 1; + } + } + } + } + if (__compare_time(¤t_time, &alarm_time)) { + (*alarm_cb)(alarm_arg); + } +} From 1ce3225c13ec92040797d3903ba89f2ba0f2c2ae Mon Sep 17 00:00:00 2001 From: steffen Date: Wed, 20 Jun 2018 12:52:07 +0200 Subject: [PATCH 2/5] cpu/atmega256rfr2/rtc.c: Added License header --- cpu/atmega256rfr2/periph/rtc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cpu/atmega256rfr2/periph/rtc.c b/cpu/atmega256rfr2/periph/rtc.c index 7e367b1e05c8..3c803e3259c1 100644 --- a/cpu/atmega256rfr2/periph/rtc.c +++ b/cpu/atmega256rfr2/periph/rtc.c @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2018 RWTH Aachen, Josua Arndt, Steffen Robertz + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_atmega256rfr2 + * @{ + * + * @file + * @brief Low-Level RTC Implementation using a watch crystal on async. Timer 2 + * + * @author Steffen Robertz + * @author Josua Arndt + * + * @} + */ #include #include #include "periph/rtc.h" From 378eebdc62ebae20217551d29e624b22a7af36bb Mon Sep 17 00:00:00 2001 From: Josarn Date: Wed, 20 Jun 2018 14:29:30 +0200 Subject: [PATCH 3/5] atemga/rtc: interrupt context switch Added context switch check in interrupt. Made get time/alarm interrupt save. Added documentation. --- cpu/atmega256rfr2/periph/rtc.c | 114 +++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 27 deletions(-) diff --git a/cpu/atmega256rfr2/periph/rtc.c b/cpu/atmega256rfr2/periph/rtc.c index 3c803e3259c1..b6c227a4e662 100644 --- a/cpu/atmega256rfr2/periph/rtc.c +++ b/cpu/atmega256rfr2/periph/rtc.c @@ -18,55 +18,106 @@ * * @} */ -#include #include +#include +#include + +#include "cpu.h" #include "periph/rtc.h" -#define ENABLE_DEBUG (0) +#define ENABLE_DEBUG (0) #include "debug.h" #define YEAR_OFFSET (1900) -static volatile struct tm no_alarm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +typedef struct tm tm_t; -static volatile struct tm current_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static volatile struct tm alarm_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static volatile tm_t no_alarm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static volatile tm_t current_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static volatile tm_t alarm_time = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static volatile rtc_alarm_cb_t alarm_cb; static void *alarm_arg; - +uint8_t __compare_time(volatile tm_t *time1, volatile tm_t *time2) +{ + if (time1->tm_hour == time2->tm_hour && time1->tm_mday == time2->tm_mday + && time1->tm_min == time2->tm_min && time1->tm_mon == time2->tm_mon + && time1->tm_sec == time2->tm_sec && time1->tm_year == time2->tm_year) { + return 1; + } + return 0; +} void rtc_init(void) { + /* Configure Port G3/4 as crystal oscillator input for TC2 */ DDRG &= ~((1 << PG3) | (1 << PG4)); + /* Allready power on so it can swing in*/ rtc_poweron(); + /* Disable interrupts of TC2 */ TIMSK2 &= ~((1 << TOIE2) | (1 << OCIE2A) | (1 << OCIE2B)); + + /* Set TC to asynchronous mode with external clock (32,768kHz) */ ASSR |= (1 << AS2); + /* Reset timer value*/ TCNT2 = 0; + /* Normal mode of operation */ TCCR2A = 0; + /* TC0 with prescaler 128 resulting in 1 second for an overflow + * 2^8*128/32768Hz = 1s*/ TCCR2B = (1 << CS22) | (1 << CS20); + + /* Wait for TC2 to be ready */ while (ASSR & ((1 << TCN2UB) | (1 << OCR2AUB) | (1 << OCR2BUB) | (1 << TCR2AUB) | (1 << TCR2BUB))) {} + + /* Clear interrupt flags */ + TIFR2 = (1 << OCF2B) | (1 << OCF2A) | (1 << TOV2); + /* Overflow Interrupt Enable */ TIMSK2 |= (1 << TOIE2); + /* Global interrupts Enable */ sei(); } -int rtc_set_time(struct tm *time) +int rtc_set_time(tm_t *time) { + /* Disable Overflow interrupts */ + TIMSK2 &= ~(1 << TOIE2); + /* Set new time */ current_time = *time; + /* Overflow Interrupt Enable */ + TIMSK2 |= (1 << TOIE2); return 0; } -int rtc_get_time(struct tm *time) +int rtc_get_time(tm_t *time) { - *(time) = current_time; + /* Ensure no overflow happend */ + do { + *time = current_time; + } while (!__compare_time(¤t_time, time)); + return 0; } -int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) +int rtc_set_alarm(tm_t *time, rtc_alarm_cb_t cb, void *arg) { - if (time->tm_sec < 60 && time->tm_min < 60 && time->tm_hour < 24 && time->tm_mday < 32 \ - && time->tm_wday < 7 && time->tm_mon < 12 && time != NULL) { - //time ok set alarm + /* Check if callback and time are given */ + if ((cb == NULL) || (time == NULL)) { + return -1; + } + + /* Check if alarm time is in the past */ + tm_t tm_now = current_time; + int32_t diff = difftime(mk_gmtime(time), mk_gmtime(&tm_now)); + if (diff <= 0) { + return -1; + } + + /* Check if time is valid */ + if (time->tm_sec < 60 && time->tm_min < 60 && time->tm_hour < 24 + && time->tm_mday < 32 && time->tm_mon < 12) { + alarm_time = *time; alarm_cb = cb; alarm_arg = arg; @@ -75,14 +126,19 @@ int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) return -2; } -int rtc_get_alarm(struct tm *time) +int rtc_get_alarm(tm_t *time) { - *time = alarm_time; + /* Ensure alarm did not happen */ + do { + *time = alarm_time; + } while (!__compare_time(&alarm_time, time)); + return 0; } void rtc_clear_alarm(void) { + /* Set alarm time to zero */ alarm_time = no_alarm; } @@ -106,17 +162,9 @@ uint8_t __not_leap(void) } } -uint8_t __compare_time(volatile struct tm *time1, volatile struct tm *time2) -{ - if (time1->tm_hour == time2->tm_hour && time1->tm_mday == time2->tm_mday \ - && time1->tm_min == time2->tm_min && time1->tm_mon == time2->tm_mon \ - && time1->tm_sec == time2->tm_sec && time1->tm_year == time2->tm_year) { - return 1; - } - return 0; -} - ISR(TIMER2_OVF_vect){ + __enter_isr(); + current_time.tm_sec++; if (current_time.tm_sec == 60) { current_time.tm_sec = 0; @@ -132,8 +180,9 @@ ISR(TIMER2_OVF_vect){ current_time.tm_mon++; current_time.tm_mday = 1; } - else if (current_time.tm_mday == 31) { - if (current_time.tm_mon == 4 || current_time.tm_mon == 6 || current_time.tm_mon == 9 || current_time.tm_mon == 11) { + else if (current_time.tm_mday == 31) { + if (current_time.tm_mon == 4 || current_time.tm_mon == 6 + || current_time.tm_mon == 9 || current_time.tm_mon == 11) { current_time.tm_mon++; current_time.tm_mday = 1; } @@ -157,7 +206,18 @@ ISR(TIMER2_OVF_vect){ } } } + if (__compare_time(¤t_time, &alarm_time)) { + /* Set alarm time to zero */ + alarm_time = no_alarm; + /* Execute callback */ (*alarm_cb)(alarm_arg); } + + if (sched_context_switch_request) { + thread_yield(); + thread_yield_isr(); + } + + __exit_isr(); } From 78b3e479814ae22657dec3075077bd59f3579984 Mon Sep 17 00:00:00 2001 From: Josarn Date: Wed, 20 Jun 2018 22:33:04 +0200 Subject: [PATCH 4/5] test/periph_rtc: get_time in interrupt. --- tests/periph_rtc/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/periph_rtc/main.c b/tests/periph_rtc/main.c index 3da6dedf0500..28f16124d131 100644 --- a/tests/periph_rtc/main.c +++ b/tests/periph_rtc/main.c @@ -62,7 +62,7 @@ static void cb(void *arg) if (++cnt < REPEAT) { struct tm time; - rtc_get_alarm(&time); + rtc_get_time(&time); inc_secs(&time, PERIOD); rtc_set_alarm(&time, cb, NULL); } From 891727bd53f8bbff99d1d5b8abb65b6b2687e656 Mon Sep 17 00:00:00 2001 From: Josarn Date: Thu, 21 Jun 2018 01:02:17 +0200 Subject: [PATCH 5/5] test/periph_rtc: add IPC, context switch test. --- tests/periph_rtc/main.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/periph_rtc/main.c b/tests/periph_rtc/main.c index 28f16124d131..c6dff3849f57 100644 --- a/tests/periph_rtc/main.c +++ b/tests/periph_rtc/main.c @@ -27,6 +27,8 @@ #include "periph_conf.h" #include "periph/rtc.h" #include "xtimer.h" +#include "msg.h" +#include "thread.h" #define PERIOD (2U) #define REPEAT (4U) @@ -56,16 +58,8 @@ static void inc_secs(struct tm *time, unsigned val) static void cb(void *arg) { - (void)arg; - - puts("Alarm!"); - - if (++cnt < REPEAT) { - struct tm time; - rtc_get_time(&time); - inc_secs(&time, PERIOD); - rtc_set_alarm(&time, cb, NULL); - } + msg_t msg; + msg_send(&msg, *(kernel_pid_t *)arg); } int main(void) @@ -94,12 +88,27 @@ int main(void) /* set initial alarm */ inc_secs(&time, PERIOD); print_time(" Setting alarm to ", &time); - rtc_set_alarm(&time, cb, NULL); + + kernel_pid_t p_main = sched_active_pid; + rtc_set_alarm(&time, cb, &p_main); /* verify alarm */ rtc_get_alarm(&time); print_time(" Alarm is set to ", &time); puts(""); + msg_t msg_req; + while (cnt++ < REPEAT) { + msg_receive(&msg_req); + puts("Alarm!"); + + struct tm time; + rtc_get_time(&time); + inc_secs(&time, PERIOD); + rtc_set_alarm(&time, cb, &p_main); + } + + puts("[SUCCESS]"); + return 0; }