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]. - + @@ -126,6 +126,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. + @@ -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]. + @@ -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]. + @@ -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. - + + - + - + - + - +
STM32STM32 SAM RP ATL4 L5 U0U3 U5 D1x
D2x
DAx
D5x
E5x
I2C
Unique ID NUCLEO-L496ZG-P NUCLEO-L552ZE-Q NUCLEO-U083RCNUCLEO-U575ZI-QNUCLEO-U385RG-Q
NUCLEO-U575ZI-Q OLIMEXINO-STM32 Raspberry Pi Pico SAMD21-MINISAMD21-XPLAINED-PRO
SAMD21-XPLAINED-PRO SAME54-XPLAINED-PRO SAME70-XPLAINED SAMG55-XPLAINED-PROSAMV71-XPLAINED-ULTRA
SAMV71-XPLAINED-ULTRA Smart Response XE STM32-F4VE STM32F030-DEMOTHINGPLUS-RP2040
THINGPLUS-RP2040 WEACT-C011F6 WEACT-G0B1CB WEACT-H503CBWEACT-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"]