From 9586365b07b2ff366917e7c0b07b3a32d52637ec Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 20 Apr 2021 16:33:47 +0200 Subject: [PATCH] Pass config directly to chip specific configure function This removes the need to duplicate the configuration for each individual chip, but will instead pass on the configuration specified in the config attribute. Update nrf, stm32, rp macros with passing the config to a per-chip configure function which assumes the appropriate configuration to be passed to it. To demonstrate this feature, the stm32l0xx clock setup and RTC is added which exposes clock configuration different from stm32f4xx (and has a different set of timers and HAL APIs). --- embassy-macros/src/chip/nrf.rs | 55 +-- embassy-macros/src/chip/rp.rs | 14 +- embassy-macros/src/chip/stm32.rs | 54 +-- embassy-macros/src/lib.rs | 38 +- embassy-stm32-examples/src/bin/rtc_async.rs | 3 +- embassy-stm32-examples/src/bin/serial.rs | 2 +- embassy-stm32-examples/src/bin/usb_serial.rs | 4 +- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/f4/mod.rs | 2 + embassy-stm32/src/{ => f4}/rtc.rs | 0 embassy-stm32/src/f4/system.rs | 61 +++ embassy-stm32/src/l0/mod.rs | 2 + embassy-stm32/src/l0/rtc.rs | 371 +++++++++++++++++++ embassy-stm32/src/l0/system.rs | 17 + embassy-stm32/src/lib.rs | 64 +++- 15 files changed, 546 insertions(+), 142 deletions(-) rename embassy-stm32/src/{ => f4}/rtc.rs (100%) create mode 100644 embassy-stm32/src/f4/system.rs create mode 100644 embassy-stm32/src/l0/mod.rs create mode 100644 embassy-stm32/src/l0/rtc.rs create mode 100644 embassy-stm32/src/l0/system.rs 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, +}