diff --git a/.cargo/config.toml b/.cargo/config.toml index d2012bd..c04e055 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,10 +1,5 @@ [target.thumbv8m.main-none-eabihf] runner = 'probe-rs run --connect-under-reset' -rustflags = [ - # LLD (shipped with the Rust toolchain) is used as the default linker - "-C", "link-arg=-Tlink.x", - "-C", "link-arg=-Tdefmt.x", -] [build] target = "thumbv8m.main-none-eabihf" # Cortex-M33F (with FPU) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6a957f8 --- /dev/null +++ b/build.rs @@ -0,0 +1,18 @@ +use std::env; + +fn main() { + if env::var("TARGET").unwrap() != "thumbv8m.main-none-eabihf" { + return; + } + + // stm32 specific + println!("cargo:rustc-link-arg=-Tlink.x"); + + // add linker script for embedded-test!! + println!("cargo::rustc-link-arg-tests=-Tembedded-test.x"); + + // Check if the `defmt` feature is enabled, and if so link its linker script + if env::var("CARGO_FEATURE_DEFMT").is_ok() { + println!("cargo:rustc-link-arg=-Tdefmt.x"); + } +} diff --git a/examples/utilities/logger.rs b/examples/utilities/logger.rs index e47f78b..c3980e4 100644 --- a/examples/utilities/logger.rs +++ b/examples/utilities/logger.rs @@ -13,6 +13,7 @@ cfg_if::cfg_if! { cfg_if::cfg_if! { if #[cfg(any(feature = "log-itm"))] { + #[cfg(not(test))] use panic_itm as _; use lazy_static::lazy_static; @@ -55,6 +56,7 @@ cfg_if::cfg_if! { pub fn init() {} } else if #[cfg(any(feature = "log-rtt"))] { + #[cfg(not(test))] use panic_rtt_target as _; use log::{Level, Metadata, Record, LevelFilter}; @@ -87,6 +89,7 @@ cfg_if::cfg_if! { } } else if #[cfg(any(feature = "log-semihost"))] { + #[cfg(not(test))] use panic_semihosting as _; use lazy_static::lazy_static; @@ -110,6 +113,7 @@ cfg_if::cfg_if! { } } else { + #[cfg(not(test))] use panic_halt as _; pub fn init() {} } diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 0000000..01314d2 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,67 @@ +use fugit::{ExtU32, MicrosDurationU32}; +use stm32h5xx_hal::stm32; + +#[non_exhaustive] +pub struct Timer; + +impl Timer { + #[allow(dead_code)] + pub fn enable_timer(cp: &mut stm32::CorePeripherals) -> Self { + cp.DCB.enable_trace(); + cp.DWT.enable_cycle_counter(); + + Timer + } + + /// Returns duration since timer start + pub fn now(&self) -> MicrosDurationU32 { + (stm32::DWT::cycle_count() / CYCLES_PER_US).micros() + } +} + +#[allow(dead_code)] +pub fn is_pax_low(pin: u8) -> bool { + let gpioa = unsafe { &*stm32::GPIOA::PTR }; + gpioa.idr().read().id(pin).is_low() +} + +#[allow(dead_code)] +#[derive(Debug, defmt::Format)] +pub struct ErrorTimedOut; + +#[allow(dead_code)] +pub fn await_lo( + timer: &Timer, + pin: u8, + timeout: MicrosDurationU32, +) -> Result { + await_p(timer, || is_pax_low(pin), timeout) +} + +#[allow(dead_code)] +pub fn await_hi( + timer: &Timer, + pin: u8, + timeout: MicrosDurationU32, +) -> Result { + await_p(timer, || !is_pax_low(pin), timeout) +} + +#[allow(dead_code)] +pub fn await_p( + timer: &Timer, + mut p: impl FnMut() -> bool, + timeout: MicrosDurationU32, +) -> Result { + let before = timer.now(); + + loop { + let passed_time = timer.now() - before; + if p() { + return Ok(passed_time); + } + if passed_time > timeout { + return Err(ErrorTimedOut); + } + } +} diff --git a/tests/nucleo-h533.rs b/tests/nucleo-h533.rs new file mode 100644 index 0000000..c3b20b3 --- /dev/null +++ b/tests/nucleo-h533.rs @@ -0,0 +1,119 @@ +#![no_std] +#![no_main] + +#[path = "../examples/utilities/mod.rs"] +mod utils; + +mod common; + +use fugit::HertzU32; +use hal::stm32; +use stm32h5xx_hal::{self as hal, gpio}; + +pub const F_SYS: HertzU32 = HertzU32::MHz(16); +pub const CYCLES_PER_US: u32 = F_SYS.raw() / 1_000_000; + +use crate::common::is_pax_low; +use embedded_hal::delay::DelayNs; +use fugit::RateExtU32; +use stm32h5xx_hal::{delay::Delay, gpio::GpioExt, pwr::PwrExt, rcc::RccExt}; + +#[embedded_test::tests] +mod tests { + use stm32h5xx_hal::gpio::{PinExt, Pull}; + + #[test] + fn gpio_resistors() { + use super::*; + + let (gpioa, mut delay) = init(); + + let mut pin = gpioa.pa8.into_pull_down_input(); + let pin_num = pin.pin_id(); + + delay.delay_ms(1); // Give the pin plenty of time to go low + assert!(is_pax_low(pin_num)); + + pin.set_internal_resistor(Pull::Up); + delay.delay_ms(1); // Give the pin plenty of time to go high + assert!(!is_pax_low(pin_num)); + } + #[test] + fn gpio_push_pull() { + use super::*; + + let (gpioa, mut delay) = init(); + + let mut pin = gpioa.pa8.into_push_pull_output(); + let pin_num = pin.pin_id(); + + pin.set_high(); + delay.delay_ms(1); // Give the pin plenty of time to go high + assert!(!is_pax_low(pin_num)); + + pin.set_low(); + delay.delay_ms(1); // Give the pin plenty of time to go low + assert!(is_pax_low(pin_num)); + } + + #[test] + fn gpio_open_drain() { + use super::*; + + let (gpioa, mut delay) = init(); + + let mut pin = gpioa.pa8.into_open_drain_output().internal_pull_up(true); + let pin_num = pin.pin_id(); + + pin.set_high(); + delay.delay_ms(1); // Give the pin plenty of time to go high + assert!(pin.is_high()); + assert!(!is_pax_low(pin_num)); + + pin.set_low(); + delay.delay_ms(1); // Give the pin plenty of time to go low + assert!(pin.is_low()); + assert!(is_pax_low(pin_num)); + } + + #[test] + fn gpio_settings() { + use super::*; + + let (gpioa, _) = init(); + let pin = gpioa.pa8; + + let mut pin = pin.into_floating_input(); + + pin.set_internal_resistor(Pull::Up); + + pin.set_internal_resistor(Pull::Down); + + let pin = pin.into_analog(); + let pin = pin.into_push_pull_output(); + let pin = pin.into_open_drain_output(); + let pin: gpio::Pin<'A', 8, gpio::Alternate<7>> = pin.into_alternate(); + let mut pin: gpio::Pin<'A', 8, gpio::Alternate<15>> = + pin.into_alternate(); + + pin.set_speed(gpio::Speed::Low); + pin.set_speed(gpio::Speed::VeryHigh); + } +} + +fn init() -> (gpio::gpioa::Parts, Delay) { + utils::logger::init(); + let cp = stm32::CorePeripherals::take().unwrap(); + let dp = stm32::Peripherals::take().unwrap(); + let pwr = dp.PWR.constrain(); + let pwrcfg = pwr.vos0().freeze(); + + // Constrain and Freeze clock + let rcc = dp.RCC.constrain(); + let ccdr = rcc.sys_ck(250u32.MHz()).freeze(pwrcfg, &dp.SBS); + + let delay = Delay::new(cp.SYST, &ccdr.clocks); + let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); + + (gpioa, delay) +}