diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 84583a9fa2..5bdfdcd936 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -183,6 +183,10 @@ jobs:
if: always()
run: |
(cd examples && ../tools/scripts/examples_compile.py nucleo_u083rc)
+ - name: Examples STM32U3 Series
+ if: always()
+ run: |
+ (cd examples && ../tools/scripts/examples_compile.py nucleo_u385rg-q)
- name: Examples STM32U5 Series
if: always()
run: |
diff --git a/README.md b/README.md
index dc40d850b4..a58257de5e 100644
--- a/README.md
+++ b/README.md
@@ -82,9 +82,9 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git
## Microcontrollers
-modm can create a HAL for 3936 devices of these vendors:
+modm can create a HAL for 3984 devices of these vendors:
-- STMicroelectronics STM32: 3192 devices.
+- STMicroelectronics STM32: 3240 devices.
- Microchip SAM: 355 devices.
- Microchip AVR: 388 devices.
- Raspberry Pi: 1 device.
@@ -104,7 +104,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
|
-STM32 |
+STM32 |
SAM |
RP |
AT |
@@ -126,6 +126,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
L4 |
L5 |
U0 |
+U3 |
U5 |
D1x D2x DAx |
D5x E5x |
@@ -156,6 +157,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
✅ |
✅ |
✅ |
@@ -181,6 +183,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✕ |
✅ |
+✅ |
✕ |
○ |
✅ |
@@ -211,6 +214,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
✕ |
✕ |
○ |
@@ -235,6 +239,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
○ |
✅ |
@@ -264,6 +269,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
✅ |
✕ |
✅ |
@@ -290,6 +296,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕ |
✕ |
✕ |
+✕ |
○ |
○ |
✕ |
@@ -317,6 +324,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
○ |
○ |
@@ -343,6 +351,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
✕ |
○ |
+○ |
✕ |
✕ |
○ |
@@ -378,6 +387,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
| I2C |
✅ |
@@ -396,6 +406,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
○ |
+○ |
✅ |
○ |
○ |
@@ -428,6 +439,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
✕ |
✕ |
✕ |
@@ -451,6 +463,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
✕ |
✕ |
✕ |
@@ -478,6 +491,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
✕ |
○ |
○ |
@@ -505,6 +519,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
○ |
○ |
@@ -534,6 +549,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
✅ |
✅ |
✅ |
@@ -564,6 +580,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
✕ |
✕ |
✕ |
@@ -586,6 +603,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
○ |
✅ |
@@ -620,6 +638,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
| Unique ID |
@@ -640,6 +659,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
✕ |
✕ |
✕ |
@@ -669,6 +689,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
○ |
✅ |
✅ |
@@ -771,28 +792,29 @@ We have out-of-box support for many development boards including documentation.
NUCLEO-L496ZG-P |
NUCLEO-L552ZE-Q |
NUCLEO-U083RC |
-NUCLEO-U575ZI-Q |
+NUCLEO-U385RG-Q |
+| NUCLEO-U575ZI-Q |
OLIMEXINO-STM32 |
Raspberry Pi Pico |
SAMD21-MINI |
-SAMD21-XPLAINED-PRO |
+| SAMD21-XPLAINED-PRO |
SAME54-XPLAINED-PRO |
SAME70-XPLAINED |
SAMG55-XPLAINED-PRO |
-SAMV71-XPLAINED-ULTRA |
+| SAMV71-XPLAINED-ULTRA |
Smart Response XE |
STM32-F4VE |
STM32F030-DEMO |
-THINGPLUS-RP2040 |
+| THINGPLUS-RP2040 |
WEACT-C011F6 |
WEACT-G0B1CB |
WEACT-H503CB |
-WEACT-H562RG |
+| WEACT-H562RG |
WEACT-U585CI |
diff --git a/examples/generic/usb/project.xml b/examples/generic/usb/project.xml
index 154a1f7d0f..5a4dbf05b0 100644
--- a/examples/generic/usb/project.xml
+++ b/examples/generic/usb/project.xml
@@ -22,6 +22,7 @@
+
diff --git a/examples/nucleo_u385rg-q/blink/main.cpp b/examples/nucleo_u385rg-q/blink/main.cpp
new file mode 100644
index 0000000000..b890fc662b
--- /dev/null
+++ b/examples/nucleo_u385rg-q/blink/main.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016-2017, 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
+
+using namespace Board;
+
+int
+main()
+{
+ Board::initialize();
+ Leds::setOutput();
+
+ // Use the logging streams to print some messages.
+ // Change MODM_LOG_LEVEL above to enable or disable these messages
+ MODM_LOG_DEBUG << "debug" << modm::endl;
+ MODM_LOG_INFO << "info" << modm::endl;
+ MODM_LOG_WARNING << "warning" << modm::endl;
+ MODM_LOG_ERROR << "error" << modm::endl;
+
+ for (const auto [traits, start, end, size] : modm::platform::HeapTable())
+ {
+ MODM_LOG_INFO.printf("Memory section %#x @[0x%p,0x%p](%u)\n",
+ traits.value, start, end, size);
+ }
+
+ uint32_t counter(0);
+ modm::ShortTimeout tmr;
+
+ while (true)
+ {
+ Leds::write(1 << (counter % (Leds::width+1) ));
+ // modm::delay(Button::read() ? 100ms : 500ms);
+ tmr.restart(Button::read() ? 100ms : 500ms);
+ while(not tmr.execute()) ;
+
+ MODM_LOG_INFO << "loop: " << counter++ << modm::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/nucleo_u385rg-q/blink/project.xml b/examples/nucleo_u385rg-q/blink/project.xml
new file mode 100644
index 0000000000..da37b2e06d
--- /dev/null
+++ b/examples/nucleo_u385rg-q/blink/project.xml
@@ -0,0 +1,11 @@
+
+ modm:nucleo-u385rg-q
+
+
+
+
+ modm:architecture:memory
+ modm:build:scons
+ modm:processing:timer
+
+
diff --git a/ext/hathach/module.lb b/ext/hathach/module.lb
index 53955faee0..d953888bc5 100644
--- a/ext/hathach/module.lb
+++ b/ext/hathach/module.lb
@@ -103,7 +103,7 @@ def build(env):
if target.platform == "stm32":
tusb_config["CFG_TUSB_MCU"] = f"OPT_MCU_STM32{target.family.upper()}"
# TODO: use modm-devices driver type for this
- fs_dev = (target.family in ["f0", "f3", "l0", "g0", "g4", "h5", "u0"] or
+ fs_dev = (target.family in ["f0", "f3", "l0", "g0", "g4", "h5", "u0", "u3"] or
(target.family == "f1" and target.name <= "03"))
if fs_dev:
# PMA buffer size: 512B or 1024B
diff --git a/ext/modm-devices b/ext/modm-devices
index 44c9ddd4b0..3bf95d16ae 160000
--- a/ext/modm-devices
+++ b/ext/modm-devices
@@ -1 +1 @@
-Subproject commit 44c9ddd4b0a51c5209716ca2b2642265353f2fe6
+Subproject commit 3bf95d16ae94424076b92b98b82e222750ccb768
diff --git a/repo.lb b/repo.lb
index 9772db20f0..25d88f7c61 100644
--- a/repo.lb
+++ b/repo.lb
@@ -80,7 +80,7 @@ class DevicesCache(dict):
"stm32g0", "stm32g4",
"stm32h5", "stm32h7",
"stm32l0", "stm32l1", "stm32l4", "stm32l5",
- "stm32u0", "stm32u5",
+ "stm32u0", "stm32u3", "stm32u5",
)
# Load and filter the device prefixes
modm_db = json.loads((modm_devices / "db.json").read_text())
diff --git a/src/modm/board/nucleo_u385rg-q/board.hpp b/src/modm/board/nucleo_u385rg-q/board.hpp
new file mode 100644
index 0000000000..c7533631da
--- /dev/null
+++ b/src/modm/board/nucleo_u385rg-q/board.hpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2026, 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
+
+using namespace modm::platform;
+
+/// @ingroup modm_board_nucleo_u385rg_q
+#define MODM_BOARD_HAS_LOGGER
+
+namespace Board
+{
+/// @ingroup modm_board_nucleo_u385rg_q
+/// @{
+using namespace modm::literals;
+
+/// STM32U385RG running at 96MHz from internal MSIS oscillator
+struct SystemClock
+{
+ // LSE 32.768 kHz crystal is connected by default (SB1/SB2 ON)
+ // HSE 16 MHz crystal is available but NOT connected by default
+ // MCO 8 MHz from STLINK-V3EC is available but NOT connected by default
+ static constexpr uint32_t Lse = 32.768_kHz;
+
+ // MSIS (Multi Speed Internal System) clock frequency
+ static constexpr uint32_t MsiS = 96_MHz;
+ static_assert(MsiS == Rcc::MaxFrequency);
+
+ static constexpr uint32_t SysClk = MsiS;
+ static constexpr uint32_t Frequency = SysClk;
+
+ static constexpr uint32_t Hclk = SysClk / 1;
+ static constexpr uint32_t Ahb = Hclk;
+
+ static constexpr uint32_t Apb = Hclk / 1;
+ static constexpr uint32_t Apb1 = Apb;
+ static constexpr uint32_t Apb2 = Apb;
+ static constexpr uint32_t Apb3 = Apb;
+
+ // AHB1 Peripherals
+ static constexpr uint32_t Gpdma1 = Ahb;
+ static constexpr uint32_t Flash = Ahb;
+ static constexpr uint32_t Crc = Ahb;
+ static constexpr uint32_t Tsc = Ahb;
+
+ // AHB2 Peripherals
+ static constexpr uint32_t Gpio = Ahb;
+ static constexpr uint32_t Adc1 = Ahb;
+ static constexpr uint32_t Adc2 = Ahb;
+ static constexpr uint32_t Dac1 = Ahb;
+ static constexpr uint32_t Aes = Ahb;
+ static constexpr uint32_t Hash = Ahb;
+ static constexpr uint32_t Rng = Ahb;
+
+ // APB1 Peripherals
+ static constexpr uint32_t Tim2 = Apb1;
+ static constexpr uint32_t Tim3 = Apb1;
+ static constexpr uint32_t Tim4 = Apb1;
+ static constexpr uint32_t Tim6 = Apb1;
+ static constexpr uint32_t Tim7 = Apb1;
+ static constexpr uint32_t Wwdg = Apb1;
+ static constexpr uint32_t Spi2 = Apb1;
+ static constexpr uint32_t Spi3 = Apb1;
+ static constexpr uint32_t Usart3 = Apb1;
+ static constexpr uint32_t Uart4 = Apb1;
+ static constexpr uint32_t Uart5 = Apb1;
+ static constexpr uint32_t I2c1 = Apb1;
+ static constexpr uint32_t I2c2 = Apb1;
+ static constexpr uint32_t I3c1 = Apb1;
+ static constexpr uint32_t Opamp1 = Apb1;
+ static constexpr uint32_t Opamp2 = Apb1;
+ static constexpr uint32_t LpTimer2 = Apb1;
+ static constexpr uint32_t FdCan1 = Apb1;
+
+ // APB2 Peripherals
+ static constexpr uint32_t Tim1 = Apb2;
+ static constexpr uint32_t Spi1 = Apb2;
+ static constexpr uint32_t Usart1 = Apb2;
+ static constexpr uint32_t Tim15 = Apb2;
+ static constexpr uint32_t Tim16 = Apb2;
+ static constexpr uint32_t Tim17 = Apb2;
+ static constexpr uint32_t Sai1 = Apb2;
+ static constexpr uint32_t I3c2 = Apb2;
+
+ // APB3 Peripherals
+ static constexpr uint32_t LpUart1 = Apb3;
+ static constexpr uint32_t I2c3 = Apb3;
+ static constexpr uint32_t LpTimer1 = Apb3;
+ static constexpr uint32_t LpTimer3 = Apb3;
+ static constexpr uint32_t LpTimer4 = Apb3;
+ static constexpr uint32_t Comp1 = Apb3;
+ static constexpr uint32_t Comp2 = Apb3;
+
+ // Timer Clocks (already defined above with correct bus assignments)
+
+ // Special Clock Sources
+ static constexpr uint32_t Rtc = Lse;
+ static constexpr uint32_t Usb = 48_MHz; // Requires HSI48
+ static constexpr uint32_t Iwdg = Rcc::LsiFrequency;
+
+ static bool inline
+ enable()
+ {
+ Rcc::enableLseCrystal();
+ Rcc::enableHsiClock();
+ Rcc::setBoosterClock(Rcc::BoosterClockSource::Hsi16, Rcc::BoosterPrescaler::Div1);
+
+ Rcc::setVoltageScaling(Rcc::VoltageScaling::Range1_96MHz);
+ Rcc::setFlashLatency();
+
+ Rcc::enableMsiSystemClock(Rcc::MsiFrequency::MHz96);
+ Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1);
+ Rcc::setApb1Prescaler(Rcc::ApbPrescaler::Div1);
+ Rcc::setApb2Prescaler(Rcc::ApbPrescaler::Div1);
+ Rcc::setApb3Prescaler(Rcc::ApbPrescaler::Div1);
+
+ Rcc::enableSystemClock(Rcc::SystemClockSource::MsiS);
+ Rcc::updateCoreFrequency();
+
+ Rcc::enableHsi48Clock();
+ Rcc::setUsbClockSource(Rcc::IclkSource::Hsi48);
+ Rcc::setRealTimeClockSource(Rcc::RealTimeClockSource::Lse);
+
+ // Enable CRS clock
+ RCC->APB1ENR1 |= RCC_APB1ENR1_CRSEN; __DSB();
+ // Configure CRS: USB SOF sync, reload=47999, error limit=34, trim=32
+ CRS->CFGR = (2U << CRS_CFGR_SYNCSRC_Pos) | // USB SOF
+ (34U << CRS_CFGR_FELIM_Pos) | // Error limit
+ (47999U << CRS_CFGR_RELOAD_Pos); // Reload value
+ CRS->CR = (32U << CRS_CR_TRIM_Pos) | // Calibration value
+ CRS_CR_AUTOTRIMEN | CRS_CR_CEN; // Enable auto-trim and CRS
+
+ return true;
+ }
+};
+
+using A0 = GpioA0;
+using A1 = GpioA1;
+using A2 = GpioA2;
+using A3 = GpioB0;
+using A4 = GpioC1;
+using A5 = GpioC0;
+
+using D0 = GpioB15;
+using D1 = GpioB14;
+using D2 = GpioA10;
+using D3 = GpioB3;
+using D4 = GpioB5;
+using D5 = GpioB4;
+using D6 = GpioB10;
+using D7 = GpioA8;
+using D8 = GpioC7;
+using D9 = GpioC6;
+using D10 = GpioC9;
+using D11 = GpioA7;
+using D12 = GpioA6;
+using D13 = GpioA5;
+using D14 = GpioB7;
+using D15 = GpioB6;
+
+using Button = GpioInputC13;
+using Led = GpioOutputA5;
+using Leds = SoftwareGpioPort< Led >;
+/// @}
+
+namespace usb
+{
+/// @ingroup modm_board_nucleo_u385rg_q
+/// @{
+using Dm = GpioA11;
+using Dp = GpioA12;
+
+using Device = UsbFs;
+/// @}
+}
+
+namespace stlink
+{
+/// @ingroup modm_board_nucleo_u385rg_q
+/// @{
+using Tx = GpioOutputA9;
+using Rx = GpioInputA10;
+using Uart = BufferedUart>;
+/// @}
+}
+
+/// @ingroup modm_board_nucleo_u385rg_q
+/// @{
+using LoggerDevice = modm::IODeviceWrapper< stlink::Uart, modm::IOBuffer::BlockIfFull >;
+
+inline void
+initialize()
+{
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ stlink::Uart::connect();
+ stlink::Uart::initialize();
+
+ Led::setOutput(modm::Gpio::Low);
+ // Button is active HIGH (LOW when released), requires pull-down
+ Button::setInput(Gpio::InputType::PullDown);
+}
+
+inline void
+initializeUsb(uint8_t priority=3)
+{
+ usb::Device::initialize(priority);
+ usb::Device::connect();
+}
+/// @}
+
+}
diff --git a/src/modm/board/nucleo_u385rg-q/board.xml b/src/modm/board/nucleo_u385rg-q/board.xml
new file mode 100644
index 0000000000..865abbee7c
--- /dev/null
+++ b/src/modm/board/nucleo_u385rg-q/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+
+
+
+ modm:board:nucleo-u385rg-q
+
+
diff --git a/src/modm/board/nucleo_u385rg-q/module.lb b/src/modm/board/nucleo_u385rg-q/module.lb
new file mode 100644
index 0000000000..a32e9d28b8
--- /dev/null
+++ b/src/modm/board/nucleo_u385rg-q/module.lb
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2026, 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 = ":board:nucleo-u385rg-q"
+ module.description = """
+# NUCLEO-U385RG-Q
+
+[Nucleo kit for STM32U385RG-Q](https://www.st.com/en/evaluation-tools/nucleo-u385rg-q.html)
+"""
+
+def prepare(module, options):
+ if not options[":target"].partname.startswith("stm32u385rgt"):
+ return False
+
+ module.depends(
+ ":platform:core",
+ ":platform:gpio",
+ ":platform:clock",
+ ":platform:uart:1",
+ ":platform:usb",
+ ":debug")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ env.substitutions = {
+ "with_logger": True,
+ "with_assert": env.has_module(":architecture:assert")
+ }
+ env.template("../board.cpp.in", "board.cpp")
+ env.copy('.')
+ env.outbasepath = "modm/openocd/modm/board/"
+ env.template(repopath("tools/openocd/modm/st_nucleo_swd.cfg.in"), "board.cfg",
+ substitutions={"target": "stm32u3x"})
+ env.collect(":build:openocd.source", "modm/board/board.cfg")
diff --git a/src/modm/platform/clock/stm32/module.lb b/src/modm/platform/clock/stm32/module.lb
index 235d751739..583aa6899f 100644
--- a/src/modm/platform/clock/stm32/module.lb
+++ b/src/modm/platform/clock/stm32/module.lb
@@ -146,7 +146,7 @@ def build(env):
ids += device.get_driver(driver)["instance"]
return list(map(int, ids))
- if t.family in ["h5", "u0", "g0"]:
+ if t.family in ["h5", "u0", "g0", "u3"]:
p["uart_ids"] = instances("usart", "uart")
p["lpuart_ids"] = instances("lpuart")
p["spi_ids"] = instances("spi")
@@ -218,6 +218,8 @@ def build(env):
per = per.replace("USB1", "USB").replace("USB2", "USB")
if t.family == "l5" and per == "USBFS":
per = "Usb"
+ if t.family == "u3" and per == "USB1":
+ per = "Usb"
if per.capitalize() not in all_peripherals:
continue
if "EN" in mode:
diff --git a/src/modm/platform/clock/stm32/rcc_impl.hpp.in b/src/modm/platform/clock/stm32/rcc_impl.hpp.in
index e56cef0372..cf5cfef79e 100644
--- a/src/modm/platform/clock/stm32/rcc_impl.hpp.in
+++ b/src/modm/platform/clock/stm32/rcc_impl.hpp.in
@@ -77,7 +77,7 @@ Rcc::setFlashLatency()
%% elif target.family == "l0"
// enable flash prefetch and pre-read
acr |= FLASH_ACR_PRFTEN | FLASH_ACR_PRE_READ;
-%% elif target.family in ["u5"]
+%% elif target.family in ["u3", "u5"]
// enable flash prefetch
acr |= FLASH_ACR_PRFTEN;
%% elif target.family not in ["h7", "l5", "h5"]
diff --git a/src/modm/platform/clock/stm32/rcc_u3.hpp.in b/src/modm/platform/clock/stm32/rcc_u3.hpp.in
new file mode 100644
index 0000000000..ba9a63adc4
--- /dev/null
+++ b/src/modm/platform/clock/stm32/rcc_u3.hpp.in
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2026, 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 "../device.hpp"
+#include
+#include
+#include
+
+namespace modm::platform
+{
+
+/**
+ * Reset and Clock Control for STM32U3 devices.
+ *
+ * This class abstracts access to clock settings on the STM32.
+ * You need to use this class to enable internal and external clock
+ * sources & outputs, set PLL parameters and AHB & APB prescalers.
+ * Don't forget to set the flash latencies.
+ *
+ * @author Niklas Hauser
+ * @ingroup modm_platform_rcc
+ */
+class Rcc
+{
+public:
+ static constexpr uint32_t LsiFrequency = 32'000;
+ static constexpr uint32_t HsiFrequency = 16'000'000;
+ static constexpr uint32_t BootFrequency = 12'000'000; // MSIS
+ static constexpr uint32_t MaxFrequency = 96'000'000;
+
+ /// Connect GPIO signals like MCO to the clock tree
+ template< class... Signals >
+ static void
+ connect()
+ {
+ using Connector = GpioConnector;
+ Connector::connect();
+ }
+
+ /// Enable the clock for a peripheral
+ template< Peripheral peripheral >
+ static void
+ enable();
+
+ /// Check if a peripheral clock is enabled
+ template< Peripheral peripheral >
+ static bool
+ isEnabled();
+
+ /// Disable the clock for a peripheral
+ template< Peripheral peripheral >
+ static void
+ disable();
+
+
+public:
+ /** Set flash latency for CPU frequency and voltage.
+ * Does nothing if CPU frequency is too high for the available
+ * voltage.
+ *
+ * @returns maximum CPU frequency for voltage.
+ * @retval <=CPU_Frequency flash latency has been set correctly.
+ * @retval >CPU_Frequency requested frequency too high for voltage.
+ */
+ template< uint32_t Core_Hz, uint16_t Core_mV = 3300>
+ static uint32_t
+ setFlashLatency();
+
+ /// Update the SystemCoreClock and delay variables.
+ template< uint32_t Core_Hz >
+ static void
+ updateCoreFrequency();
+
+
+public:
+ // clock sources
+ static inline bool
+ enableHsiClock(uint32_t waitLoops = 0x10000)
+ {
+ RCC->CR |= RCC_CR_HSION;
+ while (not (RCC->CR & RCC_CR_HSIRDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ static inline bool
+ enableHsi48Clock(uint32_t waitLoops = 0x10000)
+ {
+ RCC->CR |= RCC_CR_HSI48ON;
+ while (not (RCC->CR & RCC_CR_HSI48RDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ static inline bool
+ enableHseClock(uint32_t waitLoops = 0x10000)
+ {
+ RCC->CR |= RCC_CR_HSEBYP | RCC_CR_HSEON;
+ while (not (RCC->CR & RCC_CR_HSERDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ static inline bool
+ enableHseCrystal(uint32_t waitLoops = 0x10000)
+ {
+ RCC->CR = (RCC->CR & ~RCC_CR_HSEBYP) | RCC_CR_HSEON;
+ while (not (RCC->CR & RCC_CR_HSERDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ enum class
+ LsiPrescaler : uint8_t
+ {
+ Div1 = 0,
+ Div128 = RCC_CSR_LSIPREDIV,
+ };
+ static inline bool
+ enableLsiClock(LsiPrescaler div = LsiPrescaler::Div1, uint32_t waitLoops = 0x10000)
+ {
+ RCC->CSR |= RCC_CSR_LSION | uint32_t(div);
+ while (not (RCC->CSR & RCC_CSR_LSIRDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ static inline bool
+ enableLseClock(uint32_t waitLoops = 0x10000)
+ {
+ RCC->BDCR |= RCC_BDCR_LSEBYP | RCC_BDCR_LSEON;
+ while (not (RCC->BDCR & RCC_BDCR_LSERDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ enum class
+ LseDrive : uint8_t
+ {
+ Low = 0b00,
+ MediumLow = 0b01,
+ MediumHigh = 0b10,
+ High = 0b11,
+ };
+ static inline bool
+ enableLseCrystal(LseDrive drive = LseDrive::Low, uint32_t waitLoops = 0x10000)
+ {
+ RCC->BDCR = (RCC->BDCR & ~(RCC_BDCR_LSEDRV | RCC_BDCR_LSEBYP)) |
+ (uint32_t(drive) << RCC_BDCR_LSEDRV_Pos) | RCC_BDCR_LSEON;
+ while (not (RCC->BDCR & RCC_BDCR_LSERDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ /// MSI frequency ranges. The MSI consists of two internal oscillators:
+ /// MSIRC0 at 96 MHz and MSIRC1 at 24 MHz. Each can be divided by 1/2/4/8
+ /// to generate the MSIS (system) and MSIK (kernel) output clocks.
+ enum class
+ MsiFrequency : uint8_t
+ {
+ /// Range 0: MSIRC0/1 = 96 MHz
+ MHz96 = 0b000,
+ /// Range 1: MSIRC0/2 = 48 MHz
+ MHz48 = 0b001,
+ /// Range 2: MSIRC0/4 = 24 MHz
+ MHz24 = 0b010,
+ /// Range 3: MSIRC0/8 = 12 MHz
+ MHz12 = 0b011,
+ /// Range 4: MSIRC1/1 = 24 MHz (alternative to range 2)
+ MHz24_RC1 = 0b100,
+ /// Range 5: MSIRC1/2 = 12 MHz (alternative to range 3)
+ MHz12_RC1 = 0b101,
+ /// Range 6: MSIRC1/4 = 6 MHz
+ MHz6 = 0b110,
+ /// Range 7: MSIRC1/8 = 3 MHz
+ MHz3 = 0b111,
+ };
+
+ /// Enable MSIS (Multi Speed Internal System) oscillator for system clock
+ static inline bool
+ enableMsiSystemClock(MsiFrequency frequency = MsiFrequency::MHz12, uint32_t waitLoops = 0x10000)
+ {
+ // Configure MSIS: set MSIRGSEL=1 (software range), MSISSEL, and MSISDIV
+ RCC->ICSCR1 = (RCC->ICSCR1 & ~(RCC_ICSCR1_MSISSEL | RCC_ICSCR1_MSISDIV)) |
+ RCC_ICSCR1_MSIRGSEL | (uint32_t(frequency) << RCC_ICSCR1_MSISDIV_Pos);
+
+ // Enable MSIS
+ RCC->CR |= RCC_CR_MSISON;
+ while (not (RCC->CR & RCC_CR_MSISRDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ /// Enable MSIK (Multi Speed Internal Kernel) oscillator for peripheral clocks
+ static inline bool
+ enableMsiKernelClock(MsiFrequency frequency = MsiFrequency::MHz12, uint32_t waitLoops = 0x10000)
+ {
+ // Configure MSIK: set MSIRGSEL=1 (software range), MSIKSEL, and MSIKDIV
+ RCC->ICSCR1 = (RCC->ICSCR1 & ~(RCC_ICSCR1_MSIKSEL | RCC_ICSCR1_MSIKDIV)) |
+ RCC_ICSCR1_MSIRGSEL | (uint32_t(frequency) << RCC_ICSCR1_MSIKDIV_Pos);
+
+ // Enable MSIK
+ RCC->CR |= RCC_CR_MSIKON;
+ while (not (RCC->CR & RCC_CR_MSIKRDY) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ enum class
+ AhbPrescaler : uint8_t
+ {
+ Div1 = 0b0000,
+ Div2 = 0b1000,
+ Div4 = 0b1001,
+ Div8 = 0b1010,
+ Div16 = 0b1011,
+ Div64 = 0b1100,
+ Div128 = 0b1101,
+ Div256 = 0b1110,
+ Div512 = 0b1111,
+ };
+ static inline void
+ setAhbPrescaler(AhbPrescaler prescaler)
+ {
+ RCC->CFGR2 = (RCC->CFGR2 & ~RCC_CFGR2_HPRE) | (uint32_t(prescaler) << RCC_CFGR2_HPRE_Pos);
+ }
+
+ enum class
+ ApbPrescaler : uint8_t
+ {
+ Div1 = 0b000,
+ Div2 = 0b100,
+ Div4 = 0b101,
+ Div8 = 0b110,
+ Div16 = 0b111
+ };
+ static inline void
+ setApb1Prescaler(ApbPrescaler prescaler)
+ {
+ RCC->CFGR2 = (RCC->CFGR2 & ~RCC_CFGR2_PPRE1) | (uint32_t(prescaler) << RCC_CFGR2_PPRE1_Pos);
+ }
+
+ static inline void
+ setApb2Prescaler(ApbPrescaler prescaler)
+ {
+ RCC->CFGR2 = (RCC->CFGR2 & ~RCC_CFGR2_PPRE2) | (uint32_t(prescaler) << RCC_CFGR2_PPRE2_Pos);
+ }
+
+ static inline void
+ setApb3Prescaler(ApbPrescaler prescaler)
+ {
+ RCC->CFGR3 = (RCC->CFGR3 & ~RCC_CFGR3_PPRE3) | (uint32_t(prescaler) << RCC_CFGR3_PPRE3_Pos);
+ }
+
+ // clock sinks
+ enum class
+ SystemClockSource : uint8_t
+ {
+ MsiS = 0b00,
+ Hsi16 = 0b01,
+ Hse = 0b10,
+ // Note: STM32U3 doesn't have a traditional PLL
+ // LSI and LSE are available but rarely used as system clock
+ };
+ static inline bool
+ enableSystemClock(SystemClockSource src, uint32_t waitLoops = 0x10000)
+ {
+ RCC->CFGR1 = (RCC->CFGR1 & ~RCC_CFGR1_SW) | (uint32_t(src) << RCC_CFGR1_SW_Pos);
+ // Wait till the clock source is used as system clock source
+ while ((RCC->CFGR1 & RCC_CFGR1_SWS) != (uint32_t(src) << RCC_CFGR1_SWS_Pos) and --waitLoops) ;
+ return waitLoops;
+ }
+
+ enum class
+ RealTimeClockSource : uint32_t
+ {
+ Disabled = 0,
+ Lse = 0b01 << RCC_BDCR_RTCSEL_Pos,
+ Lsi = 0b10 << RCC_BDCR_RTCSEL_Pos,
+ HseDiv32 = 0b11 << RCC_BDCR_RTCSEL_Pos,
+ };
+ static inline void
+ setRealTimeClockSource(RealTimeClockSource src)
+ {
+ RCC->BDCR = (RCC->BDCR & ~RCC_BDCR_RTCSEL) | RCC_BDCR_RTCEN | uint32_t(src);
+ }
+
+ // CCIPR
+ enum class
+ PeripheralClockSource : uint8_t
+ {
+ Bus = 0b0,
+ Hsi16 = 0b1,
+ };
+%% for id in uart_ids | sort if id in [1,3,4,5]
+ %% set s = "s" if id in [1,2,3,6,10,11] else ""
+ static inline void
+ setU{{s}}art{{id}}ClockSource(PeripheralClockSource src)
+ {
+ RCC->CCIPR1 = (RCC->CCIPR1 & ~RCC_CCIPR1_U{{s|upper}}ART{{id}}SEL) | (uint32_t(src) << RCC_CCIPR1_U{{s|upper}}ART{{id}}SEL_Pos);
+ }
+%% endfor
+%#
+ enum class
+ LpUartClockSource : uint8_t
+ {
+ Bus = 0b00,
+ SysClk = 0b01,
+ Hsi16 = 0b10,
+ Lse = 0b11,
+ };
+%% for id in lpuart_ids | sort if id in [1]
+ static inline void
+ setLpUart{{id}}ClockSource(LpUartClockSource src)
+ {
+ RCC->CCIPR3 = (RCC->CCIPR3 & ~RCC_CCIPR3_LPUART{{id}}SEL) | (uint32_t(src) << RCC_CCIPR3_LPUART{{id}}SEL_Pos);
+ }
+%% endfor
+%#
+%% for id in i2c_ids | sort if id in [1]
+ static inline void
+ setI2c{{id}}ClockSource(PeripheralClockSource src)
+ {
+ RCC->CCIPR1 = (RCC->CCIPR1 & ~RCC_CCIPR1_I2C{{id}}SEL) | (uint32_t(src) << RCC_CCIPR1_I2C{{id}}SEL_Pos);
+ }
+%% endfor
+%#
+ enum class
+ I2c3ClockSource : uint8_t
+ {
+ Bus = 0b0,
+ Msik = 0b1,
+ };
+%% for id in i2c_ids | sort if id in [3]
+ static inline void
+ setI2c{{id}}ClockSource(I2c3ClockSource src)
+ {
+ RCC->CCIPR3 = (RCC->CCIPR3 & ~RCC_CCIPR3_I2C{{id}}SEL) | (uint32_t(src) << RCC_CCIPR3_I2C{{id}}SEL_Pos);
+ }
+%% endfor
+%#
+ enum class
+ SpiClockSource : uint8_t
+ {
+ Bus = 0b0,
+ Msik = 0b1,
+ };
+%% for id in spi_ids | sort if id in [1,2,3]
+ %% set r = 1 if id in [1,2] else 2
+ static inline void
+ setSpi{{id}}ClockSource(SpiClockSource src)
+ {
+ RCC->CCIPR{{r}} = (RCC->CCIPR{{r}} & ~RCC_CCIPR{{r}}_SPI{{id}}SEL) | (uint32_t(src) << RCC_CCIPR{{r}}_SPI{{id}}SEL_Pos);
+ }
+%% endfor
+%#
+ enum class
+ LpTimerClockSource : uint8_t
+ {
+ Bus = 0b00,
+ Lsi = 0b01,
+ Hsi16 = 0b10,
+ Lse = 0b11,
+ };
+%% for id in lptim_ids | sort if id in [1,2]
+ %% set r = 3 if id == 1 else 1
+ static inline void
+ setLpTimer{{id}}ClockSource(LpTimerClockSource src)
+ {
+ RCC->CCIPR{{r}} = (RCC->CCIPR{{r}} & ~RCC_CCIPR{{r}}_LPTIM{{id}}SEL) | (uint32_t(src) << RCC_CCIPR{{r}}_LPTIM{{id}}SEL_Pos);
+ }
+%% endfor
+%% for id in lptim_ids | sort if id in [3,4]
+ // LPTIM3 and LPTIM4 share the same clock selection
+ static inline void
+ setLpTimer{{id}}ClockSource(LpTimerClockSource src)
+ {
+ RCC->CCIPR3 = (RCC->CCIPR3 & ~RCC_CCIPR3_LPTIM34SEL) | (uint32_t(src) << RCC_CCIPR3_LPTIM34SEL_Pos);
+ }
+%% endfor
+%#
+ enum class
+ TimIcClockSource : uint8_t
+ {
+ Disabled = 0b000,
+ Hsi16 = 0b001,
+ MsiS = 0b010,
+ MsiK = 0b011,
+ Hse = 0b101,
+ };
+ static inline void
+ setTimIcClockSource(TimIcClockSource src)
+ {
+ RCC->CCIPR1 = (RCC->CCIPR1 & ~RCC_CCIPR1_TIMICSEL) | (uint32_t(src) << RCC_CCIPR1_TIMICSEL_Pos);
+ }
+
+ enum class
+ IclkSource : uint8_t
+ {
+ Hsi48 = 0b00,
+ MsiK = 0b01,
+ Hse = 0b10,
+ Sysclk = 0b11,
+ };
+ enum class
+ UsbPrescaler : uint8_t
+ {
+ Div1 = 0b0,
+ Div2 = 0b1,
+ };
+ static inline void
+ setUsbClockSource(IclkSource src, UsbPrescaler div = UsbPrescaler::Div1)
+ {
+ RCC->CCIPR1 = (RCC->CCIPR1 & ~(RCC_CCIPR1_ICLKSEL | RCC_CCIPR1_USB1SEL)) |
+ (uint32_t(src) << RCC_CCIPR1_ICLKSEL_Pos) | (uint32_t(div) << RCC_CCIPR1_USB1SEL_Pos);
+ }
+
+ enum class
+ AdcDacClockSource : uint8_t
+ {
+ Hclk = 0b00,
+ Hse = 0b01,
+ Msik = 0b10,
+ };
+ enum class
+ AdcDacPrescaler : uint8_t
+ {
+ Div1 = 0b0000,
+ Div2 = 0b0001,
+ Div4 = 0b1000,
+ Div8 = 0b1001,
+ Div16 = 0b1010,
+ Div32 = 0b1011,
+ Div64 = 0b1100,
+ Div128 = 0b1101,
+ Div256 = 0b1110,
+ Div512 = 0b1111,
+ };
+ static inline void
+ setAdcDacClockSource(AdcDacClockSource src, AdcDacPrescaler div = AdcDacPrescaler::Div1)
+ {
+ RCC->CCIPR2 = (RCC->CCIPR2 & ~(RCC_CCIPR2_ADCDACSEL | RCC_CCIPR2_ADCDACPRE)) |
+ (uint32_t(src) << RCC_CCIPR2_ADCDACSEL_Pos) | (uint32_t(div) << RCC_CCIPR2_ADCDACPRE_Pos);
+ }
+
+public:
+ // outputs
+ enum class
+ McoPrescaler : uint8_t
+ {
+ Div1 = 0b000,
+ Div2 = 0b001,
+ Div4 = 0b010,
+ Div8 = 0b011,
+ Div16 = 0b100,
+ };
+ enum class
+ McoClockSource : uint8_t
+ {
+ Disabled = 0b0000,
+ SysClk = 0b0001,
+ MsiS = 0b0010,
+ Hsi16 = 0b0011,
+ Hse = 0b0100,
+ MsiK = 0b0101,
+ Lsi = 0b0110,
+ Lse = 0b0111,
+ Hsi48 = 0b1000,
+ Iclk = 0b1001,
+ };
+ static inline void
+ setMco1ClockSource(McoClockSource src, McoPrescaler div)
+ {
+ RCC->CFGR1 = (RCC->CFGR1 & ~(RCC_CFGR1_MCOSEL | RCC_CFGR1_MCOPRE)) |
+ (uint32_t(src) << RCC_CFGR1_MCOSEL_Pos) | (uint32_t(div) << RCC_CFGR1_MCOPRE_Pos);
+ }
+ static inline void
+ setMco2ClockSource(McoClockSource src, McoPrescaler div)
+ {
+ RCC->CFGR1 = (RCC->CFGR1 & ~(RCC_CFGR1_MCO2SEL | RCC_CFGR1_MCO2PRE)) |
+ (uint32_t(src) << RCC_CFGR1_MCO2SEL_Pos) | (uint32_t(div) << RCC_CFGR1_MCO2PRE_Pos);
+ }
+
+public:
+ // TODO: move to Pwr module
+ enum class
+ BoosterClockSource : uint8_t
+ {
+ Disabled = 0b00,
+ MsiS = 0b01,
+ Hsi16 = 0b10,
+ Hse = 0b11,
+ };
+ enum class
+ BoosterPrescaler : uint8_t
+ {
+ Div1 = 0,
+ Div2 = 1,
+ Div4 = 2,
+ Div6 = 3,
+ Div8 = 4,
+ Div10 = 5,
+ Div12 = 6,
+ Div14 = 7,
+ Div16 = 8,
+ Div18 = 9,
+ Div20 = 10,
+ Div22 = 11,
+ Div24 = 12,
+ Div26 = 13,
+ Div28 = 14,
+ Div30 = 15,
+ };
+ /// Configure EPOD booster clock source and prescaler
+ /// @param source Clock source for the booster (Disabled, MSIS, HSI16, or HSE)
+ /// @param div Prescaler divider (output must be 3-16 MHz)
+ /// @warning The selected clock source must be enabled before configuring the booster
+ /// @warning When MSIS is selected, division factor is forced by hardware
+ /// @warning Must set to Disabled when booster is not in use to save power
+ static inline void
+ setBoosterClock(BoosterClockSource source, BoosterPrescaler div = BoosterPrescaler::Div1)
+ {
+ RCC->CFGR4 = (RCC->CFGR4 & ~(RCC_CFGR4_BOOSTSEL | RCC_CFGR4_BOOSTDIV)) |
+ (uint32_t(source) << RCC_CFGR4_BOOSTSEL_Pos) | (uint32_t(div) << RCC_CFGR4_BOOSTDIV_Pos);
+ }
+
+ enum class
+ VoltageScaling : uint32_t
+ {
+ /// Low-power mode (0.75V) for SYSCLK ≤ 24 MHz
+ Range2_24MHz = PWR_VOSR_R2EN,
+ /// Low-power mode (0.75V) with booster for 24 MHz < SYSCLK ≤ 48 MHz
+ Range2_48MHz = PWR_VOSR_R2EN | PWR_VOSR_BOOSTEN,
+ /// High-performance mode (0.9V) with booster for 48 MHz < SYSCLK ≤ 96 MHz
+ Range1_96MHz = PWR_VOSR_R1EN | PWR_VOSR_BOOSTEN,
+ };
+ /// Configure voltage scaling range and EPOD booster
+ /// @param voltage The target voltage range and booster configuration
+ /// @param waitLoops Maximum iterations to wait for voltage range and booster ready
+ /// @warning Must set setBoosterClock() before enabling booster in voltage scaling
+ /// @warning Range can only be changed when current range is ready (R1RDY = R1EN and R2RDY = R2EN)
+ /// @warning Do not switch from Range*Boost to Range* while SYSCLK > 24 MHz
+ static inline bool
+ setVoltageScaling(VoltageScaling voltage, uint32_t waitLoops = 0x10000)
+ {
+ const auto currentConfig = PWR->VOSR & (PWR_VOSR_R1EN | PWR_VOSR_R2EN | PWR_VOSR_BOOSTEN);
+
+ // Already at target configuration?
+ if (voltage == static_cast(currentConfig)) return true;
+
+ // Check if current range is ready before switching
+ const bool range1Ready = PWR->VOSR & PWR_VOSR_R1RDY;
+ const bool range2Ready = PWR->VOSR & PWR_VOSR_R2RDY;
+ const bool range1Enabled = PWR->VOSR & PWR_VOSR_R1EN;
+ const bool range2Enabled = PWR->VOSR & PWR_VOSR_R2EN;
+
+ // Can only change when current range is ready
+ if ((range1Enabled and not range1Ready) or (range2Enabled and not range2Ready))
+ return false;
+
+ // Extract range and booster bits from enum
+ const uint32_t rangeBits = uint32_t(voltage) & (PWR_VOSR_R1EN | PWR_VOSR_R2EN);
+ const bool enableBooster = uint32_t(voltage) & PWR_VOSR_BOOSTEN;
+
+ // Clear BOOSTSEL when disabling booster to save power
+ if (not enableBooster) setBoosterClock(BoosterClockSource::Disabled);
+
+ // R1EN and R2EN must be opposite values, optionally enable BOOSTEN
+ PWR->VOSR = (PWR->VOSR & ~(PWR_VOSR_R1EN | PWR_VOSR_R2EN | PWR_VOSR_BOOSTEN)) | uint32_t(voltage);
+
+ // Wait for the selected range to be ready
+ const uint32_t readyBit = (rangeBits == PWR_VOSR_R1EN) ? PWR_VOSR_R1RDY : PWR_VOSR_R2RDY;
+ while (not (PWR->VOSR & readyBit) and --waitLoops) ;
+
+ // If booster enabled, also wait for it to be ready
+ if (enableBooster) {
+ while (not (PWR->VOSR & PWR_VOSR_BOOSTRDY) and --waitLoops) ;
+ }
+
+ return waitLoops;
+ }
+
+private:
+ struct flash_latency
+ {
+ uint32_t latency;
+ uint32_t max_frequency;
+ };
+ static constexpr flash_latency
+ computeFlashLatency(uint32_t Core_Hz, uint16_t Core_mV);
+};
+
+} // namespace modm::platform
+
+#include "rcc_impl.hpp"
diff --git a/src/modm/platform/core/stm32/module.lb b/src/modm/platform/core/stm32/module.lb
index a6fa5c2239..6f3f26788b 100644
--- a/src/modm/platform/core/stm32/module.lb
+++ b/src/modm/platform/core/stm32/module.lb
@@ -81,6 +81,7 @@ def build(env):
"h7": (4, 4), # CM7 tested on H743 in ITCM
"l4": (3, 4), # CM4 tested on L476 in SRAM2
"l5": (3, 4), # CM33 tested on L552 in RAM
+ "u3": (3, 4), # CM33 tested on U386 in RAM
"u5": (3, 4), # CM33 tested on U575 in RAM
"f3": (3, 4), # CM4 tested on F334, F303 in CCM RAM
diff --git a/src/modm/platform/core/stm32/startup_platform.c.in b/src/modm/platform/core/stm32/startup_platform.c.in
index f9112122a5..04b76545bc 100644
--- a/src/modm/platform/core/stm32/startup_platform.c.in
+++ b/src/modm/platform/core/stm32/startup_platform.c.in
@@ -37,7 +37,7 @@ __modm_initialize_platform(void)
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; __DSB();
%% elif target.family == "h7"
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; __DSB();
-%% elif target.family == "u5"
+%% elif target.family in ["u3", "u5"]
RCC->APB3ENR |= RCC_APB3ENR_SYSCFGEN; __DSB();
%% elif target.family not in ["h5"]
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; __DSB();
@@ -52,6 +52,8 @@ __modm_initialize_platform(void)
RCC->APBENR1 |= RCC_APBENR1_PWREN; __DSB();
%% elif target.family in ["g4", "l4", "l5"]
RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; __DSB();
+%% elif target.family == "u3"
+ RCC->AHB1ENR2 |= RCC_AHB1ENR2_PWREN; __DSB();
%% elif target.family == "u5"
RCC->AHB3ENR |= RCC_AHB3ENR_PWREN; __DSB();
%% endif
@@ -62,7 +64,7 @@ __modm_initialize_platform(void)
PWR->CR1 |= PWR_CR1_DBP;
%% elif target.family == "h5"
PWR->DBPCR |= PWR_DBPCR_DBP;
-%% elif target.family in ["u5", "wba"]
+%% elif target.family in ["u3", "u5", "wba"]
PWR->DBPR |= PWR_DBPR_DBP;
%% endif
@@ -90,6 +92,14 @@ __modm_initialize_platform(void)
// Enable VDDIO2
PWR->CR2 |= PWR_CR2_IOSV;
#endif
+%% elif target.family == "u3"
+%% if target.name[1] == "6"
+ // Enable power for USB
+ PWR->SVMCR |= PWR_SVMCR_ASV | PWR_SVMCR_USV;
+%% else
+ // Enable power for VDDIO2 and USB
+ PWR->SVMCR |= PWR_SVMCR_ASV | PWR_SVMCR_IO2SV | PWR_SVMCR_USV;
+%% endif
%% elif target.family == "u5"
// Enable power for VDDIO2 and USB
PWR->SVMCR |= PWR_SVMCR_ASV | PWR_SVMCR_IO2SV | PWR_SVMCR_USV;
diff --git a/src/modm/platform/extint/stm32/module.lb b/src/modm/platform/extint/stm32/module.lb
index 3e30f4ebf9..678bed661b 100644
--- a/src/modm/platform/extint/stm32/module.lb
+++ b/src/modm/platform/extint/stm32/module.lb
@@ -47,12 +47,12 @@ def validate(env):
def build(env):
target = env[":target"].identifier
- separate_flags = target.family in ["c0", "g0", "l5", "u5", "h5", "u0"]
+ separate_flags = target.family in ["c0", "g0", "l5", "u5", "h5", "u0", "u3"]
extended = target.family in ["l4", "l5", "g4", "h7"]
if separate_flags: extended = target.name in ["b1", "c1"]
exti_reg = "SYSCFG"
- if target.family in ["c0", "g0", "l5", "u5", "h5", "u0"]: exti_reg = "EXTI"
+ if target.family in ["c0", "g0", "l5", "u5", "h5", "u0", "u3"]: exti_reg = "EXTI"
if target.family in ["f1"]: exti_reg = "AFIO"
global extimap
diff --git a/src/modm/platform/gpio/stm32/enable.cpp.in b/src/modm/platform/gpio/stm32/enable.cpp.in
index 097c835269..2e341745a9 100644
--- a/src/modm/platform/gpio/stm32/enable.cpp.in
+++ b/src/modm/platform/gpio/stm32/enable.cpp.in
@@ -26,14 +26,14 @@ modm_gpio_enable(void)
%% elif target.family in ["f1"]
%% set clock_tree = "APB2"
%% set prefix = "IOP"
-%% elif target.family in ["l4", "l5", "g4", "h5", "u5"]
+%% elif target.family in ["l4", "l5", "g4", "h5", "u3", "u5"]
%% set clock_tree = 'AHB2'
%% elif target.family in ["c0", "g0", "l0", "u0"]
%% set clock_tree = 'IOP'
%% endif
-%% set enr="ENR1" if target.family in ["u5"] else "ENR"
-%% set rstr="RSTR1" if target.family in ["u5"] else "RSTR"
+%% set enr="ENR1" if target.family in ["u3", "u5"] else "ENR"
+%% set rstr="RSTR1" if target.family in ["u3", "u5"] else "RSTR"
%% if target.family in ["h7"]
// Enable I/O compensation cell
diff --git a/src/modm/platform/uart/stm32/uart_base.hpp.in b/src/modm/platform/uart/stm32/uart_base.hpp.in
index 0d60c11937..2ec5380335 100644
--- a/src/modm/platform/uart/stm32/uart_base.hpp.in
+++ b/src/modm/platform/uart/stm32/uart_base.hpp.in
@@ -30,7 +30,7 @@
#define USART_BRR_DIV_FRACTION USART_BRR_DIV_Fraction
#endif
%% endif
-%% if tcbgt and target.family not in ["g4", "l5", "u5"]
+%% if tcbgt and target.family not in ["g4", "l5", "u5", "u3"]
#define USART_CR1_TXEIE USART_CR1_TXEIE_TXFNFIE
#define USART_CR1_RXNEIE USART_CR1_RXNEIE_RXFNEIE
#define USART_ISR_RXNE USART_ISR_RXNE_RXFNE
diff --git a/src/modm/platform/usb/stm32/usb.hpp.in b/src/modm/platform/usb/stm32/usb.hpp.in
index ce529fb2be..8f63f2dadc 100644
--- a/src/modm/platform/usb/stm32/usb.hpp.in
+++ b/src/modm/platform/usb/stm32/usb.hpp.in
@@ -33,6 +33,8 @@ public:
#ifdef PWR_CR2_USV
PWR->CR2 |= PWR_CR2_USV;
#endif
+%% elif target.family == "u3"
+ PWR->SVMCR |= PWR_SVMCR_UVMEN;
%% elif target.family == "h5" and target.name != "03"
PWR->USBSCR |= PWR_USBSCR_USB33DEN | PWR_USBSCR_USB33SV;
%% elif target.family in ["h7"]