Update for va108xx

- New `va108xx-embassy` crate.
- Embassy library uses new crate
- Updated all dependencies

va108xx-hal

- Refactored and simplified PWM driver
- Added new raw getter API for TIM peripheral blocks
This commit is contained in:
Robin Müller 2025-02-08 18:09:40 +01:00
parent b2d17e10ed
commit 809d9c7bb1
22 changed files with 912 additions and 753 deletions

View File

@ -4,6 +4,7 @@ members = [
"vorago-reb1", "vorago-reb1",
"va108xx", "va108xx",
"va108xx-hal", "va108xx-hal",
"va108xx-embassy",
"examples/simple", "examples/simple",
"examples/rtic", "examples/rtic",
"examples/embassy", "examples/embassy",

View File

@ -6,9 +6,9 @@ edition = "2021"
[dependencies] [dependencies]
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
panic-halt = "0.2" panic-halt = "1"
rtt-target = "0.5" rtt-target = "0.6"
panic-rtt-target = "0.1.3" panic-rtt-target = "0.2"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"

View File

@ -7,9 +7,9 @@ edition = "2021"
cortex-m = "0.7" cortex-m = "0.7"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
panic-rtt-target = { version = "0.1.3" } panic-rtt-target = "0.2"
panic-halt = { version = "0.2" } panic-halt = "1"
rtt-target = { version = "0.5" } rtt-target = "0.6"
crc = "3" crc = "3"
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
static_assertions = "1" static_assertions = "1"

View File

@ -20,6 +20,14 @@ cargo run --bin rtic-example
## Embassy example ## Embassy example
Blinky with time driver IRQs in library
```rs ```rs
cargo run --bin embassy-example cargo run --bin embassy-example
``` ```
Blinky with custom time driver IRQs
```rs
cargo run --bin embassy-example --no-default-features --features custom-irqs
```

View File

@ -4,37 +4,29 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cfg-if = "1"
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
rtt-target = { version = "0.5" } rtt-target = "0.6"
panic-rtt-target = { version = "0.1" } panic-rtt-target = "0.2"
critical-section = "1" critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]} portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = { version = "0.6.0" } embassy-sync = "0.6"
embassy-time = { version = "0.3.2" } embassy-time = "0.4"
embassy-time-driver = { version = "0.1" } embassy-executor = { version = "0.7", features = [
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.embassy-executor]
version = "0.6.0"
features = [
"arch-cortex-m", "arch-cortex-m",
"executor-thread", "executor-thread",
"executor-interrupt", "executor-interrupt"
"integrated-timers", ]}
]
[dependencies.va108xx-hal] va108xx-hal = { path = "../../va108xx-hal" }
path = "../../va108xx-hal" va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
[features] [features]
default = ["ticks-hz-1_000"] default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
custom-irqs = []
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"] ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"] ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]

View File

@ -1,4 +0,0 @@
#![no_std]
pub mod time_driver;
pub use time_driver::init;

View File

@ -5,6 +5,16 @@ use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin; use embedded_hal::digital::StatefulOutputPin;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
cfg_if::cfg_if! {
if #[cfg(feature = "custom-irqs")] {
use va108xx_embassy::embassy_time_driver_irqs;
use va108xx_hal::pac::interrupt;
embassy_time_driver_irqs!(timekeeper_irq = OC23, alarm_irq = OC24);
}
}
use va108xx_hal::{gpio::PinsA, pac, prelude::*}; use va108xx_hal::{gpio::PinsA, pac, prelude::*};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000); const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
@ -19,14 +29,28 @@ async fn main(_spawner: Spawner) {
// Safety: Only called once here. // Safety: Only called once here.
unsafe { unsafe {
embassy_example::init( cfg_if::cfg_if! {
if #[cfg(not(feature = "custom-irqs"))] {
embassy::init(
&mut dp.sysconfig, &mut dp.sysconfig,
&dp.irqsel, &dp.irqsel,
SYSCLK_FREQ, SYSCLK_FREQ,
dp.tim23, dp.tim23,
dp.tim22, dp.tim22,
) );
}; } else {
embassy::init_with_custom_irqs(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
pac::Interrupt::OC23,
pac::Interrupt::OC24,
);
}
}
}
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();

View File

