Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate STM32 clocks from the roots #1137

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 43 additions & 27 deletions src/modm/board/blue_pill_f103/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
*/
// ----------------------------------------------------------------------------

#ifndef MODM_STM32_F103C8T6_BLUE_PILL_HPP
#define MODM_STM32_F103C8T6_BLUE_PILL_HPP
#pragma once

#include <modm/platform.hpp>
#include <modm/architecture/interface/clock.hpp>

#include "rcc_prototype.hpp"

using namespace modm::platform;

namespace Board
Expand All @@ -24,13 +25,44 @@ namespace Board
/// @{
using namespace modm::literals;

/// STM32F103 running at 72MHz generated from the external 8MHz crystal
struct SystemClock
{
static constexpr uint32_t Frequency = 72_MHz;
static constexpr uint32_t Ahb = Frequency;
static constexpr uint32_t Apb1 = Frequency / 2;
static constexpr uint32_t Apb2 = Frequency;
// static constexpr uint32_t ExternalLSEclock = ;
static constexpr uint32_t ExternalHSEclock = 8_MHz;

static constexpr Rcc::PllFactors pllFactors{
.pllMul = 9,
.usbPrediv = Rcc::UsbPrescaler::Div1_5
};
static constexpr Rcc::AhbPrescaler Ahb_prescaler = Rcc::AhbPrescaler::Div1;
static constexpr Rcc::Apb1Prescaler Apb1_prescaler = Rcc::Apb1Prescaler::Div2;
static constexpr Rcc::Apb2Prescaler Apb2_prescaler = Rcc::Apb2Prescaler::Div1;

// ------------------------------------------

static constexpr int HsePredivision = 1;
static constexpr int PllClock = ExternalHSEclock / HsePredivision * pllFactors.pllMul; // 72 Mhz

// System Clock MUX
static constexpr uint32_t Clock = PllClock;
static_assert(Clock <= 72_MHz, "Clock has max. 72MHz!");

static constexpr uint32_t Ahb = Clock / RccProto::prescalerToValue<Ahb_prescaler>();
static_assert(Ahb <= 72_MHz, "Ahb has max. 72MHz!");

static constexpr uint32_t Apb1 = Ahb / RccProto::prescalerToValue<Apb1_prescaler>();
static_assert(Apb1 <= 36_MHz, "Apb1 has max. 36MHz!");

static constexpr uint32_t Apb2 = Ahb / RccProto::prescalerToValue<Apb2_prescaler>();
static_assert(Apb2 <= 72_MHz, "Apb2 has max. 72MHz!");

// @todo is this correct?
static constexpr uint32_t Apb1Timer = Apb1 * (RccProto::prescalerToValue<Apb1_prescaler>() == 1 ? 1 : 2);
static constexpr uint32_t Apb2Timer = Apb2 * (RccProto::prescalerToValue<Apb2_prescaler>() == 1 ? 1 : 2);

// ------------------------------------------

static constexpr uint32_t Frequency = Ahb;

static constexpr uint32_t Adc = Apb2;

Expand All @@ -49,42 +81,28 @@ struct SystemClock
static constexpr uint32_t I2c1 = Apb1;
static constexpr uint32_t I2c2 = Apb1;

static constexpr uint32_t Apb1Timer = Apb1 * 2;
static constexpr uint32_t Apb2Timer = Apb2 * 1;
static constexpr uint32_t Timer1 = Apb2Timer;
static constexpr uint32_t Timer2 = Apb1Timer;
static constexpr uint32_t Timer3 = Apb1Timer;
static constexpr uint32_t Timer4 = Apb1Timer;

static constexpr uint32_t Usb = Ahb / 1.5;
static constexpr uint32_t Usb = Ahb / RccProto::prescalerToValue<pllFactors.usbPrediv>(); // 48 MHz!
static constexpr uint32_t Iwdg = Rcc::LsiFrequency;

static bool inline
enable()
{
Rcc::enableExternalCrystal();

// external clock * 9 = 72MHz, => 72/1.5 = 48 => good for USB
const Rcc::PllFactors pllFactors{
.pllMul = 9,
.usbPrediv = Rcc::UsbPrescaler::Div1_5
};
Rcc::enablePll(Rcc::PllSource::ExternalCrystal, pllFactors);

// set flash latency for 72MHz
Rcc::setFlashLatency<Frequency>();

// switch system clock to PLL output
Rcc::enableSystemClock(Rcc::SystemClockSource::Pll);

// AHB has max 72MHz
Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1);

// APB1 has max. 36MHz
Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div2);

// APB2 has max. 72MHz
Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div1);
Rcc::setAhbPrescaler(Ahb_prescaler);
Rcc::setApb1Prescaler(Apb1_prescaler);
Rcc::setApb2Prescaler(Apb2_prescaler);

