1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! For project structure and debugging boilerplate, see the `synax_overview` example.
//! Clock config varies significantly by family
//! Reference the Cube Mx interactive clock tree tool, or the clock tree in the reference
//! manual for a visual overview. The above code lines are not valid for all STM32 families
//! supported by this crate.
//!
//! Note that the Rust docs are built for L4, and may not be accurate for other MCUs.
#![no_main]
#![no_std]
use cortex_m_rt::entry;
use hal::{
clocks::{self, ApbPrescaler, Clocks, InputSrc, MsiRng, PllCfg, PllSrc, Pllm, Pllr},
low_power, pac,
};
#[entry]
fn main() -> ! {
// Set up CPU peripherals
let mut cp = cortex_m::Peripherals::take().unwrap();
// Set up microcontroller peripherals
let mut dp = pac::Peripherals::take().unwrap();
// Set up a default setting. See documentation on Rust docs for details about
// what this does (it depends on the MCU), but it usually runs the core and most
// peripheral clocks at the max rated speed, using HSI as the input source.
// Unfortunately, the docs hosted on `docs.rs` can only show a single variant, so
// will likely be innacurate. Inspect the [clocks modules here](https://github.com/David-OConnor/stm32-hal/tree/main/src/clocks)
// for details.
// The default clock setting will (for most variants) enable the (or a) PLL, and configure
// scalers so system clock is at its maximum speed, using the internal oscillator (HSE).
// Most H7 variants default to their non-boosted
// speed, ie either 400Mhz or 520Mhz (not 480 or 550Mhz).
let mut clock_cfg = Clocks::default();
// An example modifying some settings, while leaving the rest default. This is the intended
// pattern.
let clock_cfg = Clocks {
input_src: InputSrc::Hsi,
stop_wuck: StopWuck::Hsi,
..Default::default()
};
// Here's an example clock config, where you're using a 16Mhz SMD oscillator (meaning we can enable
// HSE bypass), and are using the internal HSI48 for USB. This also demonstrates how to configure
// an external oscillator. (HSE)
let clock_cfg = Clocks {
input_src: InputSrc::Pll(PllSrc::Hse(16_000_000)),
hse_bypass: true,
hsi48_on: true,
clk48_src: Clk48Src::Hsi48,
..Default::default()
};
// If you'd prefer, you can modify fields individually from a mutable clock config, instead
// of using the code pattern above.
// Set it up to use the HSI directly, with no PLL. This will result in a reduced speed:
clock_cfg.input_src = InputSrc::Hsi;
// Set up to use a 8MHze HSE, with no PLL.
clock_cfg.input_src = InputSrc::Hse(8_000_000);
// If you set a 8Mhz HSE as above, you may need to reduce the default PLLM value to compensate
// compared to a 16Mhz HSI:
clock_cfg.pll.divm = Pllm::Div2;
// Enable PLLQ etc
clocks_cfg.pll.pllp_en = true;
clocks_cfg.pll.divp = Pllr::Div8;
// Note that on H7, we have 3 separate PLLs we can configure, at the `pll1`, `pll2`, and `pll3` fields.
// L4 and WB have a second PLL, called `PLLSAI`, primarily intended for the SAI audio peripheral.
// Its config field is `pllsai`. (and `pllsai2` for L4x5 and L4x6).
// For example, here's a PLL config on H7 that sets PLL2P as the SAI2 source, and configures
// its speed. (By default, only PLL1(R) is enabled.
let clock_cfg = Clocks {
pll2: PllCfg {
enabled: true,
pllp_en: true,
divn: 99,
divp: 16,
..PllCfg::disabled()
},
sai1_src: SaiSrc::Pll2P,
..Default::default()
};
// Or on L4 or WB, using the PLLSAI:
let clock_cfg = Clocks {
pllsai1: PllCfg {
enabled: true,
pllp_en: true,
divn: 32,
..PllCfg::disabled()
},
sai1_src: SaiSrc::PllSai1P, // Note that thsi is the default, but you can change it.
..Default::default()
};
// Note that H7 also lets you select VOS range, and will give you an error if you select an
// invalid range for your HCLK speed. VOS0 is required for full speed.
// clock_cfg.vos_range = VosRange::VOS0;
// You can use `Clocks::full_speed()` to configure an H743 etc at 480Mhz.
// Change the default wakeup from Stop mode to be HSI instead of MSI. (L4 and L5 only)
clock_cfg.stop_wuck = StopWuck::Hsi;
// Set up PLL using a 4Mhz HSE:
clock_cfg.input_src = InputSrc::Pll(PllSrc::Hse(4_000_000));
// Enable the Clock Security System (CSS)
clock_cfg.security_system = true;
// Bypass HSE output
clock_cfg.hse_bypass = true;
// Enable HSI48 (eg L4, L5, G4 etc)
clock_cfg.hse48_on = true;
// Change MSI speed (L4, L5 only)
clock_cfg.change_msi_speed(MsiRange::R2M);
// (L4 and L5 only) If you'd like to use MSI for the USB clock source, run this function.
// Do not run it if using MSI for the input source or PLL source. You must also have
// `clk48_src: Clk48Src::MSI` in the clock cfg, which is the default for L4 and L5.
clocks_cfg.enable_msi_48();
// Change PLL prescalers:
clock_cfg.pllm = Pllm::Div4;
clock_cfg.plln = 22;
clock_cfg.pllr = Pllm::Div4;
// Change some of the peripheral prescalers
clock_cfg.apb1prescaler = ApbPrescaler::Div2;
// Enable the Clock Recovery System (CRS), to automatically trim the HSI48 on variants
// that include it. (eg STM32l4x2 and L4x3, L5, G4, and H7)
clocks::enable_crs(CrsSyncSrc::Usb); // CrsSyncSrc::OtgHs on H7.
// If you need to modify functionality not supported by this library,
// you can make register writes directly using the PAC. If you find missing functionality
// you find useful, consider making an issue or PR on Github.
// For example, to set I2C1 to use HSI as its source, on L4:
dp.RCC
.ccipr
.modify(|_, w| unsafe { w.i2c1sel().bits(0b10) });
// Configure clock registers. The previous creation and modification of `clock_cfg`
// only set up a configuration struct; `Clocks::setup` performs the MCU operations.
clock_cfg.setup().unwrap();
// Show speeds.
defmt::println!("Speeds: {:?}", clock_cfg.calc_speeds());
loop {
low_power::sleep_now();
}
}
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
#[defmt::panic_handler]
fn panic() -> ! {
cortex_m::asm::udf()
}