From 18cd87ae12fb7ec925a0f50bc8e0b307d67dc716 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 28 Dec 2020 09:17:36 -0600 Subject: [PATCH] create stm32f4 crate --- Cargo.toml | 1 + embassy-stm32f4/Cargo.toml | 40 +++ embassy-stm32f4/src/fmt.rs | 118 +++++++ embassy-stm32f4/src/interrupt.rs | 129 +++++++ embassy-stm32f4/src/lib.rs | 316 +++++++++++++++++ embassy-stm32f4/src/uarte.rs | 561 +++++++++++++++++++++++++++++++ 6 files changed, 1165 insertions(+) create mode 100644 embassy-stm32f4/Cargo.toml create mode 100644 embassy-stm32f4/src/fmt.rs create mode 100644 embassy-stm32f4/src/interrupt.rs create mode 100644 embassy-stm32f4/src/lib.rs create mode 100644 embassy-stm32f4/src/uarte.rs diff --git a/Cargo.toml b/Cargo.toml index 79280404..e1a1c34b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "embassy", "embassy-nrf", + "embassy-stm32f4", "embassy-macros", "examples", ] diff --git a/embassy-stm32f4/Cargo.toml b/embassy-stm32f4/Cargo.toml new file mode 100644 index 00000000..52ecba61 --- /dev/null +++ b/embassy-stm32f4/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "embassy-stm32f4" +version = "0.1.0" +authors = ["Dario Nieuwenhuis "] +edition = "2018" + +[features] +defmt-trace = [ ] +defmt-debug = [ ] +defmt-info = [ ] +defmt-warn = [ ] +defmt-error = [ ] + +stm32f401 = ["stm32f4xx-hal/stm32f401"] +stm32f405 = ["stm32f4xx-hal/stm32f405"] +stm32f407 = ["stm32f4xx-hal/stm32f407"] +stm32f410 = ["stm32f4xx-hal/stm32f410"] +stm32f411 = ["stm32f4xx-hal/stm32f411"] +stm32f412 = ["stm32f4xx-hal/stm32f412"] +stm32f413 = ["stm32f4xx-hal/stm32f413"] +stm32f415 = ["stm32f4xx-hal/stm32f405"] +stm32f417 = ["stm32f4xx-hal/stm32f407"] +stm32f423 = ["stm32f4xx-hal/stm32f413"] +stm32f427 = ["stm32f4xx-hal/stm32f427"] +stm32f429 = ["stm32f4xx-hal/stm32f429"] +stm32f437 = ["stm32f4xx-hal/stm32f427"] +stm32f439 = ["stm32f4xx-hal/stm32f429"] +stm32f446 = ["stm32f4xx-hal/stm32f446"] +stm32f469 = ["stm32f4xx-hal/stm32f469"] +stm32f479 = ["stm32f4xx-hal/stm32f469"] + +[dependencies] +embassy = { version = "0.1.0", path = "../embassy" } + +defmt = { version = "0.1.3", optional = true } +log = { version = "0.4.11", optional = true } +cortex-m-rt = "0.6.13" +cortex-m = { version = "0.6.4" } +embedded-hal = { version = "0.2.4" } +stm32f4xx-hal = { version = "0.8.3", features = ["rt"]} diff --git a/embassy-stm32f4/src/fmt.rs b/embassy-stm32f4/src/fmt.rs new file mode 100644 index 00000000..4da69766 --- /dev/null +++ b/embassy-stm32f4/src/fmt.rs @@ -0,0 +1,118 @@ +#![macro_use] + +#[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-stm32f4/src/interrupt.rs b/embassy-stm32f4/src/interrupt.rs new file mode 100644 index 00000000..17fc9ab3 --- /dev/null +++ b/embassy-stm32f4/src/interrupt.rs @@ -0,0 +1,129 @@ +//! Interrupt management +//! +//! This module implements an API for managing interrupts compatible with +//! nrf_softdevice::interrupt. Intended for switching between the two at compile-time. + +use core::sync::atomic::{compiler_fence, Ordering}; + +use crate::pac::{NVIC, NVIC_PRIO_BITS}; + +// Re-exports +pub use crate::pac::Interrupt; +pub use crate::pac::Interrupt::*; // needed for cortex-m-rt #[interrupt] +pub use cortex_m::interrupt::{CriticalSection, Mutex}; + +#[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, +} + +impl Priority { + #[inline] + fn to_nvic(self) -> u8 { + (self as u8) << (8 - NVIC_PRIO_BITS) + } + + #[inline] + fn from_nvic(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, + _ => unreachable!(), + } + } +} + +#[inline] +pub fn free(f: F) -> R +where + F: FnOnce(&CriticalSection) -> R, +{ + unsafe { + // TODO: assert that we're in privileged level + // Needed because disabling irqs in non-privileged level is a noop, which would break safety. + + let primask: u32; + asm!("mrs {}, PRIMASK", out(reg) primask); + + asm!("cpsid i"); + + // Prevent compiler from reordering operations inside/outside the critical section. + compiler_fence(Ordering::SeqCst); + + let r = f(&CriticalSection::new()); + + compiler_fence(Ordering::SeqCst); + + if primask & 1 == 0 { + asm!("cpsie i"); + } + + r + } +} + +#[inline] +pub fn enable(irq: Interrupt) { + unsafe { + NVIC::unmask(irq); + } +} + +#[inline] +pub fn disable(irq: Interrupt) { + NVIC::mask(irq); +} + +#[inline] +pub fn is_active(irq: Interrupt) -> bool { + NVIC::is_active(irq) +} + +#[inline] +pub fn is_enabled(irq: Interrupt) -> bool { + NVIC::is_enabled(irq) +} + +#[inline] +pub fn is_pending(irq: Interrupt) -> bool { + NVIC::is_pending(irq) +} + +#[inline] +pub fn pend(irq: Interrupt) { + NVIC::pend(irq) +} + +#[inline] +pub fn unpend(irq: Interrupt) { + NVIC::unpend(irq) +} + +#[inline] +pub fn get_priority(irq: Interrupt) -> Priority { + Priority::from_nvic(NVIC::get_priority(irq)) +} + +#[inline] +pub fn set_priority(irq: Interrupt, prio: Priority) { + unsafe { + cortex_m::peripheral::Peripherals::steal() + .NVIC + .set_priority(irq, prio.to_nvic()) + } +} diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs new file mode 100644 index 00000000..fc411088 --- /dev/null +++ b/embassy-stm32f4/src/lib.rs @@ -0,0 +1,316 @@ +#![no_std] +#![feature(generic_associated_types)] +#![feature(asm)] +#![feature(type_alias_impl_trait)] + +#[cfg(not(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", +)))] +compile_error!( + "No chip feature activated. You must activate exactly one of the following features: " +); + +#[cfg(any( + all(feature = "stm32f401", feature = "stm32f405"), + all(feature = "stm32f401", feature = "stm32f407"), + all(feature = "stm32f401", feature = "stm32f410"), + all(feature = "stm32f401", feature = "stm32f411"), + all(feature = "stm32f401", feature = "stm32f412"), + all(feature = "stm32f401", feature = "stm32f413"), + all(feature = "stm32f401", feature = "stm32f415"), + all(feature = "stm32f401", feature = "stm32f417"), + all(feature = "stm32f401", feature = "stm32f423"), + all(feature = "stm32f401", feature = "stm32f427"), + all(feature = "stm32f401", feature = "stm32f429"), + all(feature = "stm32f401", feature = "stm32f437"), + all(feature = "stm32f401", feature = "stm32f439"), + all(feature = "stm32f401", feature = "stm32f446"), + all(feature = "stm32f401", feature = "stm32f469"), + all(feature = "stm32f401", feature = "stm32f479"), + all(feature = "stm32f405", feature = "stm32f401"), + all(feature = "stm32f405", feature = "stm32f407"), + all(feature = "stm32f405", feature = "stm32f410"), + all(feature = "stm32f405", feature = "stm32f411"), + all(feature = "stm32f405", feature = "stm32f412"), + all(feature = "stm32f405", feature = "stm32f413"), + all(feature = "stm32f405", feature = "stm32f415"), + all(feature = "stm32f405", feature = "stm32f417"), + all(feature = "stm32f405", feature = "stm32f423"), + all(feature = "stm32f405", feature = "stm32f427"), + all(feature = "stm32f405", feature = "stm32f429"), + all(feature = "stm32f405", feature = "stm32f437"), + all(feature = "stm32f405", feature = "stm32f439"), + all(feature = "stm32f405", feature = "stm32f446"), + all(feature = "stm32f405", feature = "stm32f469"), + all(feature = "stm32f405", feature = "stm32f479"), + all(feature = "stm32f407", feature = "stm32f401"), + all(feature = "stm32f407", feature = "stm32f405"), + all(feature = "stm32f407", feature = "stm32f410"), + all(feature = "stm32f407", feature = "stm32f411"), + all(feature = "stm32f407", feature = "stm32f412"), + all(feature = "stm32f407", feature = "stm32f413"), + all(feature = "stm32f407", feature = "stm32f415"), + all(feature = "stm32f407", feature = "stm32f417"), + all(feature = "stm32f407", feature = "stm32f423"), + all(feature = "stm32f407", feature = "stm32f427"), + all(feature = "stm32f407", feature = "stm32f429"), + all(feature = "stm32f407", feature = "stm32f437"), + all(feature = "stm32f407", feature = "stm32f439"), + all(feature = "stm32f407", feature = "stm32f446"), + all(feature = "stm32f407", feature = "stm32f469"), + all(feature = "stm32f407", feature = "stm32f479"), + all(feature = "stm32f410", feature = "stm32f401"), + all(feature = "stm32f410", feature = "stm32f405"), + all(feature = "stm32f410", feature = "stm32f407"), + all(feature = "stm32f410", feature = "stm32f411"), + all(feature = "stm32f410", feature = "stm32f412"), + all(feature = "stm32f410", feature = "stm32f413"), + all(feature = "stm32f410", feature = "stm32f415"), + all(feature = "stm32f410", feature = "stm32f417"), + all(feature = "stm32f410", feature = "stm32f423"), + all(feature = "stm32f410", feature = "stm32f427"), + all(feature = "stm32f410", feature = "stm32f429"), + all(feature = "stm32f410", feature = "stm32f437"), + all(feature = "stm32f410", feature = "stm32f439"), + all(feature = "stm32f410", feature = "stm32f446"), + all(feature = "stm32f410", feature = "stm32f469"), + all(feature = "stm32f410", feature = "stm32f479"), + all(feature = "stm32f411", feature = "stm32f401"), + all(feature = "stm32f411", feature = "stm32f405"), + all(feature = "stm32f411", feature = "stm32f407"), + all(feature = "stm32f411", feature = "stm32f410"), + all(feature = "stm32f411", feature = "stm32f412"), + all(feature = "stm32f411", feature = "stm32f413"), + all(feature = "stm32f411", feature = "stm32f415"), + all(feature = "stm32f411", feature = "stm32f417"), + all(feature = "stm32f411", feature = "stm32f423"), + all(feature = "stm32f411", feature = "stm32f427"), + all(feature = "stm32f411", feature = "stm32f429"), + all(feature = "stm32f411", feature = "stm32f437"), + all(feature = "stm32f411", feature = "stm32f439"), + all(feature = "stm32f411", feature = "stm32f446"), + all(feature = "stm32f411", feature = "stm32f469"), + all(feature = "stm32f411", feature = "stm32f479"), + all(feature = "stm32f412", feature = "stm32f401"), + all(feature = "stm32f412", feature = "stm32f405"), + all(feature = "stm32f412", feature = "stm32f407"), + all(feature = "stm32f412", feature = "stm32f410"), + all(feature = "stm32f412", feature = "stm32f411"), + all(feature = "stm32f412", feature = "stm32f413"), + all(feature = "stm32f412", feature = "stm32f415"), + all(feature = "stm32f412", feature = "stm32f417"), + all(feature = "stm32f412", feature = "stm32f423"), + all(feature = "stm32f412", feature = "stm32f427"), + all(feature = "stm32f412", feature = "stm32f429"), + all(feature = "stm32f412", feature = "stm32f437"), + all(feature = "stm32f412", feature = "stm32f439"), + all(feature = "stm32f412", feature = "stm32f446"), + all(feature = "stm32f412", feature = "stm32f469"), + all(feature = "stm32f412", feature = "stm32f479"), + all(feature = "stm32f413", feature = "stm32f401"), + all(feature = "stm32f413", feature = "stm32f405"), + all(feature = "stm32f413", feature = "stm32f407"), + all(feature = "stm32f413", feature = "stm32f410"), + all(feature = "stm32f413", feature = "stm32f411"), + all(feature = "stm32f413", feature = "stm32f412"), + all(feature = "stm32f413", feature = "stm32f415"), + all(feature = "stm32f413", feature = "stm32f417"), + all(feature = "stm32f413", feature = "stm32f423"), + all(feature = "stm32f413", feature = "stm32f427"), + all(feature = "stm32f413", feature = "stm32f429"), + all(feature = "stm32f413", feature = "stm32f437"), + all(feature = "stm32f413", feature = "stm32f439"), + all(feature = "stm32f413", feature = "stm32f446"), + all(feature = "stm32f413", feature = "stm32f469"), + all(feature = "stm32f413", feature = "stm32f479"), + all(feature = "stm32f415", feature = "stm32f401"), + all(feature = "stm32f415", feature = "stm32f405"), + all(feature = "stm32f415", feature = "stm32f407"), + all(feature = "stm32f415", feature = "stm32f410"), + all(feature = "stm32f415", feature = "stm32f411"), + all(feature = "stm32f415", feature = "stm32f412"), + all(feature = "stm32f415", feature = "stm32f413"), + all(feature = "stm32f415", feature = "stm32f417"), + all(feature = "stm32f415", feature = "stm32f423"), + all(feature = "stm32f415", feature = "stm32f427"), + all(feature = "stm32f415", feature = "stm32f429"), + all(feature = "stm32f415", feature = "stm32f437"), + all(feature = "stm32f415", feature = "stm32f439"), + all(feature = "stm32f415", feature = "stm32f446"), + all(feature = "stm32f415", feature = "stm32f469"), + all(feature = "stm32f415", feature = "stm32f479"), + all(feature = "stm32f417", feature = "stm32f401"), + all(feature = "stm32f417", feature = "stm32f405"), + all(feature = "stm32f417", feature = "stm32f407"), + all(feature = "stm32f417", feature = "stm32f410"), + all(feature = "stm32f417", feature = "stm32f411"), + all(feature = "stm32f417", feature = "stm32f412"), + all(feature = "stm32f417", feature = "stm32f413"), + all(feature = "stm32f417", feature = "stm32f415"), + all(feature = "stm32f417", feature = "stm32f423"), + all(feature = "stm32f417", feature = "stm32f427"), + all(feature = "stm32f417", feature = "stm32f429"), + all(feature = "stm32f417", feature = "stm32f437"), + all(feature = "stm32f417", feature = "stm32f439"), + all(feature = "stm32f417", feature = "stm32f446"), + all(feature = "stm32f417", feature = "stm32f469"), + all(feature = "stm32f417", feature = "stm32f479"), + all(feature = "stm32f423", feature = "stm32f401"), + all(feature = "stm32f423", feature = "stm32f405"), + all(feature = "stm32f423", feature = "stm32f407"), + all(feature = "stm32f423", feature = "stm32f410"), + all(feature = "stm32f423", feature = "stm32f411"), + all(feature = "stm32f423", feature = "stm32f412"), + all(feature = "stm32f423", feature = "stm32f413"), + all(feature = "stm32f423", feature = "stm32f415"), + all(feature = "stm32f423", feature = "stm32f417"), + all(feature = "stm32f423", feature = "stm32f427"), + all(feature = "stm32f423", feature = "stm32f429"), + all(feature = "stm32f423", feature = "stm32f437"), + all(feature = "stm32f423", feature = "stm32f439"), + all(feature = "stm32f423", feature = "stm32f446"), + all(feature = "stm32f423", feature = "stm32f469"), + all(feature = "stm32f423", feature = "stm32f479"), + all(feature = "stm32f427", feature = "stm32f401"), + all(feature = "stm32f427", feature = "stm32f405"), + all(feature = "stm32f427", feature = "stm32f407"), + all(feature = "stm32f427", feature = "stm32f410"), + all(feature = "stm32f427", feature = "stm32f411"), + all(feature = "stm32f427", feature = "stm32f412"), + all(feature = "stm32f427", feature = "stm32f413"), + all(feature = "stm32f427", feature = "stm32f415"), + all(feature = "stm32f427", feature = "stm32f417"), + all(feature = "stm32f427", feature = "stm32f423"), + all(feature = "stm32f427", feature = "stm32f429"), + all(feature = "stm32f427", feature = "stm32f437"), + all(feature = "stm32f427", feature = "stm32f439"), + all(feature = "stm32f427", feature = "stm32f446"), + all(feature = "stm32f427", feature = "stm32f469"), + all(feature = "stm32f427", feature = "stm32f479"), + all(feature = "stm32f429", feature = "stm32f401"), + all(feature = "stm32f429", feature = "stm32f405"), + all(feature = "stm32f429", feature = "stm32f407"), + all(feature = "stm32f429", feature = "stm32f410"), + all(feature = "stm32f429", feature = "stm32f411"), + all(feature = "stm32f429", feature = "stm32f412"), + all(feature = "stm32f429", feature = "stm32f413"), + all(feature = "stm32f429", feature = "stm32f415"), + all(feature = "stm32f429", feature = "stm32f417"), + all(feature = "stm32f429", feature = "stm32f423"), + all(feature = "stm32f429", feature = "stm32f427"), + all(feature = "stm32f429", feature = "stm32f437"), + all(feature = "stm32f429", feature = "stm32f439"), + all(feature = "stm32f429", feature = "stm32f446"), + all(feature = "stm32f429", feature = "stm32f469"), + all(feature = "stm32f429", feature = "stm32f479"), + all(feature = "stm32f437", feature = "stm32f401"), + all(feature = "stm32f437", feature = "stm32f405"), + all(feature = "stm32f437", feature = "stm32f407"), + all(feature = "stm32f437", feature = "stm32f410"), + all(feature = "stm32f437", feature = "stm32f411"), + all(feature = "stm32f437", feature = "stm32f412"), + all(feature = "stm32f437", feature = "stm32f413"), + all(feature = "stm32f437", feature = "stm32f415"), + all(feature = "stm32f437", feature = "stm32f417"), + all(feature = "stm32f437", feature = "stm32f423"), + all(feature = "stm32f437", feature = "stm32f427"), + all(feature = "stm32f437", feature = "stm32f429"), + all(feature = "stm32f437", feature = "stm32f439"), + all(feature = "stm32f437", feature = "stm32f446"), + all(feature = "stm32f437", feature = "stm32f469"), + all(feature = "stm32f437", feature = "stm32f479"), + all(feature = "stm32f439", feature = "stm32f401"), + all(feature = "stm32f439", feature = "stm32f405"), + all(feature = "stm32f439", feature = "stm32f407"), + all(feature = "stm32f439", feature = "stm32f410"), + all(feature = "stm32f439", feature = "stm32f411"), + all(feature = "stm32f439", feature = "stm32f412"), + all(feature = "stm32f439", feature = "stm32f413"), + all(feature = "stm32f439", feature = "stm32f415"), + all(feature = "stm32f439", feature = "stm32f417"), + all(feature = "stm32f439", feature = "stm32f423"), + all(feature = "stm32f439", feature = "stm32f427"), + all(feature = "stm32f439", feature = "stm32f429"), + all(feature = "stm32f439", feature = "stm32f437"), + all(feature = "stm32f439", feature = "stm32f446"), + all(feature = "stm32f439", feature = "stm32f469"), + all(feature = "stm32f439", feature = "stm32f479"), + all(feature = "stm32f446", feature = "stm32f401"), + all(feature = "stm32f446", feature = "stm32f405"), + all(feature = "stm32f446", feature = "stm32f407"), + all(feature = "stm32f446", feature = "stm32f410"), + all(feature = "stm32f446", feature = "stm32f411"), + all(feature = "stm32f446", feature = "stm32f412"), + all(feature = "stm32f446", feature = "stm32f413"), + all(feature = "stm32f446", feature = "stm32f415"), + all(feature = "stm32f446", feature = "stm32f417"), + all(feature = "stm32f446", feature = "stm32f423"), + all(feature = "stm32f446", feature = "stm32f427"), + all(feature = "stm32f446", feature = "stm32f429"), + all(feature = "stm32f446", feature = "stm32f437"), + all(feature = "stm32f446", feature = "stm32f439"), + all(feature = "stm32f446", feature = "stm32f469"), + all(feature = "stm32f446", feature = "stm32f479"), + all(feature = "stm32f469", feature = "stm32f401"), + all(feature = "stm32f469", feature = "stm32f405"), + all(feature = "stm32f469", feature = "stm32f407"), + all(feature = "stm32f469", feature = "stm32f410"), + all(feature = "stm32f469", feature = "stm32f411"), + all(feature = "stm32f469", feature = "stm32f412"), + all(feature = "stm32f469", feature = "stm32f413"), + all(feature = "stm32f469", feature = "stm32f415"), + all(feature = "stm32f469", feature = "stm32f417"), + all(feature = "stm32f469", feature = "stm32f423"), + all(feature = "stm32f469", feature = "stm32f427"), + all(feature = "stm32f469", feature = "stm32f429"), + all(feature = "stm32f469", feature = "stm32f437"), + all(feature = "stm32f469", feature = "stm32f439"), + all(feature = "stm32f469", feature = "stm32f446"), + all(feature = "stm32f469", feature = "stm32f479"), + all(feature = "stm32f479", feature = "stm32f401"), + all(feature = "stm32f479", feature = "stm32f405"), + all(feature = "stm32f479", feature = "stm32f407"), + all(feature = "stm32f479", feature = "stm32f410"), + all(feature = "stm32f479", feature = "stm32f411"), + all(feature = "stm32f479", feature = "stm32f412"), + all(feature = "stm32f479", feature = "stm32f413"), + all(feature = "stm32f479", feature = "stm32f415"), + all(feature = "stm32f479", feature = "stm32f417"), + all(feature = "stm32f479", feature = "stm32f423"), + all(feature = "stm32f479", feature = "stm32f427"), + all(feature = "stm32f479", feature = "stm32f429"), + all(feature = "stm32f479", feature = "stm32f437"), + all(feature = "stm32f479", feature = "stm32f439"), + all(feature = "stm32f479", feature = "stm32f446"), + all(feature = "stm32f479", feature = "stm32f469"), +))] +compile_error!( + "Multile chip features activated. You must activate exactly one of the following features: " +); + +pub use stm32f4xx_hal as hal; +pub use stm32f4xx_hal::stm32 as pac; + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod interrupt; +pub mod uarte; + +pub use cortex_m_rt::interrupt; diff --git a/embassy-stm32f4/src/uarte.rs b/embassy-stm32f4/src/uarte.rs new file mode 100644 index 00000000..3a33e759 --- /dev/null +++ b/embassy-stm32f4/src/uarte.rs @@ -0,0 +1,561 @@ +//! HAL interface to the UARTE peripheral +//! +//! See product specification: +//! +//! - nrf52832: Section 35 +//! - nrf52840: Section 6.34 +use core::cell::UnsafeCell; +use core::cmp::min; +use core::marker::PhantomPinned; +use core::ops::Deref; +use core::pin::Pin; +use core::ptr; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::{Context, Poll}; + +use embedded_hal::digital::v2::OutputPin; + +use crate::hal::gpio::{Floating, Input, Output, Pin as GpioPin, Port as GpioPort, PushPull}; +use crate::interrupt; +use crate::interrupt::CriticalSection; +#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] +use crate::pac::UARTE1; +use crate::pac::{uarte0, Interrupt, UARTE0}; + +// Re-export SVD variants to allow user to directly set values +pub use uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; + +use embassy::io::{AsyncBufRead, AsyncWrite, Result}; +use embassy::util::WakerStore; + +use crate::fmt::{assert, panic, todo, *}; + +//use crate::trace; + +const RINGBUF_SIZE: usize = 512; +struct RingBuf { + buf: [u8; RINGBUF_SIZE], + start: usize, + end: usize, + empty: bool, +} + +impl RingBuf { + fn new() -> Self { + RingBuf { + buf: [0; RINGBUF_SIZE], + start: 0, + end: 0, + empty: true, + } + } + + fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + RINGBUF_SIZE - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = Self::wrap(self.end + n); + self.empty = false; + } + + fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + RINGBUF_SIZE - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = Self::wrap(self.start + n); + self.empty = self.start == self.end; + } + + fn wrap(n: usize) -> usize { + assert!(n <= RINGBUF_SIZE); + if n == RINGBUF_SIZE { + 0 + } else { + n + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum RxState { + Idle, + Receiving, + ReceivingReady, + Stopping, +} +#[derive(Copy, Clone, Debug, PartialEq)] +enum TxState { + Idle, + Transmitting(usize), +} + +/// Interface to a UARTE instance +/// +/// This is a very basic interface that comes with the following limitations: +/// - The UARTE instances share the same address space with instances of UART. +/// You need to make sure that conflicting instances +/// are disabled before using `Uarte`. See product specification: +/// - nrf52832: Section 15.2 +/// - nrf52840: Section 6.1.2 +pub struct Uarte { + started: bool, + state: UnsafeCell>, +} + +// public because it needs to be used in Instance::{get_state, set_state}, but +// should not be used outside the module +#[doc(hidden)] +pub struct UarteState { + inner: T, + + rx: RingBuf, + rx_state: RxState, + rx_waker: WakerStore, + + tx: RingBuf, + tx_state: TxState, + tx_waker: WakerStore, + + _pin: PhantomPinned, +} + +#[cfg(any(feature = "52833", feature = "52840"))] +fn port_bit(port: GpioPort) -> bool { + match port { + GpioPort::Port0 => false, + GpioPort::Port1 => true, + } +} + +impl Uarte { + pub fn new(uarte: T, mut pins: Pins, parity: Parity, baudrate: Baudrate) -> Self { + // Select pins + uarte.psel.rxd.write(|w| { + let w = unsafe { w.pin().bits(pins.rxd.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + let w = w.port().bit(port_bit(pins.rxd.port())); + w.connect().connected() + }); + pins.txd.set_high().unwrap(); + uarte.psel.txd.write(|w| { + let w = unsafe { w.pin().bits(pins.txd.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + let w = w.port().bit(port_bit(pins.txd.port())); + w.connect().connected() + }); + + // Optional pins + uarte.psel.cts.write(|w| { + if let Some(ref pin) = pins.cts { + let w = unsafe { w.pin().bits(pin.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + let w = w.port().bit(port_bit(pin.port())); + w.connect().connected() + } else { + w.connect().disconnected() + } + }); + + uarte.psel.rts.write(|w| { + if let Some(ref pin) = pins.rts { + let w = unsafe { w.pin().bits(pin.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + let w = w.port().bit(port_bit(pin.port())); + w.connect().connected() + } else { + w.connect().disconnected() + } + }); + + // Enable UARTE instance + uarte.enable.write(|w| w.enable().enabled()); + + // Enable interrupts + uarte.intenset.write(|w| w.endrx().set().endtx().set()); + + // Configure + let hardware_flow_control = pins.rts.is_some() && pins.cts.is_some(); + uarte + .config + .write(|w| w.hwfc().bit(hardware_flow_control).parity().variant(parity)); + + // Configure frequency + uarte.baudrate.write(|w| w.baudrate().variant(baudrate)); + + Uarte { + started: false, + state: UnsafeCell::new(UarteState { + inner: uarte, + + rx: RingBuf::new(), + rx_state: RxState::Idle, + rx_waker: WakerStore::new(), + + tx: RingBuf::new(), + tx_state: TxState::Idle, + tx_waker: WakerStore::new(), + + _pin: PhantomPinned, + }), + } + } + + fn with_state<'a, R>( + self: Pin<&'a mut Self>, + f: impl FnOnce(Pin<&'a mut UarteState>) -> R, + ) -> R { + let Self { state, started } = unsafe { self.get_unchecked_mut() }; + + interrupt::free(|cs| { + let ptr = state.get(); + + if !*started { + T::set_state(cs, ptr); + + *started = true; + + // safety: safe because critical section ensures only one *mut UartState + // exists at the same time. + unsafe { Pin::new_unchecked(&mut *ptr) }.start(); + } + + // safety: safe because critical section ensures only one *mut UartState + // exists at the same time. + f(unsafe { Pin::new_unchecked(&mut *ptr) }) + }) + } +} + +impl Drop for Uarte { + fn drop(&mut self) { + // stop DMA before dropping, because DMA is using the buffer in `self`. + todo!() + } +} + +impl AsyncBufRead for Uarte { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.with_state(|s| s.poll_fill_buf(cx)) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.with_state(|s| s.consume(amt)) + } +} + +impl AsyncWrite for Uarte { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + self.with_state(|s| s.poll_write(cx, buf)) + } +} + +impl UarteState { + pub fn start(self: Pin<&mut Self>) { + interrupt::set_priority(T::interrupt(), interrupt::Priority::Level7); + interrupt::enable(T::interrupt()); + interrupt::pend(T::interrupt()); + } + + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = unsafe { self.get_unchecked_mut() }; + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started + compiler_fence(Ordering::SeqCst); + trace!("poll_read"); + + // We have data ready in buffer? Return it. + let buf = this.rx.pop_buf(); + if buf.len() != 0 { + trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len()); + return Poll::Ready(Ok(buf)); + } + + trace!(" empty"); + + if this.rx_state == RxState::ReceivingReady { + trace!(" stopping"); + this.rx_state = RxState::Stopping; + this.inner.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + } + + this.rx_waker.store(cx.waker()); + Poll::Pending + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = unsafe { self.get_unchecked_mut() }; + trace!("consume {:?}", amt); + this.rx.pop(amt); + interrupt::pend(T::interrupt()); + } + + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let this = unsafe { self.get_unchecked_mut() }; + + trace!("poll_write: {:?}", buf.len()); + + let tx_buf = this.tx.push_buf(); + if tx_buf.len() == 0 { + trace!("poll_write: pending"); + this.tx_waker.store(cx.waker()); + return Poll::Pending; + } + + let n = min(tx_buf.len(), buf.len()); + tx_buf[..n].copy_from_slice(&buf[..n]); + this.tx.push(n); + + trace!("poll_write: queued {:?}", n); + + // Conservative compiler fence to prevent optimizations that do not + // take in to account actions by DMA. The fence has been placed here, + // before any DMA action has started + compiler_fence(Ordering::SeqCst); + + interrupt::pend(T::interrupt()); + + Poll::Ready(Ok(n)) + } + + fn on_interrupt(&mut self) { + trace!("irq: start"); + let mut more_work = true; + while more_work { + more_work = false; + match self.rx_state { + RxState::Idle => { + trace!(" irq_rx: in state idle"); + + if self.inner.events_rxdrdy.read().bits() != 0 { + trace!(" irq_rx: rxdrdy?????"); + self.inner.events_rxdrdy.reset(); + } + + if self.inner.events_endrx.read().bits() != 0 { + panic!("unexpected endrx"); + } + + let buf = self.rx.push_buf(); + if buf.len() != 0 { + trace!(" irq_rx: starting {:?}", buf.len()); + self.rx_state = RxState::Receiving; + + // Set up the DMA read + self.inner.rxd.ptr.write(|w| + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(buf.as_ptr() as u32) }); + self.inner.rxd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is at least 8 bits wide and accepts the full + // range of values. + unsafe { w.maxcnt().bits(buf.len() as _) }); + trace!(" irq_rx: buf {:?} {:?}", buf.as_ptr() as u32, buf.len()); + + // Enable RXRDY interrupt. + self.inner.events_rxdrdy.reset(); + self.inner.intenset.write(|w| w.rxdrdy().set()); + + // Start UARTE Receive transaction + self.inner.tasks_startrx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + } + } + RxState::Receiving => { + trace!(" irq_rx: in state receiving"); + if self.inner.events_rxdrdy.read().bits() != 0 { + trace!(" irq_rx: rxdrdy"); + + // Disable the RXRDY event interrupt + // RXRDY is triggered for every byte, but we only care about whether we have + // some bytes or not. So as soon as we have at least one, disable it, to avoid + // wasting CPU cycles in interrupts. + self.inner.intenclr.write(|w| w.rxdrdy().clear()); + + self.inner.events_rxdrdy.reset(); + + self.rx_waker.wake(); + self.rx_state = RxState::ReceivingReady; + more_work = true; // in case we also have endrx pending + } + } + RxState::ReceivingReady | RxState::Stopping => { + trace!(" irq_rx: in state ReceivingReady"); + + if self.inner.events_rxdrdy.read().bits() != 0 { + trace!(" irq_rx: rxdrdy"); + self.inner.events_rxdrdy.reset(); + } + + if self.inner.events_endrx.read().bits() != 0 { + let n: usize = self.inner.rxd.amount.read().amount().bits() as usize; + trace!(" irq_rx: endrx {:?}", n); + self.rx.push(n); + + self.inner.events_endrx.reset(); + + self.rx_waker.wake(); + self.rx_state = RxState::Idle; + more_work = true; // start another rx if possible + } + } + } + } + + more_work = true; + while more_work { + more_work = false; + match self.tx_state { + TxState::Idle => { + trace!(" irq_tx: in state Idle"); + let buf = self.tx.pop_buf(); + if buf.len() != 0 { + trace!(" irq_tx: starting {:?}", buf.len()); + self.tx_state = TxState::Transmitting(buf.len()); + + // Set up the DMA write + self.inner.txd.ptr.write(|w| + // The PTR field is a full 32 bits wide and accepts the full range + // of values. + unsafe { w.ptr().bits(buf.as_ptr() as u32) }); + self.inner.txd.maxcnt.write(|w| + // We're giving it the length of the buffer, so no danger of + // accessing invalid memory. We have verified that the length of the + // buffer fits in an `u8`, so the cast to `u8` is also fine. + // + // The MAXCNT field is 8 bits wide and accepts the full range of + // values. + unsafe { w.maxcnt().bits(buf.len() as _) }); + + // Start UARTE Transmit transaction + self.inner.tasks_starttx.write(|w| + // `1` is a valid value to write to task registers. + unsafe { w.bits(1) }); + } + } + TxState::Transmitting(n) => { + trace!(" irq_tx: in state Transmitting"); + if self.inner.events_endtx.read().bits() != 0 { + self.inner.events_endtx.reset(); + + trace!(" irq_tx: endtx {:?}", n); + self.tx.pop(n); + self.tx_waker.wake(); + self.tx_state = TxState::Idle; + more_work = true; // start another tx if possible + } + } + } + } + trace!("irq: end"); + } +} + +pub struct Pins { + pub rxd: GpioPin>, + pub txd: GpioPin>, + pub cts: Option>>, + pub rts: Option>>, +} + +mod private { + pub trait Sealed {} + + impl Sealed for crate::pac::UARTE0 {} + #[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] + impl Sealed for crate::pac::UARTE1 {} +} + +pub trait Instance: Deref + Sized + private::Sealed { + fn interrupt() -> Interrupt; + + #[doc(hidden)] + fn get_state(_cs: &CriticalSection) -> *mut UarteState; + + #[doc(hidden)] + fn set_state(_cs: &CriticalSection, state: *mut UarteState); +} + +#[interrupt] +unsafe fn UARTE0_UART0() { + interrupt::free(|cs| UARTE0::get_state(cs).as_mut().unwrap().on_interrupt()); +} + +#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] +#[interrupt] +unsafe fn UARTE1() { + interrupt::free(|cs| UARTE1::get_state(cs).as_mut().unwrap().on_interrupt()); +} + +static mut UARTE0_STATE: *mut UarteState = ptr::null_mut(); +#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] +static mut UARTE1_STATE: *mut UarteState = ptr::null_mut(); + +impl Instance for UARTE0 { + fn interrupt() -> Interrupt { + Interrupt::UARTE0_UART0 + } + + fn get_state(_cs: &CriticalSection) -> *mut UarteState { + unsafe { UARTE0_STATE } // Safe because of CriticalSection + } + fn set_state(_cs: &CriticalSection, state: *mut UarteState) { + unsafe { UARTE0_STATE = state } // Safe because of CriticalSection + } +} + +#[cfg(any(feature = "52833", feature = "52840", feature = "9160"))] +impl Instance for UARTE1 { + fn interrupt() -> Interrupt { + Interrupt::UARTE1 + } + + fn get_state(_cs: &CriticalSection) -> *mut UarteState { + unsafe { UARTE1_STATE } // Safe because of CriticalSection + } + fn set_state(_cs: &CriticalSection, state: *mut UarteState) { + unsafe { UARTE1_STATE = state } // Safe because of CriticalSection + } +}