@ -1,333 +0,0 @@
//! This is a sample time driver implementation for the VA108xx family of devices, supporting
//! one alarm and requiring/reserving 2 TIM peripherals. You could adapt this implementation to
//! support more alarms.
//!
//! This driver implementation reserves interrupts OC31 and OC30 for the timekeeping.
use core::{cell::Cell, mem, ptr};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use portable_atomic::{AtomicU32, AtomicU8, Ordering};
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
use once_cell::sync::OnceCell;
use va108xx_hal::{
clock::enable_peripheral_clock,
enable_interrupt,
pac::{self, interrupt},
prelude::*,
timer::{enable_tim_clk, ValidTim},
PeripheralSelect,
};
pub type TimekeeperClk = pac::Tim23;
pub type AlarmClk0 = pac::Tim22;
pub type AlarmClk1 = pac::Tim21;
pub type AlarmClk2 = pac::Tim20;
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::OC31;
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::OC30;
/// Initialization method for embassy
///
/// # Safety
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper: TimekeeperClk,
alarm_tim: AlarmClk0,
) {
DRIVER.init(syscfg, irqsel, sysclk, timekeeper, alarm_tim)
}
time_driver_impl!(
static DRIVER: TimerDriverEmbassy = TimerDriverEmbassy {
periods: AtomicU32::new(0),
alarm_count: AtomicU8::new(0),
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [AlarmState::new(); ALARM_COUNT])
});
/// Timekeeper interrupt.
#[interrupt]
#[allow(non_snake_case)]
fn OC31() {
DRIVER.on_interrupt_timekeeping()
}
/// Alarm timer interrupt.
#[interrupt]
#[allow(non_snake_case)]
fn OC30() {
DRIVER.on_interrupt_alarm(0)
}
#[inline(always)]
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
// Safety: This is a static memory-mapped peripheral.
match idx {
0 => unsafe { &*AlarmClk0::ptr() },
1 => unsafe { &*AlarmClk1::ptr() },
2 => unsafe { &*AlarmClk2::ptr() },
_ => {
panic!("invalid alarm timer index")
}
}
}
#[inline(always)]
const fn timekeeping_tim() -> &'static pac::tim0::RegisterBlock {
// Safety: This is a memory-mapped peripheral.
unsafe { &*TimekeeperClk::ptr() }
}
struct AlarmState {
timestamp: Cell<u64>,
// This is really a Option<(fn(*mut ()), *mut ())>
// but fn pointers aren't allowed in const yet
callback: Cell<*const ()>,
ctx: Cell<*mut ()>,
}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
callback: Cell::new(ptr::null()),
ctx: Cell::new(ptr::null_mut()),
}
}
}
unsafe impl Send for AlarmState {}
const ALARM_COUNT: usize = 1;
static SCALE: OnceCell<u64> = OnceCell::new();
pub struct TimerDriverEmbassy {
periods: AtomicU32,
alarm_count: AtomicU8,
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
}
impl TimerDriverEmbassy {
fn init(
&self,
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper: TimekeeperClk,
alarm_tim: AlarmClk0,
) {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
let sysclk = sysclk.into();
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
timekeeper
.rst_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Decrementing counter.
timekeeper
.cnt_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Switch on. Timekeeping should always be done.
irqsel
.tim0(TimekeeperClk::TIM_ID as usize)
.write(|w| unsafe { w.bits(TIMEKEEPER_IRQ as u32) });
unsafe {
enable_interrupt(TIMEKEEPER_IRQ);
}
timekeeper.ctrl().modify(|_, w| w.irq_enb().set_bit());
timekeeper.enable().write(|w| unsafe { w.bits(1) });
enable_tim_clk(syscfg, AlarmClk0::TIM_ID);
// Explicitely disable alarm timer until needed.
alarm_tim.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_interrupt(ALARM_IRQ);
}
irqsel
.tim0(AlarmClk0::TIM_ID as usize)
.write(|w| unsafe { w.bits(ALARM_IRQ as u32) });
}
// Should be called inside the IRQ of the timekeeper timer.
fn on_interrupt_timekeeping(&self) {
self.next_period();
}
// Should be called inside the IRQ of the alarm timer.
fn on_interrupt_alarm(&self, idx: usize) {
critical_section::with(|cs| {
if self.alarms.borrow(cs)[idx].timestamp.get() <= self.now() {
self.trigger_alarm(idx, cs)
}
})
}
fn next_period(&self) {
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
let t = (period as u64) << 32;
critical_section::with(|cs| {
for i in 0..ALARM_COUNT {
let alarm = &self.alarms.borrow(cs)[i];
let at = alarm.timestamp.get();
let alarm_tim = alarm_tim(0);
if at < t {
self.trigger_alarm(i, cs);
} else {
let remaining_ticks = (at - t) * *SCALE.get().unwrap();
if remaining_ticks <= u32::MAX as u64 {
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
}
}
}
})
}
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
// safety: we're allowed to assume the AlarmState is created by us, and
// we never create one that's out of bounds.
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
}
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
alarm_tim(n).ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = &self.alarms.borrow(cs)[n];
// Setting the maximum value disables the alarm.
alarm.timestamp.set(u64::MAX);
// Call after clearing alarm, so the callback can set another alarm.
// safety:
// - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
// - other than that we only store valid function pointers into alarm.callback
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
f(alarm.ctx.get());
}
}
impl Driver for TimerDriverEmbassy {
fn now(&self) -> u64 {
if SCALE.get().is_none() {
return 0;
}
let mut period1: u32;
let mut period2: u32;
let mut counter_val: u32;
loop {
// Acquire ensures that we get the latest value of `periods` and
// no instructions can be reordered before the load.
period1 = self.periods.load(Ordering::Acquire);
counter_val = u32::MAX - timekeeping_tim().cnt_value().read().bits();
// Double read to protect against race conditions when the counter is overflowing.
period2 = self.periods.load(Ordering::Relaxed);
if period1 == period2 {
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
return now;
}
}
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
let id = self
.alarm_count
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
if x < ALARM_COUNT as u8 {
Some(x + 1)
} else {
None
}
});
match id {
Ok(id) => Some(AlarmHandle::new(id)),
Err(_) => None,
}
}
fn set_alarm_callback(
&self,
alarm: embassy_time_driver::AlarmHandle,
callback: fn(*mut ()),
ctx: *mut (),
) {
critical_section::with(|cs| {
let alarm = self.get_alarm(cs, alarm);
alarm.callback.set(callback as *const ());
alarm.ctx.set(ctx);
})
}
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
if SCALE.get().is_none() {
return false;
}
critical_section::with(|cs| {
let n = alarm.id();
let alarm_tim = alarm_tim(n.into());
alarm_tim.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
// the interrupts are enabled or not. When they are enabled at a later point, the
// right value is already set.
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
// is not missed.
//
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
// and we don't do that here.
let safe_timestamp = timestamp.max(t + 3);
let timer_ticks = (safe_timestamp - t) * *SCALE.get().unwrap();
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
if timer_ticks <= u32::MAX as u64 {
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(timer_ticks as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
// If it's too far in the future, don't enable timer yet.
// It will be enabled later by `next_period`.
true
})
}
}

View File

@ -8,37 +8,19 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
embedded-io = "0.6" embedded-io = "0.6"
rtt-target = { version = "0.5" } rtt-target = "0.6"
panic-rtt-target = { version = "0.1" } panic-rtt-target = "0.2"
# Even though we do not use this directly, we need to activate this feature explicitely # Even though we do not use this directly, we need to activate this feature explicitely
# so that RTIC compiles because thumv6 does not have CAS operations natively. # so that RTIC compiles because thumv6 does not have CAS operations natively.
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]} portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
[dependencies.rtic] rtic = { version = "2", features = ["thumbv6-backend"] }
version = "2" rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
features = ["thumbv6-backend"] rtic-sync = { version = "1.3", features = ["defmt-03"] }
[dependencies.rtic-monotonics] once_cell = {version = "1", default-features = false, features = ["critical-section"]}
version = "2" ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
features = ["cortex-m-systick"]
[dependencies.rtic-sync] va108xx-hal = "0.8"
version = "1.3" vorago-reb1 = { path = "../../vorago-reb1" }
features = ["defmt-03"]
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.ringbuf]
version = "0.4.7"
default-features = false
features = ["portable-atomic"]
[dependencies.va108xx-hal]
version = "0.8"
[dependencies.vorago-reb1]
path = "../../vorago-reb1"

View File

@ -68,10 +68,7 @@ mod app {
Shared { Shared {
rb: StaticRb::default(), rb: StaticRb::default(),
}, },
Local { Local { rx, tx },
rx,
tx,
},
) )
} }

View File

@ -6,10 +6,10 @@ edition = "2021"
[dependencies] [dependencies]
cortex-m = {version = "0.7", features = ["critical-section-single-core"]} cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
panic-halt = "0.2" panic-halt = "1"
panic-rtt-target = "0.1" panic-rtt-target = "0.2"
critical-section = "1" critical-section = "1"
rtt-target = "0.5" rtt-target = "0.6"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"

View File

@ -9,55 +9,24 @@ cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
panic-rtt-target = { version = "0.1.3" } panic-rtt-target = "0.2"
rtt-target = { version = "0.5" } rtt-target = "0.6"
num_enum = { version = "0.7", default-features = false } num_enum = { version = "0.7", default-features = false }
log = "0.4" log = "0.4"
crc = "3" crc = "3"
cobs = { version = "0.3", default-features = false }
[dependencies.satrs] satrs = { version = "0.2", default-features = false }
version = "0.2" rtt-log = "0.5"
default-features = false ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[dependencies.rtt-log] spacepackets = { version = "0.11", default-features = false }
version = "0.4"
[dependencies.ringbuf]
version = "0.4.7"
default-features = false
features = ["portable-atomic"]
[dependencies.once_cell]
version = "1"
default-features = false
features = ["critical-section"]
[dependencies.spacepackets]
version = "0.11"
default-features = false
[dependencies.cobs]
git = "https://github.com/robamu/cobs.rs.git"
branch = "all_features"
default-features = false
# Even though we do not use this directly, we need to activate this feature explicitely # Even though we do not use this directly, we need to activate this feature explicitely
# so that RTIC compiles because thumv6 does not have CAS operations natively. # so that RTIC compiles because thumv6 does not have CAS operations natively.
[dependencies.portable-atomic] portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
version = "1"
features = ["unsafe-assume-single-core"]
[dependencies.rtic] rtic = { version = "2", features = ["thumbv6-backend"] }
version = "2" rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
features = ["thumbv6-backend"] rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.rtic-monotonics]
version = "2"
features = ["cortex-m-systick"]
[dependencies.rtic-sync]
version = "1"
features = ["defmt-03"]
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
path = "../va108xx-hal" path = "../va108xx-hal"

