diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-04-30 21:08:43 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-04-30 21:08:43 +0000 |
| commit | d600f392608ebebe97d1e7461789d0a724afc9ef (patch) | |
| tree | f57df320554eb534c7aba15e4d09e5929deb8e96 | |
| parent | c474682ea97add6ae8d448e8472ee0444c3cc366 (diff) | |
| parent | e88559c5ca2450bbcfd6fe65e73fe0fe47465680 (diff) | |
Merge #743
743: Add PLL config support for F2 r=Dirbaio a=Gekkio
- minor changes to make the F2 RCC API a bit more flexible
- low-level PLL config with assertions based on datasheet specs. It shouldn't be very difficult to later add a "reverse API" where you pass the clocks you want to a function and it generates a `PLLConfig` struct for you
- PLL API tested on my custom board with 12 MHz HSE as source for PLL to generate max clocks for SYSCLK/AHB/APB/APB1/PLL48
- the example *should* work but is untested since I don't have the Nucleo board :disappointed:
Co-authored-by: Joonas Javanainen <[email protected]>
| -rw-r--r-- | embassy-stm32/src/rcc/f2.rs | 264 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/mod.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f2/src/bin/pll.rs | 56 |
3 files changed, 300 insertions, 22 deletions
diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index bece046f8..7e5992bba 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | use core::ops::Div; | 1 | use core::convert::TryFrom; |
| 2 | use core::ops::{Div, Mul}; | ||
| 2 | 3 | ||
| 3 | use crate::pac::flash::vals::Latency; | 4 | use crate::pac::flash::vals::Latency; |
| 4 | use crate::pac::rcc::vals::{Hpre, Ppre, Sw}; | 5 | use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw}; |
| 5 | use crate::pac::{FLASH, RCC}; | 6 | use crate::pac::{FLASH, RCC}; |
| 6 | use crate::rcc::{set_freqs, Clocks}; | 7 | use crate::rcc::{set_freqs, Clocks}; |
| 7 | use crate::time::Hertz; | 8 | use crate::time::Hertz; |
| @@ -9,11 +10,18 @@ use crate::time::Hertz; | |||
| 9 | /// HSI speed | 10 | /// HSI speed |
| 10 | pub const HSI: Hertz = Hertz(16_000_000); | 11 | pub const HSI: Hertz = Hertz(16_000_000); |
| 11 | 12 | ||
| 13 | #[derive(Clone, Copy)] | ||
| 14 | pub struct HSEConfig { | ||
| 15 | pub frequency: Hertz, | ||
| 16 | pub source: HSESrc, | ||
| 17 | } | ||
| 18 | |||
| 12 | /// System clock mux source | 19 | /// System clock mux source |
| 13 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
| 14 | pub enum ClockSrc { | 21 | pub enum ClockSrc { |
| 15 | HSE(Hertz, HSESrc), | 22 | HSE, |
| 16 | HSI, | 23 | HSI, |
| 24 | PLL, | ||
| 17 | } | 25 | } |
| 18 | 26 | ||
| 19 | /// HSE clock source | 27 | /// HSE clock source |
| @@ -25,6 +33,170 @@ pub enum HSESrc { | |||
| 25 | Bypass, | 33 | Bypass, |
| 26 | } | 34 | } |
| 27 | 35 | ||
| 36 | #[derive(Clone, Copy)] | ||
| 37 | pub struct PLLConfig { | ||
| 38 | pub pre_div: PLLPreDiv, | ||
| 39 | pub mul: PLLMul, | ||
| 40 | pub main_div: PLLMainDiv, | ||
| 41 | pub pll48_div: PLL48Div, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl Default for PLLConfig { | ||
| 45 | fn default() -> Self { | ||
| 46 | PLLConfig { | ||
| 47 | pre_div: PLLPreDiv(16), | ||
| 48 | mul: PLLMul(192), | ||
| 49 | main_div: PLLMainDiv::Div2, | ||
| 50 | pll48_div: PLL48Div(4), | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | impl PLLConfig { | ||
| 56 | pub fn clocks(&self, src_freq: Hertz) -> PLLClocks { | ||
| 57 | let in_freq = src_freq / self.pre_div; | ||
| 58 | let vco_freq = src_freq * self.mul / self.pre_div; | ||
| 59 | let main_freq = vco_freq / self.main_div; | ||
| 60 | let pll48_freq = vco_freq / self.pll48_div; | ||
| 61 | PLLClocks { | ||
| 62 | in_freq, | ||
| 63 | vco_freq, | ||
| 64 | main_freq, | ||
| 65 | pll48_freq, | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | /// Clock source for both main PLL and PLLI2S | ||
| 71 | #[derive(Clone, Copy, PartialEq)] | ||
| 72 | pub enum PLLSrc { | ||
| 73 | HSE, | ||
| 74 | HSI, | ||
| 75 | } | ||
| 76 | |||
| 77 | impl Into<Pllsrc> for PLLSrc { | ||
| 78 | fn into(self) -> Pllsrc { | ||
| 79 | match self { | ||
| 80 | PLLSrc::HSE => Pllsrc::HSE, | ||
| 81 | PLLSrc::HSI => Pllsrc::HSI, | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | /// Division factor for both main PLL and PLLI2S | ||
| 87 | #[derive(Clone, Copy, PartialEq)] | ||
| 88 | #[repr(transparent)] | ||
| 89 | pub struct PLLPreDiv(u8); | ||
| 90 | |||
| 91 | impl TryFrom<u8> for PLLPreDiv { | ||
| 92 | type Error = &'static str; | ||
| 93 | |||
| 94 | fn try_from(value: u8) -> Result<Self, Self::Error> { | ||
| 95 | match value { | ||
| 96 | 2..=63 => Ok(PLLPreDiv(value)), | ||
| 97 | _ => Err("PLLPreDiv must be within range 2..=63"), | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | impl Div<PLLPreDiv> for Hertz { | ||
| 103 | type Output = Hertz; | ||
| 104 | |||
| 105 | fn div(self, rhs: PLLPreDiv) -> Self::Output { | ||
| 106 | Hertz(self.0 / u32::from(rhs.0)) | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /// Multiplication factor for main PLL | ||
| 111 | #[derive(Clone, Copy, PartialEq)] | ||
| 112 | #[repr(transparent)] | ||
| 113 | pub struct PLLMul(u16); | ||
| 114 | |||
| 115 | impl Mul<PLLMul> for Hertz { | ||
| 116 | type Output = Hertz; | ||
| 117 | |||
| 118 | fn mul(self, rhs: PLLMul) -> Self::Output { | ||
| 119 | Hertz(self.0 * u32::from(rhs.0)) | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | impl TryFrom<u16> for PLLMul { | ||
| 124 | type Error = &'static str; | ||
| 125 | |||
| 126 | fn try_from(value: u16) -> Result<Self, Self::Error> { | ||
| 127 | match value { | ||
| 128 | 192..=432 => Ok(PLLMul(value)), | ||
| 129 | _ => Err("PLLMul must be within range 192..=432"), | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// PLL division factor for the main system clock | ||
| 135 | #[derive(Clone, Copy, PartialEq)] | ||
| 136 | pub enum PLLMainDiv { | ||
| 137 | Div2, | ||
| 138 | Div4, | ||
| 139 | Div6, | ||
| 140 | Div8, | ||
| 141 | } | ||
| 142 | |||
| 143 | impl Into<Pllp> for PLLMainDiv { | ||
| 144 | fn into(self) -> Pllp { | ||
| 145 | match self { | ||
| 146 | PLLMainDiv::Div2 => Pllp::DIV2, | ||
| 147 | PLLMainDiv::Div4 => Pllp::DIV4, | ||
| 148 | PLLMainDiv::Div6 => Pllp::DIV8, | ||
| 149 | PLLMainDiv::Div8 => Pllp::DIV8, | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | impl Div<PLLMainDiv> for Hertz { | ||
| 155 | type Output = Hertz; | ||
| 156 | |||
| 157 | fn div(self, rhs: PLLMainDiv) -> Self::Output { | ||
| 158 | let divisor = match rhs { | ||
| 159 | PLLMainDiv::Div2 => 2, | ||
| 160 | PLLMainDiv::Div4 => 4, | ||
| 161 | PLLMainDiv::Div6 => 6, | ||
| 162 | PLLMainDiv::Div8 => 8, | ||
| 163 | }; | ||
| 164 | Hertz(self.0 / divisor) | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | /// PLL division factor for USB OTG FS / SDIO / RNG | ||
| 169 | #[derive(Clone, Copy, PartialEq)] | ||
| 170 | #[repr(transparent)] | ||
| 171 | pub struct PLL48Div(u8); | ||
| 172 | |||
| 173 | impl Div<PLL48Div> for Hertz { | ||
| 174 | type Output = Hertz; | ||
| 175 | |||
| 176 | fn div(self, rhs: PLL48Div) -> Self::Output { | ||
| 177 | Hertz(self.0 / u32::from(rhs.0)) | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | impl TryFrom<u8> for PLL48Div { | ||
| 182 | type Error = &'static str; | ||
| 183 | |||
| 184 | fn try_from(value: u8) -> Result<Self, Self::Error> { | ||
| 185 | match value { | ||
| 186 | 2..=15 => Ok(PLL48Div(value)), | ||
| 187 | _ => Err("PLL48Div must be within range 2..=15"), | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | #[derive(Clone, Copy, PartialEq)] | ||
| 193 | pub struct PLLClocks { | ||
| 194 | pub in_freq: Hertz, | ||
| 195 | pub vco_freq: Hertz, | ||
| 196 | pub main_freq: Hertz, | ||
| 197 | pub pll48_freq: Hertz, | ||
| 198 | } | ||
| 199 | |||
| 28 | /// AHB prescaler | 200 | /// AHB prescaler |
| 29 | #[derive(Clone, Copy, PartialEq)] | 201 | #[derive(Clone, Copy, PartialEq)] |
| 30 | pub enum AHBPrescaler { | 202 | pub enum AHBPrescaler { |
| @@ -206,6 +378,10 @@ impl VoltageRange { | |||
| 206 | 378 | ||
| 207 | /// Clocks configuration | 379 | /// Clocks configuration |
| 208 | pub struct Config { | 380 | pub struct Config { |
| 381 | pub hse: Option<HSEConfig>, | ||
| 382 | pub hsi: bool, | ||
| 383 | pub pll_mux: PLLSrc, | ||
| 384 | pub pll: PLLConfig, | ||
| 209 | pub mux: ClockSrc, | 385 | pub mux: ClockSrc, |
| 210 | pub voltage: VoltageRange, | 386 | pub voltage: VoltageRange, |
| 211 | pub ahb_pre: AHBPrescaler, | 387 | pub ahb_pre: AHBPrescaler, |
| @@ -217,6 +393,10 @@ impl Default for Config { | |||
| 217 | #[inline] | 393 | #[inline] |
| 218 | fn default() -> Config { | 394 | fn default() -> Config { |
| 219 | Config { | 395 | Config { |
| 396 | hse: None, | ||
| 397 | hsi: true, | ||
| 398 | pll_mux: PLLSrc::HSI, | ||
| 399 | pll: PLLConfig::default(), | ||
| 220 | voltage: VoltageRange::Min1V8, | 400 | voltage: VoltageRange::Min1V8, |
| 221 | mux: ClockSrc::HSI, | 401 | mux: ClockSrc::HSI, |
| 222 | ahb_pre: AHBPrescaler::NotDivided, | 402 | ahb_pre: AHBPrescaler::NotDivided, |
| @@ -226,30 +406,65 @@ impl Default for Config { | |||
| 226 | } | 406 | } |
| 227 | } | 407 | } |
| 228 | 408 | ||
| 229 | #[inline] | 409 | pub(crate) unsafe fn init(config: Config) { |
| 230 | unsafe fn enable_hse(source: HSESrc) { | 410 | // Make sure HSI is enabled |
| 231 | RCC.cr().write(|w| { | 411 | RCC.cr().write(|w| w.set_hsion(true)); |
| 232 | w.set_hsebyp(match source { | 412 | while !RCC.cr().read().hsirdy() {} |
| 233 | HSESrc::Bypass => true, | 413 | |
| 234 | HSESrc::Crystal => false, | 414 | if let Some(hse_config) = config.hse { |
| 415 | RCC.cr().modify(|w| { | ||
| 416 | w.set_hsebyp(match hse_config.source { | ||
| 417 | HSESrc::Bypass => true, | ||
| 418 | HSESrc::Crystal => false, | ||
| 419 | }); | ||
| 420 | w.set_hseon(true) | ||
| 235 | }); | 421 | }); |
| 236 | w.set_hseon(true) | 422 | while !RCC.cr().read().hserdy() {} |
| 423 | } | ||
| 424 | |||
| 425 | let pll_src_freq = match config.pll_mux { | ||
| 426 | PLLSrc::HSE => { | ||
| 427 | let hse_config = config | ||
| 428 | .hse | ||
| 429 | .unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input")); | ||
| 430 | hse_config.frequency | ||
| 431 | } | ||
| 432 | PLLSrc::HSI => HSI, | ||
| 433 | }; | ||
| 434 | |||
| 435 | // Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics | ||
| 436 | let pll_clocks = config.pll.clocks(pll_src_freq); | ||
| 437 | assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000)); | ||
| 438 | assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000)); | ||
| 439 | assert!( | ||
| 440 | Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000) | ||
| 441 | ); | ||
| 442 | // USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz | ||
| 443 | assert!(pll_clocks.pll48_freq <= Hertz(48_000_000)); | ||
| 444 | |||
| 445 | RCC.pllcfgr().write(|w| { | ||
| 446 | w.set_pllsrc(config.pll_mux.into()); | ||
| 447 | w.set_pllm(config.pll.pre_div.0); | ||
| 448 | w.set_plln(config.pll.mul.0); | ||
| 449 | w.set_pllp(config.pll.main_div.into()); | ||
| 450 | w.set_pllq(config.pll.pll48_div.0); | ||
| 237 | }); | 451 | }); |
| 238 | while !RCC.cr().read().hserdy() {} | ||
| 239 | } | ||
| 240 | 452 | ||
| 241 | pub(crate) unsafe fn init(config: Config) { | ||
| 242 | let (sys_clk, sw) = match config.mux { | 453 | let (sys_clk, sw) = match config.mux { |
| 243 | ClockSrc::HSI => { | 454 | ClockSrc::HSI => { |
| 244 | // Enable HSI | 455 | assert!(config.hsi, "HSI must be enabled to be used as system clock"); |
| 245 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 246 | while !RCC.cr().read().hsirdy() {} | ||
| 247 | |||
| 248 | (HSI, Sw::HSI) | 456 | (HSI, Sw::HSI) |
| 249 | } | 457 | } |
| 250 | ClockSrc::HSE(freq, source) => { | 458 | ClockSrc::HSE => { |
| 251 | enable_hse(source); | 459 | let hse_config = config |
| 252 | (freq, Sw::HSE) | 460 | .hse |
| 461 | .unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input")); | ||
| 462 | (hse_config.frequency, Sw::HSE) | ||
| 463 | } | ||
| 464 | ClockSrc::PLL => { | ||
| 465 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 466 | while !RCC.cr().read().pllrdy() {} | ||
| 467 | (pll_clocks.main_freq, Sw::PLL) | ||
| 253 | } | 468 | } |
| 254 | }; | 469 | }; |
| 255 | // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL | 470 | // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL |
| @@ -260,7 +475,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 260 | // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions | 475 | // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions |
| 261 | assert!(ahb_freq <= Hertz(120_000_000)); | 476 | assert!(ahb_freq <= Hertz(120_000_000)); |
| 262 | 477 | ||
| 263 | let flash_ws = config.voltage.wait_states(ahb_freq).expect("Invalid HCLK"); | 478 | let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq)); |
| 264 | FLASH.acr().modify(|w| w.set_latency(flash_ws)); | 479 | FLASH.acr().modify(|w| w.set_latency(flash_ws)); |
| 265 | 480 | ||
| 266 | RCC.cfgr().modify(|w| { | 481 | RCC.cfgr().modify(|w| { |
| @@ -269,6 +484,12 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 269 | w.set_ppre1(config.apb1_pre.into()); | 484 | w.set_ppre1(config.apb1_pre.into()); |
| 270 | w.set_ppre2(config.apb2_pre.into()); | 485 | w.set_ppre2(config.apb2_pre.into()); |
| 271 | }); | 486 | }); |
| 487 | while RCC.cfgr().read().sws() != sw.0 {} | ||
| 488 | |||
| 489 | // Turn off HSI to save power if we don't need it | ||
| 490 | if !config.hsi { | ||
| 491 | RCC.cr().modify(|w| w.set_hsion(false)); | ||
| 492 | } | ||
| 272 | 493 | ||
| 273 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { | 494 | let (apb1_freq, apb1_tim_freq) = match config.apb1_pre { |
| 274 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), | 495 | APBPrescaler::NotDivided => (ahb_freq, ahb_freq), |
| @@ -299,5 +520,6 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 299 | apb1_tim: apb1_tim_freq, | 520 | apb1_tim: apb1_tim_freq, |
| 300 | apb2: apb2_freq, | 521 | apb2: apb2_freq, |
| 301 | apb2_tim: apb2_tim_freq, | 522 | apb2_tim: apb2_tim_freq, |
| 523 | pll48: Some(pll_clocks.pll48_freq), | ||
| 302 | }); | 524 | }); |
| 303 | } | 525 | } |
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 9a95836a6..d3710b8c3 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs | |||
| @@ -53,7 +53,7 @@ pub struct Clocks { | |||
| 53 | #[cfg(any(rcc_h7, rcc_h7ab))] | 53 | #[cfg(any(rcc_h7, rcc_h7ab))] |
| 54 | pub ahb4: Hertz, | 54 | pub ahb4: Hertz, |
| 55 | 55 | ||
| 56 | #[cfg(any(rcc_f4, rcc_f410, rcc_f7))] | 56 | #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))] |
| 57 | pub pll48: Option<Hertz>, | 57 | pub pll48: Option<Hertz>, |
| 58 | 58 | ||
| 59 | #[cfg(rcc_f1)] | 59 | #[cfg(rcc_f1)] |
diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs new file mode 100644 index 000000000..4bd74f0bd --- /dev/null +++ b/examples/stm32f2/src/bin/pll.rs | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::convert::TryFrom; | ||
| 6 | use defmt::*; | ||
| 7 | use embassy::executor::Spawner; | ||
| 8 | use embassy::time::{Duration, Timer}; | ||
| 9 | use embassy_stm32::{ | ||
| 10 | rcc::{ | ||
| 11 | APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLL48Div, PLLConfig, PLLMainDiv, PLLMul, | ||
| 12 | PLLPreDiv, PLLSrc, | ||
| 13 | }, | ||
| 14 | time::Hertz, | ||
| 15 | Config, Peripherals, | ||
| 16 | }; | ||
| 17 | |||
| 18 | use defmt_rtt as _; // global logger | ||
| 19 | use panic_probe as _; | ||
| 20 | |||
| 21 | // Example config for maximum performance on a NUCLEO-F207ZG board | ||
| 22 | fn config() -> Config { | ||
| 23 | let mut config = Config::default(); | ||
| 24 | // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal) | ||
| 25 | config.rcc.hse = Some(HSEConfig { | ||
| 26 | frequency: Hertz(8_000_000), | ||
| 27 | source: HSESrc::Bypass, | ||
| 28 | }); | ||
| 29 | // PLL uses HSE as the clock source | ||
| 30 | config.rcc.pll_mux = PLLSrc::HSE; | ||
| 31 | config.rcc.pll = PLLConfig { | ||
| 32 | // 8 MHz clock source / 8 = 1 MHz PLL input | ||
| 33 | pre_div: unwrap!(PLLPreDiv::try_from(8)), | ||
| 34 | // 1 MHz PLL input * 240 = 240 MHz PLL VCO | ||
| 35 | mul: unwrap!(PLLMul::try_from(240)), | ||
| 36 | // 240 MHz PLL VCO / 2 = 120 MHz main PLL output | ||
| 37 | main_div: PLLMainDiv::Div2, | ||
| 38 | // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output | ||
| 39 | pll48_div: unwrap!(PLL48Div::try_from(5)), | ||
| 40 | }; | ||
| 41 | // System clock comes from PLL (= the 120 MHz main PLL output) | ||
| 42 | config.rcc.mux = ClockSrc::PLL; | ||
| 43 | // 120 MHz / 4 = 30 MHz APB1 frequency | ||
| 44 | config.rcc.apb1_pre = APBPrescaler::Div4; | ||
| 45 | // 120 MHz / 2 = 60 MHz APB2 frequency | ||
| 46 | config.rcc.apb2_pre = APBPrescaler::Div2; | ||
| 47 | config | ||
| 48 | } | ||
| 49 | |||
| 50 | #[embassy::main(config = "config()")] | ||
| 51 | async fn main(_spawner: Spawner, _p: Peripherals) { | ||
| 52 | loop { | ||
| 53 | Timer::after(Duration::from_millis(1000)).await; | ||
| 54 | info!("1s elapsed"); | ||
| 55 | } | ||
| 56 | } | ||
