some improvements
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit
This commit is contained in:
parent
adc0e68ebd
commit
d947ed9609
@ -7,14 +7,20 @@ edition = "2021"
|
|||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embedded-hal = "1"
|
embedded-hal = "1"
|
||||||
|
|
||||||
rtt-target = { version = "0.5" }
|
rtt-target = { version = "0.5" }
|
||||||
panic-rtt-target = { version = "0.1" }
|
panic-rtt-target = { version = "0.1" }
|
||||||
critical-section = "1"
|
critical-section = "1"
|
||||||
|
|
||||||
embassy-sync = { version = "0.6.0" }
|
embassy-sync = { version = "0.6.0" }
|
||||||
embassy-time = { version = "0.3.2", features = ["tick-hz-1_000"] }
|
embassy-time = { version = "0.3.2", features = ["tick-hz-1"] }
|
||||||
embassy-time-driver = { version = "0.1" }
|
embassy-time-driver = { version = "0.1" }
|
||||||
|
|
||||||
|
[dependencies.once_cell]
|
||||||
|
version = "1"
|
||||||
|
default-features = false
|
||||||
|
features = ["critical-section"]
|
||||||
|
|
||||||
[dependencies.embassy-executor]
|
[dependencies.embassy-executor]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
features = [
|
features = [
|
||||||
|
@ -3,22 +3,19 @@ use core::{
|
|||||||
cell::Cell,
|
cell::Cell,
|
||||||
mem, ptr,
|
mem, ptr,
|
||||||
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
||||||
time,
|
|
||||||
};
|
};
|
||||||
use critical_section::CriticalSection;
|
use critical_section::CriticalSection;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
||||||
|
use rtt_target::rprintln;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
clock::Clocks,
|
clock::Clocks,
|
||||||
enable_interrupt,
|
enable_interrupt,
|
||||||
irq_router::enable_and_init_irq_router,
|
irq_router::enable_and_init_irq_router,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
pwm::{
|
pwm::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
||||||
assert_tim_reset, assert_tim_reset_for_two_cycles, deassert_tim_reset, enable_tim_clk,
|
|
||||||
ValidTim,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TimekeeperClk = pac::Tim15;
|
pub type TimekeeperClk = pac::Tim15;
|
||||||
@ -26,8 +23,17 @@ pub type AlarmClk0 = pac::Tim14;
|
|||||||
pub type AlarmClk1 = pac::Tim13;
|
pub type AlarmClk1 = pac::Tim13;
|
||||||
pub type AlarmClk2 = pac::Tim12;
|
pub type AlarmClk2 = pac::Tim12;
|
||||||
|
|
||||||
/// This has to be called to initiate the time driver for embassy.
|
// Uses integer division to get a margin of 75 % of the base value added on the ticks
|
||||||
pub fn init(
|
const fn three_quarters_of_period(period: u64) -> u64 {
|
||||||
|
(period * 3) / 4
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
syscfg: &mut pac::Sysconfig,
|
||||||
irq_router: &pac::IrqRouter,
|
irq_router: &pac::IrqRouter,
|
||||||
timekeeper: TimekeeperClk,
|
timekeeper: TimekeeperClk,
|
||||||
@ -39,7 +45,7 @@ pub fn init(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
|
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
|
||||||
// Safety: This is a memory-mapped peripheral.
|
// Safety: This is a static memory-mapped peripheral.
|
||||||
match idx {
|
match idx {
|
||||||
0 => unsafe { &*AlarmClk0::ptr() },
|
0 => unsafe { &*AlarmClk0::ptr() },
|
||||||
1 => unsafe { &*AlarmClk1::ptr() },
|
1 => unsafe { &*AlarmClk1::ptr() },
|
||||||
@ -77,8 +83,6 @@ impl AlarmState {
|
|||||||
unsafe impl Send for AlarmState {}
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
const ALARM_COUNT: usize = 1;
|
const ALARM_COUNT: usize = 1;
|
||||||
// Margin value which is used when detecting whether an alarm should fire soon.
|
|
||||||
const TIMER_MARGIN: u64 = 0xc000_0000;
|
|
||||||
|
|
||||||
pub struct TimerDriverEmbassy {
|
pub struct TimerDriverEmbassy {
|
||||||
periods: AtomicU32,
|
periods: AtomicU32,
|
||||||
@ -98,14 +102,10 @@ impl TimerDriverEmbassy {
|
|||||||
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
||||||
assert_tim_reset_for_two_cycles(syscfg, TimekeeperClk::TIM_ID);
|
assert_tim_reset_for_two_cycles(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
|
||||||
let rst_value = TimekeeperClk::clock(clocks).raw() / TICK_HZ as u32 - 1;
|
let rst_val = (TimekeeperClk::clock(clocks).raw() / TICK_HZ as u32) - 1;
|
||||||
// Safety: We have a valid instance of the tim peripheral.
|
timekeeper.rst_value().write(|w| unsafe { w.bits(rst_val) });
|
||||||
timekeeper
|
// Decrementing counter.
|
||||||
.rst_value()
|
timekeeper.cnt_value().write(|w| unsafe { w.bits(rst_val) });
|
||||||
.write(|w| unsafe { w.bits(rst_value) });
|
|
||||||
timekeeper
|
|
||||||
.cnt_value()
|
|
||||||
.write(|w| unsafe { w.bits(rst_value) });
|
|
||||||
// Switch on. Timekeeping should always be done.
|
// Switch on. Timekeeping should always be done.
|
||||||
unsafe {
|
unsafe {
|
||||||
enable_interrupt(TimekeeperClk::IRQ);
|
enable_interrupt(TimekeeperClk::IRQ);
|
||||||
@ -127,28 +127,31 @@ impl TimerDriverEmbassy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the timekeeper timer.
|
||||||
fn on_interrupt_timekeeping(&self) {
|
fn on_interrupt_timekeeping(&self) {
|
||||||
self.next_period();
|
self.next_period();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the alarm timer.
|
||||||
fn on_interrupt_alarm(&self, idx: usize) {
|
fn on_interrupt_alarm(&self, idx: usize) {
|
||||||
critical_section::with(|cs| self.trigger_alarm(idx, cs))
|
critical_section::with(|cs| self.trigger_alarm(idx, cs))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_period(&self) {
|
fn next_period(&self) {
|
||||||
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
let rst_val = timekeeping_tim().rst_value().read().bits() as u64;
|
||||||
|
let t = period as u64 * rst_val;
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
for i in 0..ALARM_COUNT {
|
for i in 0..ALARM_COUNT {
|
||||||
let alarm = &self.alarms.borrow(cs)[i];
|
let alarm = &self.alarms.borrow(cs)[i];
|
||||||
let at = alarm.timestamp.get();
|
let at = alarm.timestamp.get();
|
||||||
let t = (period as u64) << 32;
|
let alarm_tim = alarm_tim(0);
|
||||||
if at < t + TIMER_MARGIN {
|
if at < t + three_quarters_of_period(alarm_tim.rst_value().read().bits() as u64) {
|
||||||
let rst_val = alarm_tim(i).rst_value().read().bits();
|
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
||||||
alarm_tim(i)
|
let rst_val = alarm_tim.rst_value().read().bits();
|
||||||
.cnt_value()
|
alarm_tim.cnt_value().write(|w| unsafe { w.bits(rst_val) });
|
||||||
.write(|w| unsafe { w.bits(rst_val) });
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
alarm_tim(i).ctrl().modify(|_, w| w.irq_enb().set_bit());
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
|
||||||
alarm_tim(i).enable().write(|w| unsafe { w.bits(1) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -161,10 +164,7 @@ impl TimerDriverEmbassy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||||
alarm_tim(n).ctrl().modify(|_, w| {
|
alarm_tim(n).ctrl().modify(|_, w| w.irq_enb().clear_bit());
|
||||||
w.irq_enb().clear_bit();
|
|
||||||
w.enable().clear_bit()
|
|
||||||
});
|
|
||||||
|
|
||||||
let alarm = &self.alarms.borrow(cs)[n];
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
// Setting the maximum value disables the alarm.
|
// Setting the maximum value disables the alarm.
|
||||||
@ -185,32 +185,39 @@ impl Driver for TimerDriverEmbassy {
|
|||||||
let mut period1: u32;
|
let mut period1: u32;
|
||||||
let mut period2: u32;
|
let mut period2: u32;
|
||||||
let mut counter_val: u32;
|
let mut counter_val: u32;
|
||||||
|
|
||||||
|
let rst_val = timekeeping_tim().rst_value().read().bits();
|
||||||
loop {
|
loop {
|
||||||
// Acquire ensures that we get the latest value of `periods` and
|
// Acquire ensures that we get the latest value of `periods` and
|
||||||
// no instructions can be reordered before the load.
|
// no instructions can be reordered before the load.
|
||||||
period1 = self.periods.load(Ordering::Acquire);
|
period1 = self.periods.load(Ordering::Acquire);
|
||||||
|
|
||||||
counter_val = timekeeping_tim().rst_value().read().bits()
|
counter_val = rst_val - timekeeping_tim().cnt_value().read().bits();
|
||||||
- timekeeping_tim().cnt_value().read().bits();
|
|
||||||
|
|
||||||
|
// Double read to protect against race conditions when the counter is overflowing.
|
||||||
period2 = self.periods.load(Ordering::Relaxed);
|
period2 = self.periods.load(Ordering::Relaxed);
|
||||||
if period1 == period2 {
|
if period1 == period2 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
((period1 as u64) << 32) | counter_val as u64
|
(period1 as u64 * rst_val as u64) + counter_val as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn allocate_alarm(&self) -> Option<embassy_time_driver::AlarmHandle> {
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
critical_section::with(|_| {
|
let id = self
|
||||||
let id = self.alarm_count.load(Ordering::Relaxed);
|
.alarm_count
|
||||||
if id < ALARM_COUNT as u8 {
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
self.alarm_count.store(id + 1, Ordering::Relaxed);
|
if x < ALARM_COUNT as u8 {
|
||||||
Some(AlarmHandle::new(id))
|
Some(x + 1)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
match id {
|
||||||
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alarm_callback(
|
fn set_alarm_callback(
|
||||||
@ -230,20 +237,18 @@ impl Driver for TimerDriverEmbassy {
|
|||||||
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
|
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let n = alarm.id();
|
let n = alarm.id();
|
||||||
let alarm = self.get_alarm(cs, alarm);
|
|
||||||
alarm.timestamp.set(timestamp);
|
|
||||||
|
|
||||||
let alarm_tim = alarm_tim(n.into());
|
let alarm_tim = alarm_tim(n.into());
|
||||||
|
|
||||||
let t = self.now();
|
|
||||||
if timestamp <= t {
|
|
||||||
alarm_tim.ctrl().modify(|_, w| {
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
w.irq_enb().clear_bit();
|
w.irq_enb().clear_bit();
|
||||||
w.enable().clear_bit()
|
w.enable().clear_bit()
|
||||||
});
|
});
|
||||||
|
|
||||||
alarm.timestamp.set(u64::MAX);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,22 +263,22 @@ impl Driver for TimerDriverEmbassy {
|
|||||||
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
// and we don't do that here.
|
// and we don't do that here.
|
||||||
let safe_timestamp = timestamp.max(t + 3);
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
let rst_val = timekeeping_tim().rst_value().read().bits();
|
||||||
|
let rst_val_alarm = (safe_timestamp % rst_val as u64) as u32;
|
||||||
alarm_tim
|
alarm_tim
|
||||||
.rst_value()
|
.rst_value()
|
||||||
.write(|w| unsafe { w.bits((safe_timestamp & u32::MAX as u64) as u32) });
|
.write(|w| unsafe { w.bits(rst_val_alarm) });
|
||||||
|
|
||||||
let diff = timestamp - t;
|
let diff = timestamp - t;
|
||||||
if diff < TIMER_MARGIN {
|
if diff < (three_quarters_of_period(rst_val_alarm as u64)) {
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(rst_val_alarm) });
|
||||||
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
||||||
} else {
|
}
|
||||||
// If it's too far in the future, don't enable timer yet.
|
// If it's too far in the future, don't enable timer yet.
|
||||||
// It will be enabled later by `next_period`.
|
// It will be enabled later by `next_period`.
|
||||||
alarm_tim.ctrl().modify(|_, w| {
|
|
||||||
w.irq_enb().clear_bit();
|
|
||||||
w.enable().clear_bit()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
|
@ -25,17 +25,20 @@ async fn main(_spawner: Spawner) {
|
|||||||
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze(&mut dp.sysconfig)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// Safety: Only called once here.
|
||||||
|
unsafe {
|
||||||
embassy_example::init(
|
embassy_example::init(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
&dp.irq_router,
|
&dp.irq_router,
|
||||||
dp.tim15,
|
dp.tim15,
|
||||||
dp.tim14,
|
dp.tim14,
|
||||||
&clocks,
|
&clocks,
|
||||||
);
|
)
|
||||||
|
};
|
||||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
loop {
|
loop {
|
||||||
Timer::after_millis(2000).await;
|
Timer::after_secs(1).await;
|
||||||
led.toggle().ok();
|
led.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user