diff --git a/Cargo.toml b/Cargo.toml index 06943731..31fe2647 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ members = [ "embassy-traits", "embassy-nrf", "embassy-stm32f4", + "embassy-stm32l0", "embassy-nrf-examples", "embassy-stm32f4-examples", "embassy-macros", diff --git a/ci.sh b/ci.sh index b6036cac..f1f6a52e 100755 --- a/ci.sh +++ b/ci.sh @@ -29,3 +29,8 @@ set -euxo pipefail (cd embassy-stm32f4-examples; cargo build --target thumbv7em-none-eabi --bins) (cd embassy-stm32f4; cargo build --target thumbv7em-none-eabi --features stm32f405) (cd embassy-stm32f4; cargo build --target thumbv7em-none-eabi --features stm32f405,defmt) + +# embassy-stm32l0 + +(cd embassy-stm32l0; cargo build --target thumbv6m-none-eabi --features stm32l0x2) +(cd embassy-stm32l0; cargo build --target thumbv6m-none-eabi --features stm32l0x2,defmt) diff --git a/embassy-stm32l0/Cargo.toml b/embassy-stm32l0/Cargo.toml new file mode 100644 index 00000000..70aa431c --- /dev/null +++ b/embassy-stm32l0/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "embassy-stm32l0" +version = "0.1.0" +authors = ["Michael Beaumont "] +edition = "2018" + +[features] +defmt-trace = [ ] +defmt-debug = [ ] +defmt-info = [ ] +defmt-warn = [ ] +defmt-error = [ ] + +stm32l0x1 = ["stm32l0xx-hal/stm32l0x1"] +stm32l0x2 = ["stm32l0xx-hal/stm32l0x2"] +stm32l0x3 = ["stm32l0xx-hal/stm32l0x3"] + +[dependencies] +embassy = { version = "0.1.0", path = "../embassy" } +defmt = { version = "0.2.0", optional = true } +log = { version = "0.4.11", optional = true } +cortex-m-rt = "0.6.13" +cortex-m = "0.7.1" +embedded-hal = { version = "0.2.4" } +embedded-dma = { version = "0.1.2" } +stm32l0xx-hal = { version = "0.7.0", features = ["rt"], git = "https://github.com/stm32-rs/stm32l0xx-hal.git"} diff --git a/embassy-stm32l0/src/exti.rs b/embassy-stm32l0/src/exti.rs new file mode 100644 index 00000000..c93d213e --- /dev/null +++ b/embassy-stm32l0/src/exti.rs @@ -0,0 +1,224 @@ +use core::future::Future; +use core::mem; +use core::pin::Pin; + +use embassy::traits::gpio::{WaitForAnyEdge, WaitForFallingEdge, WaitForRisingEdge}; +use embassy::util::InterruptFuture; + +use crate::hal::{ + exti::{Exti, ExtiLine, GpioLine, TriggerEdge}, + gpio, + syscfg::SYSCFG, +}; +use crate::interrupt; +use crate::pac::EXTI; + +pub struct ExtiManager { + syscfg: SYSCFG, +} + +impl<'a> ExtiManager { + pub fn new(_exti: Exti, syscfg: SYSCFG) -> Self { + Self { syscfg } + } + + pub fn new_pin(&'static mut self, pin: T, interrupt: T::Interrupt) -> ExtiPin + where + T: PinWithInterrupt, + { + ExtiPin { + pin, + interrupt, + mgr: self, + } + } +} + +pub struct ExtiPin { + pin: T, + interrupt: T::Interrupt, + mgr: &'static ExtiManager, +} + +impl ExtiPin { + fn wait_for_edge<'a>( + self: Pin<&'a mut Self>, + edge: TriggerEdge, + ) -> impl Future + 'a { + let line = self.pin.line(); + let s = unsafe { self.get_unchecked_mut() }; + + Exti::unpend(line); + + async move { + let exti: EXTI = unsafe { mem::transmute(()) }; + let mut exti = Exti::new(exti); + + let fut = InterruptFuture::new(&mut s.interrupt); + + let port = s.pin.port(); + let syscfg = &s.mgr.syscfg as *const _ as *mut SYSCFG; + cortex_m::interrupt::free(|_| { + let syscfg = unsafe { &mut *syscfg }; + exti.listen_gpio(syscfg, port, line, edge); + }); + + fut.await; + + Exti::unpend(line); + } + } +} + +impl WaitForRisingEdge for ExtiPin { + type Future<'a> = impl Future + 'a; + + fn wait_for_rising_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_edge(TriggerEdge::Rising) + } +} + +impl WaitForFallingEdge for ExtiPin { + type Future<'a> = impl Future + 'a; + + fn wait_for_falling_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_edge(TriggerEdge::Falling) + } +} + +impl WaitForAnyEdge for ExtiPin { + type Future<'a> = impl Future + 'a; + + fn wait_for_any_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_edge(TriggerEdge::Both) + } +} + +mod private { + pub trait Sealed {} +} + +pub trait PinWithInterrupt: private::Sealed { + type Interrupt: interrupt::Interrupt; + fn port(&self) -> gpio::Port; + fn line(&self) -> GpioLine; +} + +macro_rules! exti { + ($($PER:ident => ($set:ident, $pin:ident),)+) => { + $( + impl private::Sealed for gpio::$set::$pin {} + impl PinWithInterrupt for gpio::$set::$pin { + type Interrupt = interrupt::$PER; + fn port(&self) -> gpio::Port { + self.port() + } + fn line(&self) -> GpioLine { + GpioLine::from_raw_line(self.pin_number()).unwrap() + } + } + )+ + } +} + +exti! { + EXTI0_1 => (gpioa, PA0), + EXTI0_1 => (gpioa, PA1), + EXTI2_3 => (gpioa, PA2), + EXTI2_3 => (gpioa, PA3), + EXTI4_15 => (gpioa, PA4), + EXTI4_15 => (gpioa, PA5), + EXTI4_15 => (gpioa, PA6), + EXTI4_15 => (gpioa, PA7), + EXTI4_15 => (gpioa, PA8), + EXTI4_15 => (gpioa, PA9), + EXTI4_15 => (gpioa, PA10), + EXTI4_15 => (gpioa, PA11), + EXTI4_15 => (gpioa, PA12), + EXTI4_15 => (gpioa, PA13), + EXTI4_15 => (gpioa, PA14), + EXTI4_15 => (gpioa, PA15), +} + +exti! { + EXTI0_1 => (gpiob, PB0), + EXTI0_1 => (gpiob, PB1), + EXTI2_3 => (gpiob, PB2), + EXTI2_3 => (gpiob, PB3), + EXTI4_15 => (gpiob, PB4), + EXTI4_15 => (gpiob, PB5), + EXTI4_15 => (gpiob, PB6), + EXTI4_15 => (gpiob, PB7), + EXTI4_15 => (gpiob, PB8), + EXTI4_15 => (gpiob, PB9), + EXTI4_15 => (gpiob, PB10), + EXTI4_15 => (gpiob, PB11), + EXTI4_15 => (gpiob, PB12), + EXTI4_15 => (gpiob, PB13), + EXTI4_15 => (gpiob, PB14), + EXTI4_15 => (gpiob, PB15), +} + +exti! { + EXTI0_1 => (gpioc, PC0), + EXTI0_1 => (gpioc, PC1), + EXTI2_3 => (gpioc, PC2), + EXTI2_3 => (gpioc, PC3), + EXTI4_15 => (gpioc, PC4), + EXTI4_15 => (gpioc, PC5), + EXTI4_15 => (gpioc, PC6), + EXTI4_15 => (gpioc, PC7), + EXTI4_15 => (gpioc, PC8), + EXTI4_15 => (gpioc, PC9), + EXTI4_15 => (gpioc, PC10), + EXTI4_15 => (gpioc, PC11), + EXTI4_15 => (gpioc, PC12), + EXTI4_15 => (gpioc, PC13), + EXTI4_15 => (gpioc, PC14), + EXTI4_15 => (gpioc, PC15), +} + +exti! { + EXTI0_1 => (gpiod, PD0), + EXTI0_1 => (gpiod, PD1), + EXTI2_3 => (gpiod, PD2), + EXTI2_3 => (gpiod, PD3), + EXTI4_15 => (gpiod, PD4), + EXTI4_15 => (gpiod, PD5), + EXTI4_15 => (gpiod, PD6), + EXTI4_15 => (gpiod, PD7), + EXTI4_15 => (gpiod, PD8), + EXTI4_15 => (gpiod, PD9), + EXTI4_15 => (gpiod, PD10), + EXTI4_15 => (gpiod, PD11), + EXTI4_15 => (gpiod, PD12), + EXTI4_15 => (gpiod, PD13), + EXTI4_15 => (gpiod, PD14), + EXTI4_15 => (gpiod, PD15), +} + +exti! { + EXTI0_1 => (gpioe, PE0), + EXTI0_1 => (gpioe, PE1), + EXTI2_3 => (gpioe, PE2), + EXTI2_3 => (gpioe, PE3), + EXTI4_15 => (gpioe, PE4), + EXTI4_15 => (gpioe, PE5), + EXTI4_15 => (gpioe, PE6), + EXTI4_15 => (gpioe, PE7), + EXTI4_15 => (gpioe, PE8), + EXTI4_15 => (gpioe, PE9), + EXTI4_15 => (gpioe, PE10), + EXTI4_15 => (gpioe, PE11), + EXTI4_15 => (gpioe, PE12), + EXTI4_15 => (gpioe, PE13), + EXTI4_15 => (gpioe, PE14), + EXTI4_15 => (gpioe, PE15), +} + +exti! { + EXTI0_1 => (gpioh, PH0), + EXTI0_1 => (gpioh, PH1), + EXTI4_15 => (gpioh, PH9), + EXTI4_15 => (gpioh, PH10), +} diff --git a/embassy-stm32l0/src/fmt.rs b/embassy-stm32l0/src/fmt.rs new file mode 100644 index 00000000..1be1057a --- /dev/null +++ b/embassy-stm32l0/src/fmt.rs @@ -0,0 +1,119 @@ +#![macro_use] +#![allow(clippy::module_inception)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +pub use fmt::*; + +#[cfg(feature = "defmt")] +mod fmt { + pub use defmt::{ + assert, assert_eq, assert_ne, debug, debug_assert, debug_assert_eq, debug_assert_ne, error, + info, panic, todo, trace, unreachable, unwrap, warn, + }; +} + +#[cfg(feature = "log")] +mod fmt { + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + pub use log::{debug, error, info, trace, warn}; +} + +#[cfg(not(any(feature = "defmt", feature = "log")))] +mod fmt { + #![macro_use] + + pub use core::{ + assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo, + unreachable, + }; + + #[macro_export] + macro_rules! trace { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! debug { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! info { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! warn { + ($($msg:expr),+ $(,)?) => { + () + }; + } + + #[macro_export] + macro_rules! error { + ($($msg:expr),+ $(,)?) => { + () + }; + } +} + +#[cfg(not(feature = "defmt"))] +#[macro_export] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-stm32l0/src/interrupt.rs b/embassy-stm32l0/src/interrupt.rs new file mode 100644 index 00000000..499f3f27 --- /dev/null +++ b/embassy-stm32l0/src/interrupt.rs @@ -0,0 +1,162 @@ +//! Interrupt management +use crate::pac::NVIC_PRIO_BITS; + +// Re-exports +pub use cortex_m::interrupt::{CriticalSection, Mutex}; +pub use embassy::interrupt::{declare, take, Interrupt}; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum Priority { + Level0 = 0, + Level1 = 1, + Level2 = 2, + Level3 = 3, + Level4 = 4, + Level5 = 5, + Level6 = 6, + Level7 = 7, + Level8 = 8, + Level9 = 9, + Level10 = 10, + Level11 = 11, + Level12 = 12, + Level13 = 13, + Level14 = 14, + Level15 = 15, +} + +impl From for Priority { + fn from(priority: u8) -> Self { + match priority >> (8 - NVIC_PRIO_BITS) { + 0 => Self::Level0, + 1 => Self::Level1, + 2 => Self::Level2, + 3 => Self::Level3, + 4 => Self::Level4, + 5 => Self::Level5, + 6 => Self::Level6, + 7 => Self::Level7, + 8 => Self::Level8, + 9 => Self::Level9, + 10 => Self::Level10, + 11 => Self::Level11, + 12 => Self::Level12, + 13 => Self::Level13, + 14 => Self::Level14, + 15 => Self::Level15, + _ => unreachable!(), + } + } +} + +impl From for u8 { + fn from(p: Priority) -> Self { + (p as u8) << (8 - NVIC_PRIO_BITS) + } +} + +#[cfg(feature = "stm32l0x1")] +mod irqs { + use super::*; + declare!(WWDG); + declare!(PVD); + declare!(RTC); + declare!(FLASH); + declare!(RCC); + declare!(EXTI0_1); + declare!(EXTI2_3); + declare!(EXTI4_15); + declare!(DMA1_CHANNEL1); + declare!(DMA1_CHANNEL2_3); + declare!(DMA1_CHANNEL4_7); + declare!(ADC_COMP); + declare!(LPTIM1); + declare!(USART4_USART5); + declare!(TIM2); + declare!(TIM3); + declare!(TIM6); + declare!(TIM7); + declare!(TIM21); + declare!(I2C3); + declare!(TIM22); + declare!(I2C1); + declare!(I2C2); + declare!(SPI1); + declare!(SPI2); + declare!(USART1); + declare!(USART2); + declare!(AES_RNG_LPUART1); +} + +#[cfg(feature = "stm32l0x2")] +mod irqs { + use super::*; + declare!(WWDG); + declare!(PVD); + declare!(RTC); + declare!(RCC); + declare!(EXTI0_1); + declare!(EXTI2_3); + declare!(EXTI4_15); + declare!(TSC); + declare!(DMA1_CHANNEL1); + declare!(DMA1_CHANNEL2_3); + declare!(DMA1_CHANNEL4_7); + declare!(ADC_COMP); + declare!(LPTIM1); + declare!(USART4_USART5); + declare!(TIM2); + declare!(TIM3); + declare!(TIM6_DAC); + declare!(TIM7); + declare!(TIM21); + declare!(I2C3); + declare!(TIM22); + declare!(I2C1); + declare!(I2C2); + declare!(SPI1); + declare!(SPI2); + declare!(USART1); + declare!(USART2); + declare!(AES_RNG_LPUART1); + declare!(USB); +} + +#[cfg(feature = "stm32l0x3")] +mod irqs { + use super::*; + declare!(WWDG); + declare!(PVD); + declare!(RTC); + declare!(RCC); + declare!(EXTI0_1); + declare!(EXTI2_3); + declare!(EXTI4_15); + declare!(TSC); + declare!(DMA1_CHANNEL1); + declare!(DMA1_CHANNEL2_3); + declare!(DMA1_CHANNEL4_7); + declare!(ADC_COMP); + declare!(LPTIM1); + declare!(USART4_USART5); + declare!(TIM2); + declare!(TIM3); + declare!(TIM6_DAC); + declare!(TIM7); + declare!(TIM21); + declare!(I2C3); + declare!(TIM22); + declare!(I2C1); + declare!(I2C2); + declare!(SPI1); + declare!(SPI2); + declare!(USART1); + declare!(USART2); + declare!(AES_RNG_LPUART1); + declare!(LCD); + declare!(USB); +} + +pub use irqs::*; diff --git a/embassy-stm32l0/src/lib.rs b/embassy-stm32l0/src/lib.rs new file mode 100644 index 00000000..d030713c --- /dev/null +++ b/embassy-stm32l0/src/lib.rs @@ -0,0 +1,29 @@ +#![no_std] +#![feature(generic_associated_types)] +#![feature(asm)] +#![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[cfg(not(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",)))] +compile_error!( + "No chip feature activated. You must activate exactly one of the following features: " +); + +#[cfg(any( + all(feature = "stm32l0x1", feature = "stm32l0x2"), + all(feature = "stm32l0x1", feature = "stm32l0x3"), + all(feature = "stm32l0x2", feature = "stm32l0x3"), +))] +compile_error!( + "Multile chip features activated. You must activate exactly one of the following features: " +); + +pub use stm32l0xx_hal as hal; +pub use stm32l0xx_hal::pac; + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod exti; +pub mod interrupt;