diff --git a/embassy-macros/src/chip/nrf.rs b/embassy-macros/src/chip/nrf.rs index b7ea8d38..aba4c004 100644 --- a/embassy-macros/src/chip/nrf.rs +++ b/embassy-macros/src/chip/nrf.rs @@ -1,60 +1,15 @@ use crate::path::ModulePrefix; -use darling::FromMeta; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use syn::spanned::Spanned; +use quote::quote; -#[derive(Debug, FromMeta)] -pub enum HfclkSource { - Internal, - ExternalXtal, -} - -impl Default for HfclkSource { - fn default() -> Self { - Self::Internal - } -} - -#[derive(Debug, FromMeta)] -pub enum LfclkSource { - InternalRC, - Synthesized, - ExternalXtal, - ExternalLowSwing, - ExternalFullSwing, -} - -impl Default for LfclkSource { - fn default() -> Self { - Self::InternalRC - } -} - -#[derive(Debug, FromMeta, Default)] -pub struct Args { - #[darling(default)] - pub embassy_prefix: ModulePrefix, - #[darling(default)] - pub hfclk_source: HfclkSource, - #[darling(default)] - pub lfclk_source: LfclkSource, -} - -pub fn generate(args: &Args) -> TokenStream { - let hfclk_source = format_ident!("{}", format!("{:?}", args.hfclk_source)); - let lfclk_source = format_ident!("{}", format!("{:?}", args.lfclk_source)); - - let embassy_path = args.embassy_prefix.append("embassy").path(); - let embassy_nrf_path = args.embassy_prefix.append("embassy_nrf").path(); +pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { + let embassy_path = embassy_prefix.append("embassy").path(); + let embassy_nrf_path = embassy_prefix.append("embassy_nrf").path(); quote!( use #embassy_nrf_path::{interrupt, peripherals, rtc}; - let mut config = #embassy_nrf_path::system::Config::default(); - config.hfclk_source = #embassy_nrf_path::system::HfclkSource::#hfclk_source; - config.lfclk_source = #embassy_nrf_path::system::LfclkSource::#lfclk_source; - unsafe { #embassy_nrf_path::system::configure(config) }; + unsafe { #embassy_nrf_path::system::configure(#config) }; let mut rtc = rtc::RTC::new(unsafe { ::steal() }, interrupt::take!(RTC1)); let rtc = unsafe { make_static(&mut rtc) }; diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs index 6093dc9d..33863de8 100644 --- a/embassy-macros/src/chip/rp.rs +++ b/embassy-macros/src/chip/rp.rs @@ -1,20 +1,12 @@ use crate::path::ModulePrefix; -use darling::FromMeta; use proc_macro2::TokenStream; use quote::quote; -#[derive(Debug, FromMeta, Default)] -pub struct Args { - #[darling(default)] - pub embassy_prefix: ModulePrefix, -} - -pub fn generate(args: &Args) -> TokenStream { - let embassy_rp_path = args.embassy_prefix.append("embassy_rp").path(); +pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { + let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); quote!( use #embassy_rp_path::{interrupt, peripherals}; - let mut config = #embassy_rp_path::system::Config::default(); - unsafe { #embassy_rp_path::system::configure(config) }; + unsafe { #embassy_rp_path::system::configure(#config) }; ) } diff --git a/embassy-macros/src/chip/stm32.rs b/embassy-macros/src/chip/stm32.rs index 486a9aa6..3f299650 100644 --- a/embassy-macros/src/chip/stm32.rs +++ b/embassy-macros/src/chip/stm32.rs @@ -1,57 +1,19 @@ use crate::path::ModulePrefix; -use darling::FromMeta; use proc_macro2::TokenStream; -use quote::{format_ident, quote}; -use syn::spanned::Spanned; +use quote::quote; -#[derive(Debug, FromMeta, Default)] -pub struct Args { - #[darling(default)] - pub embassy_prefix: ModulePrefix, - #[darling(default)] - pub use_hse: Option, - #[darling(default)] - pub sysclk: Option, - #[darling(default)] - pub pclk1: Option, - #[darling(default)] - pub require_pll48clk: bool, -} - -pub fn generate(args: &Args) -> TokenStream { - let embassy_path = args.embassy_prefix.append("embassy").path(); - let embassy_stm32_path = args.embassy_prefix.append("embassy_stm32").path(); - - let mut clock_cfg_args = quote! {}; - if args.use_hse.is_some() { - let mhz = args.use_hse.unwrap(); - clock_cfg_args = quote! { #clock_cfg_args.use_hse(#mhz.mhz()) }; - } - - if args.sysclk.is_some() { - let mhz = args.sysclk.unwrap(); - clock_cfg_args = quote! { #clock_cfg_args.sysclk(#mhz.mhz()) }; - } - - if args.pclk1.is_some() { - let mhz = args.pclk1.unwrap(); - clock_cfg_args = quote! { #clock_cfg_args.pclk1(#mhz.mhz()) }; - } - - if args.require_pll48clk { - clock_cfg_args = quote! { #clock_cfg_args.require_pll48clk() }; - } +pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { + let embassy_path = embassy_prefix.append("embassy").path(); + let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path(); quote!( use #embassy_stm32_path::{rtc, interrupt, Peripherals, pac, hal::rcc::RccExt, hal::time::U32Ext}; - let dp = pac::Peripherals::take().unwrap(); - let rcc = dp.RCC.constrain(); - let clocks = rcc.cfgr#clock_cfg_args.freeze(); + unsafe { #embassy_stm32_path::system::configure(#config) }; - unsafe { Peripherals::set_peripherals(clocks) }; + let (dp, clocks) = Peripherals::take().unwrap(); - let mut rtc = rtc::RTC::new(dp.TIM3, interrupt::take!(TIM3), clocks); + let mut rtc = rtc::RTC::new(dp.TIM2, interrupt::take!(TIM2), clocks); let rtc = unsafe { make_static(&mut rtc) }; rtc.start(); let mut alarm = rtc.alarm1(); @@ -60,5 +22,7 @@ pub fn generate(args: &Args) -> TokenStream { let alarm = unsafe { make_static(&mut alarm) }; executor.set_alarm(alarm); + + unsafe { Peripherals::set_peripherals(clocks) }; ) } diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index cca6c8a5..64411f5b 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -212,13 +212,13 @@ mod chip; #[path = "chip/rp.rs"] mod chip; -#[cfg(feature = "std")] -mod chip { - #[derive(Debug, darling::FromMeta, Default)] - pub struct Args { - #[darling(default)] - pub embassy_prefix: crate::path::ModulePrefix, - } +#[derive(Debug, FromMeta)] +struct MainArgs { + #[darling(default)] + embassy_prefix: ModulePrefix, + + #[darling(default)] + config: Option, } #[cfg(any(feature = "nrf", feature = "stm32", feature = "rp"))] @@ -227,7 +227,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); let task_fn = syn::parse_macro_input!(item as syn::ItemFn); - let macro_args = match chip::Args::from_list(¯o_args) { + let macro_args = match MainArgs::from_list(¯o_args) { Ok(v) => v, Err(e) => { return TokenStream::from(e.write_errors()); @@ -270,10 +270,21 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { return TokenStream::new(); } - let embassy_prefix_lit = macro_args.embassy_prefix.literal(); - let embassy_path = macro_args.embassy_prefix.append("embassy").path(); - let task_fn_body = task_fn.block.clone(); - let chip_setup = chip::generate(¯o_args); + let embassy_prefix = macro_args.embassy_prefix; + let embassy_prefix_lit = embassy_prefix.literal(); + let embassy_path = embassy_prefix.append("embassy").path(); + let task_fn_body = task_fn.block; + + let config = macro_args + .config + .map(|s| s.parse::().unwrap()) + .unwrap_or_else(|| { + syn::Expr::Verbatim(quote! { + Default::default() + }) + }); + + let chip_setup = chip::generate(&embassy_prefix, config); let result = quote! { #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] @@ -288,6 +299,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { } let mut executor = #embassy_path::executor::Executor::new(); + let executor = unsafe { make_static(&mut executor) }; #chip_setup @@ -307,7 +319,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); let task_fn = syn::parse_macro_input!(item as syn::ItemFn); - let macro_args = match chip::Args::from_list(¯o_args) { + let macro_args = match MainArgs::from_list(¯o_args) { Ok(v) => v, Err(e) => { return TokenStream::from(e.write_errors()); diff --git a/embassy-stm32-examples/src/bin/rtc_async.rs b/embassy-stm32-examples/src/bin/rtc_async.rs index ea54a2a9..b780c3c1 100644 --- a/embassy-stm32-examples/src/bin/rtc_async.rs +++ b/embassy-stm32-examples/src/bin/rtc_async.rs @@ -10,6 +10,7 @@ use example_common::*; use defmt::panic; use embassy; + use embassy::executor::Spawner; use embassy::time::{Duration, Timer}; use embassy_stm32; @@ -31,7 +32,7 @@ async fn run2() { } } -#[embassy::main(use_hse = 16)] +#[embassy::main(config = "embassy_stm32::system::Config::new().use_hse(16)")] async fn main(spawner: Spawner) { let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap(); diff --git a/embassy-stm32-examples/src/bin/serial.rs b/embassy-stm32-examples/src/bin/serial.rs index 9aeca537..c48ba746 100644 --- a/embassy-stm32-examples/src/bin/serial.rs +++ b/embassy-stm32-examples/src/bin/serial.rs @@ -22,7 +22,7 @@ use embassy_stm32::pac as stm32; use embassy_stm32::serial; use futures::pin_mut; -#[embassy::main(use_hse = 16, sysclk = 48, pclk1 = 24)] +#[embassy::main(config = "embassy_stm32::system::Config::new().use_hse(16).sysclk(48).pclk1(24)")] async fn main(spawner: Spawner) { let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap(); let cp = cortex_m::peripheral::Peripherals::take().unwrap(); diff --git a/embassy-stm32-examples/src/bin/usb_serial.rs b/embassy-stm32-examples/src/bin/usb_serial.rs index 6a1d27d5..e13275dd 100644 --- a/embassy-stm32-examples/src/bin/usb_serial.rs +++ b/embassy-stm32-examples/src/bin/usb_serial.rs @@ -92,7 +92,9 @@ async fn run1(bus: &'static mut UsbBusAllocator>) { static USB_BUS: Forever>> = Forever::new(); -#[embassy::main(use_hse = 25, sysclk = 48, require_pll48clk)] +#[embassy::main( + config = "embassy_stm32::system::Config::new().use_hse(25).sysclk(48).require_pll48clk()" +)] async fn main(spawner: Spawner) -> ! { static mut EP_MEMORY: [u32; 1024] = [0; 1024]; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 52335941..af30f1aa 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -38,6 +38,7 @@ embassy = { version = "0.1.0", path = "../embassy" } embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"]} embassy-extras = {version = "0.1.0", path = "../embassy-extras" } +atomic-polyfill = "0.1.1" defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } cortex-m-rt = "0.6.13" diff --git a/embassy-stm32/src/f4/mod.rs b/embassy-stm32/src/f4/mod.rs index 9edde82c..9549b3ed 100644 --- a/embassy-stm32/src/f4/mod.rs +++ b/embassy-stm32/src/f4/mod.rs @@ -1,2 +1,4 @@ +pub mod rtc; pub mod serial; pub mod spi; +pub mod system; diff --git a/embassy-stm32/src/rtc.rs b/embassy-stm32/src/f4/rtc.rs similarity index 100% rename from embassy-stm32/src/rtc.rs rename to embassy-stm32/src/f4/rtc.rs diff --git a/embassy-stm32/src/f4/system.rs b/embassy-stm32/src/f4/system.rs new file mode 100644 index 00000000..fb739272 --- /dev/null +++ b/embassy-stm32/src/f4/system.rs @@ -0,0 +1,61 @@ +use crate::{hal::prelude::*, pac, Peripherals}; + +#[derive(Default)] +pub struct Config { + pub use_hse: Option, + pub sysclk: Option, + pub pclk1: Option, + pub require_pll48clk: bool, +} + +impl Config { + pub fn new() -> Self { + Default::default() + } + + pub fn use_hse(mut self, freq: u32) -> Self { + self.use_hse = Some(freq); + self + } + + pub fn sysclk(mut self, freq: u32) -> Self { + self.sysclk = Some(freq); + self + } + + pub fn pclk1(mut self, freq: u32) -> Self { + self.pclk1 = Some(freq); + self + } + + pub fn require_pll48clk(mut self) -> Self { + self.require_pll48clk = true; + self + } +} + +/// safety: must only call once. +pub unsafe fn configure(config: Config) { + let dp = pac::Peripherals::take().unwrap(); + let mut cfgr = dp.RCC.constrain().cfgr; + + if let Some(hz) = config.use_hse { + cfgr = cfgr.use_hse(hz.mhz()); + }; + + if let Some(hz) = config.sysclk { + cfgr = cfgr.sysclk(hz.mhz()); + }; + + if let Some(hz) = config.pclk1 { + cfgr = cfgr.pclk1(hz.mhz()); + }; + + if config.require_pll48clk { + cfgr = cfgr.require_pll48clk(); + }; + + let clocks = cfgr.freeze(); + + unsafe { Peripherals::set_peripherals(clocks) }; +} diff --git a/embassy-stm32/src/l0/mod.rs b/embassy-stm32/src/l0/mod.rs new file mode 100644 index 00000000..53b44fe2 --- /dev/null +++ b/embassy-stm32/src/l0/mod.rs @@ -0,0 +1,2 @@ +pub mod rtc; +pub mod system; diff --git a/embassy-stm32/src/l0/rtc.rs b/embassy-stm32/src/l0/rtc.rs new file mode 100644 index 00000000..9a134204 --- /dev/null +++ b/embassy-stm32/src/l0/rtc.rs @@ -0,0 +1,371 @@ +use crate::hal::rcc::Clocks; +use atomic_polyfill::{compiler_fence, AtomicU32, Ordering}; +use core::cell::Cell; +use core::convert::TryInto; + +use embassy::interrupt::InterruptExt; +use embassy::time::{Clock, TICKS_PER_SECOND}; + +use crate::interrupt; +use crate::interrupt::{CriticalSection, Interrupt, Mutex}; + +// RTC timekeeping works with something we call "periods", which are time intervals +// of 2^15 ticks. The RTC counter value is 16 bits, so one "overflow cycle" is 2 periods. +// +// A `period` count is maintained in parallel to the RTC hardware `counter`, like this: +// - `period` and `counter` start at 0 +// - `period` is incremented on overflow (at counter value 0) +// - `period` is incremented "midway" between overflows (at counter value 0x8000) +// +// Therefore, when `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF +// This allows for now() to return the correct value even if it races an overflow. +// +// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches +// the expected range for the `period` parity, we're done. If it doesn't, this means that +// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value +// corresponds to the next period. +// +// `period` is a 32bit integer, so It overflows on 2^32 * 2^15 / 32768 seconds of uptime, which is 136 years. +fn calc_now(period: u32, counter: u16) -> u64 { + ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) +} + +struct AlarmState { + timestamp: Cell, + callback: Cell>, +} + +impl AlarmState { + fn new() -> Self { + Self { + timestamp: Cell::new(u64::MAX), + callback: Cell::new(None), + } + } +} + +// TODO: This is sometimes wasteful, try to find a better way +const ALARM_COUNT: usize = 3; + +/// RTC timer that can be used by the executor and to set alarms. +/// +/// It can work with Timers 2 and 3. + +/// This timer works internally with a unit of 2^15 ticks, which means that if a call to +/// [`embassy::time::Clock::now`] is blocked for that amount of ticks the returned value will be +/// wrong (an old value). The current default tick rate is 32768 ticks per second. +pub struct RTC { + rtc: T, + irq: T::Interrupt, + + /// Number of 2^23 periods elapsed since boot. + period: AtomicU32, + + /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. + alarms: Mutex<[AlarmState; ALARM_COUNT]>, + + clocks: Clocks, +} + +impl RTC { + pub fn new(rtc: T, irq: T::Interrupt, clocks: Clocks) -> Self { + Self { + rtc, + irq, + period: AtomicU32::new(0), + alarms: Mutex::new([AlarmState::new(), AlarmState::new(), AlarmState::new()]), + clocks, + } + } + + pub fn start(&'static self) { + self.rtc.enable_clock(); + self.rtc.stop_and_reset(); + + let freq = T::pclk(&self.clocks); + let psc = freq / TICKS_PER_SECOND as u32 - 1; + let psc: u16 = psc.try_into().unwrap(); + + self.rtc.set_psc_arr(psc, u16::MAX); + // Mid-way point + self.rtc.set_compare(0, 0x8000); + self.rtc.set_compare_interrupt(0, true); + + self.irq.set_handler(|ptr| unsafe { + let this = &*(ptr as *const () as *const Self); + this.on_interrupt(); + }); + self.irq.set_handler_context(self as *const _ as *mut _); + self.irq.unpend(); + self.irq.enable(); + + self.rtc.start(); + } + + fn on_interrupt(&self) { + if self.rtc.overflow_interrupt_status() { + self.rtc.overflow_clear_flag(); + self.next_period(); + } + + // Half overflow + if self.rtc.compare_interrupt_status(0) { + self.rtc.compare_clear_flag(0); + self.next_period(); + } + + for n in 1..=ALARM_COUNT { + if self.rtc.compare_interrupt_status(n) { + self.rtc.compare_clear_flag(n); + interrupt::free(|cs| self.trigger_alarm(n, cs)); + } + } + } + + fn next_period(&self) { + interrupt::free(|cs| { + let period = self.period.fetch_add(1, Ordering::Relaxed) + 1; + let t = (period as u64) << 15; + + for n in 1..=ALARM_COUNT { + let alarm = &self.alarms.borrow(cs)[n - 1]; + let at = alarm.timestamp.get(); + + let diff = at - t; + if diff < 0xc000 { + self.rtc.set_compare(n, at as u16); + self.rtc.set_compare_interrupt(n, true); + } + } + }) + } + + fn trigger_alarm(&self, n: usize, cs: &CriticalSection) { + self.rtc.set_compare_interrupt(n, false); + + let alarm = &self.alarms.borrow(cs)[n - 1]; + alarm.timestamp.set(u64::MAX); + + // Call after clearing alarm, so the callback can set another alarm. + if let Some((f, ctx)) = alarm.callback.get() { + f(ctx); + } + } + + fn set_alarm_callback(&self, n: usize, callback: fn(*mut ()), ctx: *mut ()) { + interrupt::free(|cs| { + let alarm = &self.alarms.borrow(cs)[n - 1]; + alarm.callback.set(Some((callback, ctx))); + }) + } + + fn set_alarm(&self, n: usize, timestamp: u64) { + interrupt::free(|cs| { + let alarm = &self.alarms.borrow(cs)[n - 1]; + alarm.timestamp.set(timestamp); + + let t = self.now(); + if timestamp <= t { + self.trigger_alarm(n, cs); + return; + } + + let diff = timestamp - t; + if diff < 0xc000 { + let safe_timestamp = timestamp.max(t + 3); + self.rtc.set_compare(n, safe_timestamp as u16); + self.rtc.set_compare_interrupt(n, true); + } else { + self.rtc.set_compare_interrupt(n, false); + } + }); + } + + pub fn alarm1(&'static self) -> Alarm { + Alarm { n: 1, rtc: self } + } + pub fn alarm2(&'static self) -> Option> { + if T::REAL_ALARM_COUNT >= 2 { + Some(Alarm { n: 2, rtc: self }) + } else { + None + } + } + pub fn alarm3(&'static self) -> Option> { + if T::REAL_ALARM_COUNT >= 3 { + Some(Alarm { n: 3, rtc: self }) + } else { + None + } + } +} + +impl embassy::time::Clock for RTC { + fn now(&self) -> u64 { + let period = self.period.load(Ordering::Relaxed); + compiler_fence(Ordering::Acquire); + let counter = self.rtc.counter(); + calc_now(period, counter) + } +} + +pub struct Alarm { + n: usize, + rtc: &'static RTC, +} + +impl embassy::time::Alarm for Alarm { + fn set_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { + self.rtc.set_alarm_callback(self.n, callback, ctx); + } + + fn set(&self, timestamp: u64) { + self.rtc.set_alarm(self.n, timestamp); + } + + fn clear(&self) { + self.rtc.set_alarm(self.n, u64::MAX); + } +} + +mod sealed { + pub trait Sealed {} +} + +pub trait Instance: sealed::Sealed + Sized + 'static { + type Interrupt: Interrupt; + const REAL_ALARM_COUNT: usize; + + fn enable_clock(&self); + fn set_compare(&self, n: usize, value: u16); + fn set_compare_interrupt(&self, n: usize, enable: bool); + fn compare_interrupt_status(&self, n: usize) -> bool; + fn compare_clear_flag(&self, n: usize); + fn overflow_interrupt_status(&self) -> bool; + fn overflow_clear_flag(&self); + // This method should ensure that the values are really updated before returning + fn set_psc_arr(&self, psc: u16, arr: u16); + fn stop_and_reset(&self); + fn start(&self); + fn counter(&self) -> u16; + fn pclk(clocks: &Clocks) -> u32; +} + +#[allow(unused_macros)] +macro_rules! impl_timer { + ($module:ident: ($TYPE:ident, $INT:ident, $timXen:ident, $timXrst:ident, $apbenr:ident, $apbrstr:ident, $pclk: ident)) => { + mod $module { + use super::*; + use crate::hal::pac::{$TYPE, RCC}; + + impl sealed::Sealed for $TYPE {} + + impl Instance for $TYPE { + type Interrupt = interrupt::$INT; + const REAL_ALARM_COUNT: usize = 3; + + fn enable_clock(&self) { + // NOTE(unsafe) It will only be used for atomic operations + unsafe { + let rcc = &*RCC::ptr(); + + rcc.$apbenr.modify(|_, w| w.$timXen().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().set_bit()); + rcc.$apbrstr.modify(|_, w| w.$timXrst().clear_bit()); + } + } + + fn set_compare(&self, n: usize, value: u16) { + // NOTE(unsafe) these registers accept all the range of u16 values + match n { + 0 => self.ccr1.write(|w| unsafe { w.bits(value.into()) }), + 1 => self.ccr2.write(|w| unsafe { w.bits(value.into()) }), + 2 => self.ccr3.write(|w| unsafe { w.bits(value.into()) }), + 3 => self.ccr4.write(|w| unsafe { w.bits(value.into()) }), + _ => {} + } + } + + fn set_compare_interrupt(&self, n: usize, enable: bool) { + if n > 3 { + return; + } + let bit = n as u8 + 1; + unsafe { + if enable { + self.dier.modify(|r, w| w.bits(r.bits() | (1 << bit))); + } else { + self.dier.modify(|r, w| w.bits(r.bits() & !(1 << bit))); + } + } + } + + fn compare_interrupt_status(&self, n: usize) -> bool { + let status = self.sr.read(); + match n { + 0 => status.cc1if().bit_is_set(), + 1 => status.cc2if().bit_is_set(), + 2 => status.cc3if().bit_is_set(), + 3 => status.cc4if().bit_is_set(), + _ => false, + } + } + + fn compare_clear_flag(&self, n: usize) { + if n > 3 { + return; + } + let bit = n as u8 + 1; + unsafe { + self.sr.modify(|r, w| w.bits(r.bits() & !(1 << bit))); + } + } + + fn overflow_interrupt_status(&self) -> bool { + self.sr.read().uif().bit_is_set() + } + + fn overflow_clear_flag(&self) { + unsafe { + self.sr.modify(|_, w| w.uif().clear_bit()); + } + } + + fn set_psc_arr(&self, psc: u16, arr: u16) { + // NOTE(unsafe) All u16 values are valid + self.psc.write(|w| unsafe { w.bits(psc.into()) }); + self.arr.write(|w| unsafe { w.bits(arr.into()) }); + + unsafe { + // Set URS, generate update, clear URS + self.cr1.modify(|_, w| w.urs().set_bit()); + self.egr.write(|w| w.ug().set_bit()); + self.cr1.modify(|_, w| w.urs().clear_bit()); + } + } + + fn stop_and_reset(&self) { + unsafe { + self.cr1.modify(|_, w| w.cen().clear_bit()); + } + self.cnt.reset(); + } + + fn start(&self) { + self.cr1.modify(|_, w| w.cen().set_bit()); + } + + fn counter(&self) -> u16 { + self.cnt.read().bits() as u16 + } + + fn pclk(clocks: &Clocks) -> u32 { + clocks.$pclk().0 + } + } + } + }; +} + +impl_timer!(tim2: (TIM2, TIM2, tim2en, tim2rst, apb1enr, apb1rstr, apb1_tim_clk)); +impl_timer!(tim3: (TIM3, TIM3, tim3en, tim3rst, apb1enr, apb1rstr, apb1_tim_clk)); diff --git a/embassy-stm32/src/l0/system.rs b/embassy-stm32/src/l0/system.rs new file mode 100644 index 00000000..00e417d4 --- /dev/null +++ b/embassy-stm32/src/l0/system.rs @@ -0,0 +1,17 @@ +use crate::{hal, pac, Peripherals}; + +pub use hal::{ + prelude::*, + rcc::{Clocks, Config}, +}; + +/// safety: must only call once. +pub unsafe fn configure(config: Config) { + let dp = pac::Peripherals::take().unwrap(); + + let rcc = dp.RCC.freeze(config); + + let clocks = rcc.clocks; + + unsafe { Peripherals::set_peripherals(clocks) }; +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 56efa461..98507c62 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -49,6 +49,12 @@ pub use {stm32f4xx_hal as hal, stm32f4xx_hal::stm32 as pac}; #[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] pub use {stm32l0xx_hal as hal, stm32l0xx_hal::pac}; +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +mod l0; + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +pub use l0::{rtc, system}; + pub mod fmt; pub mod exti; @@ -90,26 +96,7 @@ pub mod can; feature = "stm32f469", feature = "stm32f479", ))] -pub mod rtc; - -#[cfg(any( - feature = "stm32f401", - feature = "stm32f405", - feature = "stm32f407", - feature = "stm32f412", - feature = "stm32f413", - feature = "stm32f415", - feature = "stm32f417", - feature = "stm32f423", - feature = "stm32f427", - feature = "stm32f429", - feature = "stm32f437", - feature = "stm32f439", - feature = "stm32f446", - feature = "stm32f469", - feature = "stm32f479", -))] -pub use f4::{serial, spi}; +pub use f4::{rtc, serial, spi, system}; #[cfg(any( feature = "stm32f401", @@ -398,3 +385,40 @@ embassy_extras::std_peripherals! { FPU_CPACR, SCB_ACTRL, } + +#[cfg(feature = "stm32l0x2")] +embassy_extras::std_peripherals! { + SPI1, + SPI2, + USART1, + USART2, + USART4, + USART5, + I2C1, + I2C2, + I2C3, + RNG, + TIM2, + TIM3, + TIM6, + TIM7, + TIM21, + TIM22, + DAC, + RTC, + PWR, + CRC, + GPIOA, + GPIOB, + GPIOC, + GPIOD, + GPIOE, + GPIOH, + SYSCFG, + DMA1, + EXTI, + ADC, + IWDG, + WWDG, + DBG, +}