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).
This commit is contained in:
parent
0d02e64f62
commit
9586365b07
@ -1,60 +1,15 @@
|
|||||||
use crate::path::ModulePrefix;
|
use crate::path::ModulePrefix;
|
||||||
use darling::FromMeta;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
|
||||||
pub enum HfclkSource {
|
let embassy_path = embassy_prefix.append("embassy").path();
|
||||||
Internal,
|
let embassy_nrf_path = embassy_prefix.append("embassy_nrf").path();
|
||||||
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();
|
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
use #embassy_nrf_path::{interrupt, peripherals, rtc};
|
use #embassy_nrf_path::{interrupt, peripherals, rtc};
|
||||||
|
|
||||||
let mut config = #embassy_nrf_path::system::Config::default();
|
unsafe { #embassy_nrf_path::system::configure(#config) };
|
||||||
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) };
|
|
||||||
|
|
||||||
let mut rtc = rtc::RTC::new(unsafe { <peripherals::RTC1 as #embassy_path::util::Steal>::steal() }, interrupt::take!(RTC1));
|
let mut rtc = rtc::RTC::new(unsafe { <peripherals::RTC1 as #embassy_path::util::Steal>::steal() }, interrupt::take!(RTC1));
|
||||||
let rtc = unsafe { make_static(&mut rtc) };
|
let rtc = unsafe { make_static(&mut rtc) };
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
use crate::path::ModulePrefix;
|
use crate::path::ModulePrefix;
|
||||||
use darling::FromMeta;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
|
||||||
#[derive(Debug, FromMeta, Default)]
|
pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
|
||||||
pub struct Args {
|
let embassy_rp_path = embassy_prefix.append("embassy_rp").path();
|
||||||
#[darling(default)]
|
|
||||||
pub embassy_prefix: ModulePrefix,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate(args: &Args) -> TokenStream {
|
|
||||||
let embassy_rp_path = args.embassy_prefix.append("embassy_rp").path();
|
|
||||||
quote!(
|
quote!(
|
||||||
use #embassy_rp_path::{interrupt, peripherals};
|
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) };
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,19 @@
|
|||||||
use crate::path::ModulePrefix;
|
use crate::path::ModulePrefix;
|
||||||
use darling::FromMeta;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
|
||||||
|
|
||||||
#[derive(Debug, FromMeta, Default)]
|
pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream {
|
||||||
pub struct Args {
|
let embassy_path = embassy_prefix.append("embassy").path();
|
||||||
#[darling(default)]
|
let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path();
|
||||||
pub embassy_prefix: ModulePrefix,
|
|
||||||
#[darling(default)]
|
|
||||||
pub use_hse: Option<u32>,
|
|
||||||
#[darling(default)]
|
|
||||||
pub sysclk: Option<u32>,
|
|
||||||
#[darling(default)]
|
|
||||||
pub pclk1: Option<u32>,
|
|
||||||
#[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() };
|
|
||||||
}
|
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
use #embassy_stm32_path::{rtc, interrupt, Peripherals, pac, hal::rcc::RccExt, hal::time::U32Ext};
|
use #embassy_stm32_path::{rtc, interrupt, Peripherals, pac, hal::rcc::RccExt, hal::time::U32Ext};
|
||||||
|
|
||||||
let dp = pac::Peripherals::take().unwrap();
|
unsafe { #embassy_stm32_path::system::configure(#config) };
|
||||||
let rcc = dp.RCC.constrain();
|
|
||||||
let clocks = rcc.cfgr#clock_cfg_args.freeze();
|
|
||||||
|
|
||||||
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) };
|
let rtc = unsafe { make_static(&mut rtc) };
|
||||||
rtc.start();
|
rtc.start();
|
||||||
let mut alarm = rtc.alarm1();
|
let mut alarm = rtc.alarm1();
|
||||||
@ -60,5 +22,7 @@ pub fn generate(args: &Args) -> TokenStream {
|
|||||||
|
|
||||||
let alarm = unsafe { make_static(&mut alarm) };
|
let alarm = unsafe { make_static(&mut alarm) };
|
||||||
executor.set_alarm(alarm);
|
executor.set_alarm(alarm);
|
||||||
|
|
||||||
|
unsafe { Peripherals::set_peripherals(clocks) };
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -212,13 +212,13 @@ mod chip;
|
|||||||
#[path = "chip/rp.rs"]
|
#[path = "chip/rp.rs"]
|
||||||
mod chip;
|
mod chip;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[derive(Debug, FromMeta)]
|
||||||
mod chip {
|
struct MainArgs {
|
||||||
#[derive(Debug, darling::FromMeta, Default)]
|
#[darling(default)]
|
||||||
pub struct Args {
|
embassy_prefix: ModulePrefix,
|
||||||
#[darling(default)]
|
|
||||||
pub embassy_prefix: crate::path::ModulePrefix,
|
#[darling(default)]
|
||||||
}
|
config: Option<syn::LitStr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "nrf", feature = "stm32", feature = "rp"))]
|
#[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 macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
|
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,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return TokenStream::from(e.write_errors());
|
return TokenStream::from(e.write_errors());
|
||||||
@ -270,10 +270,21 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
return TokenStream::new();
|
return TokenStream::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let embassy_prefix_lit = macro_args.embassy_prefix.literal();
|
let embassy_prefix = macro_args.embassy_prefix;
|
||||||
let embassy_path = macro_args.embassy_prefix.append("embassy").path();
|
let embassy_prefix_lit = embassy_prefix.literal();
|
||||||
let task_fn_body = task_fn.block.clone();
|
let embassy_path = embassy_prefix.append("embassy").path();
|
||||||
let chip_setup = chip::generate(¯o_args);
|
let task_fn_body = task_fn.block;
|
||||||
|
|
||||||
|
let config = macro_args
|
||||||
|
.config
|
||||||
|
.map(|s| s.parse::<syn::Expr>().unwrap())
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
syn::Expr::Verbatim(quote! {
|
||||||
|
Default::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let chip_setup = chip::generate(&embassy_prefix, config);
|
||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
#[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)]
|
#[#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 mut executor = #embassy_path::executor::Executor::new();
|
||||||
|
|
||||||
let executor = unsafe { make_static(&mut executor) };
|
let executor = unsafe { make_static(&mut executor) };
|
||||||
|
|
||||||
#chip_setup
|
#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 macro_args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||||
let task_fn = syn::parse_macro_input!(item as syn::ItemFn);
|
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,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return TokenStream::from(e.write_errors());
|
return TokenStream::from(e.write_errors());
|
||||||
|
@ -10,6 +10,7 @@ use example_common::*;
|
|||||||
|
|
||||||
use defmt::panic;
|
use defmt::panic;
|
||||||
use embassy;
|
use embassy;
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy_stm32;
|
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) {
|
async fn main(spawner: Spawner) {
|
||||||
let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap();
|
let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ use embassy_stm32::pac as stm32;
|
|||||||
use embassy_stm32::serial;
|
use embassy_stm32::serial;
|
||||||
use futures::pin_mut;
|
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) {
|
async fn main(spawner: Spawner) {
|
||||||
let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap();
|
let (dp, clocks) = embassy_stm32::Peripherals::take().unwrap();
|
||||||
let cp = cortex_m::peripheral::Peripherals::take().unwrap();
|
let cp = cortex_m::peripheral::Peripherals::take().unwrap();
|
||||||
|
@ -92,7 +92,9 @@ async fn run1(bus: &'static mut UsbBusAllocator<UsbBus<USB>>) {
|
|||||||
|
|
||||||
static USB_BUS: Forever<UsbBusAllocator<UsbBus<USB>>> = Forever::new();
|
static USB_BUS: Forever<UsbBusAllocator<UsbBus<USB>>> = 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) -> ! {
|
async fn main(spawner: Spawner) -> ! {
|
||||||
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
|
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ embassy = { version = "0.1.0", path = "../embassy" }
|
|||||||
embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"]}
|
embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"]}
|
||||||
embassy-extras = {version = "0.1.0", path = "../embassy-extras" }
|
embassy-extras = {version = "0.1.0", path = "../embassy-extras" }
|
||||||
|
|
||||||
|
atomic-polyfill = "0.1.1"
|
||||||
defmt = { version = "0.2.0", optional = true }
|
defmt = { version = "0.2.0", optional = true }
|
||||||
log = { version = "0.4.11", optional = true }
|
log = { version = "0.4.11", optional = true }
|
||||||
cortex-m-rt = "0.6.13"
|
cortex-m-rt = "0.6.13"
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
|
pub mod rtc;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
|
pub mod system;
|
||||||
|
61
embassy-stm32/src/f4/system.rs
Normal file
61
embassy-stm32/src/f4/system.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::{hal::prelude::*, pac, Peripherals};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub use_hse: Option<u32>,
|
||||||
|
pub sysclk: Option<u32>,
|
||||||
|
pub pclk1: Option<u32>,
|
||||||
|
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) };
|
||||||
|
}
|
2
embassy-stm32/src/l0/mod.rs
Normal file
2
embassy-stm32/src/l0/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod rtc;
|
||||||
|
pub mod system;
|
371
embassy-stm32/src/l0/rtc.rs
Normal file
371
embassy-stm32/src/l0/rtc.rs
Normal file
@ -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<u64>,
|
||||||
|
callback: Cell<Option<(fn(*mut ()), *mut ())>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<T: Instance> {
|
||||||
|
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<T: Instance> RTC<T> {
|
||||||
|
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<T> {
|
||||||
|
Alarm { n: 1, rtc: self }
|
||||||
|
}
|
||||||
|
pub fn alarm2(&'static self) -> Option<Alarm<T>> {
|
||||||
|
if T::REAL_ALARM_COUNT >= 2 {
|
||||||
|
Some(Alarm { n: 2, rtc: self })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn alarm3(&'static self) -> Option<Alarm<T>> {
|
||||||
|
if T::REAL_ALARM_COUNT >= 3 {
|
||||||
|
Some(Alarm { n: 3, rtc: self })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> embassy::time::Clock for RTC<T> {
|
||||||
|
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<T: Instance> {
|
||||||
|
n: usize,
|
||||||
|
rtc: &'static RTC<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> embassy::time::Alarm for Alarm<T> {
|
||||||
|
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));
|
17
embassy-stm32/src/l0/system.rs
Normal file
17
embassy-stm32/src/l0/system.rs
Normal file
@ -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) };
|
||||||
|
}
|
@ -49,6 +49,12 @@ pub use {stm32f4xx_hal as hal, stm32f4xx_hal::stm32 as pac};
|
|||||||
#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))]
|
#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))]
|
||||||
pub use {stm32l0xx_hal as hal, stm32l0xx_hal::pac};
|
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 fmt;
|
||||||
|
|
||||||
pub mod exti;
|
pub mod exti;
|
||||||
@ -90,26 +96,7 @@ pub mod can;
|
|||||||
feature = "stm32f469",
|
feature = "stm32f469",
|
||||||
feature = "stm32f479",
|
feature = "stm32f479",
|
||||||
))]
|
))]
|
||||||
pub mod rtc;
|
pub use f4::{rtc, serial, spi, system};
|
||||||
|
|
||||||
#[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};
|
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "stm32f401",
|
feature = "stm32f401",
|
||||||
@ -398,3 +385,40 @@ embassy_extras::std_peripherals! {
|
|||||||
FPU_CPACR,
|
FPU_CPACR,
|
||||||
SCB_ACTRL,
|
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,
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user