View File

@ -251,10 +251,10 @@ mod app {
} }
let packet_len = packet_len.unwrap(); let packet_len = packet_len.unwrap();
log::info!(target: "TC Handler", "received packet with length {}", packet_len); log::info!(target: "TC Handler", "received packet with length {}", packet_len);
let popped_packet_len = cx.shared.tc_rb.lock(|rb| { let popped_packet_len = cx
rb.buf .shared
.pop_slice(&mut cx.local.tc_buf[0..packet_len]) .tc_rb
}); .lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len]));
assert_eq!(popped_packet_len, packet_len); assert_eq!(popped_packet_len, packet_len);
// Read a telecommand, now handle it. // Read a telecommand, now handle it.
handle_valid_pus_tc(&mut cx); handle_valid_pus_tc(&mut cx);
@ -272,8 +272,7 @@ mod app {
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap(); let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
cx.shared.tm_rb.lock(|prod| { cx.shared.tm_rb.lock(|prod| {
prod.sizes.try_push(tm.len_written()).unwrap(); prod.sizes.try_push(tm.len_written()).unwrap();
prod.buf prod.buf.push_slice(&cx.local.verif_buf[0..written_size]);
.push_slice(&cx.local.verif_buf[0..written_size]);
}); });
}; };
let token = cx.local.verif_reporter.add_tc(&pus_tc); let token = cx.local.verif_reporter.add_tc(&pus_tc);

View File

@ -0,0 +1,27 @@
[package]
name = "va108xx-embassy"
version = "0.1.0"
edition = "2021"
[dependencies]
critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = "0.6"
embassy-executor = "0.7"
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[dependencies.va108xx-hal]
path = "../va108xx-hal"
[features]
default = ["irq-oc30-oc31"]
irqs-in-lib = []
# This determines the reserved interrupt functions for the embassy time drivers. Only one
# is allowed to be selected!
irq-oc28-oc29 = ["irqs-in-lib"]
irq-oc29-oc30 = ["irqs-in-lib"]
irq-oc30-oc31 = ["irqs-in-lib"]

10
va108xx-embassy/README.md Normal file
View File