// update frequencies for busy-wait delay functions
Rcc::updateCoreFrequency<Frequency>();
Expand Down Expand Up @@ -129,5 +147,3 @@ initializeUsbFs(uint8_t priority=3)
/// @}

} // Board namespace

#endif // MODM_STM32_F103C8T6_BLUE_PILL_HPP
70 changes: 70 additions & 0 deletions src/modm/board/blue_pill_f103/rcc_prototype.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#pragma once

#include <modm/architecture/interface/clock.hpp>

namespace modm::platform
{

// @todo integrate with /platform/clock/stm32/rcc.hpp.in
class RccProto
{
public:
template<Rcc::AhbPrescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::AhbPrescaler::Div1: return 1;
case Rcc::AhbPrescaler::Div2: return 2;
case Rcc::AhbPrescaler::Div4: return 4;
case Rcc::AhbPrescaler::Div8: return 8;
case Rcc::AhbPrescaler::Div16: return 16;
case Rcc::AhbPrescaler::Div64: return 64;
case Rcc::AhbPrescaler::Div128: return 128;
case Rcc::AhbPrescaler::Div256: return 256;
case Rcc::AhbPrescaler::Div512: return 512;
};
}

template<Rcc::Apb1Prescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::Apb1Prescaler::Div1: return 1;
case Rcc::Apb1Prescaler::Div2: return 2;
case Rcc::Apb1Prescaler::Div4: return 4;
case Rcc::Apb1Prescaler::Div8: return 8;
case Rcc::Apb1Prescaler::Div16: return 16;
};
}

template<Rcc::Apb2Prescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::Apb2Prescaler::Div1: return 1;
case Rcc::Apb2Prescaler::Div2: return 2;
case Rcc::Apb2Prescaler::Div4: return 4;
case Rcc::Apb2Prescaler::Div8: return 8;
case Rcc::Apb2Prescaler::Div16: return 16;
}
}

template<Rcc::UsbPrescaler Prescaler>
static consteval float
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::UsbPrescaler::Div1: return 1;
case Rcc::UsbPrescaler::Div1_5: return 1.5;
}
}
};

} // namespace modm::platform
73 changes: 53 additions & 20 deletions src/modm/board/disco_f411ve/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <modm/driver/inertial/lis3dsh.hpp>
#include <modm/platform.hpp>

#include "rcc_prototype.hpp"

using namespace modm::platform;

namespace Board
Expand All @@ -22,13 +24,53 @@ namespace Board
/// @{
using namespace modm::literals;

/// STM32F411 running at 96MHz generated from the external 8MHz crystal

