Skip to content

Commit 5d8123f

Browse files
[stm32] Add RTC peripheral
Co-authored-by: rasmuskleist <[email protected]>
1 parent 05a11a6 commit 5d8123f

File tree

8 files changed

+649
-30
lines changed

8 files changed

+649
-30
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,31 @@ Please [discover modm's peripheral drivers for your specific device][discover].
458458
<td align="center">✕</td>
459459
<td align="center">✕</td>
460460
</tr><tr>
461+
<td align="left">RTC</td>
462+
<td align="center">✅</td>
463+
<td align="center">✅</td>
464+
<td align="center">○</td>
465+
<td align="center">✅</td>
466+
<td align="center">✅</td>
467+
<td align="center">✅</td>
468+
<td align="center">✅</td>
469+
<td align="center">✅</td>
470+
<td align="center">✅</td>
471+
<td align="center">✅</td>
472+
<td align="center">✅</td>
473+
<td align="center">✅</td>
474+
<td align="center">✅</td>
475+
<td align="center">✅</td>
476+
<td align="center">✅</td>
477+
<td align="center">○</td>
478+
<td align="center">○</td>
479+
<td align="center">○</td>
480+
<td align="center">○</td>
481+
<td align="center">○</td>
482+
<td align="center">✕</td>
483+
<td align="center">✕</td>
484+
<td align="center">✕</td>
485+
</tr><tr>
461486
<td align="left">SPI</td>
462487
<td align="center">✅</td>
463488
<td align="center">✅</td>

src/modm/platform/clock/stm32/module.lb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def build(env):
3939
properties["hsi_frequency"] = 48_000_000
4040
properties["lsi_frequency"] = 32_000
4141
properties["boot_frequency"] = 12_000_000
42-
elif target["family"] in ["f1", "f3"]:
42+
elif target["family"] in ["f0", "f1", "f3"]:
4343
properties["hsi_frequency"] = 8_000_000
4444
properties["lsi_frequency"] = 40_000
4545
properties["boot_frequency"] = properties["hsi_frequency"]
@@ -187,6 +187,9 @@ def build(env):
187187
nper = "DSI"
188188
if "Eth" in all_peripherals and per == "ETHMAC":
189189
per = "Eth"
190+
if "Rtc" in all_peripherals and per == "RTCAPB":
191+
per = "RTC"
192+
nper = "RTCAPB"
190193
# Fix USBOTG OTG
191194
if target.family == "u5" and per == "OTG":
192195
per = "Usbotgfs"
@@ -200,7 +203,9 @@ def build(env):
200203
if per.capitalize() not in all_peripherals:
201204
continue
202205
if "EN" in mode:
203-
rcc_enable[per.capitalize()] = (nper, mode["EN"])
206+
kw = per.capitalize()
207+
if kw not in rcc_enable:
208+
rcc_enable[kw] = (nper, mode["EN"])
204209
if "RST" in mode:
205210
rcc_reset[per.capitalize()] = (nper, mode["RST"])
206211

src/modm/platform/core/stm32/startup_platform.c.in

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Copyright (c) 2016, Sascha Schade
33
* Copyright (c) 2016-2017, Fabian Greif
4-
* Copyright (c) 2016-2017, 2019, Niklas Hauser
4+
* Copyright (c) 2016-2017, 2019, 2024, Niklas Hauser
55
* Copyright (c) 2021, Raphael Lehmann
66
* Copyright (c) 2021, Christopher Durand
77
*
@@ -30,60 +30,75 @@ __modm_initialize_platform(void)
3030
{
3131
// Enable SYSCFG
3232
%% if target.family in ["c0", "g0"]
33-
RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN;
33+
RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; __DSB();
34+
%% elif target.family == "f0"
35+
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGCOMPEN; __DSB();
3436
%% elif target.family == "f1"
35-
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
37+
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; __DSB();
3638
%% elif target.family == "h7"
37-
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN;
39+
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; __DSB();
3840
%% elif target.family == "u5"
39-
RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN;
41+
RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN; __DSB();
4042
%% else
41-
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
43+
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; __DSB();
4244
%% endif
4345

44-
%% if target.family == "f4"
45-
// Only the more powerful F4 targets have CCM or Backup SRAM
46-
#ifdef RCC_AHB1ENR_CCMDATARAMEN
4746
// Enable power to backup domain
48-
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
49-
// Enable write access to backup SRAM
47+
%% if target.family == "f1"
48+
RCC->APB1ENR |= RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN; __DSB();
49+
%% elif target.family in ["f0", "f2", "f3", "f4", "f7", "l0", "l1"]
50+
RCC->APB1ENR |= RCC_APB1ENR_PWREN; __DSB();
51+
%% elif target.family in ["c0", "g0", "u0"]
52+
RCC->APBENR1 |= RCC_APBENR1_PWREN; __DSB();
53+
%% elif target.family in ["g4", "l4", "l5"]
54+
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; __DSB();
55+
%% elif target.family == "u5"
56+
RCC->AHB3ENR |= RCC_AHB3ENR_PWREN; __DSB();
57+
%% endif
58+
59+
%% if target.family in ["f0", "f1", "f2", "f3", "f4", "l0", "l1"]
5060
PWR->CR |= PWR_CR_DBP;
61+
%% elif target.family in ["f7", "g0", "g4", "h7", "l4", "l5", "u0", "wb", "wl"]
62+
PWR->CR1 |= PWR_CR1_DBP;
63+
%% elif target.family == "h5"
64+
PWR->DBPCR |= PWR_DBPCR_DBP;
65+
%% elif target.family in ["u5", "wba"]
66+
PWR->DBPR |= PWR_DBPR_DBP;
67+
%% endif
68+
69+
%% if target.family == "f4"
70+
// Only the more powerful F4 targets have CCM or Backup SRAM
71+
#ifdef RCC_AHB1ENR_CCMDATARAMEN
5172
// Enable Core Coupled Memory (CCM) and backup SRAM (BKPSRAM)
5273
RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN | RCC_AHB1ENR_BKPSRAMEN;
5374
#endif
5475
%% elif target.family == "f7"
5576
// Reset from DFU settings to reset values.
5677
RCC->DCKCFGR2 = 0;
57-
// Enable power to backup domain
58-
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
59-
// Enable write access to backup SRAM
60-
PWR->CR1 |= PWR_CR1_DBP;
6178
// Enable Data Tighly Coupled Memory (DTCM) and backup SRAM (BKPSRAM)
6279
RCC->AHB1ENR |= RCC_AHB1ENR_DTCMRAMEN | RCC_AHB1ENR_BKPSRAMEN;
63-
%% elif target.family in ["g0", "g4", "l4", "l5"]
64-
%% if target.family in ["l4", "g4"]
65-
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
66-
%% elif target.family != "g0"
67-
#ifdef PWR_CR2_IOSV
68-
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN;
69-
#endif
70-
%% endif
71-
80+
%% elif target.family == "h7"
81+
// Enable all SRAMs
82+
%% if target.name[0].isnumeric()
83+
RCC->AHB2ENR |= RCC_AHB2ENR_SRAM1EN | RCC_AHB2ENR_SRAM2EN;
84+
%% else
85+
RCC->AHB2ENR |= RCC_AHB2ENR_AHBSRAM1EN | RCC_AHB2ENR_AHBSRAM2EN;
86+
%% endif
87+
RCC->AHB4ENR |= RCC_AHB4ENR_BKPRAMEN;
88+
%% elif target.family in ["g4", "l4", "l5"]
7289
#ifdef PWR_CR2_IOSV
7390
// Enable VDDIO2
7491
PWR->CR2 |= PWR_CR2_IOSV;
7592
#endif
76-
%% elif target.family in ["u5"]
77-
RCC->AHB3ENR |= RCC_AHB3ENR_PWREN;
93+
%% elif target.family == "u5"
7894
// Enable power for VDDIO2 and USB
7995
PWR->SVMCR |= PWR_SVMCR_ASV | PWR_SVMCR_IO2SV | PWR_SVMCR_USV;
80-
8196
// Enable Backup SRAM (BKPSRAM)
82-
PWR->DBPR |= PWR_DBPR_DBP;
8397
RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN;
8498
%% endif
8599

86100
%% if vector_table_location == "ram"
101+
__DSB();
87102
// Remap SRAM to 0x0 for vector table relocation without VTOR register
88103
SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE;
89104
%% endif
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
5+
# Copyright (c) 2024, Niklas Hauser
6+
#
7+
# This file is part of the modm project.
8+
#
9+
# This Source Code Form is subject to the terms of the Mozilla Public
10+
# License, v. 2.0. If a copy of the MPL was not distributed with this
11+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
12+
# -----------------------------------------------------------------------------
13+
14+
def init(module):
15+
module.name = ":platform:rtc"
16+
module.description = FileReader("module.md")
17+
18+
def prepare(module, options):
19+
device = options[":target"]
20+
if not device.has_driver("rtc:stm32*") or device.identifier.family in ["f1"]:
21+
return False
22+
23+
module.depends(
24+
":cmsis:device",
25+
":platform:rcc",
26+
":math:calendar",
27+
":architecture:fiber",
28+
)
29+
30+
return True
31+
32+
def build(env):
33+
env.outbasepath = "modm/src/modm/platform/rtc"
34+
target = env[":target"].identifier
35+
env.substitutions = {
36+
# F1, F2, L1 do not have the RTC->SSR register.
37+
# (Some L1 device do have a SSR field, but the CMSIS headers are inconsistent).
38+
"with_ssr": target.family not in ["f1", "f2", "l1"],
39+
# F2, L1 have a smaller PREDIV_S register field.
40+
"bits_prediv_s": 13 if target.family in ["f2", "l1"] else 15,
41+
}
42+
env.template("rtc.hpp.in")
43+
env.template("rtc_impl.hpp.in")
44+
env.copy("rtc.cpp")
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Real Time Clock (RTC)
2+
3+
The STM32 RTC implements a full calendar in hardware to provide a date and time
4+
in binary-coded decimal (BCD) format. Several optimized methods are provided to
5+
provide an efficient conversion from this hardware format to a software
6+
representation.
7+
8+
The epoch of the RTC is chosen to be the 1st of January 1970 to be compatible
9+
with UNIX timestamps. Since the year is limited to two BCD digits, the RTC will
10+
roll over in 2070.
11+
12+
Note that the RTC hardware has no support for time zones, so you have to handle
13+
that in software.
14+
15+
16+
## Initialization
17+
18+
The RTC keeps running during a reset of the microcontroller when the backup
19+
domain is powered. To prevent clock drift, the `initialize()` function will
20+
check if the RTC is already running and only initialize the prescaler differs
21+
from the programmed one. If the return value is `false` the RTC was already
22+
initialized and running:
23+
24+
```cpp
25+
struct SystemClock
26+
{
27+
static constexpr uint32_t Rtc = 32'768;
28+
};
29+
const bool inited = Rtc::initialize<SystemClock>();
30+
if (not inited) { /* RTC was already running from before reset */ }
31+
```
32+
33+
To always initialize the RTC, set the `forced` argument to `true`:
34+
35+
```cpp
36+
Rtc::initialize<SystemClock>(true);
37+
```
38+
39+
To give the RTC an initial date and time, use the `setDateTime()` function. You
40+
can use the compile time as a basic reference time, and only set the time
41+
forward to not reset the time on every reset:
42+
43+
```cpp
44+
constexpr auto cdt = modm::DateTime::fromBuildTime();
45+
if (Rtc::dateTime() < cdt) Rtc::setDateTime(cdt);
46+
```
47+
48+
49+
## Accessing Date and Time
50+
51+
The RTC hardware provides the date and time in BCD format which can be
52+
atomically read out with the `dateTime()` function, which returns a `DateTime`
53+
object that can be used to access the individual date and time components:
54+
55+
```cpp
56+
const auto dt = Rtc::dateTime();
57+
dt.year_month_day();
58+
dt.day_of_year();
59+
dt.weekday();
60+
61+
dt.hours();
62+
dt.minutes();
63+
dt.seconds();
64+
dt.subseconds();
65+
66+
// prints ISO encoding: 2024-12-22 18:39:21.342
67+
MODM_LOG_INFO << dt << modm::endl;
68+
69+
// Efficient conversion to std::tm
70+
const std::tm tm = dt.tm();
71+
```
72+
73+
Please note that while the `DateTime` object provides methods to compute to
74+
seconds and milliseconds since epoch, these are slow and should be avoided.
75+
Instead, use the `Rtc::now()`, `Rtc::time_t()` and `Rtc::timeval()` functions
76+
to access optimized and cached conversion methods which are much faster:
77+
78+
```cpp
79+
const Rtc::time_point tp = Rtc::now();
80+
// instead of Rtc::dateTime().time_since_epoch();
81+
const std::time_t tt = Rtc::time_t();
82+
// instead of Rtc::dateTime().time_t();
83+
const struct timeval tv = Rtc::timeval();
84+
// instead of Rtc::dateTime().timeval();
85+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2024, Niklas Hauser
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#include "rtc.hpp"
13+
14+
extern "C" int
15+
_gettimeofday(struct timeval *tp, void *)
16+
{
17+
*tp = modm::platform::Rtc::timeval();
18+
return 0;
19+
}

0 commit comments

Comments
 (0)