diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-02-16 23:38:49 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-16 23:38:49 +0000 |
| commit | 9352621058bb4e96b8897ec431cc939838729442 (patch) | |
| tree | ff6cbdc1f12e1cb2ab69c9b423a090038778e874 | |
| parent | e9907de39d640fee02df9eac1fbd7efa33e63da2 (diff) | |
| parent | 6d7458dac7e768425342910e04a75c85e667cb82 (diff) | |
Merge pull request #2579 from barnabywalters/g4rcc
[embassy-stm32]: stm32g4 RCC refactor
| -rw-r--r-- | embassy-stm32/src/rcc/g4.rs | 354 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc.rs | 16 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/pll.rs | 16 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/usb_serial.rs | 31 |
4 files changed, 239 insertions, 178 deletions
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 3e20bf6af..382ffbead 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | use stm32_metapac::flash::vals::Latency; | 1 | use stm32_metapac::flash::vals::Latency; |
| 2 | use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; | 2 | use stm32_metapac::rcc::vals::{Adcsel, Sw}; |
| 3 | use stm32_metapac::FLASH; | 3 | use stm32_metapac::FLASH; |
| 4 | 4 | ||
| 5 | pub use crate::pac::rcc::vals::{ | 5 | pub use crate::pac::rcc::vals::{ |
| 6 | Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, | 6 | Adcsel as AdcClockSource, Clk48sel as Clk48Src, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, |
| 7 | Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler, | 7 | Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc, Ppre as APBPrescaler, |
| 8 | Sw as Sysclk, | ||
| 8 | }; | 9 | }; |
| 9 | use crate::pac::{PWR, RCC}; | 10 | use crate::pac::{PWR, RCC}; |
| 10 | use crate::time::Hertz; | 11 | use crate::time::Hertz; |
| @@ -12,28 +13,22 @@ use crate::time::Hertz; | |||
| 12 | /// HSI speed | 13 | /// HSI speed |
| 13 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); | 14 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 14 | 15 | ||
| 15 | /// System clock mux source | 16 | /// HSE Mode |
| 16 | #[derive(Clone, Copy)] | 17 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 17 | pub enum ClockSrc { | 18 | pub enum HseMode { |
| 18 | HSE(Hertz), | 19 | /// crystal/ceramic oscillator (HSEBYP=0) |
| 19 | HSI, | 20 | Oscillator, |
| 20 | PLL, | 21 | /// external analog clock (low swing) (HSEBYP=1) |
| 22 | Bypass, | ||
| 21 | } | 23 | } |
| 22 | 24 | ||
| 23 | /// PLL clock input source | 25 | /// HSE Configuration |
| 24 | #[derive(Clone, Copy, Debug)] | 26 | #[derive(Clone, Copy, Eq, PartialEq)] |
| 25 | pub enum PllSource { | 27 | pub struct Hse { |
| 26 | HSI, | 28 | /// HSE frequency. |
| 27 | HSE(Hertz), | 29 | pub freq: Hertz, |
| 28 | } | 30 | /// HSE mode. |
| 29 | 31 | pub mode: HseMode, | |
| 30 | impl Into<Pllsrc> for PllSource { | ||
| 31 | fn into(self) -> Pllsrc { | ||
| 32 | match self { | ||
| 33 | PllSource::HSE(..) => Pllsrc::HSE, | ||
| 34 | PllSource::HSI => Pllsrc::HSI, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | 32 | } |
| 38 | 33 | ||
| 39 | /// PLL Configuration | 34 | /// PLL Configuration |
| @@ -43,69 +38,89 @@ impl Into<Pllsrc> for PllSource { | |||
| 43 | /// frequency ranges for each of these settings. | 38 | /// frequency ranges for each of these settings. |
| 44 | pub struct Pll { | 39 | pub struct Pll { |
| 45 | /// PLL Source clock selection. | 40 | /// PLL Source clock selection. |
| 46 | pub source: PllSource, | 41 | pub source: Pllsrc, |
| 47 | 42 | ||
| 48 | /// PLL pre-divider | 43 | /// PLL pre-divider |
| 49 | pub prediv_m: PllM, | 44 | pub prediv: PllPreDiv, |
| 50 | 45 | ||
| 51 | /// PLL multiplication factor for VCO | 46 | /// PLL multiplication factor for VCO |
| 52 | pub mul_n: PllN, | 47 | pub mul: PllMul, |
| 53 | 48 | ||
| 54 | /// PLL division factor for P clock (ADC Clock) | 49 | /// PLL division factor for P clock (ADC Clock) |
| 55 | pub div_p: Option<PllP>, | 50 | pub divp: Option<PllPDiv>, |
| 56 | 51 | ||
| 57 | /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) | 52 | /// PLL division factor for Q clock (USB, I2S23, SAI1, FDCAN, QSPI) |
| 58 | pub div_q: Option<PllQ>, | 53 | pub divq: Option<PllQDiv>, |
| 59 | 54 | ||
| 60 | /// PLL division factor for R clock (SYSCLK) | 55 | /// PLL division factor for R clock (SYSCLK) |
| 61 | pub div_r: Option<PllR>, | 56 | pub divr: Option<PllRDiv>, |
| 62 | } | ||
| 63 | |||
| 64 | /// Sets the source for the 48MHz clock to the USB and RNG peripherals. | ||
| 65 | pub enum Clock48MhzSrc { | ||
| 66 | /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the | ||
| 67 | /// oscillator to comply with the USB specification for oscillator tolerance. | ||
| 68 | Hsi48(super::Hsi48Config), | ||
| 69 | /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the | ||
| 70 | /// PLL needs to be using the HSE source to comply with the USB specification for oscillator | ||
| 71 | /// tolerance. | ||
| 72 | PllQ, | ||
| 73 | } | 57 | } |
| 74 | 58 | ||
| 75 | /// Clocks configutation | 59 | /// Clocks configutation |
| 60 | #[non_exhaustive] | ||
| 76 | pub struct Config { | 61 | pub struct Config { |
| 77 | pub mux: ClockSrc, | 62 | /// HSI Enable |
| 63 | pub hsi: bool, | ||
| 64 | |||
| 65 | /// HSE Configuration | ||
| 66 | pub hse: Option<Hse>, | ||
| 67 | |||
| 68 | /// System Clock Configuration | ||
| 69 | pub sys: Sysclk, | ||
| 70 | |||
| 71 | /// HSI48 Configuration | ||
| 72 | pub hsi48: Option<super::Hsi48Config>, | ||
| 73 | |||
| 74 | /// PLL Configuration | ||
| 75 | pub pll: Option<Pll>, | ||
| 76 | |||
| 77 | /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration | ||
| 78 | /// MUST turn on the PLLR output. | ||
| 78 | pub ahb_pre: AHBPrescaler, | 79 | pub ahb_pre: AHBPrescaler, |
| 79 | pub apb1_pre: APBPrescaler, | 80 | pub apb1_pre: APBPrescaler, |
| 80 | pub apb2_pre: APBPrescaler, | 81 | pub apb2_pre: APBPrescaler, |
| 82 | |||
| 81 | pub low_power_run: bool, | 83 | pub low_power_run: bool, |
| 82 | /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration | 84 | |
| 83 | /// MUST turn on the PLLR output. | ||
| 84 | pub pll: Option<Pll>, | ||
| 85 | /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. | 85 | /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals. |
| 86 | pub clock_48mhz_src: Option<Clock48MhzSrc>, | 86 | pub clk48_src: Clk48Src, |
| 87 | |||
| 88 | /// Low-Speed Clock Configuration | ||
| 89 | pub ls: super::LsConfig, | ||
| 90 | |||
| 91 | /// Clock Source for ADCs 1 and 2 | ||
| 87 | pub adc12_clock_source: AdcClockSource, | 92 | pub adc12_clock_source: AdcClockSource, |
| 93 | |||
| 94 | /// Clock Source for ADCs 3, 4 and 5 | ||
| 88 | pub adc345_clock_source: AdcClockSource, | 95 | pub adc345_clock_source: AdcClockSource, |
| 96 | |||
| 97 | /// Clock Source for FDCAN | ||
| 89 | pub fdcan_clock_source: FdCanClockSource, | 98 | pub fdcan_clock_source: FdCanClockSource, |
| 90 | 99 | ||
| 91 | pub ls: super::LsConfig, | 100 | /// Enable range1 boost mode |
| 101 | /// Recommended when the SYSCLK frequency is greater than 150MHz. | ||
| 102 | pub boost: bool, | ||
| 92 | } | 103 | } |
| 93 | 104 | ||
| 94 | impl Default for Config { | 105 | impl Default for Config { |
| 95 | #[inline] | 106 | #[inline] |
| 96 | fn default() -> Config { | 107 | fn default() -> Config { |
| 97 | Config { | 108 | Config { |
| 98 | mux: ClockSrc::HSI, | 109 | hsi: true, |
| 110 | hse: None, | ||
| 111 | sys: Sysclk::HSI, | ||
| 112 | hsi48: Some(Default::default()), | ||
| 113 | pll: None, | ||
| 99 | ahb_pre: AHBPrescaler::DIV1, | 114 | ahb_pre: AHBPrescaler::DIV1, |
| 100 | apb1_pre: APBPrescaler::DIV1, | 115 | apb1_pre: APBPrescaler::DIV1, |
| 101 | apb2_pre: APBPrescaler::DIV1, | 116 | apb2_pre: APBPrescaler::DIV1, |
| 102 | low_power_run: false, | 117 | low_power_run: false, |
| 103 | pll: None, | 118 | clk48_src: Clk48Src::HSI48, |
| 104 | clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), | 119 | ls: Default::default(), |
| 105 | adc12_clock_source: Adcsel::DISABLE, | 120 | adc12_clock_source: Adcsel::DISABLE, |
| 106 | adc345_clock_source: Adcsel::DISABLE, | 121 | adc345_clock_source: Adcsel::DISABLE, |
| 107 | fdcan_clock_source: FdCanClockSource::PCLK1, | 122 | fdcan_clock_source: FdCanClockSource::PCLK1, |
| 108 | ls: Default::default(), | 123 | boost: false, |
| 109 | } | 124 | } |
| 110 | } | 125 | } |
| 111 | } | 126 | } |
| @@ -117,34 +132,65 @@ pub struct PllFreq { | |||
| 117 | } | 132 | } |
| 118 | 133 | ||
| 119 | pub(crate) unsafe fn init(config: Config) { | 134 | pub(crate) unsafe fn init(config: Config) { |
| 120 | let pll_freq = config.pll.map(|pll_config| { | 135 | // Configure HSI |
| 121 | let src_freq = match pll_config.source { | 136 | let hsi = match config.hsi { |
| 122 | PllSource::HSI => { | 137 | false => { |
| 123 | RCC.cr().write(|w| w.set_hsion(true)); | 138 | RCC.cr().modify(|w| w.set_hsion(false)); |
| 124 | while !RCC.cr().read().hsirdy() {} | 139 | None |
| 140 | } | ||
| 141 | true => { | ||
| 142 | RCC.cr().modify(|w| w.set_hsion(true)); | ||
| 143 | while !RCC.cr().read().hsirdy() {} | ||
| 144 | Some(HSI_FREQ) | ||
| 145 | } | ||
| 146 | }; | ||
| 125 | 147 | ||
| 126 | HSI_FREQ | 148 | // Configure HSE |
| 127 | } | 149 | let hse = match config.hse { |
| 128 | PllSource::HSE(freq) => { | 150 | None => { |
| 129 | RCC.cr().write(|w| w.set_hseon(true)); | 151 | RCC.cr().modify(|w| w.set_hseon(false)); |
| 130 | while !RCC.cr().read().hserdy() {} | 152 | None |
| 131 | freq | 153 | } |
| 154 | Some(hse) => { | ||
| 155 | match hse.mode { | ||
| 156 | HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), | ||
| 157 | HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), | ||
| 132 | } | 158 | } |
| 159 | |||
| 160 | RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); | ||
| 161 | RCC.cr().modify(|w| w.set_hseon(true)); | ||
| 162 | while !RCC.cr().read().hserdy() {} | ||
| 163 | Some(hse.freq) | ||
| 164 | } | ||
| 165 | }; | ||
| 166 | |||
| 167 | // Configure HSI48 if required | ||
| 168 | if let Some(hsi48_config) = config.hsi48 { | ||
| 169 | super::init_hsi48(hsi48_config); | ||
| 170 | } | ||
| 171 | |||
| 172 | let pll_freq = config.pll.map(|pll_config| { | ||
| 173 | let src_freq = match pll_config.source { | ||
| 174 | Pllsrc::HSI => unwrap!(hsi), | ||
| 175 | Pllsrc::HSE => unwrap!(hse), | ||
| 176 | _ => unreachable!(), | ||
| 133 | }; | 177 | }; |
| 134 | 178 | ||
| 179 | // TODO: check PLL input, internal and output frequencies for validity | ||
| 180 | |||
| 135 | // Disable PLL before configuration | 181 | // Disable PLL before configuration |
| 136 | RCC.cr().modify(|w| w.set_pllon(false)); | 182 | RCC.cr().modify(|w| w.set_pllon(false)); |
| 137 | while RCC.cr().read().pllrdy() {} | 183 | while RCC.cr().read().pllrdy() {} |
| 138 | 184 | ||
| 139 | let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n; | 185 | let internal_freq = src_freq / pll_config.prediv * pll_config.mul; |
| 140 | 186 | ||
| 141 | RCC.pllcfgr().write(|w| { | 187 | RCC.pllcfgr().write(|w| { |
| 142 | w.set_plln(pll_config.mul_n); | 188 | w.set_plln(pll_config.mul); |
| 143 | w.set_pllm(pll_config.prediv_m); | 189 | w.set_pllm(pll_config.prediv); |
| 144 | w.set_pllsrc(pll_config.source.into()); | 190 | w.set_pllsrc(pll_config.source.into()); |
| 145 | }); | 191 | }); |
| 146 | 192 | ||
| 147 | let pll_p_freq = pll_config.div_p.map(|div_p| { | 193 | let pll_p_freq = pll_config.divp.map(|div_p| { |
| 148 | RCC.pllcfgr().modify(|w| { | 194 | RCC.pllcfgr().modify(|w| { |
| 149 | w.set_pllp(div_p); | 195 | w.set_pllp(div_p); |
| 150 | w.set_pllpen(true); | 196 | w.set_pllpen(true); |
| @@ -152,7 +198,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 152 | internal_freq / div_p | 198 | internal_freq / div_p |
| 153 | }); | 199 | }); |
| 154 | 200 | ||
| 155 | let pll_q_freq = pll_config.div_q.map(|div_q| { | 201 | let pll_q_freq = pll_config.divq.map(|div_q| { |
| 156 | RCC.pllcfgr().modify(|w| { | 202 | RCC.pllcfgr().modify(|w| { |
| 157 | w.set_pllq(div_q); | 203 | w.set_pllq(div_q); |
| 158 | w.set_pllqen(true); | 204 | w.set_pllqen(true); |
| @@ -160,7 +206,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 160 | internal_freq / div_q | 206 | internal_freq / div_q |
| 161 | }); | 207 | }); |
| 162 | 208 | ||
| 163 | let pll_r_freq = pll_config.div_r.map(|div_r| { | 209 | let pll_r_freq = pll_config.divr.map(|div_r| { |
| 164 | RCC.pllcfgr().modify(|w| { | 210 | RCC.pllcfgr().modify(|w| { |
| 165 | w.set_pllr(div_r); | 211 | w.set_pllr(div_r); |
| 166 | w.set_pllren(true); | 212 | w.set_pllren(true); |
| @@ -179,22 +225,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 179 | } | 225 | } |
| 180 | }); | 226 | }); |
| 181 | 227 | ||
| 182 | let (sys_clk, sw) = match config.mux { | 228 | let (sys_clk, sw) = match config.sys { |
| 183 | ClockSrc::HSI => { | 229 | Sysclk::HSI => (HSI_FREQ, Sw::HSI), |
| 184 | // Enable HSI | 230 | Sysclk::HSE => (unwrap!(hse), Sw::HSE), |
| 185 | RCC.cr().write(|w| w.set_hsion(true)); | 231 | Sysclk::PLL1_R => { |
| 186 | while !RCC.cr().read().hsirdy() {} | ||
| 187 | |||
| 188 | (HSI_FREQ, Sw::HSI) | ||
| 189 | } | ||
| 190 | ClockSrc::HSE(freq) => { | ||
| 191 | // Enable HSE | ||
| 192 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 193 | while !RCC.cr().read().hserdy() {} | ||
| 194 | |||
| 195 | (freq, Sw::HSE) | ||
| 196 | } | ||
| 197 | ClockSrc::PLL => { | ||
| 198 | assert!(pll_freq.is_some()); | 232 | assert!(pll_freq.is_some()); |
| 199 | assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); | 233 | assert!(pll_freq.as_ref().unwrap().pll_r.is_some()); |
| 200 | 234 | ||
| @@ -202,41 +236,51 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 202 | 236 | ||
| 203 | assert!(freq <= 170_000_000); | 237 | assert!(freq <= 170_000_000); |
| 204 | 238 | ||
| 205 | if freq >= 150_000_000 { | ||
| 206 | // Enable Core Boost mode on freq >= 150Mhz ([RM0440] p234) | ||
| 207 | PWR.cr5().modify(|w| w.set_r1mode(false)); | ||
| 208 | // Set flash wait state in boost mode based on frequency ([RM0440] p191) | ||
| 209 | if freq <= 36_000_000 { | ||
| 210 | FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); | ||
| 211 | } else if freq <= 68_000_000 { | ||
| 212 | FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); | ||
| 213 | } else if freq <= 102_000_000 { | ||
| 214 | FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); | ||
| 215 | } else if freq <= 136_000_000 { | ||
| 216 | FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); | ||
| 217 | } else { | ||
| 218 | FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); | ||
| 219 | } | ||
| 220 | } else { | ||
| 221 | PWR.cr5().modify(|w| w.set_r1mode(true)); | ||
| 222 | // Set flash wait state in normal mode based on frequency ([RM0440] p191) | ||
| 223 | if freq <= 30_000_000 { | ||
| 224 | FLASH.acr().modify(|w| w.set_latency(Latency::WS0)); | ||
| 225 | } else if freq <= 60_000_000 { | ||
| 226 | FLASH.acr().modify(|w| w.set_latency(Latency::WS1)); | ||
| 227 | } else if freq <= 80_000_000 { | ||
| 228 | FLASH.acr().modify(|w| w.set_latency(Latency::WS2)); | ||
| 229 | } else if freq <= 120_000_000 { | ||
| 230 | FLASH.acr().modify(|w| w.set_latency(Latency::WS3)); | ||
| 231 | } else { | ||
| 232 | FLASH.acr().modify(|w| w.set_latency(Latency::WS4)); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | (Hertz(freq), Sw::PLL1_R) | 239 | (Hertz(freq), Sw::PLL1_R) |
| 237 | } | 240 | } |
| 241 | _ => unreachable!(), | ||
| 238 | }; | 242 | }; |
| 239 | 243 | ||
| 244 | // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. | ||
| 245 | let hclk = sys_clk / config.ahb_pre; | ||
| 246 | |||
| 247 | // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) | ||
| 248 | if config.boost { | ||
| 249 | // RM0440 p235 | ||
| 250 | // “The sequence to switch from Range1 normal mode to Range1 boost mode is: | ||
| 251 | // 1. The system clock must be divided by 2 using the AHB prescaler before switching to a higher system frequency. | ||
| 252 | RCC.cfgr().modify(|w| w.set_hpre(AHBPrescaler::DIV2)); | ||
| 253 | // 2. Clear the R1MODE bit in the PWR_CR5 register. (enables boost mode) | ||
| 254 | PWR.cr5().modify(|w| w.set_r1mode(false)); | ||
| 255 | |||
| 256 | // Below: | ||
| 257 | // 3. Adjust wait states according to new freq target | ||
| 258 | // 4. Configure and switch to new frequency | ||
| 259 | } | ||
| 260 | |||
| 261 | // Configure flash read access latency based on boost mode and frequency (RM0440 p98) | ||
| 262 | FLASH.acr().modify(|w| { | ||
| 263 | w.set_latency(match (config.boost, hclk.0) { | ||
| 264 | (true, ..=34_000_000) => Latency::WS0, | ||
| 265 | (true, ..=68_000_000) => Latency::WS1, | ||
| 266 | (true, ..=102_000_000) => Latency::WS2, | ||
| 267 | (true, ..=136_000_000) => Latency::WS3, | ||
| 268 | (true, _) => Latency::WS4, | ||
| 269 | |||
| 270 | (false, ..=36_000_000) => Latency::WS0, | ||
| 271 | (false, ..=60_000_000) => Latency::WS1, | ||
| 272 | (false, ..=90_000_000) => Latency::WS2, | ||
| 273 | (false, ..=120_000_000) => Latency::WS3, | ||
| 274 | (false, _) => Latency::WS4, | ||
| 275 | }) | ||
| 276 | }); | ||
| 277 | |||
| 278 | if config.boost { | ||
| 279 | // 5. Wait for at least 1us and then reconfigure the AHB prescaler to get the needed HCLK clock frequency. | ||
| 280 | cortex_m::asm::delay(16); | ||
| 281 | } | ||
| 282 | |||
| 283 | // Now that boost mode and flash read access latency are configured, set up SYSCLK | ||
| 240 | RCC.cfgr().modify(|w| { | 284 | RCC.cfgr().modify(|w| { |
| 241 | w.set_sw(sw); | 285 | w.set_sw(sw); |
| 242 | w.set_hpre(config.ahb_pre); | 286 | w.set_hpre(config.ahb_pre); |
| @@ -244,42 +288,26 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 244 | w.set_ppre2(config.apb2_pre); | 288 | w.set_ppre2(config.apb2_pre); |
| 245 | }); | 289 | }); |
| 246 | 290 | ||
| 247 | let ahb_freq = sys_clk / config.ahb_pre; | 291 | let (apb1_freq, apb1_tim_freq) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 248 | 292 | let (apb2_freq, apb2_tim_freq) = super::util::calc_pclk(hclk, config.apb2_pre); | |
| 249 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | ||
| 250 | APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||
| 251 | pre => { | ||
| 252 | let freq = ahb_freq / pre; | ||
| 253 | (freq, freq * 2u32) | ||
| 254 | } | ||
| 255 | }; | ||
| 256 | |||
| 257 | let (apb2_freq, apb2_tim_freq) = match config.apb2_pre { | ||
| 258 | APBPrescaler::DIV1 => (ahb_freq, ahb_freq), | ||
| 259 | pre => { | ||
| 260 | let freq = ahb_freq / pre; | ||
| 261 | (freq, freq * 2u32) | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | |||
| 265 | // Setup the 48 MHz clock if needed | ||
| 266 | if let Some(clock_48mhz_src) = config.clock_48mhz_src { | ||
| 267 | let source = match clock_48mhz_src { | ||
| 268 | Clock48MhzSrc::PllQ => { | ||
| 269 | // Make sure the PLLQ is enabled and running at 48Mhz | ||
| 270 | let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q); | ||
| 271 | assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000); | ||
| 272 | 293 | ||
| 294 | // Configure the 48MHz clock source for USB and RNG peripherals. | ||
| 295 | RCC.ccipr().modify(|w| { | ||
| 296 | w.set_clk48sel(match config.clk48_src { | ||
| 297 | Clk48Src::PLL1_Q => { | ||
| 298 | // Not checking that PLL1_Q is 48MHz here so as not to require the user to have a 48MHz clock. | ||
| 299 | // Peripherals which require one (USB, RNG) should check that they‘re driven by a valid 48MHz | ||
| 300 | // clock at init. | ||
| 273 | crate::pac::rcc::vals::Clk48sel::PLL1_Q | 301 | crate::pac::rcc::vals::Clk48sel::PLL1_Q |
| 274 | } | 302 | } |
| 275 | Clock48MhzSrc::Hsi48(config) => { | 303 | Clk48Src::HSI48 => { |
| 276 | super::init_hsi48(config); | 304 | // Make sure HSI48 is enabled |
| 305 | assert!(config.hsi48.is_some()); | ||
| 277 | crate::pac::rcc::vals::Clk48sel::HSI48 | 306 | crate::pac::rcc::vals::Clk48sel::HSI48 |
| 278 | } | 307 | } |
| 279 | }; | 308 | _ => unreachable!(), |
| 280 | 309 | }) | |
| 281 | RCC.ccipr().modify(|w| w.set_clk48sel(source)); | 310 | }); |
| 282 | } | ||
| 283 | 311 | ||
| 284 | RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); | 312 | RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); |
| 285 | RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); | 313 | RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); |
| @@ -308,18 +336,42 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 308 | 336 | ||
| 309 | set_clocks!( | 337 | set_clocks!( |
| 310 | sys: Some(sys_clk), | 338 | sys: Some(sys_clk), |
| 311 | hclk1: Some(ahb_freq), | 339 | hclk1: Some(hclk), |
| 312 | hclk2: Some(ahb_freq), | 340 | hclk2: Some(hclk), |
| 313 | hclk3: Some(ahb_freq), | 341 | hclk3: Some(hclk), |
| 314 | pclk1: Some(apb1_freq), | 342 | pclk1: Some(apb1_freq), |
| 315 | pclk1_tim: Some(apb1_tim_freq), | 343 | pclk1_tim: Some(apb1_tim_freq), |
| 316 | pclk2: Some(apb2_freq), | 344 | pclk2: Some(apb2_freq), |
| 317 | pclk2_tim: Some(apb2_tim_freq), | 345 | pclk2_tim: Some(apb2_tim_freq), |
| 318 | adc: adc12_ck, | 346 | adc: adc12_ck, |
| 319 | adc34: adc345_ck, | 347 | adc34: adc345_ck, |
| 320 | pll1_p: None, | 348 | pll1_p: pll_freq.as_ref().and_then(|pll| pll.pll_p), |
| 321 | pll1_q: None, // TODO | 349 | pll1_q: pll_freq.as_ref().and_then(|pll| pll.pll_p), |
| 322 | hse: None, // TODO | 350 | hse: hse, |
| 323 | rtc: rtc, | 351 | rtc: rtc, |
| 324 | ); | 352 | ); |
| 325 | } | 353 | } |
| 354 | |||
| 355 | // TODO: if necessary, make more of these, gated behind cfg attrs | ||
| 356 | mod max { | ||
| 357 | use core::ops::RangeInclusive; | ||
| 358 | |||
| 359 | use crate::time::Hertz; | ||
| 360 | |||
| 361 | /// HSE 4-48MHz (RM0440 p280) | ||
| 362 | pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(48_000_000); | ||
| 363 | |||
| 364 | /// External Clock ?-48MHz (RM0440 p280) | ||
| 365 | pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(0)..=Hertz(48_000_000); | ||
| 366 | |||
| 367 | // SYSCLK ?-170MHz (RM0440 p282) | ||
| 368 | //pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000); | ||
| 369 | |||
| 370 | // PLL Output frequency ?-170MHz (RM0440 p281) | ||
| 371 | //pub(crate) const PCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(170_000_000); | ||
| 372 | |||
| 373 | // Left over from f.rs, remove if not necessary | ||
| 374 | //pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000); | ||
| 375 | //pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000); | ||
| 376 | //pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000); | ||
| 377 | } | ||
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 35324d931..99e3ef63b 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::adc::{Adc, SampleTime}; | 6 | use embassy_stm32::adc::{Adc, SampleTime}; |
| 7 | use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSource}; | 7 | use embassy_stm32::rcc::{AdcClockSource, Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; |
| 8 | use embassy_stm32::Config; | 8 | use embassy_stm32::Config; |
| 9 | use embassy_time::{Delay, Timer}; | 9 | use embassy_time::{Delay, Timer}; |
| 10 | use {defmt_rtt as _, panic_probe as _}; | 10 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -14,17 +14,17 @@ async fn main(_spawner: Spawner) { | |||
| 14 | let mut config = Config::default(); | 14 | let mut config = Config::default(); |
| 15 | 15 | ||
| 16 | config.rcc.pll = Some(Pll { | 16 | config.rcc.pll = Some(Pll { |
| 17 | source: PllSource::HSI, | 17 | source: Pllsrc::HSI, |
| 18 | prediv_m: PllM::DIV4, | 18 | prediv: PllPreDiv::DIV4, |
| 19 | mul_n: PllN::MUL85, | 19 | mul: PllMul::MUL85, |
| 20 | div_p: None, | 20 | divp: None, |
| 21 | div_q: None, | 21 | divq: None, |
| 22 | // Main system clock at 170 MHz | 22 | // Main system clock at 170 MHz |
| 23 | div_r: Some(PllR::DIV2), | 23 | divr: Some(PllRDiv::DIV2), |
| 24 | }); | 24 | }); |
| 25 | 25 | ||
| 26 | config.rcc.adc12_clock_source = AdcClockSource::SYS; | 26 | config.rcc.adc12_clock_source = AdcClockSource::SYS; |
| 27 | config.rcc.mux = ClockSrc::PLL; | 27 | config.rcc.sys = Sysclk::PLL1_R; |
| 28 | 28 | ||
| 29 | let mut p = embassy_stm32::init(config); | 29 | let mut p = embassy_stm32::init(config); |
| 30 | info!("Hello World!"); | 30 | info!("Hello World!"); |
diff --git a/examples/stm32g4/src/bin/pll.rs b/examples/stm32g4/src/bin/pll.rs index 46ebe0b0d..5274de79d 100644 --- a/examples/stm32g4/src/bin/pll.rs +++ b/examples/stm32g4/src/bin/pll.rs | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::*; | 4 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllR, PllSource}; | 6 | use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, Pllsrc, Sysclk}; |
| 7 | use embassy_stm32::Config; | 7 | use embassy_stm32::Config; |
| 8 | use embassy_time::Timer; | 8 | use embassy_time::Timer; |
| 9 | use {defmt_rtt as _, panic_probe as _}; | 9 | use {defmt_rtt as _, panic_probe as _}; |
| @@ -13,16 +13,16 @@ async fn main(_spawner: Spawner) { | |||
| 13 | let mut config = Config::default(); | 13 | let mut config = Config::default(); |
| 14 | 14 | ||
| 15 | config.rcc.pll = Some(Pll { | 15 | config.rcc.pll = Some(Pll { |
| 16 | source: PllSource::HSI, | 16 | source: Pllsrc::HSI, |
| 17 | prediv_m: PllM::DIV4, | 17 | prediv: PllPreDiv::DIV4, |
| 18 | mul_n: PllN::MUL85, | 18 | mul: PllMul::MUL85, |
| 19 | div_p: None, | 19 | divp: None, |
| 20 | div_q: None, | 20 | divq: None, |
| 21 | // Main system clock at 170 MHz | 21 | // Main system clock at 170 MHz |
| 22 | div_r: Some(PllR::DIV2), | 22 | divr: Some(PllRDiv::DIV2), |
| 23 | }); | 23 | }); |
| 24 | 24 | ||
| 25 | config.rcc.mux = ClockSrc::PLL; | 25 | config.rcc.sys = Sysclk::PLL1_R; |
| 26 | 26 | ||
| 27 | let _p = embassy_stm32::init(config); | 27 | let _p = embassy_stm32::init(config); |
| 28 | info!("Hello World!"); | 28 | info!("Hello World!"); |
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index c26fa76b7..989fef5b0 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs | |||
| @@ -3,7 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | use defmt::{panic, *}; | 4 | use defmt::{panic, *}; |
| 5 | use embassy_executor::Spawner; | 5 | use embassy_executor::Spawner; |
| 6 | use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSource}; | 6 | use embassy_stm32::rcc::{ |
| 7 | Clk48Src, Hse, HseMode, Hsi48Config, Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, Pllsrc, Sysclk, | ||
| 8 | }; | ||
| 7 | use embassy_stm32::time::Hertz; | 9 | use embassy_stm32::time::Hertz; |
| 8 | use embassy_stm32::usb::{self, Driver, Instance}; | 10 | use embassy_stm32::usb::{self, Driver, Instance}; |
| 9 | use embassy_stm32::{bind_interrupts, peripherals, Config}; | 11 | use embassy_stm32::{bind_interrupts, peripherals, Config}; |
| @@ -24,25 +26,32 @@ async fn main(_spawner: Spawner) { | |||
| 24 | // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. | 26 | // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE. |
| 25 | const USE_HSI48: bool = true; | 27 | const USE_HSI48: bool = true; |
| 26 | 28 | ||
| 27 | let plldivq = if USE_HSI48 { None } else { Some(PllQ::DIV6) }; | 29 | let plldivq = if USE_HSI48 { None } else { Some(PllQDiv::DIV6) }; |
| 30 | |||
| 31 | config.rcc.hse = Some(Hse { | ||
| 32 | freq: Hertz(8_000_000), | ||
| 33 | mode: HseMode::Oscillator, | ||
| 34 | }); | ||
| 28 | 35 | ||
| 29 | config.rcc.pll = Some(Pll { | 36 | config.rcc.pll = Some(Pll { |
| 30 | source: PllSource::HSE(Hertz(8_000_000)), | 37 | source: Pllsrc::HSE, |
| 31 | prediv_m: PllM::DIV2, | 38 | prediv: PllPreDiv::DIV2, |
| 32 | mul_n: PllN::MUL72, | 39 | mul: PllMul::MUL72, |
| 33 | div_p: None, | 40 | divp: None, |
| 34 | div_q: plldivq, | 41 | divq: plldivq, |
| 35 | // Main system clock at 144 MHz | 42 | // Main system clock at 144 MHz |
| 36 | div_r: Some(PllR::DIV2), | 43 | divr: Some(PllRDiv::DIV2), |
| 37 | }); | 44 | }); |
| 38 | 45 | ||
| 39 | config.rcc.mux = ClockSrc::PLL; | 46 | config.rcc.sys = Sysclk::PLL1_R; |
| 47 | config.rcc.boost = true; // BOOST! | ||
| 40 | 48 | ||
| 41 | if USE_HSI48 { | 49 | if USE_HSI48 { |
| 42 | // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. | 50 | // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. |
| 43 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true })); | 51 | config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); |
| 52 | config.rcc.clk48_src = Clk48Src::HSI48; | ||
| 44 | } else { | 53 | } else { |
| 45 | config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); | 54 | config.rcc.clk48_src = Clk48Src::PLL1_Q; |
| 46 | } | 55 | } |
| 47 | 56 | ||
| 48 | let p = embassy_stm32::init(config); | 57 | let p = embassy_stm32::init(config); |