@ -0,0 +1,10 @@
[![Crates.io](https://img.shields.io/crates/v/va108xx-embassy)](https://crates.io/crates/va108xx-embassy)
[![docs.rs](https://img.shields.io/docsrs/va108xx-embassy)](https://docs.rs/va108xx-embassy)
# Embassy-rs support for the Vorago VA108xx MCU family
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
peripherals provided by the VA108xx family for this purpose.
The documentation contains more information on how to use this crate.

416
va108xx-embassy/src/lib.rs Normal file
View File

@ -0,0 +1,416 @@
//! # Embassy-rs support for the Vorago VA108xx MCU family
//!
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
//! VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
//! peripherals provided by the VA108xx family for this purpose.
//!
//! ## Usage
//!
//! This library only exposes the [embassy::init] method which sets up the time driver. This
//! function must be called once at the start of the application.
//!
//! This implementation requires two TIM peripherals provided by the VA108xx device.
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
//! into the [embassy::init_with_custom_irqs] and [embassy::init] method.
//!
//! The application also requires two interrupt handlers to handle the timekeeper and alarm
//! interrupts. By default, this library will define the interrupt handler inside the library
//! itself by using the `irq-oc30-oc31` feature flag. This library exposes three combinations:
//!
//! - `irq-oc30-oc31`: Uses [pac::Interrupt::OC30] and [pac::Interrupt::OC31]
//! - `irq-oc29-oc30`: Uses [pac::Interrupt::OC29] and [pac::Interrupt::OC30]
//! - `irq-oc28-oc29`: Uses [pac::Interrupt::OC28] and [pac::Interrupt::OC20]
//!
//! You can disable the default features and then specify one of the features above to use the
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
//! using the [embassy::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
//!
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
#![no_std]
use core::cell::{Cell, RefCell};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use portable_atomic::{AtomicU32, Ordering};
use embassy_time_driver::{time_driver_impl, Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use once_cell::sync::OnceCell;
#[cfg(feature = "irqs-in-lib")]
use va108xx_hal::pac::interrupt;
use va108xx_hal::{
clock::enable_peripheral_clock,
enable_interrupt, pac,
prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
PeripheralSelect,
};
time_driver_impl!(
static TIME_DRIVER: TimerDriver = TimerDriver {
periods: AtomicU32::new(0),
alarms: Mutex::new(AlarmState::new()),
queue: Mutex::new(RefCell::new(Queue::new())),
});
/// Macro to define the IRQ handlers for the time driver.
///
/// By default, the code generated by this macro will be defined inside the library depending on
/// the feature flags specified. However, the macro is exported to allow users to specify the
/// interrupt handlers themselves.
///
/// Please note that you have to explicitely import the [va108xx_hal::pac::interrupt]
/// macro in the application code in case this macro is used there.
#[macro_export]
macro_rules! embassy_time_driver_irqs {
(
timekeeper_irq = $timekeeper_irq:ident,
alarm_irq = $alarm_irq:ident
) => {
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::$timekeeper_irq;
#[interrupt]
#[allow(non_snake_case)]
fn $timekeeper_irq() {
// Safety: We call it once here.
unsafe { $crate::embassy::time_driver().on_interrupt_timekeeping() }
}
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
#[interrupt]
#[allow(non_snake_case)]
fn $alarm_irq() {
// Safety: We call it once here.
unsafe { $crate::embassy::time_driver().on_interrupt_alarm() }
}
};
}
// Provide three combinations of IRQs for the time driver by default.
#[cfg(feature = "irq-oc30-oc31")]
embassy_time_driver_irqs!(timekeeper_irq = OC31, alarm_irq = OC30);
#[cfg(feature = "irq-oc29-oc30")]
embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
#[cfg(feature = "irq-oc28-oc29")]
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
pub mod embassy {
use super::*;
use va108xx_hal::{pac, timer::TimRegInterface};
/// Expose the time driver so the user can specify the IRQ handlers themselves.
pub fn time_driver() -> &'static TimerDriver {
&TIME_DRIVER
}
/// Initialization method for embassy
///
/// # Safety
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
#[cfg(feature = "irqs-in-lib")]
pub unsafe fn init(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
TIMEKEEPER_IRQ,
ALARM_IRQ,
)
}
/// Initialization method for embassy
///
/// # Safety
///
/// This has to be called once at initialization time to initiate the time driver for
/// embassy.
pub unsafe fn init_with_custom_irqs(
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
TIME_DRIVER.init(
syscfg,
irqsel,
sysclk,
timekeeper_tim,
alarm_tim,
timekeeper_irq,
alarm_irq,
)
}
}
struct AlarmState {
timestamp: Cell<u64>,
}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
unsafe impl Send for AlarmState {}
static SCALE: OnceCell<u64> = OnceCell::new();
static TIMEKEEPER_TIM: OnceCell<u8> = OnceCell::new();
static ALARM_TIM: OnceCell<u8> = OnceCell::new();
pub struct TimerDriver {
periods: AtomicU32,
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<AlarmState>,
queue: Mutex<RefCell<Queue>>,
}
impl TimerDriver {
#[allow(clippy::too_many_arguments)]
fn init(
&self,
syscfg: &mut pac::Sysconfig,
irqsel: &pac::Irqsel,
sysclk: impl Into<Hertz>,
timekeeper_tim: impl TimRegInterface,
alarm_tim: impl TimRegInterface,
timekeeper_irq: pac::Interrupt,
alarm_irq: pac::Interrupt,
) {
if ALARM_TIM.get().is_some() {
return;
}
ALARM_TIM.set(alarm_tim.tim_id()).ok();
TIMEKEEPER_TIM.set(timekeeper_tim.tim_id()).ok();
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
enable_tim_clk(syscfg, timekeeper_tim.tim_id());
let timekeeper_reg_block = timekeeper_tim.reg_block();
let alarm_tim_reg_block = alarm_tim.reg_block();
let sysclk = sysclk.into();
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
timekeeper_reg_block
.rst_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Decrementing counter.
timekeeper_reg_block
.cnt_value()
.write(|w| unsafe { w.bits(u32::MAX) });
// Switch on. Timekeeping should always be done.
irqsel
.tim0(timekeeper_tim.tim_id() as usize)
.write(|w| unsafe { w.bits(timekeeper_irq as u32) });
unsafe {
enable_interrupt(timekeeper_irq);
}
timekeeper_reg_block
.ctrl()
.modify(|_, w| w.irq_enb().set_bit());
timekeeper_reg_block
.enable()
.write(|w| unsafe { w.bits(1) });
enable_tim_clk(syscfg, alarm_tim.tim_id());
// Explicitely disable alarm timer until needed.
alarm_tim_reg_block.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_interrupt(alarm_irq);
}
irqsel
.tim0(alarm_tim.tim_id() as usize)
.write(|w| unsafe { w.bits(alarm_irq as u32) });
}
/// Should be called inside the IRQ of the timekeeper timer.
///
/// # Safety
///
/// This function has to be called once by the TIM IRQ used for the timekeeping.
pub unsafe fn on_interrupt_timekeeping(&self) {
self.next_period();
}
/// Should be called inside the IRQ of the alarm timer.
///
/// # Safety
///
///This function has to be called once by the TIM IRQ used for the timekeeping.
pub unsafe fn on_interrupt_alarm(&self) {
critical_section::with(|cs| {
if self.alarms.borrow(cs).timestamp.get() <= self.now() {
self.trigger_alarm(cs)
}
})
}
fn timekeeper_tim() -> &'static pac::tim0::RegisterBlock {
TIMEKEEPER_TIM
.get()
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
.unwrap()
}
fn alarm_tim() -> &'static pac::tim0::RegisterBlock {
ALARM_TIM
.get()
.map(|idx| unsafe { get_tim_raw(*idx as usize) })
.unwrap()
}
fn next_period(&self) {
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
let t = (period as u64) << 32;
critical_section::with(|cs| {
let alarm = &self.alarms.borrow(cs);
let at = alarm.timestamp.get();
if at < t {
self.trigger_alarm(cs);
} else {
let alarm_tim = Self::alarm_tim();
let remaining_ticks = (at - t).checked_mul(*SCALE.get().unwrap());
if remaining_ticks.is_some_and(|v| v <= u32::MAX as u64) {
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
}
}
})
}
fn trigger_alarm(&self, cs: CriticalSection) {
Self::alarm_tim().ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = &self.alarms.borrow(cs);
// Setting the maximum value disables the alarm.
alarm.timestamp.set(u64::MAX);
// Call after clearing alarm, so the callback can set another alarm.
let mut next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = self
.queue
.borrow(cs)
.borrow_mut()
.next_expiration(self.now());
}
}
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
if SCALE.get().is_none() {
return false;
}
let alarm_tim = Self::alarm_tim();
alarm_tim.ctrl().modify(|_, w| {
w.irq_enb().clear_bit();
w.enable().clear_bit()
});
let alarm = self.alarms.borrow(cs);
alarm.timestamp.set(timestamp);
let t = self.now();
if timestamp <= t {
alarm.timestamp.set(u64::MAX);
return false;
}
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
// the interrupts are enabled or not. When they are enabled at a later point, the
// right value is already set.
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
// is not missed.
//
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
// and we don't do that here.
let safe_timestamp = timestamp.max(t + 3);
let timer_ticks = (safe_timestamp - t).checked_mul(*SCALE.get().unwrap());
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
if timer_ticks.is_some_and(|v| v <= u32::MAX as u64) {
alarm_tim
.cnt_value()
.write(|w| unsafe { w.bits(timer_ticks.unwrap() as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
// If it's too far in the future, don't enable timer yet.
// It will be enabled later by `next_period`.
true
}
}
impl Driver for TimerDriver {
fn now(&self) -> u64 {
if SCALE.get().is_none() {
return 0;
}
let mut period1: u32;
let mut period2: u32;
let mut counter_val: u32;
loop {
// Acquire ensures that we get the latest value of `periods` and
// no instructions can be reordered before the load.
period1 = self.periods.load(Ordering::Acquire);
counter_val = u32::MAX - Self::timekeeper_tim().cnt_value().read().bits();
// Double read to protect against race conditions when the counter is overflowing.
period2 = self.periods.load(Ordering::Relaxed);
if period1 == period2 {
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
return now;
}
}
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}
}
})
}
}

View File

@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [v0.9.0] 2024-10-07 ## [v0.9.0]
- Deleted some HAL re-exports in the PWM module - Deleted some HAL re-exports in the PWM module
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
- Simplified PWM module implementation.
## [v0.8.0] 2024-09-30 ## [v0.8.0] 2024-09-30

View File

@ -15,32 +15,18 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
nb = "1" nb = "1"
paste = "1" paste = "1"
embedded-hal = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
fugit = "0.3" fugit = "0.3"
typenum = "1" typenum = "1"
critical-section = "1" critical-section = "1"
delegate = "0.12" delegate = ">=0.12, <=0.13"
void = { version = "1", default-features = false }
once_cell = {version = "1", default-features = false }
va108xx = { version = "0.3", default-features = false, features = ["critical-section"]}
[dependencies.va108xx] defmt = { version = "0.3", optional = true }
version = "0.3"
default-features = false
features = ["critical-section"]
[dependencies.embedded-hal]
version = "1"
[dependencies.void]
version = "1"
default-features = false
[dependencies.once_cell]
version = "1.14"
default-features = false
[dependencies.defmt]
version = "0.3"
optional = true
[features] [features]
default = ["rt"] default = ["rt"]

View File

