Compare commits
5 Commits
6402b9f014
...
5c17d85a5e
Author | SHA1 | Date | |
---|---|---|---|
5c17d85a5e | |||
6842e06bc6 | |||
5b614e1280 | |||
16e5a5f197 | |||
da1f2902b2 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -10,8 +10,8 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: "thumbv6m-none-eabi"
|
||||
- run: cargo check --target thumbv6m-none-eabi --release
|
||||
- run: cargo check --target thumbv6m-none-eabi --examples --release
|
||||
- run: cargo check --target thumbv6m-none-eabi
|
||||
- run: cargo check --target thumbv6m-none-eabi --examples
|
||||
|
||||
test:
|
||||
name: Run Tests
|
||||
@ -21,7 +21,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
- run: cargo nextest run --all-features -p va108xx-hal
|
||||
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
|
||||
# I think we can skip those on an embedded crate..
|
||||
# - run: cargo test --doc -p va108xx-hal
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"vorago-reb1",
|
||||
"va108xx",
|
||||
"va108xx-hal",
|
||||
"vorago-reb1",
|
||||
"va108xx",
|
||||
"va108xx-hal",
|
||||
"va108xx-embassy",
|
||||
"examples/simple",
|
||||
"examples/rtic",
|
||||
"examples/embassy",
|
||||
@ -22,7 +23,7 @@ codegen-units = 1
|
||||
debug = 2
|
||||
debug-assertions = true # <-
|
||||
incremental = false
|
||||
# 1 instead of 0, the flashloader is too larger otherwise..
|
||||
# 1 instead of 0, the flashloader is too larger otherwise..
|
||||
# opt-level = 1 # <-
|
||||
overflow-checks = true # <-
|
||||
|
||||
|
@ -6,9 +6,9 @@ edition = "2021"
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "0.2"
|
||||
rtt-target = "0.5"
|
||||
panic-rtt-target = "0.1.3"
|
||||
panic-halt = "1"
|
||||
rtt-target = "0.6"
|
||||
panic-rtt-target = "0.2"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
|
@ -99,9 +99,11 @@ fn main() -> ! {
|
||||
}
|
||||
TestCase::TestMask => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let input = pinsa.pa1.into_pull_down_input().clear_datamask();
|
||||
let mut input = pinsa.pa1.into_pull_down_input();
|
||||
input.clear_datamask();
|
||||
assert!(!input.datamask());
|
||||
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
||||
let mut out = pinsa.pa0.into_push_pull_output();
|
||||
out.clear_datamask();
|
||||
assert!(input.is_low_masked().is_err());
|
||||
assert!(out.set_high_masked().is_err());
|
||||
}
|
||||
@ -119,17 +121,15 @@ fn main() -> ! {
|
||||
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||
}
|
||||
TestCase::Pulse => {
|
||||
let mut output_pulsed = pinsa
|
||||
.pa0
|
||||
.into_push_pull_output()
|
||||
.pulse_mode(true, PinState::Low);
|
||||
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
|
||||
output_pulsed.pulse_mode(true, PinState::Low);
|
||||
rprintln!("Pulsing high 10 times..");
|
||||
output_pulsed.set_low().unwrap();
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_high().unwrap();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High);
|
||||
output_pulsed.pulse_mode(true, PinState::High);
|
||||
rprintln!("Pulsing low 10 times..");
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_low().unwrap();
|
||||
|
@ -7,9 +7,9 @@ edition = "2021"
|
||||
cortex-m = "0.7"
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
panic-rtt-target = { version = "0.1.3" }
|
||||
panic-halt = { version = "0.2" }
|
||||
rtt-target = { version = "0.5" }
|
||||
panic-rtt-target = "0.2"
|
||||
panic-halt = "1"
|
||||
rtt-target = "0.6"
|
||||
crc = "3"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
static_assertions = "1"
|
||||
|
@ -20,6 +20,14 @@ cargo run --bin rtic-example
|
||||
|
||||
## Embassy example
|
||||
|
||||
Blinky with time driver IRQs in library
|
||||
|
||||
```rs
|
||||
cargo run --bin embassy-example
|
||||
```
|
||||
|
||||
Blinky with custom time driver IRQs
|
||||
|
||||
```rs
|
||||
cargo run --bin embassy-example --no-default-features --features custom-irqs
|
||||
```
|
||||
|
@ -4,37 +4,29 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
|
||||
rtt-target = { version = "0.5" }
|
||||
panic-rtt-target = { version = "0.1" }
|
||||
rtt-target = "0.6"
|
||||
panic-rtt-target = "0.2"
|
||||
critical-section = "1"
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||
|
||||
embassy-sync = { version = "0.6.0" }
|
||||
embassy-time = { version = "0.3.2" }
|
||||
embassy-time-driver = { version = "0.1" }
|
||||
embassy-sync = "0.6"
|
||||
embassy-time = "0.4"
|
||||
embassy-executor = { version = "0.7", features = [
|
||||
"arch-cortex-m",
|
||||
"executor-thread",
|
||||
"executor-interrupt"
|
||||
]}
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1"
|
||||
default-features = false
|
||||
features = ["critical-section"]
|
||||
|
||||
[dependencies.embassy-executor]
|
||||
version = "0.6.0"
|
||||
features = [
|
||||
"arch-cortex-m",
|
||||
"executor-thread",
|
||||
"executor-interrupt",
|
||||
"integrated-timers",
|
||||
]
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../../va108xx-hal"
|
||||
va108xx-hal = { path = "../../va108xx-hal" }
|
||||
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
|
||||
|
||||
[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-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||
|
@ -1,4 +0,0 @@
|
||||
#![no_std]
|
||||
pub mod time_driver;
|
||||
|
||||
pub use time_driver::init;
|
@ -5,6 +5,16 @@ use embassy_time::{Duration, Instant, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_rtt_target as _;
|
||||
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::*};
|
||||
|
||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||
@ -19,14 +29,28 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy_example::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
)
|
||||
};
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(feature = "custom-irqs"))] {
|
||||
embassy::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irqsel,
|
||||
SYSCLK_FREQ,
|
||||
dp.tim23,
|
||||
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 mut led0 = porta.pa10.into_readable_push_pull_output();
|
||||
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
@ -8,37 +8,19 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
embedded-io = "0.6"
|
||||
rtt-target = { version = "0.5" }
|
||||
panic-rtt-target = { version = "0.1" }
|
||||
rtt-target = "0.6"
|
||||
panic-rtt-target = "0.2"
|
||||
|
||||
# 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.
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
|
||||
|
||||
[dependencies.rtic]
|
||||
version = "2"
|
||||
features = ["thumbv6-backend"]
|
||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
||||
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
|
||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||
|
||||
[dependencies.rtic-sync]
|
||||
version = "1.3"
|
||||
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"
|
||||
va108xx-hal = "0.8"
|
||||
vorago-reb1 = { path = "../../vorago-reb1" }
|
||||
|
@ -68,10 +68,7 @@ mod app {
|
||||
Shared {
|
||||
rb: StaticRb::default(),
|
||||
},
|
||||
Local {
|
||||
rx,
|
||||
tx,
|
||||
},
|
||||
Local { rx, tx },
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,10 @@ edition = "2021"
|
||||
[dependencies]
|
||||
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||
cortex-m-rt = "0.7"
|
||||
panic-halt = "0.2"
|
||||
panic-rtt-target = "0.1"
|
||||
panic-halt = "1"
|
||||
panic-rtt-target = "0.2"
|
||||
critical-section = "1"
|
||||
rtt-target = "0.5"
|
||||
rtt-target = "0.6"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
|
@ -9,55 +9,24 @@ cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
panic-rtt-target = { version = "0.1.3" }
|
||||
rtt-target = { version = "0.5" }
|
||||
panic-rtt-target = "0.2"
|
||||
rtt-target = "0.6"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
log = "0.4"
|
||||
crc = "3"
|
||||
|
||||
[dependencies.satrs]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.rtt-log]
|
||||
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
|
||||
|
||||
cobs = { version = "0.3", default-features = false }
|
||||
satrs = { version = "0.2", default-features = false }
|
||||
rtt-log = "0.5"
|
||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||
spacepackets = { version = "0.11", default-features = false }
|
||||
# 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.
|
||||
[dependencies.portable-atomic]
|
||||
version = "1"
|
||||
features = ["unsafe-assume-single-core"]
|
||||
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
||||
|
||||
[dependencies.rtic]
|
||||
version = "2"
|
||||
features = ["thumbv6-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[dependencies.rtic-sync]
|
||||
version = "1"
|
||||
features = ["defmt-03"]
|
||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||
rtic-sync = {version = "1", features = ["defmt-03"]}
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
path = "../va108xx-hal"
|
||||
|
@ -251,10 +251,10 @@ mod app {
|
||||
}
|
||||
let packet_len = packet_len.unwrap();
|
||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||
let popped_packet_len = cx.shared.tc_rb.lock(|rb| {
|
||||
rb.buf
|
||||
.pop_slice(&mut cx.local.tc_buf[0..packet_len])
|
||||
});
|
||||
let popped_packet_len = cx
|
||||
.shared
|
||||
.tc_rb
|
||||
.lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len]));
|
||||
assert_eq!(popped_packet_len, packet_len);
|
||||
// Read a telecommand, now handle it.
|
||||
handle_valid_pus_tc(&mut cx);
|
||||
@ -272,8 +272,7 @@ mod app {
|
||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||
cx.shared.tm_rb.lock(|prod| {
|
||||
prod.sizes.try_push(tm.len_written()).unwrap();
|
||||
prod.buf
|
||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
prod.buf.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
});
|
||||
};
|
||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||
|
27
va108xx-embassy/Cargo.toml
Normal file
27
va108xx-embassy/Cargo.toml
Normal 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
10
va108xx-embassy/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
[](https://crates.io/crates/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
416
va108xx-embassy/src/lib.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -8,9 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.9.0] 2024-10-07
|
||||
## [v0.9.0]
|
||||
|
||||
- Deleted some HAL re-exports in the PWM module
|
||||
- GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now
|
||||
methods which mutable modify the pin instead of consuming and returning it.
|
||||
- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion
|
||||
methods.
|
||||
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
|
||||
- Simplified PWM module implementation.
|
||||
|
||||
## [v0.8.0] 2024-09-30
|
||||
|
||||
|
@ -15,32 +15,18 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||
cortex-m-rt = "0.7"
|
||||
nb = "1"
|
||||
paste = "1"
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
embedded-io = "0.6"
|
||||
fugit = "0.3"
|
||||
typenum = "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]
|
||||
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
|
||||
defmt = { version = "0.3", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["rt"]
|
||||
|
@ -172,7 +172,7 @@ pub struct DynPinId {
|
||||
///
|
||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||
/// access the corresponding regsiters.
|
||||
struct DynRegisters {
|
||||
pub(crate) struct DynRegisters {
|
||||
id: DynPinId,
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ impl DynRegisters {
|
||||
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||
pub struct DynPin {
|
||||
regs: DynRegisters,
|
||||
pub(crate) regs: DynRegisters,
|
||||
mode: DynPinMode,
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ impl DynPin {
|
||||
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||
/// time. Violating this requirement is `unsafe`.
|
||||
#[inline]
|
||||
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
DynPin {
|
||||
regs: DynRegisters::new(id),
|
||||
mode,
|
||||
@ -306,7 +306,69 @@ impl DynPin {
|
||||
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||
}
|
||||
|
||||
common_reg_if_functions!();
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(&mut self) {
|
||||
self.regs.clear_datamask();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(&mut self) {
|
||||
self.regs.set_datamask();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
|
||||
pub(crate) fn irq_enb(
|
||||
&mut self,
|
||||
irq_cfg: crate::IrqCfg,
|
||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||
irqsel: Option<&mut va108xx::Irqsel>,
|
||||
) {
|
||||
if let Some(syscfg) = syscfg {
|
||||
crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel);
|
||||
}
|
||||
self.regs.enable_irq();
|
||||
if let Some(irqsel) = irqsel {
|
||||
if irq_cfg.route {
|
||||
match self.regs.id().group {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
DynGroup::A => {
|
||||
irqsel
|
||||
.porta0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||
}
|
||||
DynGroup::B => {
|
||||
irqsel
|
||||
.portb0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.53 of the programmers guide for more information.
|
||||
/// Possible delays in clock cycles:
|
||||
@ -327,15 +389,16 @@ impl DynPin {
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
#[inline]
|
||||
pub fn pulse_mode(
|
||||
self,
|
||||
&mut self,
|
||||
enable: bool,
|
||||
default_state: PinState,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.pulse_mode(enable, default_state);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError),
|
||||
}
|
||||
@ -344,48 +407,50 @@ impl DynPin {
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn filter_type(
|
||||
self,
|
||||
&mut self,
|
||||
filter: FilterType,
|
||||
clksel: FilterClkSel,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) => {
|
||||
self.regs.filter_type(filter, clksel);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut pac::Sysconfig>,
|
||||
irqsel: Option<&mut pac::Irqsel>,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut pac::Sysconfig>,
|
||||
irqsel: Option<&mut pac::Irqsel>,
|
||||
) -> Result<Self, InvalidPinTypeError> {
|
||||
) -> Result<(), InvalidPinTypeError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||
self.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InvalidPinTypeError),
|
||||
}
|
||||
@ -438,6 +503,21 @@ impl DynPin {
|
||||
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||
self._write(true)
|
||||
}
|
||||
|
||||
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||
///
|
||||
/// There is no way for the compiler to know if the conversion will be
|
||||
/// successful at compile-time. We must verify the conversion at run-time
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
|
||||
if self.regs.id == I::DYN && self.mode == M::DYN {
|
||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `Pin`
|
||||
return Ok(unsafe { Pin::new() });
|
||||
}
|
||||
Err(InvalidPinTypeError)
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
@ -448,10 +528,8 @@ impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
|
||||
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||
/// [`DynPin`]
|
||||
#[inline]
|
||||
fn from(_pin: Pin<I, M>) -> Self {
|
||||
// The `Pin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `DynPin`
|
||||
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||
fn from(pin: Pin<I, M>) -> Self {
|
||||
pin.downgrade()
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,13 +543,7 @@ impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
||||
/// or refuse to perform it.
|
||||
#[inline]
|
||||
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||
// corresponding `Pin`
|
||||
Ok(unsafe { Self::new() })
|
||||
} else {
|
||||
Err(InvalidPinTypeError)
|
||||
}
|
||||
pin.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,10 +578,12 @@ impl embedded_hal::digital::InputPin for DynPin {
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::StatefulOutputPin for DynPin {
|
||||
#[inline]
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
self._is_low()
|
||||
}
|
||||
|
@ -26,81 +26,6 @@
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct IsMaskedError;
|
||||
|
||||
macro_rules! common_reg_if_functions {
|
||||
() => {
|
||||
paste::paste!(
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.regs.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(self) -> Self {
|
||||
self.regs.clear_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(self) -> Self {
|
||||
self.regs.set_datamask();
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
|
||||
fn irq_enb(
|
||||
&mut self,
|
||||
irq_cfg: crate::IrqCfg,
|
||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||
irqsel: Option<&mut va108xx::Irqsel>,
|
||||
) {
|
||||
if syscfg.is_some() {
|
||||
crate::clock::enable_peripheral_clock(
|
||||
syscfg.unwrap(),
|
||||
crate::clock::PeripheralClocks::Irqsel,
|
||||
);
|
||||
}
|
||||
self.regs.enable_irq();
|
||||
if let Some(irqsel) = irqsel {
|
||||
if irq_cfg.route {
|
||||
match self.regs.id().group {
|
||||
// Set the correct interrupt number in the IRQSEL register
|
||||
DynGroup::A => {
|
||||
irqsel
|
||||
.porta0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||
}
|
||||
DynGroup::B => {
|
||||
irqsel
|
||||
.portb0(self.regs.id().num as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub mod dynpin;
|
||||
pub use dynpin::*;
|
||||
|
||||
|
@ -72,6 +72,7 @@
|
||||
//! and [`StatefulOutputPin`].
|
||||
use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||
use super::reg::RegisterInterface;
|
||||
use super::DynPin;
|
||||
use crate::{
|
||||
pac::{Irqsel, Porta, Portb, Sysconfig},
|
||||
typelevel::Sealed,
|
||||
@ -321,8 +322,8 @@ macro_rules! pin_id {
|
||||
|
||||
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
|
||||
pub struct Pin<I: PinId, M: PinMode> {
|
||||
pub(in crate::gpio) regs: Registers<I>,
|
||||
mode: PhantomData<M>,
|
||||
inner: DynPin,
|
||||
phantom: PhantomData<(I, M)>,
|
||||
}
|
||||
|
||||
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
@ -336,8 +337,8 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
#[inline]
|
||||
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||
Pin {
|
||||
regs: Registers::new(),
|
||||
mode: PhantomData,
|
||||
inner: DynPin::new(I::DYN, M::DYN),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,7 +348,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
// This check should compile away
|
||||
if N::DYN != M::DYN {
|
||||
self.regs.change_mode::<N>();
|
||||
self.inner.regs.change_mode(N::DYN);
|
||||
}
|
||||
// Safe because we drop the existing Pin
|
||||
unsafe { Pin::new() }
|
||||
@ -407,31 +408,78 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||
self.into_mode()
|
||||
}
|
||||
|
||||
common_reg_if_functions!();
|
||||
#[inline]
|
||||
pub fn datamask(&self) -> bool {
|
||||
self.inner.datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_datamask(&mut self) {
|
||||
self.inner.clear_datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_datamask(&mut self) {
|
||||
self.inner.set_datamask()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.inner.is_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||
self.inner.is_low_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_high_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||
self.inner.set_low_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn downgrade(self) -> DynPin {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn irq_enb(
|
||||
&mut self,
|
||||
irq_cfg: crate::IrqCfg,
|
||||
syscfg: Option<&mut va108xx::Sysconfig>,
|
||||
irqsel: Option<&mut va108xx::Irqsel>,
|
||||
) {
|
||||
self.inner.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _set_high(&mut self) {
|
||||
self.regs.write_pin(true)
|
||||
self.inner.regs.write_pin(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _set_low(&mut self) {
|
||||
self.regs.write_pin(false)
|
||||
self.inner.regs.write_pin(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
|
||||
self.regs.toggle();
|
||||
self.inner.regs.toggle();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _is_low(&self) -> bool {
|
||||
!self.regs.read_pin()
|
||||
!self.inner.regs.read_pin()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn _is_high(&self) -> bool {
|
||||
self.regs.read_pin()
|
||||
self.inner.regs.read_pin()
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,27 +572,25 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) -> Self {
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
) {
|
||||
self.inner.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) -> Self {
|
||||
self.regs.interrupt_level(level_type);
|
||||
) {
|
||||
self.inner.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,7 +602,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
/// - Delay 1 + Delay 2: 3
|
||||
#[inline]
|
||||
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||
self.regs.delay(delay_1, delay_2);
|
||||
self.inner.regs.delay(delay_1, delay_2);
|
||||
self
|
||||
}
|
||||
|
||||
@ -568,42 +614,38 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self {
|
||||
self.regs.pulse_mode(enable, default_state);
|
||||
self
|
||||
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.inner.regs.pulse_mode(enable, default_state);
|
||||
}
|
||||
|
||||
pub fn interrupt_edge(
|
||||
mut self,
|
||||
&mut self,
|
||||
edge_type: InterruptEdge,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) -> Self {
|
||||
self.regs.interrupt_edge(edge_type);
|
||||
) {
|
||||
self.inner.regs.interrupt_edge(edge_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn interrupt_level(
|
||||
mut self,
|
||||
&mut self,
|
||||
level_type: InterruptLevel,
|
||||
irq_cfg: IrqCfg,
|
||||
syscfg: Option<&mut Sysconfig>,
|
||||
irqsel: Option<&mut Irqsel>,
|
||||
) -> Self {
|
||||
self.regs.interrupt_level(level_type);
|
||||
) {
|
||||
self.inner.regs.interrupt_level(level_type);
|
||||
self.irq_enb(irq_cfg, syscfg, irqsel);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||
self.regs.filter_type(filter, clksel);
|
||||
self
|
||||
pub fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.inner.regs.filter_type(filter, clksel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,47 +721,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Registers
|
||||
//==================================================================================================
|
||||
|
||||
/// Provide a safe register interface for [`Pin`]s
|
||||
///
|
||||
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||
/// access the corresponding registers.
|
||||
pub(in crate::gpio) struct Registers<I: PinId> {
|
||||
id: PhantomData<I>,
|
||||
}
|
||||
|
||||
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
|
||||
// each pin is a singleton, so this implementation is safe.
|
||||
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
|
||||
#[inline]
|
||||
fn id(&self) -> DynPinId {
|
||||
I::DYN
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PinId> Registers<I> {
|
||||
/// Create a new instance of [`Registers`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users must never create two simultaneous instances of this `struct` with
|
||||
/// the same [`PinId`]
|
||||
#[inline]
|
||||
unsafe fn new() -> Self {
|
||||
Registers { id: PhantomData }
|
||||
}
|
||||
|
||||
/// Provide a type-level equivalent for the
|
||||
/// [`RegisterInterface::change_mode`] method.
|
||||
#[inline]
|
||||
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
|
||||
RegisterInterface::change_mode(self, M::DYN);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Pin definitions
|
||||
//==================================================================================================
|
||||
|
@ -293,7 +293,7 @@ pub(super) unsafe trait RegisterInterface {
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
||||
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.iocfg_port().modify(|_, w| {
|
||||
// Safety: Only write to register for this Pin ID
|
||||
unsafe {
|
||||
@ -331,7 +331,7 @@ pub(super) unsafe trait RegisterInterface {
|
||||
/// See p.52 of the programmers guide for more information.
|
||||
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||
/// one clock cycle before returning to the configured default state
|
||||
fn pulse_mode(&self, enable: bool, default_state: PinState) {
|
||||
fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
let portreg = self.port_reg();
|
||||
unsafe {
|
||||
if enable {
|
||||
|
@ -10,14 +10,12 @@ use core::marker::PhantomData;
|
||||
|
||||
use crate::pac;
|
||||
use crate::time::Hertz;
|
||||
use crate::timer::{
|
||||
TimAndPinRegister, TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin,
|
||||
};
|
||||
use crate::timer::{TimDynRegister, TimPin, TimRegInterface, ValidTim, ValidTimAndPin};
|
||||
use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
|
||||
|
||||
const DUTY_MAX: u16 = u16::MAX;
|
||||
|
||||
pub struct PwmBase {
|
||||
pub struct PwmCommon {
|
||||
sys_clk: Hertz,
|
||||
/// For PWMB, this is the upper limit
|
||||
current_duty: u16,
|
||||
@ -35,123 +33,13 @@ enum StatusSelPwm {
|
||||
pub struct PwmA {}
|
||||
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
|
||||
//==================================================================================================
|
||||
|
||||
pub struct PwmPin<Pin: TimPin, Tim: ValidTim, Mode = PwmA> {
|
||||
reg: TimAndPinRegister<Pin, Tim>,
|
||||
pwm_base: PwmBase,
|
||||
pin_and_tim: (Pin, Tim),
|
||||
inner: ReducedPwmPin<Mode>,
|
||||
mode: PhantomData<Mode>,
|
||||
}
|
||||
|
||||
@ -163,34 +51,82 @@ where
|
||||
pub fn new(
|
||||
sys_cfg: &mut pac::Sysconfig,
|
||||
sys_clk: impl Into<Hertz> + Copy,
|
||||
tim_and_pin: (Pin, Tim),
|
||||
pin_and_tim: (Pin, Tim),
|
||||
initial_period: impl Into<Hertz> + Copy,
|
||||
) -> Self {
|
||||
let mut pin = PwmPin {
|
||||
pwm_base: PwmBase {
|
||||
current_duty: 0,
|
||||
current_lower_limit: 0,
|
||||
current_period: initial_period.into(),
|
||||
current_rst_val: 0,
|
||||
sys_clk: sys_clk.into(),
|
||||
},
|
||||
reg: unsafe { TimAndPinRegister::new(tim_and_pin.0, tim_and_pin.1) },
|
||||
pin_and_tim,
|
||||
inner: ReducedPwmPin::<Mode>::new(
|
||||
Tim::TIM_ID,
|
||||
Pin::DYN,
|
||||
PwmCommon {
|
||||
current_duty: 0,
|
||||
current_lower_limit: 0,
|
||||
current_period: initial_period.into(),
|
||||
current_rst_val: 0,
|
||||
sys_clk: sys_clk.into(),
|
||||
},
|
||||
),
|
||||
//unsafe { TimAndPin::new(tim_and_pin.0, tim_and_pin.1) },
|
||||
mode: PhantomData,
|
||||
};
|
||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Gpio);
|
||||
enable_peripheral_clock(sys_cfg, crate::clock::PeripheralClocks::Ioconfig);
|
||||
sys_cfg
|
||||
.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.set_period(initial_period);
|
||||
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>
|
||||
@ -199,9 +135,9 @@ where
|
||||
{
|
||||
fn from(other: PwmPin<Pin, Tim, PwmA>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
reg: other.reg,
|
||||
pwm_base: other.pwm_base,
|
||||
mode: PhantomData,
|
||||
pin_and_tim: other.pin_and_tim,
|
||||
inner: other.inner.into(),
|
||||
};
|
||||
pwmb.enable_pwm_b();
|
||||
pwmb
|
||||
@ -213,13 +149,13 @@ where
|
||||
(PIN, TIM): ValidTimAndPin<PIN, TIM>,
|
||||
{
|
||||
fn from(other: PwmPin<PIN, TIM, PwmB>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
reg: other.reg,
|
||||
pwm_base: other.pwm_base,
|
||||
let mut pwma = Self {
|
||||
mode: PhantomData,
|
||||
pin_and_tim: other.pin_and_tim,
|
||||
inner: other.inner.into(),
|
||||
};
|
||||
pwmb.enable_pwm_a();
|
||||
pwmb
|
||||
pwma.enable_pwm_a();
|
||||
pwma
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,33 +199,105 @@ where
|
||||
|
||||
/// Reduced version where type information is deleted
|
||||
pub struct ReducedPwmPin<Mode = PwmA> {
|
||||
reg: TimDynRegister,
|
||||
pwm_base: PwmBase,
|
||||
pin_id: DynPinId,
|
||||
dyn_reg: TimDynRegister,
|
||||
common: PwmCommon,
|
||||
mode: PhantomData<Mode>,
|
||||
}
|
||||
|
||||
impl<PIN: TimPin, TIM: ValidTim> From<PwmPin<PIN, TIM>> for ReducedPwmPin<PwmA> {
|
||||
fn from(pwm_pin: PwmPin<PIN, TIM>) -> Self {
|
||||
ReducedPwmPin {
|
||||
reg: TimDynRegister::from(pwm_pin.reg),
|
||||
pwm_base: pwm_pin.pwm_base,
|
||||
pin_id: PIN::DYN,
|
||||
impl<Mode> ReducedPwmPin<Mode> {
|
||||
pub(crate) fn new(tim_id: u8, pin_id: DynPinId, common: PwmCommon) -> Self {
|
||||
Self {
|
||||
dyn_reg: TimDynRegister { tim_id, pin_id },
|
||||
common,
|
||||
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> {
|
||||
fn from(other: ReducedPwmPin<PwmA>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
reg: other.reg,
|
||||
pwm_base: other.pwm_base,
|
||||
pin_id: other.pin_id,
|
||||
dyn_reg: other.dyn_reg,
|
||||
common: other.common,
|
||||
mode: PhantomData,
|
||||
};
|
||||
pwmb.enable_pwm_b();
|
||||
@ -300,9 +308,8 @@ impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
|
||||
impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||
fn from(other: ReducedPwmPin<PwmB>) -> Self {
|
||||
let mut pwmb = Self {
|
||||
reg: other.reg,
|
||||
pwm_base: other.pwm_base,
|
||||
pin_id: other.pin_id,
|
||||
dyn_reg: other.dyn_reg,
|
||||
common: other.common,
|
||||
mode: PhantomData,
|
||||
};
|
||||
pwmb.enable_pwm_a();
|
||||
@ -314,15 +321,83 @@ impl From<ReducedPwmPin<PwmB>> for ReducedPwmPin<PwmA> {
|
||||
// PWMB implementations
|
||||
//==================================================================================================
|
||||
|
||||
impl<PIN: TimPin, TIM: ValidTim> PwmPin<PIN, TIM, PwmB>
|
||||
impl<Pin: TimPin, Tim: ValidTim> PwmPin<Pin, Tim, PwmB>
|
||||
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> {
|
||||
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]
|
||||
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||
self.pwm_base.current_duty = duty;
|
||||
let pwma_val: u64 = (self.pwm_base.current_rst_val as u64
|
||||
* (DUTY_MAX as u64 - self.pwm_base.current_duty as u64))
|
||||
self.common.current_duty = duty;
|
||||
let pwma_val: u64 = (self.common.current_rst_val as u64
|
||||
* (DUTY_MAX as u64 - self.common.current_duty as u64))
|
||||
/ DUTY_MAX as u64;
|
||||
self.reg
|
||||
.reg()
|
||||
self.dyn_reg
|
||||
.reg_block()
|
||||
.pwma_value()
|
||||
.write(|w| unsafe { w.bits(pwma_val as u32) });
|
||||
Ok(())
|
||||
@ -365,15 +440,7 @@ impl<Pin: TimPin, Tim: ValidTim> embedded_hal::pwm::SetDutyCycle for PwmPin<Pin,
|
||||
|
||||
#[inline]
|
||||
fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
|
||||
self.pwm_base.current_duty = 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(())
|
||||
self.inner.set_duty_cycle(duty)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,48 @@ use fugit::RateExtU32;
|
||||
const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||
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
|
||||
//==================================================================================================
|
||||
@ -248,7 +290,7 @@ pub type TimRegBlock = tim0::RegisterBlock;
|
||||
/// implementations should be overridden. The implementing type must also have
|
||||
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||
/// pin ID is a singleton.
|
||||
pub(super) unsafe trait TimRegInterface {
|
||||
pub unsafe trait TimRegInterface {
|
||||
fn tim_id(&self) -> u8;
|
||||
|
||||
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
|
||||
/// memory mapped peripheral depending on the TIM ID.
|
||||
#[inline(always)]
|
||||
fn reg(&self) -> &TimRegBlock {
|
||||
fn reg_block(&self) -> &TimRegBlock {
|
||||
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
|
||||
///
|
||||
/// 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> {
|
||||
unsafe impl<Tim: ValidTim> TimRegInterface for Tim {
|
||||
fn tim_id(&self) -> u8 {
|
||||
TIM::TIM_ID
|
||||
Tim::TIM_ID
|
||||
}
|
||||
}
|
||||
|
||||
impl<PIN: TimPin, TIM: ValidTim> TimAndPinRegister<PIN, TIM>
|
||||
where
|
||||
(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,
|
||||
pub(crate) struct TimDynRegister {
|
||||
pub(crate) tim_id: u8,
|
||||
#[allow(dead_code)]
|
||||
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,
|
||||
}
|
||||
}
|
||||
pub(crate) pin_id: DynPinId,
|
||||
}
|
||||
|
||||
unsafe impl TimRegInterface for TimDynRegister {
|
||||
@ -371,8 +359,8 @@ unsafe impl TimRegInterface for TimDynRegister {
|
||||
//==================================================================================================
|
||||
|
||||
/// Hardware timers
|
||||
pub struct CountdownTimer<TIM: ValidTim> {
|
||||
tim: TimRegister<TIM>,
|
||||
pub struct CountdownTimer<Tim: ValidTim> {
|
||||
tim: Tim,
|
||||
curr_freq: Hertz,
|
||||
irq_cfg: Option<IrqCfg>,
|
||||
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
|
||||
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: TIM) -> Self {
|
||||
enable_tim_clk(syscfg, TIM::TIM_ID);
|
||||
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, tim: Tim) -> Self {
|
||||
enable_tim_clk(syscfg, Tim::TIM_ID);
|
||||
let cd_timer = CountdownTimer {
|
||||
tim: unsafe { TimRegister::new(tim) },
|
||||
tim,
|
||||
sys_clk: sys_clk.into(),
|
||||
irq_cfg: None,
|
||||
rst_val: 0,
|
||||
@ -416,7 +404,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
};
|
||||
cd_timer
|
||||
.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.enable().set_bit());
|
||||
cd_timer
|
||||
@ -441,7 +429,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
}
|
||||
if let Some(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) });
|
||||
}
|
||||
}
|
||||
@ -460,7 +448,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
Event::TimeOut => {
|
||||
enable_peripheral_clock(syscfg, PeripheralClocks::Irqsel);
|
||||
irqsel
|
||||
.tim0(TIM::TIM_ID as usize)
|
||||
.tim0(Tim::TIM_ID as usize)
|
||||
.write(|w| unsafe { w.bits(IRQ_DST_NONE) });
|
||||
self.disable_interrupt();
|
||||
self.listening = false;
|
||||
@ -470,25 +458,37 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
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 {
|
||||
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||
pub fn release(self, syscfg: &mut pac::Sysconfig) -> Tim {
|
||||
self.tim
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.write(|w| w.enable().clear_bit());
|
||||
syscfg
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << TIM::TIM_ID)) });
|
||||
self.tim.release()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << Tim::TIM_ID)) });
|
||||
self.tim
|
||||
}
|
||||
|
||||
/// Load the count down timer with a timeout but do not start it.
|
||||
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.rst_val = self.sys_clk.raw() / self.curr_freq.raw();
|
||||
self.set_reload(self.rst_val);
|
||||
@ -497,17 +497,23 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
|
||||
#[inline(always)]
|
||||
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)]
|
||||
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)]
|
||||
pub fn count(&self) -> u32 {
|
||||
self.tim.reg().cnt_value().read().bits()
|
||||
self.tim.reg_block().cnt_value().read().bits()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -518,24 +524,30 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
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)]
|
||||
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
|
||||
pub fn auto_disable(self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.auto_disable().set_bit());
|
||||
} else {
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.auto_disable().clear_bit());
|
||||
}
|
||||
@ -549,12 +561,12 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
pub fn auto_deactivate(self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.auto_deactivate().set_bit());
|
||||
} else {
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.modify(|_, w| w.auto_deactivate().clear_bit());
|
||||
}
|
||||
@ -563,7 +575,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
|
||||
/// Configure the cascade parameters
|
||||
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.csdinv0().bit(ctrl.inv_csd0);
|
||||
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> {
|
||||
let id = src.id()?;
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.cascade0()
|
||||
.write(|w| unsafe { w.cassel().bits(id) });
|
||||
Ok(())
|
||||
@ -589,7 +601,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
pub fn cascade_1_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||
let id = src.id()?;
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.cascade1()
|
||||
.write(|w| unsafe { w.cassel().bits(id) });
|
||||
Ok(())
|
||||
@ -598,7 +610,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
pub fn cascade_2_source(&mut self, src: CascadeSource) -> Result<(), InvalidCascadeSourceId> {
|
||||
let id = src.id()?;
|
||||
self.tim
|
||||
.reg()
|
||||
.reg_block()
|
||||
.cascade2()
|
||||
.write(|w| unsafe { w.cassel().bits(id) });
|
||||
Ok(())
|
||||
@ -627,7 +639,7 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
/// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the
|
||||
/// flag and restart the time if configured correctly
|
||||
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 {
|
||||
self.last_cnt = self.rst_val;
|
||||
Ok(())
|
||||
@ -639,10 +651,13 @@ impl<TIM: ValidTim> CountdownTimer<TIM> {
|
||||
|
||||
/// Returns [false] if the timer was not active, and true otherwise.
|
||||
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;
|
||||
}
|
||||
self.tim.reg().ctrl().write(|w| w.enable().clear_bit());
|
||||
self.tim
|
||||
.reg_block()
|
||||
.ctrl()
|
||||
.write(|w| w.enable().clear_bit());
|
||||
true
|
||||
}
|
||||
}
|
||||
|
3
va108xx/docs.sh
Executable file
3
va108xx/docs.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --all-features --open
|
@ -2,8 +2,8 @@
|
||||
#[doc = "Register block"]
|
||||
pub struct RegisterBlock {
|
||||
porta: [Porta; 32],
|
||||
portbs: Portbs,
|
||||
_reserved2: [u8; 0x0f78],
|
||||
portb0: [Portb; 32],
|
||||
_reserved2: [u8; 0x0efc],
|
||||
perid: Perid,
|
||||
}
|
||||
impl RegisterBlock {
|
||||
@ -18,10 +18,16 @@ impl RegisterBlock {
|
||||
pub fn porta_iter(&self) -> impl Iterator<Item = &Porta> {
|
||||
self.porta.iter()
|
||||
}
|
||||
#[doc = "0x80 - PORTB Pin Configuration Register"]
|
||||
#[doc = "0x80..0x100 - PORTB Pin Configuration Register"]
|
||||
#[inline(always)]
|
||||
pub const fn portbs(&self) -> &Portbs {
|
||||
&self.portbs
|
||||
pub const fn portb0(&self, n: usize) -> &Portb {
|
||||
&self.portb0[n]
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x80..0x100 - PORTB Pin Configuration Register"]
|
||||
#[inline(always)]
|
||||
pub fn portb0_iter(&self) -> impl Iterator<Item = &Portb> {
|
||||
self.portb0.iter()
|
||||
}
|
||||
#[doc = "0xffc - Peripheral ID Register"]
|
||||
#[inline(always)]
|
||||
@ -35,8 +41,8 @@ module"]
|
||||
pub type Porta = crate::Reg<porta::PortaSpec>;
|
||||
#[doc = "PORTA Pin Configuration Register"]
|
||||
pub mod porta;
|
||||
pub use porta as portbs;
|
||||
pub use Porta as Portbs;
|
||||
pub use porta as portb;
|
||||
pub use Porta as Portb;
|
||||
#[doc = "PERID (r) register accessor: Peripheral ID Register\n\nYou can [`read`](crate::Reg::read) this register and get [`perid::R`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@perid`]
|
||||
module"]
|
||||
#[doc(alias = "PERID")]
|
||||
|
@ -45,8 +45,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x04 - Data In Raw Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn datainrawbytes(&self) -> &Datainrawbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).cast() }
|
||||
pub const fn datainrawbyte0(&self, n: usize) -> &Datainrawbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x04 - Data In Raw Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn datainrawbyte0_iter(&self) -> impl Iterator<Item = &Datainrawbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(4).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x04 - Data In Raw Register"]
|
||||
#[inline(always)]
|
||||
@ -74,8 +83,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x0c - Data Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn dataoutrawbytes(&self) -> &Dataoutrawbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).cast() }
|
||||
pub const fn dataoutrawbyte0(&self, n: usize) -> &Dataoutrawbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x0c - Data Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn dataoutrawbyte0_iter(&self) -> impl Iterator<Item = &Dataoutrawbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(12).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x0c - Data Out Register"]
|
||||
#[inline(always)]
|
||||
@ -84,8 +102,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x10 - Set Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn setoutbytes(&self) -> &Setoutbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).cast() }
|
||||
pub const fn setoutbyte0(&self, n: usize) -> &Setoutbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x10 - Set Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn setoutbyte0_iter(&self) -> impl Iterator<Item = &Setoutbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(16).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x10 - Set Out Register"]
|
||||
#[inline(always)]
|
||||
@ -94,8 +121,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x14 - Clear Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn clroutbytes(&self) -> &Clroutbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).cast() }
|
||||
pub const fn clroutbyte0(&self, n: usize) -> &Clroutbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x14 - Clear Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn clroutbyte0_iter(&self) -> impl Iterator<Item = &Clroutbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(20).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x14 - Clear Out Register"]
|
||||
#[inline(always)]
|
||||
@ -104,8 +140,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x18 - Toggle Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn togoutbytes(&self) -> &Togoutbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).cast() }
|
||||
pub const fn togoutbyte0(&self, n: usize) -> &Togoutbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x18 - Toggle Out Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn togoutbyte0_iter(&self) -> impl Iterator<Item = &Togoutbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(24).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x18 - Toggle Out Register"]
|
||||
#[inline(always)]
|
||||
@ -133,8 +178,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x20 - Direction Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn dirbytes(&self) -> &Dirbytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).cast() }
|
||||
pub const fn dirbyte0(&self, n: usize) -> &Dirbyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x20 - Direction Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn dirbyte0_iter(&self) -> impl Iterator<Item = &Dirbyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(32).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x20 - Direction Register (1:Output, 0:Input)"]
|
||||
#[inline(always)]
|
||||
@ -143,8 +197,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x24 - Pulse Mode Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn pulsebytes(&self) -> &Pulsebytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).cast() }
|
||||
pub const fn pulsebyte0(&self, n: usize) -> &Pulsebyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x24 - Pulse Mode Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn pulsebyte0_iter(&self) -> impl Iterator<Item = &Pulsebyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(36).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x24 - Pulse Mode Register"]
|
||||
#[inline(always)]
|
||||
@ -153,8 +216,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x28 - Pulse Base Mode Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn pulsebasebytes(&self) -> &Pulsebasebytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).cast() }
|
||||
pub const fn pulsebasebyte0(&self, n: usize) -> &Pulsebasebyte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x28 - Pulse Base Mode Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn pulsebasebyte0_iter(&self) -> impl Iterator<Item = &Pulsebasebyte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(40).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x28 - Pulse Base Value Register"]
|
||||
#[inline(always)]
|
||||
@ -163,8 +235,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x2c - Delay1 Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn delay1bytes(&self) -> &Delay1bytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).cast() }
|
||||
pub const fn delay1byte0(&self, n: usize) -> &Delay1byte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x2c - Delay1 Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn delay1byte0_iter(&self) -> impl Iterator<Item = &Delay1byte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(44).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x2c - Delay1 Register"]
|
||||
#[inline(always)]
|
||||
@ -173,8 +254,17 @@ impl RegisterBlock {
|
||||
}
|
||||
#[doc = "0x30 - Delay2 Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub const fn delay2bytes(&self) -> &Delay2bytes {
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).cast() }
|
||||
pub const fn delay2byte0(&self, n: usize) -> &Delay2byte {
|
||||
#[allow(clippy::no_effect)]
|
||||
[(); 4][n];
|
||||
unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).add(n).cast() }
|
||||
}
|
||||
#[doc = "Iterator for array of:"]
|
||||
#[doc = "0x30 - Delay2 Register by Byte"]
|
||||
#[inline(always)]
|
||||
pub fn delay2byte0_iter(&self) -> impl Iterator<Item = &Delay2byte> {
|
||||
(0..4)
|
||||
.map(move |n| unsafe { &*core::ptr::from_ref(self).cast::<u8>().add(48).add(n).cast() })
|
||||
}
|
||||
#[doc = "0x30 - Delay2 Register"]
|
||||
#[inline(always)]
|
||||
@ -235,9 +325,9 @@ pub type Datainbyte = crate::Reg<datainbyte::DatainbyteSpec>;
|
||||
#[doc = "Data In Register by Byte"]
|
||||
pub mod datainbyte;
|
||||
pub use datain as datainraw;
|
||||
pub use datainbyte as datainrawbytes;
|
||||
pub use datainbyte as datainrawbyte;
|
||||
pub use Datain as Datainraw;
|
||||
pub use Datainbyte as Datainrawbytes;
|
||||
pub use Datainbyte as Datainrawbyte;
|
||||
#[doc = "DATAOUT (w) register accessor: Data Out Register\n\nYou can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`dataout::W`]. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@dataout`]
|
||||
module"]
|
||||
#[doc(alias = "DATAOUT")]
|
||||
@ -254,18 +344,18 @@ pub use dataout as dataoutraw;
|
||||
pub use dataout as setout;
|
||||
pub use dataout as clrout;
|
||||
pub use dataout as togout;
|
||||
pub use dataoutbyte as dataoutrawbytes;
|
||||
pub use dataoutbyte as setoutbytes;
|
||||
pub use dataoutbyte as clroutbytes;
|
||||
pub use dataoutbyte as togoutbytes;
|
||||
pub use dataoutbyte as dataoutrawbyte;
|
||||
pub use dataoutbyte as setoutbyte;
|
||||
pub use dataoutbyte as clroutbyte;
|
||||
pub use dataoutbyte as togoutbyte;
|
||||
pub use Dataout as Dataoutraw;
|
||||
pub use Dataout as Setout;
|
||||
pub use Dataout as Clrout;
|
||||
pub use Dataout as Togout;
|
||||
pub use Dataoutbyte as Dataoutrawbytes;
|
||||
pub use Dataoutbyte as Setoutbytes;
|
||||
pub use Dataoutbyte as Clroutbytes;
|
||||
pub use Dataoutbyte as Togoutbytes;
|
||||
pub use Dataoutbyte as Dataoutrawbyte;
|
||||
pub use Dataoutbyte as Setoutbyte;
|
||||
pub use Dataoutbyte as Clroutbyte;
|
||||
pub use Dataoutbyte as Togoutbyte;
|
||||
#[doc = "DATAMASK (rw) register accessor: Data mask Register\n\nYou can [`read`](crate::Reg::read) this register and get [`datamask::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`datamask::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@datamask`]
|
||||
module"]
|
||||
#[doc(alias = "DATAMASK")]
|
||||
@ -283,21 +373,21 @@ pub use datamask as pulse;
|
||||
pub use datamask as pulsebase;
|
||||
pub use datamask as delay1;
|
||||
pub use datamask as delay2;
|
||||
pub use datamaskbyte as dirbytes;
|
||||
pub use datamaskbyte as pulsebytes;
|
||||
pub use datamaskbyte as pulsebasebytes;
|
||||
pub use datamaskbyte as delay1bytes;
|
||||
pub use datamaskbyte as delay2bytes;
|
||||
pub use datamaskbyte as dirbyte;
|
||||
pub use datamaskbyte as pulsebyte;
|
||||
pub use datamaskbyte as pulsebasebyte;
|
||||
pub use datamaskbyte as delay1byte;
|
||||
pub use datamaskbyte as delay2byte;
|
||||
pub use Datamask as Dir;
|
||||
pub use Datamask as Pulse;
|
||||
pub use Datamask as Pulsebase;
|
||||
pub use Datamask as Delay1;
|
||||
pub use Datamask as Delay2;
|
||||
pub use Datamaskbyte as Dirbytes;
|
||||
pub use Datamaskbyte as Pulsebytes;
|
||||
pub use Datamaskbyte as Pulsebasebytes;
|
||||
pub use Datamaskbyte as Delay1bytes;
|
||||
pub use Datamaskbyte as Delay2bytes;
|
||||
pub use Datamaskbyte as Dirbyte;
|
||||
pub use Datamaskbyte as Pulsebyte;
|
||||
pub use Datamaskbyte as Pulsebasebyte;
|
||||
pub use Datamaskbyte as Delay1byte;
|
||||
pub use Datamaskbyte as Delay2byte;
|
||||
#[doc = "IRQ_SEN (rw) register accessor: Interrupt Sense Register (1:Level Sensitive, 0:Edge Sensitive)\n\nYou can [`read`](crate::Reg::read) this register and get [`irq_sen::R`]. You can [`reset`](crate::Reg::reset), [`write`](crate::Reg::write), [`write_with_zero`](crate::Reg::write_with_zero) this register using [`irq_sen::W`]. You can also [`modify`](crate::Reg::modify) this register. See [API](https://docs.rs/svd2rust/#read--modify--write-api).\n\nFor information about available fields see [`mod@irq_sen`]
|
||||
module"]
|
||||
#[doc(alias = "IRQ_SEN")]
|
||||
|
@ -15,10 +15,8 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
nb = "1"
|
||||
bitfield = "0.17"
|
||||
|
||||
[dependencies.max116xx-10bit]
|
||||
version = "0.3"
|
||||
bitfield = ">=0.17, <=0.18"
|
||||
max116xx-10bit = "0.3"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = ">=0.8, <0.9"
|
||||
@ -28,11 +26,11 @@ features = ["rt"]
|
||||
rt = ["va108xx-hal/rt"]
|
||||
|
||||
[dev-dependencies]
|
||||
panic-halt = "0.2"
|
||||
panic-halt = "1"
|
||||
nb = "1"
|
||||
rtt-target = "0.5"
|
||||
panic-rtt-target = "0.1"
|
||||
embedded-hal-bus = "0.2"
|
||||
rtt-target = "0.6"
|
||||
panic-rtt-target = "0.2"
|
||||
embedded-hal-bus = "0.3"
|
||||
dummy-pin = "1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
Loading…
x
Reference in New Issue
Block a user