Skip to content

Commit

Permalink
delay: Add delay module (#23)
Browse files Browse the repository at this point in the history
This implements the embedded-hal 1.0 `DelayNs` trait. The cortex-m crate
has essentially the same logic, but the published version is based on
embedded-hal 0.2. This can be removed (repurposed for use with timers?)
when a new version of the cortex-m crate is published.

The blinky example has been updated to use the delay module.
  • Loading branch information
astapleton authored Nov 14, 2024
1 parent f3b4914 commit 4e2bed9
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 9 deletions.
22 changes: 13 additions & 9 deletions examples/blinky.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,38 @@
mod utilities;

use cortex_m_rt::entry;
use stm32h5xx_hal::{pac, prelude::*};
use embedded_hal::delay::DelayNs;
use fugit::SecsDurationU32;
use stm32h5xx_hal::{delay::Delay, pac, prelude::*, rcc::ResetEnable};

#[entry]
fn main() -> ! {
utilities::logger::init();

let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::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(250.MHz()).freeze(pwrcfg, &dp.SBS);
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);

ccdr.peripheral.GPIOA.enable();

dp.GPIOA.moder().write(|w| w.mode5().output()); // output
dp.GPIOA.pupdr().write(|w| w.pupd5().pull_up()); // pull-up

// dp.GPIOA.odr.write(|w| w.od5().set_bit());
let mut delay = Delay::new(cp.SYST, &ccdr.clocks);
let duration = SecsDurationU32::secs(1).to_millis();

loop {
dp.GPIOA.odr().write(|w| w.od5().low());
for _ in 0..10_000 {
cortex_m::asm::nop();
}
delay.delay_ms(duration);
log::info!("Off");
dp.GPIOA.odr().write(|w| w.od5().high());
for _ in 0..10_000 {
cortex_m::asm::nop();
}
delay.delay_ms(duration);
log::info!("On");
}
}
119 changes: 119 additions & 0 deletions src/delay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! Delay providers
//!
//! # Examples
//!
//! ## Delay
//!
//! ```no_run
//! let mut delay = Delay::new(core.SYST, device.clocks);
//!
//! delay.delay_ms(500);
//!
//! // Release SYST from the delay
//! let syst = delay.free();
//! ```
//!
//! # Examples
//!
//! - [Blinky](https://github.com/stm32-rs/stm32h7xx-hal/blob/master/examples/blinky.rs)
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m::peripheral::SYST;
use embedded_hal::delay::DelayNs;
use fugit::SecsDurationU64;

use crate::rcc::CoreClocks;

const SYSTICK_HCLK_DIV: u32 = 8;

pub trait DelayExt {
fn delay(self, clocks: &CoreClocks) -> Delay;
}

impl DelayExt for SYST {
fn delay(self, clocks: &CoreClocks) -> Delay {
Delay::new(self, clocks)
}
}

/// System timer (SysTick) as a delay provider
pub struct Delay {
hclk_hz: u32,
syst: SYST,
}

fn calc_ticks(ns: u32, hclk: u32) -> u32 {
// Default is for SYSTICK to be fed by HCLK/8
let ticks: u64 = (SecsDurationU64::secs(1) * SYSTICK_HCLK_DIV).to_nanos();
((ns as u64 * hclk as u64) / ticks) as u32
}

impl Delay {
/// Configures the system timer (SysTick) as a delay provider
pub fn new(mut syst: SYST, clocks: &CoreClocks) -> Self {
syst.set_clock_source(SystClkSource::External);

Delay {
hclk_hz: clocks.hclk().raw(),
syst,
}
}

/// Releases the system timer (SysTick) resource
pub fn free(self) -> SYST {
self.syst
}
}

impl DelayNs for Delay {
fn delay_ns(&mut self, ns: u32) {
// The SysTick Reload Value register supports values between 1 and 0x00FFFFFF.
const MAX_RVR: u32 = 0x00FF_FFFF;

let mut total_ticks = calc_ticks(ns, self.hclk_hz);

while total_ticks != 0 {
let current_ticks = if total_ticks <= MAX_RVR {
// To count N ticks, set RVR to N-1
// (see ARM Cortex M33 Devices Generic User Guide section 4.3.2.1)
core::cmp::max(total_ticks - 1, 1)
} else {
MAX_RVR
};

self.syst.set_reload(current_ticks);
self.syst.clear_current();
self.syst.enable_counter();

// For an RVR value of N, the SYSTICK counts N+1 ticks
// (see ARM Cortex M33 Devices Generic User Guide section 4.3.2.1)
total_ticks = total_ticks.saturating_sub(current_ticks + 1);

while !self.syst.has_wrapped() {}

self.syst.disable_counter();
}
}
}

#[cfg(test)]
mod tests {
use super::calc_ticks;
#[test]
fn test_calc_rvr() {
let rvr = calc_ticks(1_000, 8_000_000);
assert_eq!(rvr, 1);

let rvr = calc_ticks(1_000_000, 8_000_000);
assert_eq!(rvr, 1000);

let rvr = calc_ticks(1_000_000, 10_000_000);
assert_eq!(rvr, 1250);

let rvr = calc_ticks(1_000_000_000, 250_000_000);
assert_eq!(rvr, 31_250_000);

let rvr = calc_ticks(32, 250_000_000);
assert_eq!(rvr, 1);
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub mod gpio;
#[cfg(feature = "device-selected")]
pub mod icache;

#[cfg(feature = "device-selected")]
pub mod delay;

#[cfg(feature = "device-selected")]
mod sealed {
pub trait Sealed {}
Expand Down
2 changes: 2 additions & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Prelude
pub use crate::delay::DelayExt as _stm32h5xx_hal_delay_DelayExt;
pub use crate::gpio::GpioExt as _stm32h5xx_hal_gpio_GpioExt;
pub use crate::icache::ICacheExt as _stm32h5xx_hal_delay_ICacheExt;
pub use crate::pwr::PwrExt as _stm32h5xx_hal_pwr_PwrExt;
pub use crate::rcc::RccExt as _stm32h5xx_hal_rcc_RccExt;

pub use crate::time::U32Ext as _;
pub use fugit::{ExtU32 as _, RateExtU32 as _};

0 comments on commit 4e2bed9

Please sign in to comment.