From f3eb7197b97286279e45ec3cb761d819d40219a3 Mon Sep 17 00:00:00 2001 From: rasmuskleist Date: Fri, 27 Dec 2024 17:47:01 +0100 Subject: [PATCH 1/5] [math] Add BCD conversion algorithms --- src/modm/math/utils.hpp | 1 + src/modm/math/utils/bcd.hpp | 50 +++++++++++++++++++++++++++++++ test/modm/math/utils/bcd_test.cpp | 42 ++++++++++++++++++++++++++ test/modm/math/utils/bcd_test.hpp | 23 ++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 src/modm/math/utils/bcd.hpp create mode 100644 test/modm/math/utils/bcd_test.cpp create mode 100644 test/modm/math/utils/bcd_test.hpp diff --git a/src/modm/math/utils.hpp b/src/modm/math/utils.hpp index 74fd7ef5d7..5f45b315d8 100644 --- a/src/modm/math/utils.hpp +++ b/src/modm/math/utils.hpp @@ -22,5 +22,6 @@ #include "utils/operator.hpp" #include "utils/endianness.hpp" #include "utils/crc.hpp" +#include "utils/bcd.hpp" #endif // MODM_MATH_UTILS_HPP diff --git a/src/modm/math/utils/bcd.hpp b/src/modm/math/utils/bcd.hpp new file mode 100644 index 0000000000..4eaf24d7fc --- /dev/null +++ b/src/modm/math/utils/bcd.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include + +namespace modm +{ + +/// @ingroup modm_math_utils +/// @{ + +constexpr uint32_t +fromBcd(uint32_t bcd) +{ + uint32_t decimal = 0; + for (uint16_t multiplier = 1; bcd; multiplier *= 10) + { + decimal += (bcd & 0b1111) * multiplier; + bcd >>= 4; + } + return decimal; +} + +constexpr uint32_t +toBcd(uint32_t decimal) +{ + uint32_t bcd = 0; + for (uint16_t shift = 0; decimal; shift += 4) + { + const auto dv = std::div(decimal, 10l); + bcd |= dv.rem << shift; + decimal = dv.quot; + } + return bcd; +} + +/// @} + +} // namespace modm diff --git a/test/modm/math/utils/bcd_test.cpp b/test/modm/math/utils/bcd_test.cpp new file mode 100644 index 0000000000..7c20adbba5 --- /dev/null +++ b/test/modm/math/utils/bcd_test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +#include "bcd_test.hpp" + +void +BcdTest::testFromBcd() +{ + TEST_ASSERT_EQUALS(modm::fromBcd(0x01), 0x01U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x02), 0x02U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x03), 0x03U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x04), 0x04U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x05), 0x05U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x06), 0x06U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x07), 0x07U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x08), 0x08U); + TEST_ASSERT_EQUALS(modm::fromBcd(0x09), 0x09U); +} + +void +BcdTest::testToBcd() +{ + TEST_ASSERT_EQUALS(modm::toBcd(0x01), 0x01U); + TEST_ASSERT_EQUALS(modm::toBcd(0x02), 0x02U); + TEST_ASSERT_EQUALS(modm::toBcd(0x03), 0x03U); + TEST_ASSERT_EQUALS(modm::toBcd(0x04), 0x04U); + TEST_ASSERT_EQUALS(modm::toBcd(0x05), 0x05U); + TEST_ASSERT_EQUALS(modm::toBcd(0x06), 0x06U); + TEST_ASSERT_EQUALS(modm::toBcd(0x07), 0x07U); + TEST_ASSERT_EQUALS(modm::toBcd(0x08), 0x08U); + TEST_ASSERT_EQUALS(modm::toBcd(0x09), 0x09U); +} \ No newline at end of file diff --git a/test/modm/math/utils/bcd_test.hpp b/test/modm/math/utils/bcd_test.hpp new file mode 100644 index 0000000000..24f313d293 --- /dev/null +++ b/test/modm/math/utils/bcd_test.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +/// @ingroup modm_test_test_math +class BcdTest : public unittest::TestSuite +{ +public: + void + testFromBcd(); + + void + testToBcd(); +}; From 05a11a60128ecaf06f76fde9276892b16d64d36b Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 23 Dec 2024 17:46:32 +0100 Subject: [PATCH 2/5] [math] Add :math:calendar module with DateTime class --- src/modm/math/calendar/date_time.hpp | 276 ++++++++++++++++++++++ src/modm/math/calendar/module.lb | 24 ++ test/modm/math/calendar/datetime_test.cpp | 126 ++++++++++ test/modm/math/calendar/datetime_test.hpp | 20 ++ test/modm/math/module.lb | 7 +- 5 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 src/modm/math/calendar/date_time.hpp create mode 100644 src/modm/math/calendar/module.lb create mode 100644 test/modm/math/calendar/datetime_test.cpp create mode 100644 test/modm/math/calendar/datetime_test.hpp diff --git a/src/modm/math/calendar/date_time.hpp b/src/modm/math/calendar/date_time.hpp new file mode 100644 index 0000000000..92f9edaad8 --- /dev/null +++ b/src/modm/math/calendar/date_time.hpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace modm +{ + +/// Efficient representation of a date and time +/// @ingroup modm_math_calendar +class DateTime +{ +public: + using duration = std::chrono::milliseconds; + using precision = typename duration::period; + + constexpr DateTime() = default; + + /// This is an efficient conversion. + constexpr explicit + DateTime(uint16_t year, uint8_t month, uint8_t day, + uint8_t hour = 0, uint8_t minute = 0, uint8_t second = 0, + uint16_t millisecond = 0, uint8_t weekday = 0) + : data(year - epoch, month, day, hour, minute, second, millisecond), _weekday(weekday) + {} + + /// This computes the weekday from the date, which is somewhat expensive + constexpr explicit + DateTime(const std::chrono::year_month_day& ymd, + const std::chrono::hours& hours = std::chrono::hours::zero(), + const std::chrono::minutes& minutes = std::chrono::minutes::zero(), + const std::chrono::seconds& seconds = std::chrono::seconds::zero(), + const std::chrono::milliseconds& subseconds = std::chrono::milliseconds::zero()) + : DateTime(uint16_t(int(ymd.year())), uint8_t(unsigned(ymd.month())), uint8_t(unsigned(ymd.day())), + uint8_t(hours.count()), uint8_t(minutes.count()), uint8_t(seconds.count()), + uint16_t(subseconds.count()), std::chrono::weekday{ymd}.c_encoding()) + {} + + constexpr std::chrono::year + year() const + { return std::chrono::year{epoch + data.year}; } + + constexpr std::chrono::month + month() const + { return std::chrono::month{data.month}; } + + constexpr std::chrono::day + day() const + { return std::chrono::day{data.day}; } + + /// This is an efficient conversion. + constexpr std::chrono::year_month_day + year_month_day() const + { return std::chrono::year_month_day{year(), month(), day()}; } + + constexpr std::chrono::weekday + weekday() const + { return std::chrono::weekday{_weekday}; } + + constexpr std::chrono::days + day_of_year() const + { + uint16_t yday = m2d[data.month] + data.day - 1u; + if ((data.year & 0b11) == 0b10 and data.month > 2u) yday++; + return std::chrono::days{yday}; + } + + + constexpr std::chrono::hours + hours() const + { return std::chrono::hours{data.hour}; } + + constexpr std::chrono::minutes + minutes() const + { return std::chrono::minutes{data.minute}; } + + constexpr std::chrono::seconds + seconds() const + { return std::chrono::seconds{data.second}; } + + constexpr std::chrono::milliseconds + subseconds() const + { return std::chrono::milliseconds{data.millisecond}; } + + /// @warning This function is *very* inefficient due to an unnecessary conversion from hh:mm:ss.ms to ms + /// then back to hh:mm:ss.ms in the constructor. This is a limitation of the stdc++ constructor. + constexpr std::chrono::hh_mm_ss + hh_mm_ss() const + { + uint32_t ms = ((data.hour * 60ul + data.minute) * 60ul + data.second) * 1000ul + data.millisecond; + return std::chrono::hh_mm_ss{duration{ms}}; + } + + /// This is an efficient conversion. + constexpr std::tm + tm() const + { + std::tm tm{}; + + tm.tm_sec = data.second; + tm.tm_min = data.minute; + tm.tm_hour = data.hour; + + tm.tm_mday = data.day; // 1-indexed + tm.tm_mon = data.month - 1u; // 0-indexed + tm.tm_year = data.year + epoch - 1900u; + + tm.tm_wday = weekday().c_encoding(); // 0-indexed + + tm.tm_yday = day_of_year().count(); // 0-indexed + + return tm; + } + + /// @warning This function is inefficient since it always converts the datetime to seconds. + constexpr std::time_t + time_t() const + { + return (data.year * seconds_per_year + + ((data.year + 1u) / 4u + day_of_year().count()) * seconds_per_day + + (data.hour * 60l + data.minute) * 60l + data.second); + } + + /// @warning This function is inefficient since it always converts the datetime to microseconds. + constexpr struct timeval + timeval() const + { + return {time_t(), data.millisecond * 1000}; + } + + /// @warning This function is inefficient since it always converts the datetime to milliseconds. + constexpr duration + time_since_epoch() const + { + return duration{time_t() * 1000 + data.millisecond}; + } + + + constexpr auto operator<=>(const DateTime& other) const + { return data.value <=> other.data.value; } + + constexpr auto operator==(const DateTime& other) const + { return data.value == other.data.value; } + +private: + union Data + { + constexpr Data() = default; + constexpr explicit + Data(uint8_t year, uint8_t month, uint8_t day, + uint8_t hour, uint8_t minute, uint8_t second, uint16_t millisecond) + : millisecond(millisecond), second(second), minute(minute), hour(hour), + day(day), month(month), year(year) {} + struct + { + uint16_t millisecond; + uint8_t second; + uint8_t minute; + uint8_t hour; + + uint8_t day; + uint8_t month; + uint8_t year; + } modm_packed; + uint64_t value; + }; + + Data data{}; + uint8_t _weekday{}; + + static constexpr uint16_t epoch{1970}; + static constexpr uint32_t seconds_per_day{24*60*60}; + static constexpr uint64_t seconds_per_year{365*seconds_per_day}; + // accumulated (non-leap) days per month, 1-indexed! + static constexpr uint16_t m2d[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +public: + /// Efficient conversion + static constexpr DateTime + from_tm(const std::tm& tm) + { + return DateTime(uint16_t(tm.tm_year + 1900), uint8_t(tm.tm_mon + 1), uint8_t(tm.tm_mday), + uint8_t(tm.tm_hour), uint8_t(tm.tm_min), uint8_t(tm.tm_sec), 0u, uint8_t(tm.tm_wday)); + } + + /// Really expensive conversion! + static constexpr DateTime + from_time_t(std::time_t tt, const std::chrono::milliseconds& subseconds = std::chrono::milliseconds::zero()) + { + const auto seconds = std::chrono::seconds(tt); + const auto days = std::chrono::floor(seconds); + const auto ymd = std::chrono::year_month_day(std::chrono::sys_days(days)); + const auto hms = std::chrono::hh_mm_ss(seconds - days); + return DateTime(ymd, hms.hours(), hms.minutes(), hms.seconds(), subseconds); + } + + /// Really expensive conversion! + static constexpr DateTime + from_timeval(const struct timeval& tv) + { + return from_time_t(std::time_t(tv.tv_sec), std::chrono::milliseconds(tv.tv_usec / 1000ul)); + } + + static consteval DateTime + fromBuildTime() + { + // Example: "Mon Dec 23 17:45:35 2024" + const std::string_view timestamp{__TIMESTAMP__}; + const auto to_uint = [=](uint8_t offset, uint8_t length) -> uint16_t + { + const auto str = timestamp.substr(offset, length); + int integer; + (void) std::from_chars(str.begin(), str.end(), integer); + return uint16_t(integer); + }; + // All easy to parse integers + const uint16_t cyear{to_uint(20, 4)}; + const auto cday{uint8_t(to_uint(8, 2))}; + const auto chour{uint8_t(to_uint(11, 2))}; + const auto cminute{uint8_t(to_uint(14, 2))}; + const auto csecond{uint8_t(to_uint(17, 2))}; + + // Annoying to parse strings + const std::string_view months[] + {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + uint8_t cmonth{1u}; + while (months[cmonth] != timestamp.substr(4, 3) and cmonth <= 12u) ++cmonth; + + const std::string_view weekdays[] + {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + uint8_t cweekday{}; + while (weekdays[cweekday] != timestamp.substr(0, 3) and cweekday < 7u) ++cweekday; + + return DateTime{cyear, cmonth, cday, chour, cminute, csecond, 0, cweekday}; + } +}; + +} // namespace modm + +#if MODM_HAS_IOSTREAM +#include +#include + +namespace modm +{ + +/// @ingroup modm_math_calendar +inline modm::IOStream& +operator << (modm::IOStream& s, const DateTime& dt) +{ + // ISO encoding: 2024-12-22 18:39:21.342 + s.printf("%04" PRIu16 "-%02" PRIu8 "-%02" PRIu8 " %02" PRIu8 ":%02" PRIu8 ":%02" PRIu8 ".%03" PRIu16, + uint16_t(int(dt.year())), uint8_t(unsigned(dt.month())), uint8_t(unsigned(dt.day())), + uint8_t(dt.hours().count()), uint8_t(dt.minutes().count()), uint8_t(dt.seconds().count()), + uint16_t(dt.subseconds().count())); + return s; +} + +} // modm namespace + +#endif diff --git a/src/modm/math/calendar/module.lb b/src/modm/math/calendar/module.lb new file mode 100644 index 0000000000..0d17561c77 --- /dev/null +++ b/src/modm/math/calendar/module.lb @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2024, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":math:calendar" + module.description = "Calendar Operations" + +def prepare(module, options): + module.depends(":stdc++") + # AVR libstdc++ has no support for calendar/time in + return options[":target"].identifier.platform != "avr" + +def build(env): + env.outbasepath = "modm/src/modm/math/calendar" + env.copy(".") diff --git a/test/modm/math/calendar/datetime_test.cpp b/test/modm/math/calendar/datetime_test.cpp new file mode 100644 index 0000000000..c79b770d30 --- /dev/null +++ b/test/modm/math/calendar/datetime_test.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "datetime_test.hpp" +#include + +using namespace std::chrono_literals; + +void +DateTimeTest::testConversion() +{ + const auto dt1 = modm::DateTime(1970, 1, 1); + TEST_ASSERT_EQUALS(dt1.day_of_year().count(), 0l); + TEST_ASSERT_EQUALS(dt1.time_t(), 0l); + TEST_ASSERT_EQUALS(dt1.year(), 1970y); + TEST_ASSERT_EQUALS(dt1.month(), std::chrono::January); + TEST_ASSERT_EQUALS(dt1.day(), 1d); + TEST_ASSERT_EQUALS(dt1.hours(), 0h); + TEST_ASSERT_EQUALS(dt1.minutes(), 0min); + TEST_ASSERT_EQUALS(dt1.seconds(), 0s); + TEST_ASSERT_EQUALS(dt1.subseconds(), 0ms); + + const auto dt2 = modm::DateTime(1970, 1, 1, 0, 0, 1, 42, 2); + TEST_ASSERT_EQUALS(dt2.day_of_year().count(), 0l); + TEST_ASSERT_EQUALS(dt2.time_t(), 1); + TEST_ASSERT_EQUALS(dt2.time_since_epoch(), 1042ms); + TEST_ASSERT_TRUE(dt1 < dt2); + TEST_ASSERT_TRUE(dt1 <= dt2); + TEST_ASSERT_FALSE(dt1 == dt2); + TEST_ASSERT_FALSE(dt1 >= dt2); + TEST_ASSERT_FALSE(dt1 > dt2); + + // first leap year since epoch + const auto dt3 = modm::DateTime(1972, 3, 1, 0, 0, 0); + TEST_ASSERT_EQUALS(dt3.day_of_year().count(), 31+29); + TEST_ASSERT_EQUALS(dt3.time_t(), 24*60*60*(365*2+31+29)); + TEST_ASSERT_TRUE(dt1 < dt3); + TEST_ASSERT_TRUE(dt2 < dt3); + + const auto dt4 = modm::DateTime(2024, 12, 24, 12, 24, 12); + TEST_ASSERT_EQUALS(dt4.day_of_year().count(), 358); + TEST_ASSERT_EQUALS(dt4.time_t(), 1735043052); + TEST_ASSERT_TRUE(dt1 < dt4); + TEST_ASSERT_TRUE(dt2 < dt4); + TEST_ASSERT_TRUE(dt3 < dt4); + TEST_ASSERT_EQUALS(dt4, dt4); + + const auto dt5 = modm::DateTime(2010, 11, 12, 13, 14, 15, 16, 4); + TEST_ASSERT_EQUALS(dt5.time_t(), 1289567655); + TEST_ASSERT_EQUALS(dt5.day_of_year().count(), 315); + TEST_ASSERT_EQUALS(dt5.year(), 2010y); + TEST_ASSERT_EQUALS(dt5.month(), std::chrono::November); + TEST_ASSERT_EQUALS(dt5.day(), 12d); + TEST_ASSERT_EQUALS(dt5.weekday(), std::chrono::weekday{4}); + TEST_ASSERT_EQUALS(dt5.weekday(), std::chrono::Thursday); + TEST_ASSERT_EQUALS(dt5.hours(), 13h); + TEST_ASSERT_EQUALS(dt5.minutes(), 14min); + TEST_ASSERT_EQUALS(dt5.seconds(), 15s); + TEST_ASSERT_EQUALS(dt5.subseconds(), 16ms); + + const auto tm5 = dt5.tm(); + TEST_ASSERT_EQUALS(tm5.tm_year, 110); // epoch=1900 + TEST_ASSERT_EQUALS(tm5.tm_mon, 10); // 0-indexed! + TEST_ASSERT_EQUALS(tm5.tm_mday, 12); + TEST_ASSERT_EQUALS(tm5.tm_hour, 13); + TEST_ASSERT_EQUALS(tm5.tm_min, 14); + TEST_ASSERT_EQUALS(tm5.tm_sec, 15); + TEST_ASSERT_EQUALS(tm5.tm_wday, 4); + TEST_ASSERT_EQUALS(tm5.tm_yday, 315); + TEST_ASSERT_EQUALS(tm5.tm_isdst, 0); + + const auto tv5 = dt5.timeval(); + TEST_ASSERT_EQUALS(tv5.tv_sec, 1289567655); + TEST_ASSERT_EQUALS(tv5.tv_usec, 16000); + + const auto dt6 = modm::DateTime::from_tm(tm5); + TEST_ASSERT_EQUALS(dt5.time_t(), dt6.time_t()); + TEST_ASSERT_EQUALS(dt5.weekday(), dt6.weekday()); + + const auto dt7 = modm::DateTime::from_timeval(tv5); + TEST_ASSERT_EQUALS(dt5, dt7); + + const auto dt8 = modm::DateTime(2012y / std::chrono::April / 15d); + TEST_ASSERT_EQUALS(dt8.time_t(), 1334448000); + TEST_ASSERT_EQUALS(dt8.day_of_year().count(), 105); + TEST_ASSERT_EQUALS(dt8.year(), 2012y); + TEST_ASSERT_EQUALS(dt8.month(), std::chrono::April); + TEST_ASSERT_EQUALS(dt8.day(), 15d); + TEST_ASSERT_EQUALS(dt8.weekday(), std::chrono::Sunday); + TEST_ASSERT_EQUALS(dt8.hours(), 0h); + TEST_ASSERT_EQUALS(dt8.minutes(), 0min); + TEST_ASSERT_EQUALS(dt8.seconds(), 0s); + TEST_ASSERT_EQUALS(dt8.subseconds(), 0ms); + + const auto dt9 = modm::DateTime(25d / std::chrono::July / 2015y, 15h, 25min, 23s, 780ms); + TEST_ASSERT_EQUALS(dt9.time_t(), 1437837923); + TEST_ASSERT_EQUALS(dt9.day_of_year().count(), 205); + TEST_ASSERT_EQUALS(dt9.year(), 2015y); + TEST_ASSERT_EQUALS(dt9.month(), std::chrono::July); + TEST_ASSERT_EQUALS(dt9.day(), 25d); + TEST_ASSERT_EQUALS(dt9.weekday(), std::chrono::Saturday); + TEST_ASSERT_EQUALS(dt9.hours(), 15h); + TEST_ASSERT_EQUALS(dt9.minutes(), 25min); + TEST_ASSERT_EQUALS(dt9.seconds(), 23s); + TEST_ASSERT_EQUALS(dt9.subseconds(), 780ms); + + const auto dt10 = modm::DateTime::from_time_t(1181611501); + TEST_ASSERT_EQUALS(dt10.time_t(), 1181611501); + TEST_ASSERT_EQUALS(dt10.day_of_year().count(), 162); + TEST_ASSERT_EQUALS(dt10.year(), 2007y); + TEST_ASSERT_EQUALS(dt10.month(), std::chrono::June); + TEST_ASSERT_EQUALS(dt10.day(), 12d); + TEST_ASSERT_EQUALS(dt10.weekday(), std::chrono::Tuesday); + TEST_ASSERT_EQUALS(dt10.hours(), 01h); + TEST_ASSERT_EQUALS(dt10.minutes(), 25min); + TEST_ASSERT_EQUALS(dt10.seconds(), 01s); + TEST_ASSERT_EQUALS(dt10.subseconds(), 0ms); +} diff --git a/test/modm/math/calendar/datetime_test.hpp b/test/modm/math/calendar/datetime_test.hpp new file mode 100644 index 0000000000..92d8b698e4 --- /dev/null +++ b/test/modm/math/calendar/datetime_test.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +/// @ingroup modm_test_test_math +class DateTimeTest : public unittest::TestSuite +{ +public: + void + testConversion(); +}; diff --git a/test/modm/math/module.lb b/test/modm/math/module.lb index 5dd07445e1..b281c7057a 100644 --- a/test/modm/math/module.lb +++ b/test/modm/math/module.lb @@ -25,9 +25,14 @@ def prepare(module, options): "modm:math:matrix", "modm:math:algorithm", "modm:math:utils") + if options[":target"].identifier.platform != "avr": + module.depends("modm:math:calendar") return True def build(env): env.outbasepath = "modm-test/src/modm-test/math" - env.copy('.') + patterns = [] + if env[":target"].identifier.platform == "avr": + patterns += ["*calendar*"] + env.copy('.', ignore=env.ignore_patterns(*patterns)) From 5d8123f3cd2828336b824ab91e4c7b724c91c40c Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 30 Dec 2024 20:39:36 +0100 Subject: [PATCH 3/5] [stm32] Add RTC peripheral Co-authored-by: rasmuskleist --- README.md | 25 ++ src/modm/platform/clock/stm32/module.lb | 9 +- .../platform/core/stm32/startup_platform.c.in | 71 +++-- src/modm/platform/rtc/stm32/module.lb | 44 +++ src/modm/platform/rtc/stm32/module.md | 85 ++++++ src/modm/platform/rtc/stm32/rtc.cpp | 19 ++ src/modm/platform/rtc/stm32/rtc.hpp.in | 168 ++++++++++++ src/modm/platform/rtc/stm32/rtc_impl.hpp.in | 258 ++++++++++++++++++ 8 files changed, 649 insertions(+), 30 deletions(-) create mode 100644 src/modm/platform/rtc/stm32/module.lb create mode 100644 src/modm/platform/rtc/stm32/module.md create mode 100644 src/modm/platform/rtc/stm32/rtc.cpp create mode 100644 src/modm/platform/rtc/stm32/rtc.hpp.in create mode 100644 src/modm/platform/rtc/stm32/rtc_impl.hpp.in diff --git a/README.md b/README.md index d3629a3a55..4981c582bc 100644 --- a/README.md +++ b/README.md @@ -458,6 +458,31 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✕ ✕ +RTC +✅ +✅ +○ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +✅ +○ +○ +○ +○ +○ +✕ +✕ +✕ + SPI ✅ ✅ diff --git a/src/modm/platform/clock/stm32/module.lb b/src/modm/platform/clock/stm32/module.lb index 31fc0313a1..c84c89f340 100644 --- a/src/modm/platform/clock/stm32/module.lb +++ b/src/modm/platform/clock/stm32/module.lb @@ -39,7 +39,7 @@ def build(env): properties["hsi_frequency"] = 48_000_000 properties["lsi_frequency"] = 32_000 properties["boot_frequency"] = 12_000_000 - elif target["family"] in ["f1", "f3"]: + elif target["family"] in ["f0", "f1", "f3"]: properties["hsi_frequency"] = 8_000_000 properties["lsi_frequency"] = 40_000 properties["boot_frequency"] = properties["hsi_frequency"] @@ -187,6 +187,9 @@ def build(env): nper = "DSI" if "Eth" in all_peripherals and per == "ETHMAC": per = "Eth" + if "Rtc" in all_peripherals and per == "RTCAPB": + per = "RTC" + nper = "RTCAPB" # Fix USBOTG OTG if target.family == "u5" and per == "OTG": per = "Usbotgfs" @@ -200,7 +203,9 @@ def build(env): if per.capitalize() not in all_peripherals: continue if "EN" in mode: - rcc_enable[per.capitalize()] = (nper, mode["EN"]) + kw = per.capitalize() + if kw not in rcc_enable: + rcc_enable[kw] = (nper, mode["EN"]) if "RST" in mode: rcc_reset[per.capitalize()] = (nper, mode["RST"]) diff --git a/src/modm/platform/core/stm32/startup_platform.c.in b/src/modm/platform/core/stm32/startup_platform.c.in index 338a68ce8c..85085967d8 100644 --- a/src/modm/platform/core/stm32/startup_platform.c.in +++ b/src/modm/platform/core/stm32/startup_platform.c.in @@ -1,7 +1,7 @@ /* * Copyright (c) 2016, Sascha Schade * Copyright (c) 2016-2017, Fabian Greif - * Copyright (c) 2016-2017, 2019, Niklas Hauser + * Copyright (c) 2016-2017, 2019, 2024, Niklas Hauser * Copyright (c) 2021, Raphael Lehmann * Copyright (c) 2021, Christopher Durand * @@ -30,60 +30,75 @@ __modm_initialize_platform(void) { // Enable SYSCFG %% if target.family in ["c0", "g0"] - RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; + RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; __DSB(); +%% elif target.family == "f0" + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN; __DSB(); %% elif target.family == "f1" - RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; + RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; __DSB(); %% elif target.family == "h7" - RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; + RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; __DSB(); %% elif target.family == "u5" - RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN; + RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN; __DSB(); %% else - RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; __DSB(); %% endif -%% if target.family == "f4" -// Only the more powerful F4 targets have CCM or Backup SRAM -#ifdef RCC_AHB1ENR_CCMDATARAMEN // Enable power to backup domain - RCC->APB1ENR |= RCC_APB1ENR_PWREN; - // Enable write access to backup SRAM +%% if target.family == "f1" + RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; __DSB(); +%% elif target.family in ["f0", "f2", "f3", "f4", "f7", "l0", "l1"] + RCC->APB1ENR |= RCC_APB1ENR_PWREN; __DSB(); +%% elif target.family in ["c0", "g0", "u0"] + RCC->APBENR1 |= RCC_APBENR1_PWREN; __DSB(); +%% elif target.family in ["g4", "l4", "l5"] + RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; __DSB(); +%% elif target.family == "u5" + RCC->AHB3ENR |= RCC_AHB3ENR_PWREN; __DSB(); +%% endif + +%% if target.family in ["f0", "f1", "f2", "f3", "f4", "l0", "l1"] PWR->CR |= PWR_CR_DBP; +%% elif target.family in ["f7", "g0", "g4", "h7", "l4", "l5", "u0", "wb", "wl"] + PWR->CR1 |= PWR_CR1_DBP; +%% elif target.family == "h5" + PWR->DBPCR |= PWR_DBPCR_DBP; +%% elif target.family in ["u5", "wba"] + PWR->DBPR |= PWR_DBPR_DBP; +%% endif + +%% if target.family == "f4" + // Only the more powerful F4 targets have CCM or Backup SRAM +#ifdef RCC_AHB1ENR_CCMDATARAMEN // Enable Core Coupled Memory (CCM) and backup SRAM (BKPSRAM) RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN | RCC_AHB1ENR_BKPSRAMEN; #endif %% elif target.family == "f7" // Reset from DFU settings to reset values. RCC->DCKCFGR2 = 0; - // Enable power to backup domain - RCC->APB1ENR |= RCC_APB1ENR_PWREN; - // Enable write access to backup SRAM - PWR->CR1 |= PWR_CR1_DBP; // Enable Data Tighly Coupled Memory (DTCM) and backup SRAM (BKPSRAM) RCC->AHB1ENR |= RCC_AHB1ENR_DTCMRAMEN | RCC_AHB1ENR_BKPSRAMEN; -%% elif target.family in ["g0", "g4", "l4", "l5"] -%% if target.family in ["l4", "g4"] - RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; -%% elif target.family != "g0" -#ifdef PWR_CR2_IOSV - RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; -#endif -%% endif - +%% elif target.family == "h7" + // Enable all SRAMs + %% if target.name[0].isnumeric() + RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN; + %% else + RCC->AHB2ENR |= RCC_AHB2ENR_AHBSRAM1EN | RCC_AHB2ENR_AHBSRAM2EN; + %% endif + RCC->AHB4ENR |= RCC_AHB4ENR_BKPRAMEN; +%% elif target.family in ["g4", "l4", "l5"] #ifdef PWR_CR2_IOSV // Enable VDDIO2 PWR->CR2 |= PWR_CR2_IOSV; #endif -%% elif target.family in ["u5"] - RCC->AHB3ENR |= RCC_AHB3ENR_PWREN; +%% elif target.family == "u5" // Enable power for VDDIO2 and USB PWR->SVMCR |= PWR_SVMCR_ASV | PWR_SVMCR_IO2SV | PWR_SVMCR_USV; - // Enable Backup SRAM (BKPSRAM) - PWR->DBPR |= PWR_DBPR_DBP; RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN; %% endif %% if vector_table_location == "ram" + __DSB(); // Remap SRAM to 0x0 for vector table relocation without VTOR register SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE; %% endif diff --git a/src/modm/platform/rtc/stm32/module.lb b/src/modm/platform/rtc/stm32/module.lb new file mode 100644 index 0000000000..29b21ca137 --- /dev/null +++ b/src/modm/platform/rtc/stm32/module.lb @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen +# Copyright (c) 2024, Niklas Hauser +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:rtc" + module.description = FileReader("module.md") + +def prepare(module, options): + device = options[":target"] + if not device.has_driver("rtc:stm32*") or device.identifier.family in ["f1"]: + return False + + module.depends( + ":cmsis:device", + ":platform:rcc", + ":math:calendar", + ":architecture:fiber", + ) + + return True + +def build(env): + env.outbasepath = "modm/src/modm/platform/rtc" + target = env[":target"].identifier + env.substitutions = { + # F1, F2, L1 do not have the RTC->SSR register. + # (Some L1 device do have a SSR field, but the CMSIS headers are inconsistent). + "with_ssr": target.family not in ["f1", "f2", "l1"], + # F2, L1 have a smaller PREDIV_S register field. + "bits_prediv_s": 13 if target.family in ["f2", "l1"] else 15, + } + env.template("rtc.hpp.in") + env.template("rtc_impl.hpp.in") + env.copy("rtc.cpp") diff --git a/src/modm/platform/rtc/stm32/module.md b/src/modm/platform/rtc/stm32/module.md new file mode 100644 index 0000000000..f3a8a3ec66 --- /dev/null +++ b/src/modm/platform/rtc/stm32/module.md @@ -0,0 +1,85 @@ +# Real Time Clock (RTC) + +The STM32 RTC implements a full calendar in hardware to provide a date and time +in binary-coded decimal (BCD) format. Several optimized methods are provided to +provide an efficient conversion from this hardware format to a software +representation. + +The epoch of the RTC is chosen to be the 1st of January 1970 to be compatible +with UNIX timestamps. Since the year is limited to two BCD digits, the RTC will +roll over in 2070. + +Note that the RTC hardware has no support for time zones, so you have to handle +that in software. + + +## Initialization + +The RTC keeps running during a reset of the microcontroller when the backup +domain is powered. To prevent clock drift, the `initialize()` function will +check if the RTC is already running and only initialize the prescaler differs +from the programmed one. If the return value is `false` the RTC was already +initialized and running: + +```cpp +struct SystemClock +{ + static constexpr uint32_t Rtc = 32'768; +}; +const bool inited = Rtc::initialize(); +if (not inited) { /* RTC was already running from before reset */ } +``` + +To always initialize the RTC, set the `forced` argument to `true`: + +```cpp +Rtc::initialize(true); +``` + +To give the RTC an initial date and time, use the `setDateTime()` function. You +can use the compile time as a basic reference time, and only set the time +forward to not reset the time on every reset: + +```cpp +constexpr auto cdt = modm::DateTime::fromBuildTime(); +if (Rtc::dateTime() < cdt) Rtc::setDateTime(cdt); +``` + + +## Accessing Date and Time + +The RTC hardware provides the date and time in BCD format which can be +atomically read out with the `dateTime()` function, which returns a `DateTime` +object that can be used to access the individual date and time components: + +```cpp +const auto dt = Rtc::dateTime(); +dt.year_month_day(); +dt.day_of_year(); +dt.weekday(); + +dt.hours(); +dt.minutes(); +dt.seconds(); +dt.subseconds(); + +// prints ISO encoding: 2024-12-22 18:39:21.342 +MODM_LOG_INFO << dt << modm::endl; + +// Efficient conversion to std::tm +const std::tm tm = dt.tm(); +``` + +Please note that while the `DateTime` object provides methods to compute to +seconds and milliseconds since epoch, these are slow and should be avoided. +Instead, use the `Rtc::now()`, `Rtc::time_t()` and `Rtc::timeval()` functions +to access optimized and cached conversion methods which are much faster: + +```cpp +const Rtc::time_point tp = Rtc::now(); +// instead of Rtc::dateTime().time_since_epoch(); +const std::time_t tt = Rtc::time_t(); +// instead of Rtc::dateTime().time_t(); +const struct timeval tv = Rtc::timeval(); +// instead of Rtc::dateTime().timeval(); +``` diff --git a/src/modm/platform/rtc/stm32/rtc.cpp b/src/modm/platform/rtc/stm32/rtc.cpp new file mode 100644 index 0000000000..0eeb9522fa --- /dev/null +++ b/src/modm/platform/rtc/stm32/rtc.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "rtc.hpp" + +extern "C" int +_gettimeofday(struct timeval *tp, void *) +{ + *tp = modm::platform::Rtc::timeval(); + return 0; +} diff --git a/src/modm/platform/rtc/stm32/rtc.hpp.in b/src/modm/platform/rtc/stm32/rtc.hpp.in new file mode 100644 index 0000000000..e61b5b985e --- /dev/null +++ b/src/modm/platform/rtc/stm32/rtc.hpp.in @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_RTC_HPP +#define MODM_STM32_RTC_HPP + +#include +#include +#include + +#include +#include + +namespace modm::platform +{ + +/** + * Real Time Clock (RTC) control for STM32 devices + * + * @author Niklas Hauser + * @author Rasmus Kleist Hørlyck Sørensen + * @ingroup modm_platform_rtc + */ +class Rtc : public modm::PeripheralDriver +{ +public: +%% if with_ssr + using duration = std::chrono::milliseconds; +%% else + using duration = std::chrono::seconds; +%% endif + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + /// RTC time can be changed forward/backward at any time + static constexpr bool is_steady = false; + + /// Reads the RTC atomically and converts it to a time_point + static time_point + now() noexcept; + + /// Consider using the optimized `Rtc::time_t()` function directly instead! + static std::time_t + to_time_t(const time_point& t) noexcept + { + return std::time_t(duration_cast(t.time_since_epoch()).count()); + } + + static time_point + from_time_t(std::time_t t) noexcept + { + using from_t = std::chrono::time_point; + return time_point_cast(from_t(std::chrono::seconds(t))); + } + +public: + /// Reads the RTC and converts to seconds in an optimized way + static std::time_t + time_t(); + + /// Reads the RTC and converts to `struct timeval` in an optimized way + static struct timeval + timeval(); + + /// Reads the RTC and returns the split up calendar and time values in an optimized way + static modm::DateTime + dateTime(); + + /// Sets the RTC time + static void + setDateTime(const modm::DateTime &dt); + +public: + /// Enable the RTC clock + static void + enable(); + + /// Initialize the RTC clock + template< class SystemClock > + requires requires { SystemClock::Rtc; } + static bool + initialize(bool forced=false); + + /// Disable the RTC clock + static void + disable(); + +private: + /// Unlock RTC register write protection + static void + unlock(); + + /// Lock RTC register write protection + static void + lock(); + + /// Read the RTC registers and store them in the data field. +%% if with_ssr + /// Returns the milliseconds converted from the SSR register + static uint16_t +%% else + static void +%% endif + read(); + + /// Update the cached seconds if necessary + static void + update_cache(); + + struct Data + { + union + { + struct + { + /// milliseconds are NOT cached + uint8_t second; + uint8_t minute; + uint8_t hour; + } modm_packed; + uint32_t time32; + }; + union + { + struct + { + uint8_t weekday; + uint8_t day; + uint8_t month; + uint8_t year; + } modm_packed; + uint32_t date32; + }; + }; + + static inline Data data{}; + + /// Cached computed values +%% if with_ssr + static inline uint64_t cache_time_milliseconds{}; +%% endif + static inline uint32_t cache_time_seconds{}; + static inline uint32_t cache_date_seconds{}; + static inline uint32_t cache_time{}; + static inline uint32_t cache_date{}; +%% if with_ssr +%# + /// Function points for efficient implementations of the SSR <=> millisecond conversion + static inline uint32_t (*t2ms)(uint32_t) = [](uint32_t) { return 0ul; }; + static inline uint32_t (*ms2t)(uint32_t) = [](uint32_t) { return 0ul; }; +%% endif +%# + static constexpr uint16_t epoch{1970}; +}; + +} // namespace modm::platform + +#include "rtc_impl.hpp" + +#endif // MODM_STM32_RTC_HPP diff --git a/src/modm/platform/rtc/stm32/rtc_impl.hpp.in b/src/modm/platform/rtc/stm32/rtc_impl.hpp.in new file mode 100644 index 0000000000..28c915bb6e --- /dev/null +++ b/src/modm/platform/rtc/stm32/rtc_impl.hpp.in @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +// Fix the inconsistent naming in ST's register files +#ifdef RTC_ICSR_INIT +# define RTC_ICSR RTC->ICSR +#else +# define RTC_ICSR_INIT RTC_ISR_INIT +# define RTC_ICSR_INITS RTC_ISR_INITS +# define RTC_ICSR_INITF RTC_ISR_INITF +# define RTC_ICSR_RSF RTC_ISR_RSF +# define RTC_ICSR RTC->ISR +#endif + + +namespace modm::platform +{ + +template< class SystemClock > +requires requires { SystemClock::Rtc; } +bool +Rtc::initialize(bool forced) +{ + // Fastest tick2ms on 37kHz LSI requires prediv_a = 74, not 125! + constexpr uint8_t max_prediv_a = (SystemClock::Rtc == 37_kHz) ? 74 : 128; + constexpr auto result = modm::PrescalerCounter::from_linear( + SystemClock::Rtc, 1_Hz, 1ul << {{bits_prediv_s}}, max_prediv_a, 1); + modm::PeripheralDriver::assertBaudrateInTolerance< result.frequency, 1_Hz, 0.f >(); + constexpr uint32_t c_prediv_a = result.prescaler - 1; + constexpr uint32_t c_prediv_s = result.counter - 1; + constexpr uint32_t prer = (c_prediv_a << RTC_PRER_PREDIV_A_Pos) | (c_prediv_s << RTC_PRER_PREDIV_S_Pos); +%% if with_ssr +%# + // Manually optimize division away for common LSE frequencies: + switch(result.counter) + { + // 32kHz: prediv_a=128, prediv_s=250 -> 1000/250 = 4 + case 250: t2ms = [](uint32_t ticks) -> uint32_t { return (c_prediv_s - ticks) << 2; }; break; + // 32.768kHz: prediv_a=128, prediv_s=256 -> 1000/256 = 1000 >> 8 + case 256: t2ms = [](uint32_t ticks) -> uint32_t { return ((c_prediv_s - ticks) * 1000u) >> 8; }; break; + // 40kHz: prediv_a=125, prediv_s=320 -> 1000/320 = 25 >> 3 + case 320: t2ms = [](uint32_t ticks) -> uint32_t { return ((c_prediv_s - ticks) * 25u) >> 3; }; break; + // 37kHz: prediv_a=74, prediv_s=500 -> 1000/500 = 2 + case 500: t2ms = [](uint32_t ticks) -> uint32_t { return (c_prediv_s - ticks) << 1; }; break; + // Otherwise just do the division + default: t2ms = [](uint32_t ticks) -> uint32_t { return ((c_prediv_s - ticks) * 1000u) / (c_prediv_s + 1); }; break; + } + // other way around for setting the SSR register + ms2t = [](uint32_t ms) -> uint32_t { return c_prediv_s - ((c_prediv_s + 1) * ms) / 1000u; }; +%% endif +%# + enable(); + // Do not initialize again to prevent clock drift + if (not forced and RTC_ICSR & RTC_ICSR_INITS and RTC->PRER == prer) return false; + + unlock(); + // Enter initialization mode + RTC_ICSR |= RTC_ICSR_INIT; + + // Wait until initialization phase mode is entered when INITF bit is set + modm::this_fiber::poll([]{ return RTC_ICSR & RTC_ICSR_INITF; }); + + // To generate a 1 Hz clock for the calendar counter, program both the prescaler factors + RTC->PRER = prer; + + // Configure 24 hour format + RTC->CR &= ~RTC_CR_FMT; + + // Exit the initialization mode + RTC_ICSR &= ~RTC_ICSR_INIT; + lock(); + + // wait until the RTC registers are synchronized + modm::this_fiber::poll([]{ return RTC_ICSR & RTC_ICSR_RSF; }); + + return true; +} + +inline void +Rtc::setDateTime(const modm::DateTime &dateTime) +{ + unlock(); + RTC_ICSR |= RTC_ICSR_INIT; + // Wait until initialization phase mode is entered when INITF bit is set + modm::this_fiber::poll([]{ return RTC_ICSR & RTC_ICSR_INITF; }); + + RTC->DR = ((toBcd(int(dateTime.year()) - epoch) << RTC_DR_YU_Pos) & (RTC_DR_YT_Msk | RTC_DR_YU_Msk)) | + ((toBcd(unsigned(dateTime.month())) << RTC_DR_MU_Pos) & (RTC_DR_MT_Msk | RTC_DR_MU_Msk)) | + ((toBcd(unsigned(dateTime.day())) << RTC_DR_DU_Pos) & (RTC_DR_DT_Msk | RTC_DR_DU_Msk)) | + ((dateTime.weekday().iso_encoding() << RTC_DR_WDU_Pos) & RTC_DR_WDU_Msk); + + RTC->TR = ((toBcd(dateTime.hours().count()) << RTC_TR_HU_Pos) & (RTC_TR_HT_Msk | RTC_TR_HU_Msk)) | + ((toBcd(dateTime.minutes().count()) << RTC_TR_MNU_Pos) & (RTC_TR_MNT_Msk | RTC_TR_MNU_Msk)) | + ((toBcd(dateTime.seconds().count()) << RTC_TR_SU_Pos) & (RTC_TR_ST_Msk | RTC_TR_SU_Msk)); +%% if with_ssr +%# + RTC->SSR = ms2t(dateTime.subseconds().count()); +%% endif +%# + RTC_ICSR &= ~RTC_ICSR_INIT; + lock(); + modm::this_fiber::poll([]{ return RTC_ICSR & RTC_ICSR_RSF; }); +} +%# +%% if with_ssr +inline uint16_t +%% else +inline void +%% endif +Rtc::read() +{ + const uint32_t tr = RTC->TR; +%% if with_ssr + const uint16_t ssr = RTC->SSR; +%% endif + const uint32_t dr = RTC->DR; + + data.year = modm::fromBcd((dr & (RTC_DR_YT_Msk | RTC_DR_YU_Msk)) >> RTC_DR_YU_Pos); + data.month = modm::fromBcd((dr & (RTC_DR_MT_Msk | RTC_DR_MU_Msk)) >> RTC_DR_MU_Pos); + data.day = modm::fromBcd((dr & (RTC_DR_DT_Msk | RTC_DR_DU_Msk)) >> RTC_DR_DU_Pos); + + data.weekday = (dr & RTC_DR_WDU_Msk) >> RTC_DR_WDU_Pos; + + data.hour = modm::fromBcd((tr & (RTC_TR_HT_Msk | RTC_TR_HU_Msk)) >> RTC_TR_HU_Pos); + data.minute = modm::fromBcd((tr & (RTC_TR_MNT_Msk | RTC_TR_MNU_Msk)) >> RTC_TR_MNU_Pos); + data.second = modm::fromBcd((tr & (RTC_TR_ST_Msk | RTC_TR_SU_Msk)) >> RTC_TR_SU_Pos); +%% if with_ssr +%# + return t2ms(ssr); +%% endif +} + +inline modm::DateTime +Rtc::dateTime() +{ +%% if with_ssr + const auto milliseconds = read(); +%% else + read(); +%% endif + return DateTime(data.year + epoch, data.month, data.day, + data.hour, data.minute, data.second, +%% if with_ssr + milliseconds, data.weekday); +%% else + 0, data.weekday); +%% endif +} + +inline void +Rtc::update_cache() +{ + static constexpr uint32_t seconds_per_day{24*60*60}; + static constexpr uint32_t seconds_per_year{365*seconds_per_day}; + static constexpr uint16_t m2d[] = {0, /* 1-index shortcut */ + 0, 31, 59 /* or 60 if leap year */, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + if (cache_date != data.date32) + { + uint16_t day_of_year = m2d[data.month] + data.day - 1u; + // Every forth year from 1972 until 2068 is a leap year + if ((data.year & 0b11) == 0b10 and data.month > 2u) day_of_year++; + // We must not count the leap day of the current leap year, since that's already part of day_of_year! + const uint8_t leap_days_since_epoch = (data.year + 2u - 1u) / 4u; + + // won't overflow since year≤100 -> less than 3.2e9 ≤ max(uint32_t) + cache_date_seconds = data.year * seconds_per_year + + (leap_days_since_epoch + day_of_year) * seconds_per_day; + cache_date = data.date32; + } + if (cache_time != data.time32) + { + cache_time_seconds = cache_date_seconds + + (data.hour * 60ul + data.minute) * 60ul + data.second; +%% if with_ssr + // but this will overflow therefore use of ull + cache_time_milliseconds = cache_time_seconds * 1000ull; +%% endif + cache_time = data.time32; + } +} + +inline Rtc::time_point +Rtc::now() +{ +%% if with_ssr + const auto milliseconds = read(); + update_cache(); + return time_point{duration{cache_time_milliseconds + milliseconds}}; +%% else + return time_point{duration{Rtc::time_t()}}; +%% endif +} + +inline std::time_t +Rtc::time_t() +{ + read(); + update_cache(); + return cache_time_seconds; +} + +inline struct timeval +Rtc::timeval() +{ +%% if with_ssr + const auto milliseconds = read(); + update_cache(); + return {cache_time_seconds, milliseconds * 1000}; +%% else + return {Rtc::time_t(), 0}; +%% endif +} + +// ---------------------------------------------------------------------------- +void inline +Rtc::unlock() +{ + // Unlock the write protection on the protected RTC registers. + RTC->WPR = 0xCA; + RTC->WPR = 0x53; + __DSB(); +} + +void inline +Rtc::lock() +{ + // Lock the write protection on the protected RTC registers. + RTC->WPR = 0xFF; +} + +void inline +Rtc::enable() +{ + Rcc::enable(); +} + +void inline +Rtc::disable() +{ + Rcc::disable(); +} + +} // namespace modm::platform + +#undef RTC_ICSR From 42f359906b92b5b19d257f6d78a3d3e7196c1cc3 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 30 Dec 2024 18:32:04 +0100 Subject: [PATCH 4/5] [board] Add RTC clock source support to all BSPs --- src/modm/board/black_pill_f103/board.hpp | 4 ++++ src/modm/board/black_pill_f411/board.hpp | 4 ++++ src/modm/board/blue_pill_f103/board.hpp | 4 ++++ src/modm/board/devebox_stm32f4xx/board.hpp | 4 ++++ src/modm/board/devebox_stm32h750vb/board.hpp | 4 ++++ src/modm/board/disco_f051r8/board.hpp | 12 ++++++++---- src/modm/board/disco_f072rb/board.hpp | 6 +++++- src/modm/board/disco_f100rb/board.hpp | 4 ++++ src/modm/board/disco_f303vc/board.hpp | 4 ++++ src/modm/board/disco_f401vc/board.hpp | 4 ++++ src/modm/board/disco_f407vg/board.hpp | 4 ++++ src/modm/board/disco_f411ve/board.hpp | 8 +++++++- src/modm/board/disco_f429zi/board.hpp | 4 ++++ src/modm/board/disco_f469ni/board.hpp.in | 4 ++++ src/modm/board/disco_f746ng/board.hpp | 4 ++++ src/modm/board/disco_f769ni/board.hpp | 4 ++++ src/modm/board/disco_l152rc/board.hpp | 4 ++++ src/modm/board/disco_l476vg/board.hpp | 4 ++++ src/modm/board/nucleo_c031c6/board.hpp | 4 ++++ src/modm/board/nucleo_f031k6/board.hpp | 4 ++++ src/modm/board/nucleo_f042k6/board.hpp | 4 ++++ src/modm/board/nucleo_f072rb/board.hpp | 6 +++++- src/modm/board/nucleo_f091rc/board.hpp | 6 +++++- src/modm/board/nucleo_f103rb/board.hpp | 4 ++++ src/modm/board/nucleo_f303k8/board.hpp | 7 ++++++- src/modm/board/nucleo_f303re/board.hpp | 4 ++++ src/modm/board/nucleo_f334r8/board.hpp | 4 ++++ src/modm/board/nucleo_f401re/board.hpp | 4 ++++ src/modm/board/nucleo_f411re/board.hpp | 4 ++++ src/modm/board/nucleo_f429zi/board.hpp | 4 ++++ src/modm/board/nucleo_f446re/board.hpp | 4 ++++ src/modm/board/nucleo_f446ze/board.hpp | 4 ++++ src/modm/board/nucleo_f746zg/board.hpp | 4 ++++ src/modm/board/nucleo_f767zi/board.hpp | 4 ++++ src/modm/board/nucleo_g071rb/board.hpp | 9 ++++++--- src/modm/board/nucleo_g474re/board.hpp | 6 +++++- src/modm/board/nucleo_h723zg/board.hpp | 4 ++++ src/modm/board/nucleo_h743zi/board.hpp | 4 ++++ src/modm/board/nucleo_l031k6/board.hpp | 4 ++++ src/modm/board/nucleo_l053r8/board.hpp | 4 ++++ src/modm/board/nucleo_l152re/board.hpp | 4 ++++ src/modm/board/nucleo_l432kc/board.hpp | 4 ++++ src/modm/board/nucleo_l452re/board.hpp | 4 ++++ src/modm/board/nucleo_l476rg/board.hpp | 4 ++++ src/modm/board/nucleo_l496zg-p/board.hpp | 4 ++++ src/modm/board/nucleo_l552ze-q/board.hpp | 4 ++++ src/modm/board/nucleo_u575zi-q/board.hpp | 4 ++++ src/modm/board/olimexino_stm32/board.hpp | 4 ++++ src/modm/board/stm32_f4ve/board.hpp | 4 ++++ src/modm/board/stm32f030f4p6_demo/board.hpp | 4 ++++ 50 files changed, 215 insertions(+), 13 deletions(-) diff --git a/src/modm/board/black_pill_f103/board.hpp b/src/modm/board/black_pill_f103/board.hpp index ea441ab6a9..17da5ac36a 100644 --- a/src/modm/board/black_pill_f103/board.hpp +++ b/src/modm/board/black_pill_f103/board.hpp @@ -58,10 +58,14 @@ struct SystemClock static constexpr uint32_t Usb = Ahb / 1.5; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // external clock * 9 = 72MHz, => 72/1.5 = 48 => good for USB diff --git a/src/modm/board/black_pill_f411/board.hpp b/src/modm/board/black_pill_f411/board.hpp index fce7c901ae..bf997a3953 100644 --- a/src/modm/board/black_pill_f411/board.hpp +++ b/src/modm/board/black_pill_f411/board.hpp @@ -60,10 +60,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); const Rcc::PllFactors pllFactors{ .pllM = 25, // 25MHz / M=25 -> 1MHz diff --git a/src/modm/board/blue_pill_f103/board.hpp b/src/modm/board/blue_pill_f103/board.hpp index dcd053c20c..91f5d20caf 100644 --- a/src/modm/board/blue_pill_f103/board.hpp +++ b/src/modm/board/blue_pill_f103/board.hpp @@ -58,10 +58,14 @@ struct SystemClock static constexpr uint32_t Usb = Ahb / 1.5; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // external clock * 9 = 72MHz, => 72/1.5 = 48 => good for USB diff --git a/src/modm/board/devebox_stm32f4xx/board.hpp b/src/modm/board/devebox_stm32f4xx/board.hpp index 241df28de9..fa78e19113 100644 --- a/src/modm/board/devebox_stm32f4xx/board.hpp +++ b/src/modm/board/devebox_stm32f4xx/board.hpp @@ -75,10 +75,14 @@ struct SystemClock static constexpr uint32_t Timer13 = Apb1Timer; static constexpr uint32_t Timer14 = Apb1Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/devebox_stm32h750vb/board.hpp b/src/modm/board/devebox_stm32h750vb/board.hpp index 721e1b73ba..05b9b6af37 100644 --- a/src/modm/board/devebox_stm32h750vb/board.hpp +++ b/src/modm/board/devebox_stm32h750vb/board.hpp @@ -95,10 +95,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; // From PLL3Q static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // 25MHz Rcc::setVoltageScaling(Rcc::VoltageScaling::Scale0); // required for 400MHz/480MHz const Rcc::PllFactors pllFactors1{ diff --git a/src/modm/board/disco_f051r8/board.hpp b/src/modm/board/disco_f051r8/board.hpp index 13db330f32..2c4ee24127 100644 --- a/src/modm/board/disco_f051r8/board.hpp +++ b/src/modm/board/disco_f051r8/board.hpp @@ -28,15 +28,19 @@ using namespace modm::literals; /// STM32F0 running at 48MHz generated from the internal 8MHz with PLL. struct SystemClock { - static constexpr int Frequency = 48_MHz; - static constexpr int Usart1 = Frequency; - static constexpr int Usart2 = Frequency; - static constexpr int Spi2 = Frequency; + static constexpr uint32_t Frequency = 48_MHz; + static constexpr uint32_t Usart1 = Frequency; + static constexpr uint32_t Usart2 = Frequency; + static constexpr uint32_t Spi2 = Frequency; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + // enable internal 8 MHz HSI RC clock Rcc::enableInternalClock(); // (internal clock / 2) * 12 = 48MHz diff --git a/src/modm/board/disco_f072rb/board.hpp b/src/modm/board/disco_f072rb/board.hpp index 8d109ffc5c..ce51bd725b 100644 --- a/src/modm/board/disco_f072rb/board.hpp +++ b/src/modm/board/disco_f072rb/board.hpp @@ -30,7 +30,7 @@ using namespace modm::literals; /// STM32F072 running at 48MHz generated from the internal 48MHz clock struct SystemClock { - static constexpr int Frequency = 48_MHz; + static constexpr uint32_t Frequency = 48_MHz; static constexpr uint32_t Ahb = Frequency; static constexpr uint32_t Apb = Frequency; @@ -60,10 +60,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + // Enable the internal 48MHz clock Rcc::enableInternalClockMHz48(); // set flash latency for 48MHz diff --git a/src/modm/board/disco_f100rb/board.hpp b/src/modm/board/disco_f100rb/board.hpp index 441e1042a8..f1b3bc4f62 100644 --- a/src/modm/board/disco_f100rb/board.hpp +++ b/src/modm/board/disco_f100rb/board.hpp @@ -68,10 +68,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb2Timer; static constexpr uint32_t Timer17 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllMul = 3, diff --git a/src/modm/board/disco_f303vc/board.hpp b/src/modm/board/disco_f303vc/board.hpp index 02ad1ef169..0f04c955ef 100644 --- a/src/modm/board/disco_f303vc/board.hpp +++ b/src/modm/board/disco_f303vc/board.hpp @@ -85,10 +85,14 @@ struct SystemClock static constexpr uint32_t Usb = Ahb / 1.5; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalClock(); // 8MHz const Rcc::PllFactors pllFactors{ .pllMul = 9, diff --git a/src/modm/board/disco_f401vc/board.hpp b/src/modm/board/disco_f401vc/board.hpp index 5f73233742..c1d382d154 100644 --- a/src/modm/board/disco_f401vc/board.hpp +++ b/src/modm/board/disco_f401vc/board.hpp @@ -68,10 +68,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/disco_f407vg/board.hpp b/src/modm/board/disco_f407vg/board.hpp index 5cefecd167..56a65631bc 100644 --- a/src/modm/board/disco_f407vg/board.hpp +++ b/src/modm/board/disco_f407vg/board.hpp @@ -79,10 +79,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/disco_f411ve/board.hpp b/src/modm/board/disco_f411ve/board.hpp index ffb5286710..f6a4e32bfc 100644 --- a/src/modm/board/disco_f411ve/board.hpp +++ b/src/modm/board/disco_f411ve/board.hpp @@ -63,9 +63,15 @@ struct SystemClock static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Usb = 48_MHz; + static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; - static bool inline enable() + static bool inline + enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 7, // 8MHz / M=7 -> ~1.14MHz diff --git a/src/modm/board/disco_f429zi/board.hpp b/src/modm/board/disco_f429zi/board.hpp index 0ccd4ecca8..2ea9c98424 100644 --- a/src/modm/board/disco_f429zi/board.hpp +++ b/src/modm/board/disco_f429zi/board.hpp @@ -80,10 +80,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalCrystal(); // 8 MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M -> 2MHz diff --git a/src/modm/board/disco_f469ni/board.hpp.in b/src/modm/board/disco_f469ni/board.hpp.in index dc1591e179..6978235b67 100644 --- a/src/modm/board/disco_f469ni/board.hpp.in +++ b/src/modm/board/disco_f469ni/board.hpp.in @@ -82,10 +82,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // 8 MHz const Rcc::PllFactors pllFactors{ .pllM = 8, // 8MHz / M=8 -> 1MHz !!! Must be 1 MHz for PLLSAI !!! diff --git a/src/modm/board/disco_f746ng/board.hpp b/src/modm/board/disco_f746ng/board.hpp index 3bbc7c36f0..a42fa0a13a 100644 --- a/src/modm/board/disco_f746ng/board.hpp +++ b/src/modm/board/disco_f746ng/board.hpp @@ -81,10 +81,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 25 MHz const Rcc::PllFactors pllFactors{ .pllM = 25, // 25MHz / M=25 -> 1MHz diff --git a/src/modm/board/disco_f769ni/board.hpp b/src/modm/board/disco_f769ni/board.hpp index 6f59252cfe..b26d085837 100644 --- a/src/modm/board/disco_f769ni/board.hpp +++ b/src/modm/board/disco_f769ni/board.hpp @@ -81,10 +81,14 @@ struct SystemClock static constexpr uint32_t Timer13 = Apb1Timer; static constexpr uint32_t Timer14 = Apb1Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 25 MHz const Rcc::PllFactors pllFactors{ .pllM = 25, // 25MHz / M=25 -> 1MHz diff --git a/src/modm/board/disco_l152rc/board.hpp b/src/modm/board/disco_l152rc/board.hpp index c405981635..fa90ef3a01 100644 --- a/src/modm/board/disco_l152rc/board.hpp +++ b/src/modm/board/disco_l152rc/board.hpp @@ -58,10 +58,14 @@ struct SystemClock static constexpr uint32_t Timer10 = Apb2Timer; static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // Enable power scaling for 1.8V PWR->CR = (PWR->CR & ~PWR_CR_VOS) | PWR_CR_VOS_0; while(PWR->CSR & PWR_CSR_VOSF) ; diff --git a/src/modm/board/disco_l476vg/board.hpp b/src/modm/board/disco_l476vg/board.hpp index c5ae9096b4..8e737acd4e 100644 --- a/src/modm/board/disco_l476vg/board.hpp +++ b/src/modm/board/disco_l476vg/board.hpp @@ -44,10 +44,14 @@ struct SystemClock static constexpr uint32_t Usart4 = Apb1; static constexpr uint32_t Usart5 = Apb1; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // set flash latency first because system already runs from MSI Rcc::setFlashLatency(); diff --git a/src/modm/board/nucleo_c031c6/board.hpp b/src/modm/board/nucleo_c031c6/board.hpp index 4cac0fa817..e3c83ea604 100644 --- a/src/modm/board/nucleo_c031c6/board.hpp +++ b/src/modm/board/nucleo_c031c6/board.hpp @@ -50,10 +50,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb; static constexpr uint32_t Timer17 = Apb; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + // 48MHz generated from internal RC Rcc::enableInternalClock(); Rcc::setHsiSysDivider(Rcc::HsiSysDivider::Div1); diff --git a/src/modm/board/nucleo_f031k6/board.hpp b/src/modm/board/nucleo_f031k6/board.hpp index b301f3528b..33d3752901 100644 --- a/src/modm/board/nucleo_f031k6/board.hpp +++ b/src/modm/board/nucleo_f031k6/board.hpp @@ -49,10 +49,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb; static constexpr uint32_t Timer17 = Apb; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 8MHz // (internal clock / 2) * 12 = 48MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f042k6/board.hpp b/src/modm/board/nucleo_f042k6/board.hpp index f9ebc59a0b..d752512009 100644 --- a/src/modm/board/nucleo_f042k6/board.hpp +++ b/src/modm/board/nucleo_f042k6/board.hpp @@ -52,10 +52,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb; static constexpr uint32_t Timer17 = Apb; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 8MHz // (internal clock / 2) * 12 = 48MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f072rb/board.hpp b/src/modm/board/nucleo_f072rb/board.hpp index 24e2240e94..6ada20df16 100644 --- a/src/modm/board/nucleo_f072rb/board.hpp +++ b/src/modm/board/nucleo_f072rb/board.hpp @@ -30,7 +30,7 @@ using namespace modm::literals; /// STM32f072rb running at 48MHz generated from the internal 8MHz crystal struct SystemClock { - static constexpr int Frequency = 48_MHz; + static constexpr uint32_t Frequency = 48_MHz; static constexpr uint32_t Ahb = Frequency; static constexpr uint32_t Apb = Frequency; @@ -60,10 +60,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // Enable the internal 48MHz clock Rcc::enableInternalClockMHz48(); // set flash latency for 48MHz diff --git a/src/modm/board/nucleo_f091rc/board.hpp b/src/modm/board/nucleo_f091rc/board.hpp index 86efd959b1..75c44507e5 100644 --- a/src/modm/board/nucleo_f091rc/board.hpp +++ b/src/modm/board/nucleo_f091rc/board.hpp @@ -29,7 +29,7 @@ using namespace modm::literals; /// STM32F091RC running at 48MHz generated from the internal 8MHz crystal struct SystemClock { - static constexpr int Frequency = 48_MHz; + static constexpr uint32_t Frequency = 48_MHz; static constexpr uint32_t Ahb = Frequency; static constexpr uint32_t Apb = Frequency; @@ -59,10 +59,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // Enable the internal 48MHz clock Rcc::enableInternalClockMHz48(); // set flash latency for 48MHz diff --git a/src/modm/board/nucleo_f103rb/board.hpp b/src/modm/board/nucleo_f103rb/board.hpp index 754e2f9107..ed73766f07 100644 --- a/src/modm/board/nucleo_f103rb/board.hpp +++ b/src/modm/board/nucleo_f103rb/board.hpp @@ -63,10 +63,14 @@ struct SystemClock static constexpr uint32_t Timer7 = Apb1Timer; static constexpr uint32_t Timer8 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableInternalClock(); // 8MHz // internal clock / 2 * 16 = 64MHz, => 64/1.5 = 42.6 => bad for USB const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f303k8/board.hpp b/src/modm/board/nucleo_f303k8/board.hpp index f4273302b8..34dc864254 100644 --- a/src/modm/board/nucleo_f303k8/board.hpp +++ b/src/modm/board/nucleo_f303k8/board.hpp @@ -28,7 +28,8 @@ namespace Board /// STM32F303K8 running at 64MHz generated from the internal 8MHz clock // Dummy clock for devices -struct SystemClock { +struct SystemClock +{ static constexpr uint32_t Frequency = 64_MHz; static constexpr uint32_t Ahb = Frequency; static constexpr uint32_t Apb1 = Frequency / 2; @@ -60,10 +61,14 @@ struct SystemClock { static constexpr uint32_t Timer16 = Apb2Timer; static constexpr uint32_t Timer17 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableInternalClock(); // 8MHz // 8MHz / 2 * 16 = 64MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f303re/board.hpp b/src/modm/board/nucleo_f303re/board.hpp index a2fedcfc49..3bceae79d7 100644 --- a/src/modm/board/nucleo_f303re/board.hpp +++ b/src/modm/board/nucleo_f303re/board.hpp @@ -63,10 +63,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb2Timer; static constexpr uint32_t Timer17 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 8MHz // 8MHz / 2 * 16 = 64MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f334r8/board.hpp b/src/modm/board/nucleo_f334r8/board.hpp index 1e6a065da8..1942e365a0 100644 --- a/src/modm/board/nucleo_f334r8/board.hpp +++ b/src/modm/board/nucleo_f334r8/board.hpp @@ -60,10 +60,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb2Timer; static constexpr uint32_t Timer17 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 8MHz // 8MHz / 2 * 16 = 64MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_f401re/board.hpp b/src/modm/board/nucleo_f401re/board.hpp index aa3bf641f6..c82237d2e5 100644 --- a/src/modm/board/nucleo_f401re/board.hpp +++ b/src/modm/board/nucleo_f401re/board.hpp @@ -61,10 +61,14 @@ struct SystemClock static constexpr uint32_t Timer10 = Apb2Timer; static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 16MHz / M= 4 -> 4MHz diff --git a/src/modm/board/nucleo_f411re/board.hpp b/src/modm/board/nucleo_f411re/board.hpp index ae974cf914..87c10bd0cf 100644 --- a/src/modm/board/nucleo_f411re/board.hpp +++ b/src/modm/board/nucleo_f411re/board.hpp @@ -63,10 +63,14 @@ struct SystemClock static constexpr uint32_t Timer10 = Apb2Timer; static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 16MHz / M= 4 -> 4MHz diff --git a/src/modm/board/nucleo_f429zi/board.hpp b/src/modm/board/nucleo_f429zi/board.hpp index d96cf71469..264cdf9588 100644 --- a/src/modm/board/nucleo_f429zi/board.hpp +++ b/src/modm/board/nucleo_f429zi/board.hpp @@ -79,10 +79,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 8 MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M= 4 -> 2MHz diff --git a/src/modm/board/nucleo_f446re/board.hpp b/src/modm/board/nucleo_f446re/board.hpp index 9d847edaee..d5fec7b5aa 100644 --- a/src/modm/board/nucleo_f446re/board.hpp +++ b/src/modm/board/nucleo_f446re/board.hpp @@ -70,10 +70,14 @@ struct SystemClock static constexpr uint32_t Timer10 = Apb2Timer; static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz const Rcc::PllFactors pllFactors{ .pllM = 8, // 16MHz / M= 8 -> 2MHz diff --git a/src/modm/board/nucleo_f446ze/board.hpp b/src/modm/board/nucleo_f446ze/board.hpp index ee9f66b7b1..0cb372217b 100644 --- a/src/modm/board/nucleo_f446ze/board.hpp +++ b/src/modm/board/nucleo_f446ze/board.hpp @@ -71,10 +71,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M= 4 -> 2MHz diff --git a/src/modm/board/nucleo_f746zg/board.hpp b/src/modm/board/nucleo_f746zg/board.hpp index c3bd028392..6e8949cf6b 100755 --- a/src/modm/board/nucleo_f746zg/board.hpp +++ b/src/modm/board/nucleo_f746zg/board.hpp @@ -84,10 +84,14 @@ struct SystemClock static constexpr uint32_t Timer13 = Apb1Timer; static constexpr uint32_t Timer14 = Apb1Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 8 MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/nucleo_f767zi/board.hpp b/src/modm/board/nucleo_f767zi/board.hpp index 72c233639c..d31633b528 100755 --- a/src/modm/board/nucleo_f767zi/board.hpp +++ b/src/modm/board/nucleo_f767zi/board.hpp @@ -82,10 +82,14 @@ struct SystemClock static constexpr uint32_t Timer13 = Apb1Timer; static constexpr uint32_t Timer14 = Apb1Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 8 MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/nucleo_g071rb/board.hpp b/src/modm/board/nucleo_g071rb/board.hpp index 8fd2ac808e..5c77b3c517 100644 --- a/src/modm/board/nucleo_g071rb/board.hpp +++ b/src/modm/board/nucleo_g071rb/board.hpp @@ -29,8 +29,8 @@ using namespace modm::literals; struct SystemClock { static constexpr uint32_t Frequency = 64_MHz; - static constexpr uint32_t Ahb = Frequency; - static constexpr uint32_t Apb = Frequency; + static constexpr uint32_t Ahb = Frequency; + static constexpr uint32_t Apb = Frequency; static constexpr uint32_t Aes = Ahb; static constexpr uint32_t Rng = Ahb; @@ -72,16 +72,19 @@ struct SystemClock static constexpr uint32_t Spi2 = Apb; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; static constexpr uint32_t Wwdg = Apb; - static constexpr uint32_t Rtc = Apb; static constexpr uint32_t Timer14 = Apb; static constexpr uint32_t Timer7 = Apb; static constexpr uint32_t Timer6 = Apb; static constexpr uint32_t Timer3 = Apb; static constexpr uint32_t Timer2 = Apb; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz // (internal clock / 1_M) * 8_N / 2_R = 128MHz / 2 = 64MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_g474re/board.hpp b/src/modm/board/nucleo_g474re/board.hpp index 31005e0eb4..4c5ac64d37 100644 --- a/src/modm/board/nucleo_g474re/board.hpp +++ b/src/modm/board/nucleo_g474re/board.hpp @@ -66,7 +66,6 @@ struct SystemClock static constexpr uint32_t I2c4 = I2c; static constexpr uint32_t Lptim = Apb1; static constexpr uint32_t Lpuart = Apb1; - static constexpr uint32_t Rtc = Apb1; static constexpr uint32_t Spi2 = Apb1; static constexpr uint32_t Spi3 = Apb1; static constexpr uint32_t Uart4 = Apb1; @@ -94,6 +93,8 @@ struct SystemClock static constexpr uint32_t Timer20 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; + static bool inline enable() { @@ -115,6 +116,9 @@ struct SystemClock // update frequencies for busy-wait delay functions Rcc::updateCoreFrequency(); + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::setCanClockSource(Rcc::CanClockSource::Pclk); return true; } diff --git a/src/modm/board/nucleo_h723zg/board.hpp b/src/modm/board/nucleo_h723zg/board.hpp index 9c7cbed75d..b8cceb25c9 100644 --- a/src/modm/board/nucleo_h723zg/board.hpp +++ b/src/modm/board/nucleo_h723zg/board.hpp @@ -108,10 +108,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; // From PLL3Q static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // Switch core supply voltage to maximum level // Required for running at 550 MHz Rcc::setVoltageScaling(Rcc::VoltageScaling::Scale0); diff --git a/src/modm/board/nucleo_h743zi/board.hpp b/src/modm/board/nucleo_h743zi/board.hpp index 15a1e0d845..1e566584e8 100644 --- a/src/modm/board/nucleo_h743zi/board.hpp +++ b/src/modm/board/nucleo_h743zi/board.hpp @@ -100,10 +100,14 @@ struct SystemClock static constexpr uint32_t Usb = 48_MHz; // From PLL3Q static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalClock(); // 8 MHz Rcc::setVoltageScaling(Rcc::VoltageScaling::Scale0); // required for 400MHz/480MHz const Rcc::PllFactors pllFactors1{ diff --git a/src/modm/board/nucleo_l031k6/board.hpp b/src/modm/board/nucleo_l031k6/board.hpp index 643358e917..2973934918 100644 --- a/src/modm/board/nucleo_l031k6/board.hpp +++ b/src/modm/board/nucleo_l031k6/board.hpp @@ -54,10 +54,14 @@ struct SystemClock static constexpr uint32_t Usart2 = Apb1; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz // (internal clock / 1) * 4 / 2 = 32MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_l053r8/board.hpp b/src/modm/board/nucleo_l053r8/board.hpp index c90b17bf71..dbed228ddd 100644 --- a/src/modm/board/nucleo_l053r8/board.hpp +++ b/src/modm/board/nucleo_l053r8/board.hpp @@ -60,10 +60,14 @@ struct SystemClock static constexpr uint32_t Timer21 = Apb2Timer; static constexpr uint32_t Timer22 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz // (internal clock / 1) * 4 / 2 = 32MHz const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_l152re/board.hpp b/src/modm/board/nucleo_l152re/board.hpp index 4c67150a60..ada3f7d2ff 100644 --- a/src/modm/board/nucleo_l152re/board.hpp +++ b/src/modm/board/nucleo_l152re/board.hpp @@ -61,10 +61,14 @@ struct SystemClock static constexpr uint32_t Timer10 = Apb2Timer; static constexpr uint32_t Timer11 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // Enable power scaling for 1.8V PWR->CR = (PWR->CR & ~PWR_CR_VOS) | PWR_CR_VOS_0; while(PWR->CSR & PWR_CSR_VOSF) ; diff --git a/src/modm/board/nucleo_l432kc/board.hpp b/src/modm/board/nucleo_l432kc/board.hpp index 428f5c27a7..cedd99a913 100644 --- a/src/modm/board/nucleo_l432kc/board.hpp +++ b/src/modm/board/nucleo_l432kc/board.hpp @@ -44,10 +44,14 @@ struct SystemClock static constexpr uint32_t Spi1 = Apb2; static constexpr uint32_t Spi2 = Apb2; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + const Rcc::PllFactors pllFactors{ .pllM = 1, // 4MHz / 1 -> 4MHz .pllN = 40, // 4MHz * 40 -> 160MHz <= 344MHz = PLL VCO output max, >= 64 MHz = PLL VCO out min diff --git a/src/modm/board/nucleo_l452re/board.hpp b/src/modm/board/nucleo_l452re/board.hpp index c36e5251c4..cbb13b5b62 100644 --- a/src/modm/board/nucleo_l452re/board.hpp +++ b/src/modm/board/nucleo_l452re/board.hpp @@ -74,10 +74,14 @@ struct SystemClock static constexpr uint32_t Usb = Apb1; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 16MHz Rcc::PllFactors pllFactors{ .pllM = 1, // 16MHz / M= 1 -> 16MHz diff --git a/src/modm/board/nucleo_l476rg/board.hpp b/src/modm/board/nucleo_l476rg/board.hpp index e313429440..6425e2a3a9 100644 --- a/src/modm/board/nucleo_l476rg/board.hpp +++ b/src/modm/board/nucleo_l476rg/board.hpp @@ -54,10 +54,14 @@ struct SystemClock static constexpr uint32_t Usart4 = Apb1; static constexpr uint32_t Usart5 = Apb1; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + // set flash latency first because system already runs from MSI Rcc::setFlashLatency(); Rcc::enableMultiSpeedInternalClock(Rcc::MsiFrequency::MHz48); diff --git a/src/modm/board/nucleo_l496zg-p/board.hpp b/src/modm/board/nucleo_l496zg-p/board.hpp index 1b2a2a2469..235c6c81b5 100644 --- a/src/modm/board/nucleo_l496zg-p/board.hpp +++ b/src/modm/board/nucleo_l496zg-p/board.hpp @@ -82,10 +82,14 @@ struct SystemClock static constexpr uint32_t Rng = 48_MHz; static constexpr uint32_t Sdmmc = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableMultiSpeedInternalClock(Rcc::MsiFrequency::MHz16); const Rcc::PllFactors pllFactors{ .pllM = 2, // 16MHz / M=2 -> 8MHz diff --git a/src/modm/board/nucleo_l552ze-q/board.hpp b/src/modm/board/nucleo_l552ze-q/board.hpp index a99b2d5e39..c5d758123e 100644 --- a/src/modm/board/nucleo_l552ze-q/board.hpp +++ b/src/modm/board/nucleo_l552ze-q/board.hpp @@ -83,10 +83,14 @@ struct SystemClock static constexpr uint32_t Rng = 48_MHz; static constexpr uint32_t Sdmmc = 48_MHz; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::setVoltageScaling(Rcc::VoltageScaling::Range0); Rcc::enableMultiSpeedInternalClock(Rcc::MsiFrequency::MHz4); const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/nucleo_u575zi-q/board.hpp b/src/modm/board/nucleo_u575zi-q/board.hpp index e5c7f737d5..4a67d1ef2e 100644 --- a/src/modm/board/nucleo_u575zi-q/board.hpp +++ b/src/modm/board/nucleo_u575zi-q/board.hpp @@ -85,10 +85,14 @@ struct SystemClock static constexpr uint32_t Rng = Hsi48; static constexpr uint32_t Sdmmc = Pll1P; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::setVoltageScaling(Rcc::VoltageScaling::Range1); Rcc::enableInternalClock(); // HSI16 const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/olimexino_stm32/board.hpp b/src/modm/board/olimexino_stm32/board.hpp index b69c910bcc..5397cec539 100644 --- a/src/modm/board/olimexino_stm32/board.hpp +++ b/src/modm/board/olimexino_stm32/board.hpp @@ -63,10 +63,14 @@ struct SystemClock static constexpr uint32_t Timer7 = Apb1Timer; static constexpr uint32_t Timer8 = Apb2Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableInternalClock(); // 8MHz // internal clock / 2 * 16 = 64MHz, => 64/1.5 = 42.6 => bad for USB const Rcc::PllFactors pllFactors{ diff --git a/src/modm/board/stm32_f4ve/board.hpp b/src/modm/board/stm32_f4ve/board.hpp index 495d61d3a8..da657252bf 100644 --- a/src/modm/board/stm32_f4ve/board.hpp +++ b/src/modm/board/stm32_f4ve/board.hpp @@ -80,10 +80,14 @@ struct SystemClock static constexpr uint32_t Timer13 = Apb1Timer; static constexpr uint32_t Timer14 = Apb1Timer; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = 32.768_kHz; static bool inline enable() { + Rcc::enableLowSpeedExternalCrystal(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::LowSpeedExternalCrystal); + Rcc::enableExternalCrystal(); // 8MHz const Rcc::PllFactors pllFactors{ .pllM = 4, // 8MHz / M=4 -> 2MHz diff --git a/src/modm/board/stm32f030f4p6_demo/board.hpp b/src/modm/board/stm32f030f4p6_demo/board.hpp index e4c129201c..fb328f1be6 100644 --- a/src/modm/board/stm32f030f4p6_demo/board.hpp +++ b/src/modm/board/stm32f030f4p6_demo/board.hpp @@ -48,10 +48,14 @@ struct SystemClock static constexpr uint32_t Timer16 = Apb; static constexpr uint32_t Timer17 = Apb; static constexpr uint32_t Iwdg = Rcc::LsiFrequency; + static constexpr uint32_t Rtc = Rcc::LsiFrequency; static bool inline enable() { + Rcc::enableLowSpeedInternalClock(); + Rcc::enableRealTimeClock(Rcc::RealTimeClockSource::Lsi); + Rcc::enableExternalCrystal(); // 8MHz // external clock / 1 * 6 = 48MHz From f4028937a570318d8775aede4c651d7996954fe4 Mon Sep 17 00:00:00 2001 From: Niklas Hauser Date: Mon, 30 Dec 2024 20:36:56 +0100 Subject: [PATCH 5/5] [example] Add generic RTC for most STM32 boards --- examples/generic/rtc/main.cpp | 59 ++++++++++++++++++++++++++++++++ examples/generic/rtc/openocd.cfg | 2 ++ examples/generic/rtc/project.xml | 48 ++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 examples/generic/rtc/main.cpp create mode 100644 examples/generic/rtc/openocd.cfg create mode 100644 examples/generic/rtc/project.xml diff --git a/examples/generic/rtc/main.cpp b/examples/generic/rtc/main.cpp new file mode 100644 index 0000000000..974842f589 --- /dev/null +++ b/examples/generic/rtc/main.cpp @@ -0,0 +1,59 @@ +// coding: utf-8 +/* + * Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen + * Copyright (c) 2024, Niklas Hauser + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include + +int +main() +{ + Board::initialize(); + Board::Leds::setOutput(); +#ifdef MODM_BOARD_HAS_LOGGER + MODM_LOG_INFO << "Initialize RTC" << modm::endl; +#endif + const bool inited = Rtc::initialize(); +#ifdef MODM_BOARD_HAS_LOGGER + if (not inited) { MODM_LOG_INFO << "RTC was already initialized." << modm::endl; } +#endif + + constexpr auto cdt = modm::DateTime::fromBuildTime(); + if (Rtc::dateTime() < cdt) Rtc::setDateTime(cdt); +#ifdef MODM_BOARD_HAS_LOGGER + MODM_LOG_INFO << "Compile DateTime: " << cdt << modm::endl; + MODM_LOG_INFO << "YMD: " << cdt.year_month_day() << modm::endl; + MODM_LOG_INFO << "HMS: " << cdt.hh_mm_ss() << modm::endl; + MODM_LOG_INFO << "Weekday: " << cdt.weekday() << modm::endl; +#endif + + + while (true) + { + const auto dt = Rtc::dateTime(); +#ifdef MODM_BOARD_HAS_LOGGER + const auto now = Rtc::now(); + MODM_LOG_INFO << dt << " (" << dt.weekday() << ") = " << now << " since 1970" << modm::endl; + modm::delay(1.1s); +#else + static uint8_t prev_second{}; + if (prev_second != dt.seconds().count()) + { + prev_second = dt.seconds().count(); + Board::Leds::toggle(); + } + modm::delay(10ms); +#endif + + } + + return 0; +} diff --git a/examples/generic/rtc/openocd.cfg b/examples/generic/rtc/openocd.cfg new file mode 100644 index 0000000000..2edce20b3d --- /dev/null +++ b/examples/generic/rtc/openocd.cfg @@ -0,0 +1,2 @@ +# Replace this with your custom programmer +source [find interface/stlink.cfg] diff --git a/examples/generic/rtc/project.xml b/examples/generic/rtc/project.xml new file mode 100644 index 0000000000..47c9f2500e --- /dev/null +++ b/examples/generic/rtc/project.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + modm:nucleo-l432kc + + + + + + + + + + + + + + + + + + + + + + + + + + + + modm:platform:rtc + modm:build:scons + +