diff --git a/README.md b/README.md
index fc4f82adb6..9fedddfe46 100644
--- a/README.md
+++ b/README.md
@@ -445,6 +445,30 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕ |
✕ |
+RTC |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+✅ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+○ |
+✕ |
+✕ |
+✕ |
+
SPI |
✅ |
✅ |
diff --git a/examples/nucleo_g474re/rtc/main.cpp b/examples/nucleo_g474re/rtc/main.cpp
new file mode 100644
index 0000000000..c23ad2cd2a
--- /dev/null
+++ b/examples/nucleo_g474re/rtc/main.cpp
@@ -0,0 +1,63 @@
+// coding: utf-8
+/*
+ * 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
+#include
+#include
+
+using namespace modm::literals;
+
+// Set the log level
+#undef MODM_LOG_LEVEL
+#define MODM_LOG_LEVEL modm::log::INFO
+
+static constexpr Rtc::DateTime datetime = {
+ .year = 0,
+ .month = 12,
+ .date = 31,
+ .weekday = 1,
+ .hour = 23,
+ .minute = 59,
+ .second = 30,
+};
+
+int
+main()
+{
+ Board::initialize();
+
+ modm::PeriodicTimer timer(std::chrono::milliseconds(1000));
+
+ Rtc::initialize();
+ while (true)
+ {
+ if (timer.execute())
+ {
+ float ss;
+ Rtc::DateTime dt;
+ if (Rtc::getSubSecond(ss) && Rtc::getDateTime(dt))
+ {
+ MODM_LOG_INFO << "Date: " << dt.date << "-" << dt.month << "-" << dt.year << modm::endl;
+ MODM_LOG_INFO << "Weekday: " << dt.weekday << modm::endl;
+ MODM_LOG_INFO << "Time: " << dt.hour << ":" << dt.minute << ":" << dt.second << modm::endl;
+ MODM_LOG_INFO << "Sub Second: " << ss << modm::endl;
+ }
+ else
+ {
+ MODM_LOG_INFO << "Error: Unable to get Date Time and Sub Second" << modm::endl;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/nucleo_g474re/rtc/project.xml b/examples/nucleo_g474re/rtc/project.xml
new file mode 100644
index 0000000000..f2a8865c21
--- /dev/null
+++ b/examples/nucleo_g474re/rtc/project.xml
@@ -0,0 +1,12 @@
+
+ modm:nucleo-g474re
+
+
+
+
+ modm:debug
+ modm:platform:rtc
+ modm:processing:timer
+ modm:build:scons
+
+
diff --git a/src/modm/board/nucleo_g474re/board.hpp b/src/modm/board/nucleo_g474re/board.hpp
index 870a8c178e..fc8541cccf 100644
--- a/src/modm/board/nucleo_g474re/board.hpp
+++ b/src/modm/board/nucleo_g474re/board.hpp
@@ -114,6 +114,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/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..fe1c341d8f
--- /dev/null
+++ b/src/modm/math/utils/bcd.hpp
@@ -0,0 +1,53 @@
+/*
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#ifndef MODM_MATH_BCD_HPP
+#define MODM_MATH_BCD_HPP
+
+#include
+
+namespace modm
+{
+
+/// @ingroup modm_math_utils
+/// @{
+
+constexpr uint32_t
+fromBcd(uint32_t bcd)
+{
+ uint32_t decimal = 0;
+ for (uint16_t multiplier = 1; bcd != 0; multiplier *= 10)
+ {
+ decimal += (bcd & 0b1111) * multiplier;
+ bcd >>= 4;
+ }
+ return decimal;
+}
+
+constexpr uint32_t
+toBcd(uint32_t decimal)
+{
+ uint32_t bcd = 0;
+ uint16_t remainder = decimal % 10;
+ for (uint16_t shift = 0; decimal != 0; shift += 4)
+ {
+ bcd |= remainder << shift;
+ decimal = (decimal - remainder) / 10;
+ remainder = decimal % 10;
+ }
+ return bcd;
+}
+
+/// @}
+
+} // namespace modm
+
+#endif // MODM_MATH_BCD_HPP
diff --git a/src/modm/platform/clock/stm32/module.lb b/src/modm/platform/clock/stm32/module.lb
index 6dff923a6f..c81af65a2b 100644
--- a/src/modm/platform/clock/stm32/module.lb
+++ b/src/modm/platform/clock/stm32/module.lb
@@ -179,6 +179,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"
@@ -193,7 +196,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[nper] = mode["RST"]
diff --git a/src/modm/platform/clock/stm32/rcc_impl.hpp.in b/src/modm/platform/clock/stm32/rcc_impl.hpp.in
index 1ed38f1485..5e94f23d88 100644
--- a/src/modm/platform/clock/stm32/rcc_impl.hpp.in
+++ b/src/modm/platform/clock/stm32/rcc_impl.hpp.in
@@ -167,4 +167,4 @@ Rcc::isEnabled()
%% endfor
}
-} // namespace modm::platform
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/core/stm32/startup_platform.c.in b/src/modm/platform/core/stm32/startup_platform.c.in
index 5764524b36..408a57856f 100644
--- a/src/modm/platform/core/stm32/startup_platform.c.in
+++ b/src/modm/platform/core/stm32/startup_platform.c.in
@@ -61,6 +61,8 @@ __modm_initialize_platform(void)
// 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"]
+ // Enable access to RTC and Backup registers
+ PWR->CR1 |= PWR_CR1_DBP;
%% if target.family in ["l4", "g4"]
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
%% elif target.family != "g0"
diff --git a/src/modm/platform/rtc/stm32/module.lb b/src/modm/platform/rtc/stm32/module.lb
new file mode 100644
index 0000000000..92f29e6b1b
--- /dev/null
+++ b/src/modm/platform/rtc/stm32/module.lb
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# 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/.
+# -----------------------------------------------------------------------------
+
+def init(module):
+ module.name = ":platform:rtc"
+ module.description = "Real Time Clock (RTC)"
+
+def prepare(module, options):
+ device = options[":target"]
+ if device.identifier.family not in ["g4"]:
+ return False
+ if not device.has_driver("rtc:stm32*"):
+ return False
+
+ module.depends(
+ ":cmsis:device",
+ ":platform:rcc",
+ ":architecture:register",
+ ":math:utils",
+ ":math:units",)
+
+ return True
+
+def build(env):
+ target = env[":target"].identifier
+
+ env.outbasepath = "modm/src/modm/platform/rtc"
+ env.template("rtc_impl.hpp.in")
+ env.template("rtc.cpp.in")
+ env.template("rtc.hpp.in")
diff --git a/src/modm/platform/rtc/stm32/rtc.cpp.in b/src/modm/platform/rtc/stm32/rtc.cpp.in
new file mode 100644
index 0000000000..78dcbebf00
--- /dev/null
+++ b/src/modm/platform/rtc/stm32/rtc.cpp.in
@@ -0,0 +1,61 @@
+/*
+ * 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 "rtc.hpp"
+
+#include
+
+namespace modm::platform
+{
+
+// ----------------------------------------------------------------------------
+
+bool
+Rtc::getDateTime(DateTime &dateTime, uint32_t waitCycles)
+{
+ /// TODO: Determine if ABP1 frequency is less than seven times RTC frequency
+ /// TODO: Determine if BYPSHAD control bit is set or cleared in the RTC_CR register
+
+ // After initialization, synchronization software must wait until RSF is set
+ while (!(RTC->ICSR & RTC_ICSR_RSF))
+ if (--waitCycles == 0) return false;
+
+ dateTime.hour = fromBcd((RTC->TR & (RTC_TR_HT_Msk | RTC_TR_HU_Msk)) >> RTC_TR_HU_Pos);
+ dateTime.minute = fromBcd((RTC->TR & (RTC_TR_MNT_Msk | RTC_TR_MNU_Msk)) >> RTC_TR_MNU_Pos);
+ dateTime.second = fromBcd((RTC->TR & (RTC_TR_ST_Msk | RTC_TR_SU_Msk)) >> RTC_TR_SU_Pos);
+
+ dateTime.year = fromBcd((RTC->DR & (RTC_DR_YT_Msk | RTC_DR_YU_Msk)) >> RTC_DR_YU_Pos);
+ dateTime.month = fromBcd((RTC->DR & (RTC_DR_MT_Msk | RTC_DR_MU_Msk)) >> RTC_DR_MU_Pos);
+ dateTime.date = fromBcd((RTC->DR & (RTC_DR_DT_Msk | RTC_DR_DU_Msk)) >> RTC_DR_DU_Pos);
+ dateTime.weekday = ((RTC->DR & RTC_DR_WDU_Msk) >> RTC_DR_WDU_Pos);
+
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+
+bool
+Rtc::getSubSecond(float &subsecond, uint32_t waitCycles)
+{
+ // After initialization, synchronization software must wait until RSF is set
+ while (!(RTC->ICSR & RTC_ICSR_RSF))
+ if (--waitCycles == 0) return false;
+
+ /*
+ uint32_t predivS = getSynchronousPrescaler();
+ uint32_t ss = (RTC->SSR & RTC_SSR_SS_Msk) >> RTC_SSR_SS_Pos;
+ subsecond = float(predivS - ss) / float(predivS + 1);
+ /// TODO: Handle case when ss > predivS
+ */
+ return true;
+}
+
+}
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..8387f74b1b
--- /dev/null
+++ b/src/modm/platform/rtc/stm32/rtc.hpp.in
@@ -0,0 +1,178 @@
+/*
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+#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 Rasmus Kleist Hørlyck Sørensen
+ * @ingroup modm_platform_rtc
+ */
+class Rtc
+{
+ using duration = std::chrono::duration;
+ using rep = duration::rep;
+ using period = duration::period;
+ using time_point = std::chrono::time_point