Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
[target.thumbv7em-none-eabihf]
runner = 'probe-rs run --connect-under-reset'
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
]

[build]
target = "thumbv7em-none-eabihf"
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"rust-analyzer.check.allTargets": false,
"rust-analyzer.check.extraArgs": [
"--examples",
"--tests"
],
"rust-analyzer.cargo.target": "thumbv7em-none-eabihf",
"rust-analyzer.cargo.features": [
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ optional = true
[dev-dependencies]
cortex-m-rt = "0.7.2"
defmt-rtt = "0.4.0"
defmt-test = "0.3.2"
embedded-test = "0.6.1"
cortex-m-rtic = "1.1.4"
cortex-m-semihosting = "0.3.5"
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
Expand Down Expand Up @@ -104,6 +104,7 @@ defmt = [
"nb/defmt-0-3",
"embedded-hal/defmt-03",
"embedded-io/defmt-03",
"embedded-test/defmt",
]
cordic = ["dep:fixed"]
adc3 = []
Expand Down
14 changes: 14 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::env;

fn main() {
// 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");
}
}
4 changes: 4 additions & 0 deletions examples/utils/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ cfg_if::cfg_if! {

cfg_if::cfg_if! {
if #[cfg(feature = "log-itm")] {
#[cfg(not(test))]
use panic_itm as _;

use lazy_static::lazy_static;
Expand Down Expand Up @@ -57,6 +58,7 @@ cfg_if::cfg_if! {
}
else if #[cfg(feature = "defmt")] {
use defmt_rtt as _; // global logger
#[cfg(not(test))]
use panic_probe as _;
#[allow(unused_imports)]
pub use defmt::Logger;
Expand All @@ -67,6 +69,7 @@ cfg_if::cfg_if! {
pub fn init() {}
}
else if #[cfg(feature = "log-semihost")] {
#[cfg(not(test))]
use panic_semihosting as _;

use lazy_static::lazy_static;
Expand Down Expand Up @@ -104,6 +107,7 @@ cfg_if::cfg_if! {
}
}
else {
#[cfg(not(test))]
use panic_halt as _;

#[allow(dead_code)]
Expand Down
67 changes: 67 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use fugit::{ExtU32, MicrosDurationU32};
use stm32g4xx_hal::stm32;

#[non_exhaustive]
pub struct Timer<const CYCLES_PER_US: u32>;

impl<const CYCLES_PER_US: u32> Timer<CYCLES_PER_US> {
#[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().idr(pin).is_low()
}

#[allow(dead_code)]
#[derive(Debug, defmt::Format)]
pub struct ErrorTimedOut;

#[allow(dead_code)]
pub fn await_lo<const CYCLES_PER_US: u32>(
timer: &Timer<CYCLES_PER_US>,
pin: u8,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
await_p(timer, || is_pax_low(pin), timeout)
}

#[allow(dead_code)]
pub fn await_hi<const CYCLES_PER_US: u32>(
timer: &Timer<CYCLES_PER_US>,
pin: u8,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
await_p(timer, || !is_pax_low(pin), timeout)
}

#[allow(dead_code)]
pub fn await_p<const CYCLES_PER_US: u32>(
timer: &Timer<CYCLES_PER_US>,
mut p: impl FnMut() -> bool,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
let before = timer.now();

loop {
let passed_time = timer.now() - before;
if p() {
return Ok(passed_time);
}
if passed_time > timeout {
return Err(ErrorTimedOut);
}
}
}
121 changes: 30 additions & 91 deletions tests/nucleo-g474.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#[path = "../examples/utils/mod.rs"]
mod utils;

mod common;

use utils::logger::debug;

use core::ops::FnMut;
use core::result::Result;
use fugit::{ExtU32, HertzU32, MicrosDurationU32};
use hal::delay::DelayExt;
use hal::stm32;
Expand All @@ -16,16 +16,7 @@ use stm32g4xx_hal as hal;
pub const F_SYS: HertzU32 = HertzU32::MHz(16);
pub const CYCLES_PER_US: u32 = F_SYS.raw() / 1_000_000;

pub fn enable_timer(cp: &mut stm32::CorePeripherals) {
cp.DCB.enable_trace();
cp.DWT.enable_cycle_counter();
}

pub fn now() -> MicrosDurationU32 {
(stm32::DWT::cycle_count() / CYCLES_PER_US).micros()
}

#[defmt_test::tests]
#[embedded_test::tests]
mod tests {
use embedded_hal::pwm::SetDutyCycle;
use fixed::types::I1F15;
Expand All @@ -47,110 +38,98 @@ mod tests {
stm32::GPIOA,
};

use crate::common::{await_hi, await_lo, is_pax_low};
type Timer = crate::common::Timer<{ crate::CYCLES_PER_US }>;

#[test]
fn gpio_push_pull() {
use super::*;

// TODO: Is it ok to steal these?
let cp = unsafe { stm32::CorePeripherals::steal() };
let dp = unsafe { stm32::Peripherals::steal() };
let cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut delay = cp.SYST.delay(&rcc.clocks);
defmt::dbg!(rcc.clocks.sys_clk);

let gpioa = dp.GPIOA.split(&mut rcc);
let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input();
let mut pin = gpioa.pa8.into_push_pull_output();
let pin_num = 8; // PA8

pin.set_high();
delay.delay(1.millis()); // Give the pin plenty of time to go high
{
let gpioa = unsafe { &*GPIOA::PTR };
assert!(!is_pax_low(gpioa, 8));
}
assert!(!is_pax_low(pin_num));

pin.set_low();
delay.delay(1.millis()); // Give the pin plenty of time to go low
{
let gpioa = unsafe { &*GPIOA::PTR };
assert!(is_pax_low(gpioa, 8));
}
assert!(is_pax_low(pin_num));
}

#[test]
fn gpio_open_drain() {
use super::*;

// TODO: Is it ok to steal these?
let cp = unsafe { stm32::CorePeripherals::steal() };
let dp = unsafe { stm32::Peripherals::steal() };
let cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut delay = cp.SYST.delay(&rcc.clocks);

let gpioa = dp.GPIOA.split(&mut rcc);
let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input();
let mut pin = gpioa.pa8.into_open_drain_output();
let pin_num = 8; // PA8

// Enable pull-up resistor
{
let gpioa = unsafe { &*GPIOA::PTR };
gpioa.pupdr().modify(|_, w| w.pupdr8().pull_up());
gpioa.pupdr().modify(|_, w| w.pupdr(pin_num).pull_up());
}

pin.set_high();
delay.delay(1.millis()); // Give the pin plenty of time to go high
assert!(pin.is_high());
{
let gpioa = unsafe { &*GPIOA::PTR };
assert!(!is_pax_low(gpioa, 8));
}
assert!(!is_pax_low(pin_num));

pin.set_low();
delay.delay(1.millis()); // Give the pin plenty of time to go low
assert!(pin.is_low());
{
let gpioa = unsafe { &*GPIOA::PTR };
assert!(is_pax_low(gpioa, 8));
}
assert!(is_pax_low(pin_num));
}

#[test]
fn pwm() {
use super::*;

// TODO: Is it ok to steal these?
let mut cp = unsafe { stm32::CorePeripherals::steal() };
let dp = unsafe { stm32::Peripherals::steal() };
enable_timer(&mut cp);
let mut cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();
let timer = Timer::enable_timer(&mut cp);

let mut rcc = dp.RCC.constrain();
assert_eq!(rcc.clocks.sys_clk, F_SYS);

let gpioa = dp.GPIOA.split(&mut rcc);
let _pa1_important_dont_use_as_output = gpioa.pa1.into_floating_input();
let pin: PA8<AF6> = gpioa.pa8.into_alternate();
let pin_num = 8; // PA8

let mut pwm = dp.TIM1.pwm(pin, 1000u32.Hz(), &mut rcc);

pwm.set_duty_cycle_percent(50).unwrap();
pwm.enable();

let gpioa = unsafe { &*GPIOA::PTR };

let min: MicrosDurationU32 = 495u32.micros();
let max: MicrosDurationU32 = 505u32.micros();

debug!("Awaiting first rising edge...");
let duration_until_lo = await_lo(gpioa, max).unwrap();
let first_lo_duration = await_hi(gpioa, max).unwrap();
let duration_until_lo = await_lo(&timer, pin_num, max).unwrap();
let first_lo_duration = await_hi(&timer, pin_num, max).unwrap();

let mut hi_duration = 0.micros();
let mut lo_duration = 0.micros();

for _ in 0..10 {
// Make sure the timer half periods are within 495-505us

hi_duration = await_lo(gpioa, max).unwrap();
hi_duration = await_lo(&timer, pin_num, max).unwrap();
assert!(
hi_duration > min && hi_duration < max,
"hi: {} < {} < {}",
Expand All @@ -159,7 +138,7 @@ mod tests {
max
);

lo_duration = await_hi(gpioa, max).unwrap();
lo_duration = await_hi(&timer, pin_num, max).unwrap();
assert!(
lo_duration > min && lo_duration < max,
"lo: {} < {} < {}",
Expand Down Expand Up @@ -189,7 +168,7 @@ mod tests {

use super::*;

let dp = unsafe { stm32::Peripherals::steal() };
let dp = stm32::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();

let mut cordic = dp
Expand Down Expand Up @@ -223,9 +202,8 @@ mod tests {
fn adc() {
use super::*;

// TODO: Is it ok to steal these?
let cp = unsafe { stm32::CorePeripherals::steal() };
let dp = unsafe { stm32::Peripherals::steal() };
let cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut delay = cp.SYST.delay(&rcc.clocks);

Expand Down Expand Up @@ -264,9 +242,8 @@ mod tests {
dma::{channel::DMAExt, config::DmaConfig, TransferExt},
};

// TODO: Is it ok to steal these?
let cp = unsafe { stm32::CorePeripherals::steal() };
let dp = unsafe { stm32::Peripherals::steal() };
let cp = stm32::CorePeripherals::take().unwrap();
let dp = stm32::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut delay = cp.SYST.delay(&rcc.clocks);

Expand Down Expand Up @@ -328,41 +305,3 @@ mod tests {
assert!((20.0..35.0).contains(&temp), "20.0 < {} < 35.0", temp);
}
}

fn is_pax_low(gpioa: &stm32::gpioa::RegisterBlock, x: u8) -> bool {
gpioa.idr().read().idr(x).is_low()
}

#[derive(Debug, defmt::Format)]
struct ErrorTimedOut;

fn await_lo(
gpioa: &stm32::gpioa::RegisterBlock,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
await_p(|| is_pax_low(gpioa, 8), timeout)
}

fn await_hi(
gpioa: &stm32::gpioa::RegisterBlock,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
await_p(|| !is_pax_low(gpioa, 8), timeout)
}

fn await_p(
mut p: impl FnMut() -> bool,
timeout: MicrosDurationU32,
) -> Result<MicrosDurationU32, ErrorTimedOut> {
let before = now();

loop {
let passed_time = now() - before;
if p() {
return Ok(passed_time);
}
if passed_time > timeout {
return Err(ErrorTimedOut);
}
}
}
Loading
Loading