@ -10,14 +10,12 @@ use core::marker::PhantomData;
use crate::pac; use crate::pac;
use crate::time::Hertz; use crate::time::Hertz;
use crate::timer::{ use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin};
TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin,
};
use crate::{clock::enable_peripheral_clock, gpio::DynPinId}; use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX; const DUTY_MAX: u16 = u16::MAX;
pub struct PwmBase { pub struct PwmCommon {
sys_clk: Hertz, sys_clk: Hertz,
/// For PWMB, this is the upper limit /// For PWMB, this is the upper limit
current_duty: u16, current_duty: u16,
@ -35,123 +33,13 @@ enum StatusSelPwm {
pub struct PwmA {} pub struct PwmA {}
pub struct PwmB {} pub struct PwmB {}
//==================================================================================================
// Common
//==================================================================================================
macro_rules! pwm_common_func {
() => {
#[inline]
fn enable_pwm_a(&mut self) {
self.reg
.reg()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
}
#[inline]
fn enable_pwm_b(&mut self) {
self.reg
.reg()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.pwm_base.current_period
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.pwm_base.current_period = period.into();
// Avoid division by 0
if self.pwm_base.current_period.raw() == 0 {
return;
}
self.pwm_base.current_rst_val =
self.pwm_base.sys_clk.raw() / self.pwm_base.current_period.raw();
self.reg
.reg()
.rst_value()
.write(|w| unsafe { w.bits(self.pwm_base.current_rst_val) });
}
#[inline]
pub fn disable(&mut self) {
self.reg.reg().ctrl().modify(|_, w| w.enable().clear_bit());
}
#[inline]
pub fn enable(&mut self) {
self.reg.reg().ctrl().modify(|_, w| w.enable().set_bit());
}
#[inline]
pub fn period(&self) -> Hertz {
self.pwm_base.current_period
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.pwm_base.current_duty
}
};
}
macro_rules! pwmb_func {
() => {
pub fn pwmb_lower_limit(&self) -> u16 {
self.pwm_base.current_lower_limit
}
pub fn pwmb_upper_limit(&self) -> u16 {
self.pwm_base.current_duty
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.pwm_base.current_lower_limit = duty;
let pwmb_val: u64 = (self.pwm_base.current_rst_val as u64
* self.pwm_base.current_lower_limit as u64)
/ DUTY_MAX as u64;
self.reg
.reg()
.pwmb_value()
.write(|w| unsafe { w.bits(pwmb_val as u32) });
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.pwm_base.current_duty = duty;
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
* self.pwm_base.current_duty as u64)
/ DUTY_MAX as u64;
self.reg
.reg()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
}
};
}
//================================================================================================== //==================================================================================================
// Strongly typed PWM pin // Strongly typed PWM pin
//================================================================================================== //==================================================================================================
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> { pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
reg: TimAndPinRegister<Pin, Tim>, pin_and_tim: (Pin, Tim),
pwm_base: PwmBase, inner: ReducedPwmPin<Mode>,
mode: PhantomData<Mode>, mode: PhantomData<Mode>,
} }
@ -163,34 +51,82 @@ where
pub fn new( pub fn new(
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
sys_clk: impl Into<Hertz> + Copy, sys_clk: impl Into<Hertz> + Copy,
tim_and_pin: (Pin, Tim), pin_and_tim: (Pin, Tim),
initial_period: impl Into<Hertz> + Copy, initial_period: impl Into<Hertz> + Copy,
) -> Self { ) -> Self {
let mut pin = PwmPin { let mut pin = PwmPin {
pwm_base: PwmBase { pin_and_tim,
inner: ReducedPwmPin::<Mode>::new(
Tim::TIM_ID,
Pin::DYN,
PwmCommon {
current_duty: 0, current_duty: 0,
current_lower_limit: 0, current_lower_limit: 0,
current_period: initial_period.into(), current_period: initial_period.into(),
current_rst_val: 0, current_rst_val: 0,
sys_clk: sys_clk.into(), sys_clk: sys_clk.into(),
}, },
reg: unsafe { TimAndPinRegister::new(tim_and_pin.0, tim_and_pin.1) }, ),
//unsafe { TimAndPin::new(tim_and_pin.0, tim_and_pin.1) },
mode: PhantomData, mode: PhantomData,
}; };
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio); enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig); enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
sys_cfg sys_cfg
.tim_clk_enable() .tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | pin.reg.mask_32()) }); .modify(|r, w| unsafe { w.bits(r.bits() | pin.pin_and_tim.1.mask_32()) });
pin.enable_pwm_a(); pin.enable_pwm_a();
pin.set_period(initial_period); pin.set_period(initial_period);
pin pin
} }
pub fn release(self) -> (Pin, Tim) {
self.reg.release() pub fn reduce(self) -> ReducedPwmPin<Mode> {
self.inner
} }
pwm_common_func!(); pub fn release(self) -> (Pin, Tim) {
self.pin_and_tim
}
#[inline]
fn enable_pwm_a(&mut self) {
self.inner.enable_pwm_a();
}
#[inline]
fn enable_pwm_b(&mut self) {
self.inner.enable_pwm_b();
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.inner.get_period()
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.inner.set_period(period);
}
#[inline]
pub fn disable(&mut self) {
self.inner.disable();
}
#[inline]
pub fn enable(&mut self) {
self.inner.enable();
}
#[inline]
pub fn period(&self) -> Hertz {
self.inner.period()
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.inner.duty()
}
} }
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB> impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for PwmPin<Pin, Tim, PwmB>
@ -199,9 +135,9 @@ where
{ {
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self { fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg,
pwm_base: other.pwm_base,
mode: PhantomData, mode: PhantomData,
pin_and_tim: other.pin_and_tim,
inner: other.inner.into(),
}; };
pwmb.enable_pwm_b(); pwmb.enable_pwm_b();
pwmb pwmb
@ -213,13 +149,13 @@ where
(PIN, TIM): ValidTimAndPin<PIN, TIM>, (PIN, TIM): ValidTimAndPin<PIN, TIM>,
{ {
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self { fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
let mut pwmb = Self { let mut pwma = Self {
reg: other.reg,
pwm_base: other.pwm_base,
mode: PhantomData, mode: PhantomData,
pin_and_tim: other.pin_and_tim,
inner: other.inner.into(),
}; };
pwmb.enable_pwm_a(); pwma.enable_pwm_a();
pwmb pwma
} }
} }
@ -263,33 +199,105 @@ where
/// Reduced version where type information is deleted /// Reduced version where type information is deleted
pub struct ReducedPwmPin<Mode = PwmA> { pub struct ReducedPwmPin<Mode = PwmA> {
reg: TimDynRegister, dyn_reg: TimDynRegister,
pwm_base: PwmBase, common: PwmCommon,
pin_id: DynPinId,
mode: PhantomData<Mode>, mode: PhantomData<Mode>,
} }
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> { impl<Mode> ReducedPwmPin<Mode> {
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self { pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
ReducedPwmPin { Self {
reg: TimDynRegister::from(pwm_pin.reg), dyn_reg: TimDynRegister { tim_id, pin_id },
pwm_base: pwm_pin.pwm_base, common,
pin_id: PIN::DYN,
mode: PhantomData, mode: PhantomData,
} }
} }
} }
/*
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim>> for ReducedPwmPin<PwmA> {
fn from(pwm_pin: PwmPin<Pin, Tim>) -> Self {
ReducedPwmPin {
dyn_reg: TimDynRegister {
impl<MODE> ReducedPwmPin<MODE> {
pwm_common_func!(); }
// ::from(pwm_pin.reg),
common: pwm_pin.pwm_base,
pin_id: Pin::DYN,
mode: PhantomData,
}
}
}
*/
impl<Mode> ReducedPwmPin<Mode> {
#[inline]
fn enable_pwm_a(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmA as u8) });
}
#[inline]
fn enable_pwm_b(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| unsafe { w.status_sel().bits(StatusSelPwm::PwmB as u8) });
}
#[inline]
pub fn get_period(&self) -> Hertz {
self.common.current_period
}
#[inline]
pub fn set_period(&mut self, period: impl Into<Hertz>) {
self.common.current_period = period.into();
// Avoid division by 0
if self.common.current_period.raw() == 0 {
return;
}
self.common.current_rst_val = self.common.sys_clk.raw() / self.common.current_period.raw();
self.dyn_reg
.reg_block()
.rst_value()
.write(|w| unsafe { w.bits(self.common.current_rst_val) });
}
#[inline]
pub fn disable(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().clear_bit());
}
#[inline]
pub fn enable(&mut self) {
self.dyn_reg
.reg_block()
.ctrl()
.modify(|_, w| w.enable().set_bit());
}
#[inline]
pub fn period(&self) -> Hertz {
self.common.current_period
}
#[inline(always)]
pub fn duty(&self) -> u16 {
self.common.current_duty
}
} }
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> { impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
fn from(other: ReducedPwmPin<PwmA>) -> Self { fn from(other: ReducedPwmPin<PwmA>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg, dyn_reg: other.dyn_reg,
pwm_base: other.pwm_base, common: other.common,
pin_id: other.pin_id,
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_b(); pwmb.enable_pwm_b();
@ -300,9 +308,8 @@ impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> { impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
fn from(other: ReducedPwmPin<PwmB>) -> Self { fn from(other: ReducedPwmPin<PwmB>) -> Self {
let mut pwmb = Self { let mut pwmb = Self {
reg: other.reg, dyn_reg: other.dyn_reg,
pwm_base: other.pwm_base, common: other.common,
pin_id: other.pin_id,
mode: PhantomData, mode: PhantomData,
}; };
pwmb.enable_pwm_a(); pwmb.enable_pwm_a();
@ -314,15 +321,83 @@ impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
// PWMB implementations // PWMB implementations
//================================================================================================== //==================================================================================================
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB> impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
where where
(PIN, TIM): ValidTimAndPin<PIN, TIM>, (Pin, Tim): ValidTimAndPin<Pin, Tim>,
{ {
pwmb_func!(); pub fn pwmb_lower_limit(&self) -> u16 {
self.inner.pwmb_lower_limit()
}
pub fn pwmb_upper_limit(&self) -> u16 {
self.inner.pwmb_upper_limit()
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.inner.set_pwmb_lower_limit(duty);
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.inner.set_pwmb_upper_limit(duty);
}
} }
impl ReducedPwmPin<PwmB> { impl ReducedPwmPin<PwmB> {
pwmb_func!(); #[inline(always)]
pub fn pwmb_lower_limit(&self) -> u16 {
self.common.current_lower_limit
}
#[inline(always)]
pub fn pwmb_upper_limit(&self) -> u16 {
self.common.current_duty
}
/// Set the lower limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is larger than
/// the lower limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
#[inline(always)]
pub fn set_pwmb_lower_limit(&mut self, duty: u16) {
self.common.current_lower_limit = duty;
let pwmb_val: u64 = (self.common.current_rst_val as u64
* self.common.current_lower_limit as u64)
/ DUTY_MAX as u64;
self.dyn_reg
.reg_block()
.pwmb_value()
.write(|w| unsafe { w.bits(pwmb_val as u32) });
}
/// Set the higher limit for PWMB
///
/// The PWM signal will be 1 as long as the current RST counter is smaller than
/// the higher limit. For example, with a lower limit of 0.5 and and an upper limit
/// of 0.7, Only a fixed period between 0.5 * period and 0.7 * period will be in a high
/// state
pub fn set_pwmb_upper_limit(&mut self, duty: u16) {
self.common.current_duty = duty;
let pwma_val: u64 = (self.common.current_rst_val as u64 * self.common.current_duty as u64)
/ DUTY_MAX as u64;
self.dyn_reg
.reg_block()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
}
} }
//================================================================================================== //==================================================================================================
@ -345,12 +420,12 @@ impl embedded_hal::pwm::SetDutyCycle for ReducedPwmPin {
#[inline] #[inline]
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.pwm_base.current_duty = duty; self.common.current_duty = duty;
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64 let pwma_val: u64 = (self.common.current_rst_val as u64
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64)) * (DUTY_MAX as u64 - self.common.current_duty as u64))
/ DUTY_MAX as u64; / DUTY_MAX as u64;
self.reg self.dyn_reg
.reg() .reg_block()
.pwma_value() .pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) }); .write(|w| unsafe { w.bits(pwma_val as u32) });
Ok(()) Ok(())
@ -365,15 +440,7 @@ impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin,
#[inline] #[inline]
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
self.pwm_base.current_duty = duty; self.inner.set_duty_cycle(duty)
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
/ DUTY_MAX as u64;
self.reg
.reg()
.pwma_value()
.write(|w| unsafe { w.bits(pwma_val as u32) });
Ok(())
} }
} }

