Skip to content

Commit 9a8f2a0

Browse files
committed
Added clock initialisation to the stm32f469
1 parent 5b92186 commit 9a8f2a0

2 files changed

Lines changed: 303 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef KLIB_STM32F469_SYSTEM_HPP
2+
#define KLIB_STM32F469_SYSTEM_HPP
3+
4+
#include <targets/core/stm/stm32f4xx/system.hpp>
5+
6+
namespace klib::stm32f469::io::system {
7+
using namespace klib::core::stm32f4xx::io::system;
8+
}
9+
10+
#endif
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
#ifndef KLIB_STM_STM32F4XX_SYSTEM_HPP
2+
#define KLIB_STM_STM32F4XX_SYSTEM_HPP
3+
4+
#include <klib/klib.hpp>
5+
#include <klib/io/core_clock.hpp>
6+
7+
namespace klib::core::stm32f4xx::io::system {
8+
class flash {
9+
public:
10+
/**
11+
* @brief Set the amount of waitstates used for accessing flash
12+
*
13+
* @details
14+
* 0 = for 0 to 30Mhz
15+
* 1 = for 30 to 60Mhz
16+
* 2 = for 60 to 90Mhz
17+
* 3 = for 90 to 120Mhz
18+
* 4 = for 120 to 150Mhz
19+
* 5 = for 150 to 180Mhz
20+
*
21+
* Note: for voltage ranges of 2.7V - 3.6V
22+
*
23+
* More info can be found in the datasheet at page 81
24+
*
25+
* @tparam WaitState
26+
* @tparam EnablePrefetch
27+
* @tparam EnableInstructionCache
28+
* @tparam EnableDataCache
29+
*/
30+
template <uint8_t WaitState, bool EnablePrefetch, bool EnableInstructionCache, bool EnableDataCache>
31+
static void setup() {
32+
// check the wait state from the user
33+
static_assert(WaitState <= 15, "Max wait cycles is 15");
34+
35+
// clear the wait state bits and set the new wait state
36+
FLASH->ACR = (
37+
(FLASH->ACR & (~0x70F)) | WaitState | (EnablePrefetch << 8) |
38+
(EnableInstructionCache << 9) | (EnableDataCache << 10)
39+
);
40+
}
41+
};
42+
43+
class crystal {
44+
public:
45+
/**
46+
* @brief Available clock sources for the system clock
47+
*
48+
*/
49+
enum class source {
50+
internal = 0,
51+
external = 1,
52+
};
53+
54+
/**
55+
* @brief Returns if the requested crystal is enabled
56+
*
57+
* @tparam Source
58+
* @return true
59+
* @return false
60+
*/
61+
template <source Source>
62+
static bool is_enabled() {
63+
// calculate the bit to check
64+
constexpr static uint32_t bit = (Source == source::internal) ? 0 : 16;
65+
66+
// return the status of the bit
67+
return RCC->CR & (0x1 << bit);
68+
}
69+
70+
/**
71+
* @brief Returns if the requested crystal is stable
72+
*
73+
* @tparam Source
74+
* @return true
75+
* @return false
76+
*/
77+
template <source Source>
78+
static bool is_stable() {
79+
// calculate the bit to check
80+
constexpr static uint32_t bit = (Source == source::internal) ? 1 : 17;
81+
82+
// return the status of the bit
83+
return RCC->CR & (0x1 << bit);
84+
}
85+
86+
/**
87+
* @brief Enable or disable the requested crystal
88+
*
89+
* @tparam Source
90+
* @tparam Enable
91+
*/
92+
template <source Source, bool Enable>
93+
static void enable() {
94+
// calculate the bit to check
95+
constexpr static uint32_t bit = (Source == source::internal) ? 0 : 16;
96+
97+
// enable the crystal by setting the requested source bit
98+
if constexpr (Enable) {
99+
RCC->CR |= (0x1 << bit);
100+
}
101+
else {
102+
RCC->CR &= ~(0x1 << bit);
103+
}
104+
}
105+
};
106+
107+
class clock {
108+
public:
109+
/**
110+
* @brief Available plls on the chip
111+
*
112+
*/
113+
enum class pll {
114+
main = 0,
115+
plli2s = 1,
116+
pllsai = 2
117+
};
118+
119+
/**
120+
* @brief Available clock sources for the system clock
121+
*
122+
*/
123+
enum class sysclock_source {
124+
internal = 0,
125+
external = 1,
126+
pll = 2,
127+
};
128+
129+
/**
130+
* @brief Get the current sysclock source
131+
*
132+
* @return sysclock_source
133+
*/
134+
static sysclock_source get_sysclock_source() {
135+
return static_cast<sysclock_source>((RCC->CFGR >> 2) & 0x3);
136+
}
137+
138+
/**
139+
* @brief Set the sysclock source
140+
*
141+
* @note the clock needs to be stable before switching to it
142+
*
143+
* @param source
144+
*/
145+
template <sysclock_source Source>
146+
static void set_sysclock_source() {
147+
// set the new source
148+
RCC->CFGR = (RCC->CFGR & ~(0x3)) | static_cast<uint32_t>(Source);
149+
}
150+
151+
template <pll Pll, bool Enable>
152+
static void enable() {
153+
// calculate the bit to set or clear
154+
constexpr static uint32_t bit = 24 + (static_cast<uint32_t>(Pll) * 2);
155+
156+
if constexpr (Enable) {
157+
RCC->CR |= (0x1 << bit);
158+
}
159+
else {
160+
RCC->CR &= ~(0x1 << bit);
161+
}
162+
}
163+
164+
template <pll Pll>
165+
static bool is_locked() {
166+
// calculate the bit to check
167+
constexpr static uint32_t bit = 25 + (static_cast<uint32_t>(Pll) * 2);
168+
169+
// return if the pll is locked by checking the ready flag
170+
return RCC->CR & (0x1 << bit);
171+
}
172+
173+
/**
174+
* @brief Set the main pll to the specified frequency and source
175+
*
176+
* @tparam Source
177+
* @tparam Freq
178+
* @tparam Multiplier
179+
* @tparam PreDivider
180+
* @tparam Div
181+
* @tparam UsbSdRngDiv
182+
* @tparam DsiDiv
183+
*/
184+
template <crystal::source Source, uint32_t Freq, uint16_t Multiplier, uint8_t PreDivider, uint8_t Div, uint8_t UsbSdRngDiv = 2, uint8_t DsiDiv = 2>
185+
static void set_main() {
186+
// check if the requested source is stable and enabled
187+
if (!crystal::is_enabled<Source>()) {
188+
crystal::enable<Source, true>();
189+
190+
// wait until the crystal is stable
191+
while (!crystal::is_stable<Source>()) {
192+
// do nothing
193+
}
194+
}
195+
196+
// check if we need to use the pll or not
197+
if constexpr (Multiplier > 1) {
198+
// we need to enable the pll. Check everything is valid before enabling it
199+
static_assert(Multiplier >= 50 && Multiplier <= 432, "Invalid multiplier");
200+
static_assert(PreDivider >= 2 && PreDivider <= 63, "Invalid pre-divider");
201+
static_assert(Div == 2 || Div == 4 || Div == 6 || Div == 8, "Invalid divider");
202+
static_assert(UsbSdRngDiv >= 2 && UsbSdRngDiv <= 7, "Invalid USB/SD/RNG divider");
203+
static_assert(DsiDiv >= 2 && DsiDiv <= 15, "Invalid DSI divider");
204+
205+
// calculate the vco input frequency
206+
constexpr static uint32_t vco_input_freq = Freq / PreDivider;
207+
208+
static_assert(
209+
vco_input_freq >= 1'000'000 && vco_input_freq <= 2'000'000,
210+
"Invalid vco input frequency. Freq / PreDivider must be between 1 and 2mHz to limit pll jitter"
211+
);
212+
213+
// calculate the vco frequency using the oscilator and multiplier
214+
//
215+
// Note: we use the formula (Freq * Multiplier) / PreDivider
216+
// instead of (Freq / PreDivider) * Multiplier to avoid overflow
217+
// when calculating the vco frequency
218+
constexpr static uint32_t partial_freq = (Freq * Multiplier) / PreDivider;
219+
220+
static_assert(
221+
(Multiplier == 1) || (partial_freq >= 100'000'000 && partial_freq <= 432'000'000),
222+
"Invalid vco output. PLL frequency needs to be between 100 and 432mhz"
223+
);
224+
225+
// calculate the final frequency
226+
constexpr static uint32_t freq = (partial_freq / Div);
227+
228+
// disconnect the main pll if it is connected.
229+
if (get_sysclock_source() == sysclock_source::pll) {
230+
// get the current source of the pll
231+
const auto source = static_cast<crystal::source>((RCC->PLLCFGR >> 22) & 0x1);
232+
233+
// check where we need to switch back to before
234+
// reconfiguring the main PLL
235+
if (source == crystal::source::external) {
236+
set_sysclock_source<sysclock_source::external>();
237+
}
238+
else {
239+
set_sysclock_source<sysclock_source::internal>();
240+
}
241+
}
242+
243+
// disable the pll
244+
enable<pll::main, false>();
245+
246+
// wait until the pll is disabled
247+
while (is_locked<pll::main>()) {
248+
// do nothing
249+
}
250+
251+
// set the new configuration for the pll
252+
RCC->PLLCFGR = (
253+
(DsiDiv << 28) | (UsbSdRngDiv << 24) | (static_cast<uint32_t>(Source) << 22) |
254+
((((Div / 2) - 1) << 16) | (Multiplier << 6) | PreDivider)
255+
);
256+
257+
// enable the pll
258+
enable<pll::main, true>();
259+
260+
// wait until the pll is locked
261+
while (!is_locked<pll::main>()) {
262+
// do nothing
263+
}
264+
265+
// switch to the pll
266+
set_sysclock_source<sysclock_source::pll>();
267+
268+
// notify klib what freqency we are running
269+
klib::io::clock::set(freq);
270+
}
271+
else {
272+
// we don't need to enable the pll, just switch to the new source
273+
if constexpr (Source == crystal::source::external) {
274+
set_sysclock_source<sysclock_source::external>();
275+
}
276+
else {
277+
static_assert(
278+
Freq == 16'000'000, "Internal oscillator frequency is fixed at 16Mhz"
279+
);
280+
281+
set_sysclock_source<sysclock_source::internal>();
282+
}
283+
284+
klib::io::clock::set(Freq);
285+
}
286+
287+
// clear APB1 and APB2 dividers to 0 so they are not divided
288+
RCC->CFGR &= ~((0x7 << 10) | (0x7 << 13));
289+
}
290+
};
291+
}
292+
293+
#endif

0 commit comments

Comments
 (0)