struct SystemClock
{
static constexpr uint32_t Frequency = 96_MHz;
static constexpr uint32_t Ahb = Frequency;
static constexpr uint32_t Apb1 = Frequency / 2;
static constexpr uint32_t Apb2 = Frequency;
// static constexpr uint32_t ExternalLSEclock = ;
static constexpr uint32_t ExternalHSEclock = 8_MHz;

static constexpr Rcc::PllFactors pllFactors{
.pllM = 8,
.pllN = 336,
.pllP = 4,
.pllQ = 7,
};
salkinium marked this conversation as resolved.
Show resolved Hide resolved
static constexpr Rcc::AhbPrescaler Ahb_prescaler = Rcc::AhbPrescaler::Div1;
static constexpr Rcc::Apb1Prescaler Apb1_prescaler = Rcc::Apb1Prescaler::Div2;
static constexpr Rcc::Apb2Prescaler Apb2_prescaler = Rcc::Apb2Prescaler::Div1;

// ------------------------------------------

static constexpr int HsePredivision = 1;
static constexpr uint32_t PllClock = ExternalHSEclock / HsePredivision / pllFactors.pllM * pllFactors.pllN;

static constexpr uint32_t Clock = PllClock / pllFactors.pllP; // 96_Mhz
static_assert(Clock <= 100_MHz, "Clock has max. 100MHz!");

static constexpr uint32_t Ahb = Clock / RccProto::prescalerToValue<Ahb_prescaler>();
static_assert(Ahb <= 100_MHz, "Ahb has max. 100MHz!");

static constexpr uint32_t Apb1 = Ahb / RccProto::prescalerToValue<Apb1_prescaler>();
static_assert(Apb1 <= 50_MHz, "Apb1 has max. 50MHz!");

static constexpr uint32_t Apb2 = Ahb / RccProto::prescalerToValue<Apb2_prescaler>();
static_assert(Apb2 <= 100_MHz, "Apb2 has max. 100MHz!");

// @todo is this correct?
// prescaler is one ? -> multiply by one, otherwise multiply by two
static constexpr uint32_t Apb1Timer = Apb1 * (RccProto::prescalerToValue<Apb1_prescaler>() == 1 ? 1 : 2);
static constexpr uint32_t Apb2Timer = Apb2 * (RccProto::prescalerToValue<Apb2_prescaler>() == 1 ? 1 : 2);

// static_assert(Ahb == 84_MHz, "Wrong");
// static_assert(Apb1 == 42_MHz, "Wrong");
// static_assert(Apb2 == 84_MHz, "Wrong");
// static_assert(Apb1Timer == 84_MHz, "Wrong");
// static_assert(Apb2Timer == 84_MHz, "Wrong");

// ------------------------------------------

static constexpr uint32_t Frequency = Ahb;

static constexpr uint32_t Adc = Apb2;

Expand All @@ -51,8 +93,6 @@ struct SystemClock
static constexpr uint32_t I2c2 = Apb1;
static constexpr uint32_t I2c3 = Apb1;

static constexpr uint32_t Apb1Timer = Apb1 * 2;
static constexpr uint32_t Apb2Timer = Apb2 * 2;
static constexpr uint32_t Timer1 = Apb2Timer;
static constexpr uint32_t Timer2 = Apb1Timer;
static constexpr uint32_t Timer3 = Apb1Timer;
Expand All @@ -62,27 +102,20 @@ struct SystemClock
static constexpr uint32_t Timer10 = Apb2Timer;
static constexpr uint32_t Timer11 = Apb2Timer;

static constexpr uint32_t Usb = 48_MHz;
static constexpr uint32_t Usb = PllClock / pllFactors.pllQ; // 48_Mhz

static bool inline enable()
{
Rcc::enableExternalCrystal(); // 8MHz
const Rcc::PllFactors pllFactors{
.pllM = 7, // 8MHz / M=7 -> ~1.14MHz
.pllN = 336, // 1.14MHz * N=336 -> 384MHz
.pllP = 4, // 384MHz / P=4 -> 96MHz = F_cpu
.pllQ = 8, // 384MHz / P=8 -> 48MHz = F_usb
};
/// STM32F411 running at 84MHz generated from the external 8MHz crystal
Rcc::enableExternalCrystal();
Rcc::enablePll(Rcc::PllSource::ExternalCrystal, pllFactors);
// set flash latency for 100MHz
Rcc::setFlashLatency<Frequency>();
// switch system clock to PLL output
Rcc::enableSystemClock(Rcc::SystemClockSource::Pll);
Rcc::setAhbPrescaler(Rcc::AhbPrescaler::Div1);
// APB1 has max. 50MHz
// APB2 has max. 100MHz
Rcc::setApb1Prescaler(Rcc::Apb1Prescaler::Div2);
Rcc::setApb2Prescaler(Rcc::Apb2Prescaler::Div1);
Rcc::setAhbPrescaler(Ahb_prescaler);
Rcc::setApb1Prescaler(Apb1_prescaler);
Rcc::setApb2Prescaler(Apb2_prescaler);
// update frequencies for busy-wait delay functions
Rcc::updateCoreFrequency<Frequency>();

Expand Down
58 changes: 58 additions & 0 deletions src/modm/board/disco_f411ve/rcc_prototype.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#pragma once

#include <modm/architecture/interface/clock.hpp>

namespace modm::platform
{

// @todo integrate with /platform/clock/stm32/rcc.hpp.in
class RccProto
{
public:
template<Rcc::AhbPrescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::AhbPrescaler::Div1: return 1;
case Rcc::AhbPrescaler::Div2: return 2;
case Rcc::AhbPrescaler::Div4: return 4;
case Rcc::AhbPrescaler::Div8: return 8;
case Rcc::AhbPrescaler::Div16: return 16;
case Rcc::AhbPrescaler::Div64: return 64;
case Rcc::AhbPrescaler::Div128: return 128;
case Rcc::AhbPrescaler::Div256: return 256;
case Rcc::AhbPrescaler::Div512: return 512;
};
}

template<Rcc::Apb1Prescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::Apb1Prescaler::Div1: return 1;
case Rcc::Apb1Prescaler::Div2: return 2;
case Rcc::Apb1Prescaler::Div4: return 4;
case Rcc::Apb1Prescaler::Div8: return 8;
case Rcc::Apb1Prescaler::Div16: return 16;
};
}
Comment on lines +30 to +42
Copy link
Contributor Author

@TomSaw TomSaw Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To associate prescaler values to their registers, i've borrowed this construct from modm::platform::GeneralPurposeTimer::signalToChannel<...>(...) but using a switch operator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but I'd rather use a normal constexpr function like uint8_t Rcc::Apb1Div(Rcc::Apb1Prescaler pre) {...}, then it can also be used at runtime if necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of course!


template<Rcc::Apb2Prescaler Prescaler>
static consteval int
prescalerToValue()
{
switch (Prescaler)
{
case Rcc::Apb2Prescaler::Div1: return 1;
case Rcc::Apb2Prescaler::Div2: return 2;
case Rcc::Apb2Prescaler::Div4: return 4;
case Rcc::Apb2Prescaler::Div8: return 8;
case Rcc::Apb2Prescaler::Div16: return 16;
}
}
};
} // namespace modm::platform