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/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
+
+
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
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/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/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