View File

@ -26,6 +26,48 @@ use fugit::RateExtU32;
const IRQ_DST_NONE: u32 = 0xffffffff; const IRQ_DST_NONE: u32 = 0xffffffff;
pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0)); pub static MS_COUNTER: Mutex<Cell<u32>> = Mutex::new(Cell::new(0));
/// Get the peripheral block of a TIM peripheral given the index.
///
/// This function panics if the given index is greater than 23.
///
/// # Safety
///
/// This returns a direct handle to the peripheral block, which allows to circumvent ownership
/// rules for the peripheral block. You have to ensure that the retrieved peripheral block is not
/// used by any other software component.
#[inline(always)]
pub const unsafe fn get_tim_raw(tim_idx: usize) -> &'static pac::tim0::RegisterBlock {
match tim_idx {
0 => unsafe { &*pac::Tim0::ptr() },
1 => unsafe { &*pac::Tim1::ptr() },
2 => unsafe { &*pac::Tim2::ptr() },
3 => unsafe { &*pac::Tim3::ptr() },
4 => unsafe { &*pac::Tim4::ptr() },
5 => unsafe { &*pac::Tim5::ptr() },
6 => unsafe { &*pac::Tim6::ptr() },
7 => unsafe { &*pac::Tim7::ptr() },
8 => unsafe { &*pac::Tim8::ptr() },
9 => unsafe { &*pac::Tim9::ptr() },
10 => unsafe { &*pac::Tim10::ptr() },
11 => unsafe { &*pac::Tim11::ptr() },
12 => unsafe { &*pac::Tim12::ptr() },
13 => unsafe { &*pac::Tim13::ptr() },
14 => unsafe { &*pac::Tim14::ptr() },
15 => unsafe { &*pac::Tim15::ptr() },
16 => unsafe { &*pac::Tim16::ptr() },
17 => unsafe { &*pac::Tim17::ptr() },
18 => unsafe { &*pac::Tim18::ptr() },
19 => unsafe { &*pac::Tim19::ptr() },
20 => unsafe { &*pac::Tim20::ptr() },
21 => unsafe { &*pac::Tim21::ptr() },
22 => unsafe { &*pac::Tim22::ptr() },
23 => unsafe { &*pac::Tim23::ptr() },
_ => {
panic!("invalid alarm timer index")
}
}
}
//================================================================================================== //==================================================================================================
// Defintions // Defintions
//================================================================================================== //==================================================================================================
@ -248,7 +290,7 @@ pub type TimRegBlock = tim0::RegisterBlock;
/// implementations should be overridden. The implementing type must also have /// implementations should be overridden. The implementing type must also have
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each /// "control" over the corresponding pin ID, i.e. it must guarantee that a each
/// pin ID is a singleton. /// pin ID is a singleton.
pub(super) unsafe trait TimRegInterface { pub unsafe trait TimRegInterface {
fn tim_id(&self) -> u8; fn tim_id(&self) -> u8;
const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _; const PORT_BASE: *const tim0::RegisterBlock = pac::Tim0::ptr() as *const _;
@ -256,7 +298,7 @@ pub(super) unsafe trait TimRegInterface {
/// All 24 TIM blocks are identical. This helper functions returns the correct /// All 24 TIM blocks are identical. This helper functions returns the correct
/// memory mapped peripheral depending on the TIM ID. /// memory mapped peripheral depending on the TIM ID.
#[inline(always)] #[inline(always)]
fn reg(&self) -> &TimRegBlock { fn reg_block(&self) -> &TimRegBlock {
unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) } unsafe { &*Self::PORT_BASE.offset(self.tim_id() as isize) }
} }
@ -293,70 +335,16 @@ pub(super) unsafe trait TimRegInterface {
} }
} }
/// Provide a safe register interface for [`ValidTimAndPin`]s unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
///
/// This `struct` takes ownership of a [`ValidTimAndPin`] and provides an API to
/// access the corresponding registers.
pub(super) struct TimAndPinRegister<Pin: TimPin, Tim: ValidTim> {
pin: Pin,
tim: Tim,
}
pub(super) struct TimRegister<TIM: ValidTim> {
tim: TIM,
}
impl<TIM: ValidTim> TimRegister<TIM> {
#[inline]
pub(super) unsafe fn new(tim: TIM) -> Self {
TimRegister { tim }
}
pub(super) fn release(self) -> TIM {
self.tim
}
}
unsafe impl<TIM: ValidTim> TimRegInterface for TimRegister<TIM> {
fn tim_id(&self) -> u8 { fn tim_id(&self) -> u8 {
TIM::TIM_ID Tim::TIM_ID
} }
} }
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM> pub(crate) struct TimDynRegister {
where pub(crate) tim_id: u8,
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
{
#[inline]
pub(super) unsafe fn new(pin: PIN, tim: TIM) -> Self {
TimAndPinRegister { pin, tim }
}
pub(super) fn release(self) -> (PIN, TIM) {
(self.pin, self.tim)
}
}
unsafe impl<PIN: TimPin, TIM: ValidTim> TimRegInterface for TimAndPinRegister<PIN, TIM> {
#[inline(always)]
fn tim_id(&self) -> u8 {
TIM::TIM_ID
}
}
pub(super) struct TimDynRegister {
tim_id: u8,
#[allow(dead_code)] #[allow(dead_code)]
pin_id: DynPinId, pub(crate) pin_id: DynPinId,
}
impl<PIN: TimPin, TIM: ValidTim> From<TimAndPinRegister<PIN, TIM>> for TimDynRegister {
fn from(_reg: TimAndPinRegister<PIN, TIM>) -> Self {
Self {
tim_id: TIM::TIM_ID,
pin_id: PIN::DYN,
}
}
} }
unsafe impl TimRegInterface for TimDynRegister { unsafe impl TimRegInterface for TimDynRegister {
@ -371,8 +359,8 @@ unsafe impl TimRegInterface for TimDynRegister {
//================================================================================================== //==================================================================================================
/// Hardware timers /// Hardware timers
pub struct CountdownTimer<TIM: ValidTim> { pub struct CountdownTimer<Tim: ValidTim> {
tim: TimRegister<TIM>, tim: Tim,
curr_freq: Hertz, curr_freq: Hertz,
irq_cfg: Option<IrqCfg>, irq_cfg: Option<IrqCfg>,
sys_clk: Hertz, sys_clk: Hertz,
@ -401,12 +389,12 @@ unsafe impl<TIM: ValidTim> TimRegInterface for CountdownTimer<TIM> {
} }
} }
impl<TIM: ValidTim> CountdownTimer<TIM> { impl<Tim: ValidTim> CountdownTimer<Tim> {
/// Configures a TIM peripheral as a periodic count down timer /// Configures a TIM peripheral as a periodic count down timer
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self { pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: Tim) -> Self {
enable_tim_clk(syscfg, TIM::TIM_ID); enable_tim_clk(syscfg, Tim::TIM_ID);
let cd_timer = CountdownTimer { let cd_timer = CountdownTimer {
tim: unsafe { TimRegister::new(tim) }, tim,
sys_clk: sys_clk.into(), sys_clk: sys_clk.into(),
irq_cfg: None, irq_cfg: None,
rst_val: 0, rst_val: 0,
@ -416,7 +404,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
}; };
cd_timer cd_timer
.tim .tim
.reg() .reg_block()
.ctrl() .ctrl()
.modify(|_, w| w.enable().set_bit()); .modify(|_, w| w.enable().set_bit());
cd_timer cd_timer
@ -441,7 +429,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
} }
if let Some(irq_sel) = irq_sel { if let Some(irq_sel) = irq_sel {
irq_sel irq_sel
.tim0(TIM::TIM_ID as usize) .tim0(Tim::TIM_ID as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); .write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
} }
} }
@ -460,7 +448,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
Event::TimeOut => { Event::TimeOut => {
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel); enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
irqsel irqsel
.tim0(TIM::TIM_ID as usize) .tim0(Tim::TIM_ID as usize)
.write(|w| unsafe { w.bits(IRQ_DST_NONE) }); .write(|w| unsafe { w.bits(IRQ_DST_NONE) });
self.disable_interrupt(); self.disable_interrupt();
self.listening = false; self.listening = false;
@ -470,25 +458,37 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
#[inline(always)] #[inline(always)]
pub fn enable_interrupt(&mut self) { pub fn enable_interrupt(&mut self) {
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().set_bit()); self.tim
.reg_block()
.ctrl()
.modify(|_, w| w.irq_enb().set_bit());
} }
#[inline(always)] #[inline(always)]
pub fn disable_interrupt(&mut self) { pub fn disable_interrupt(&mut self) {
self.tim.reg().ctrl().modify(|_, w| w.irq_enb().clear_bit()); self.tim
.reg_block()
.ctrl()
.modify(|_, w| w.irq_enb().clear_bit());
} }
pub fn release(self, syscfg: &mut pac::Sysconfig) -> TIM { pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
self.tim.reg().ctrl().write(|w| w.enable().clear_bit()); self.tim
.reg_block()
.ctrl()
.write(|w| w.enable().clear_bit());
syscfg syscfg
.tim_clk_enable() .tim_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << TIM::TIM_ID)) }); .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) });
self.tim.release() self.tim
} }
/// Load the count down timer with a timeout but do not start it. /// Load the count down timer with a timeout but do not start it.
pub fn load(&mut self, timeout: impl Into<Hertz>) { pub fn load(&mut self, timeout: impl Into<Hertz>) {
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit()); self.tim
.reg_block()
.ctrl()
.modify(|_, w| w.enable().clear_bit());
self.curr_freq = timeout.into(); self.curr_freq = timeout.into();
self.rst_val = self.sys_clk.raw() / self.curr_freq.raw(); self.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
self.set_reload(self.rst_val); self.set_reload(self.rst_val);
@ -497,17 +497,23 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
#[inline(always)] #[inline(always)]
pub fn set_reload(&mut self, val: u32) { pub fn set_reload(&mut self, val: u32) {
self.tim.reg().rst_value().write(|w| unsafe { w.bits(val) }); self.tim
.reg_block()
.rst_value()
.write(|w| unsafe { w.bits(val) });
} }
#[inline(always)] #[inline(always)]
pub fn set_count(&mut self, val: u32) { pub fn set_count(&mut self, val: u32) {
self.tim.reg().cnt_value().write(|w| unsafe { w.bits(val) }); self.tim
.reg_block()
.cnt_value()
.write(|w| unsafe { w.bits(val) });
} }
#[inline(always)] #[inline(always)]
pub fn count(&self) -> u32 { pub fn count(&self) -> u32 {
self.tim.reg().cnt_value().read().bits() self.tim.reg_block().cnt_value().read().bits()
} }
#[inline(always)] #[inline(always)]
@ -518,24 +524,30 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
unsafe { enable_interrupt(irq_cfg.irq) }; unsafe { enable_interrupt(irq_cfg.irq) };
} }
} }
self.tim.reg().enable().write(|w| unsafe { w.bits(1) }); self.tim
.reg_block()
.enable()
.write(|w| unsafe { w.bits(1) });
} }
#[inline(always)] #[inline(always)]
pub fn disable(&mut self) { pub fn disable(&mut self) {
self.tim.reg().enable().write(|w| unsafe { w.bits(0) }); self.tim
.reg_block()
.enable()
.write(|w| unsafe { w.bits(0) });
} }
/// Disable the counter, setting both enable and active bit to 0 /// Disable the counter, setting both enable and active bit to 0
pub fn auto_disable(self, enable: bool) -> Self { pub fn auto_disable(self, enable: bool) -> Self {
if enable { if enable {
self.tim self.tim
.reg() .reg_block()
.ctrl() .ctrl()
.modify(|_, w| w.auto_disable().set_bit()); .modify(|_, w| w.auto_disable().set_bit());
} else { } else {
self.tim self.tim
.reg() .reg_block()
.ctrl() .ctrl()
.modify(|_, w| w.auto_disable().clear_bit()); .modify(|_, w| w.auto_disable().clear_bit());
} }
@ -549,12 +561,12 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
pub fn auto_deactivate(self, enable: bool) -> Self { pub fn auto_deactivate(self, enable: bool) -> Self {
if enable { if enable {
self.tim self.tim
.reg() .reg_block()
.ctrl() .ctrl()
.modify(|_, w| w.auto_deactivate().set_bit()); .modify(|_, w| w.auto_deactivate().set_bit());
} else { } else {
self.tim self.tim
.reg() .reg_block()
.ctrl() .ctrl()
.modify(|_, w| w.auto_deactivate().clear_bit()); .modify(|_, w| w.auto_deactivate().clear_bit());
} }
@ -563,7 +575,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
/// Configure the cascade parameters /// Configure the cascade parameters
pub fn cascade_control(&mut self, ctrl: CascadeCtrl) { pub fn cascade_control(&mut self, ctrl: CascadeCtrl) {
self.tim.reg().csd_ctrl().write(|w| { self.tim.reg_block().csd_ctrl().write(|w| {
w.csden0().bit(ctrl.enb_start_src_csd0); w.csden0().bit(ctrl.enb_start_src_csd0);
w.csdinv0().bit(ctrl.inv_csd0); w.csdinv0().bit(ctrl.inv_csd0);
w.csden1().bit(ctrl.enb_start_src_csd1); w.csden1().bit(ctrl.enb_start_src_csd1);
@ -580,7 +592,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { pub fn cascade_0_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?; let id = src.id()?;
self.tim self.tim
.reg() .reg_block()
.cascade0() .cascade0()
.write(|w| unsafe { w.cassel().bits(id) }); .write(|w| unsafe { w.cassel().bits(id) });
Ok(()) Ok(())
@ -589,7 +601,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?; let id = src.id()?;
self.tim self.tim
.reg() .reg_block()
.cascade1() .cascade1()
.write(|w| unsafe { w.cassel().bits(id) }); .write(|w| unsafe { w.cassel().bits(id) });
Ok(()) Ok(())
@ -598,7 +610,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> { pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
let id = src.id()?; let id = src.id()?;
self.tim self.tim
.reg() .reg_block()
.cascade2() .cascade2()
.write(|w| unsafe { w.cassel().bits(id) }); .write(|w| unsafe { w.cassel().bits(id) });
Ok(()) Ok(())
@ -627,7 +639,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
/// flag and restart the time if configured correctly /// flag and restart the time if configured correctly
pub fn wait(&mut self) -> nb::Result<(), void::Void> { pub fn wait(&mut self) -> nb::Result<(), void::Void> {
let cnt = self.tim.reg().cnt_value().read().bits(); let cnt = self.tim.reg_block().cnt_value().read().bits();
if (cnt > self.last_cnt) || cnt == 0 { if (cnt > self.last_cnt) || cnt == 0 {
self.last_cnt = self.rst_val; self.last_cnt = self.rst_val;
Ok(()) Ok(())
@ -639,10 +651,13 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
/// Returns [false] if the timer was not active, and true otherwise. /// Returns [false] if the timer was not active, and true otherwise.
pub fn cancel(&mut self) -> bool { pub fn cancel(&mut self) -> bool {
if !self.tim.reg().ctrl().read().enable().bit_is_set() { if !self.tim.reg_block().ctrl().read().enable().bit_is_set() {
return false; return false;
} }
self.tim.reg().ctrl().write(|w| w.enable().clear_bit()); self.tim
.reg_block()
.ctrl()
.write(|w| w.enable().clear_bit());
true true
} }
} }

3
va108xx/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -15,10 +15,8 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "1" embedded-hal = "1"
nb = "1" nb = "1"
bitfield = "0.17" bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3"
[dependencies.max116xx-10bit]
version = "0.3"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
version = ">=0.8, <0.9" version = ">=0.8, <0.9"
@ -28,11 +26,11 @@ features = ["rt"]
rt = ["va108xx-hal/rt"] rt = ["va108xx-hal/rt"]
[dev-dependencies] [dev-dependencies]
panic-halt = "0.2" panic-halt = "1"
nb = "1" nb = "1"
rtt-target = "0.5" rtt-target = "0.6"
panic-rtt-target = "0.1" panic-rtt-target = "0.2"
embedded-hal-bus = "0.2" embedded-hal-bus = "0.3"
dummy-pin = "1" dummy-pin = "1"
[package.metadata.docs.rs] [package.metadata.docs.rs]