Compare commits
6 Commits
d947ed9609
...
va416xx-ha
Author | SHA1 | Date | |
---|---|---|---|
dfab81a813 | |||
2c0728102a
|
|||
5ce2dae236 | |||
19c64a8257
|
|||
4ed0a806a6 | |||
a1a83700f8 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -39,7 +39,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features va41630
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features va41630
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
|
@@ -24,7 +24,11 @@ It also contains the following helper crates:
|
|||||||
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
||||||
is compatible to the provided bootloader as well.
|
is compatible to the provided bootloader as well.
|
||||||
- The `examples` folder contains various example applications crates for the HAL and the PAC.
|
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples)
|
||||||
|
folder contains various example applications crates using the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
|
native Rust RTOSes.
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@@ -25,7 +25,9 @@ pipeline {
|
|||||||
stage('Docs') {
|
stage('Docs') {
|
||||||
steps {
|
steps {
|
||||||
sh """
|
sh """
|
||||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features va41630
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
examples/README.md
Normal file
25
examples/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
VA416xx Example Applications
|
||||||
|
========
|
||||||
|
|
||||||
|
This folder contains various examples
|
||||||
|
Consult the main README first for setup of the repository.
|
||||||
|
|
||||||
|
## Simple examples
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
You can have a look at the `simple/examples` folder to see all available simple examples
|
||||||
|
|
||||||
|
## RTIC example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin rtic-example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Embassy example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example
|
||||||
|
```
|
@@ -13,7 +13,7 @@ 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"] }
|
embassy-time = { version = "0.3.2" }
|
||||||
embassy-time-driver = { version = "0.1" }
|
embassy-time-driver = { version = "0.1" }
|
||||||
|
|
||||||
[dependencies.once_cell]
|
[dependencies.once_cell]
|
||||||
@@ -33,3 +33,8 @@ features = [
|
|||||||
[dependencies.va416xx-hal]
|
[dependencies.va416xx-hal]
|
||||||
path = "../../va416xx-hal"
|
path = "../../va416xx-hal"
|
||||||
features = ["va41630"]
|
features = ["va41630"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["ticks-hz-1_000"]
|
||||||
|
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||||
|
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||||
|
@@ -1,305 +1,4 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
use core::{
|
pub mod time_driver;
|
||||||
cell::Cell,
|
|
||||||
mem, ptr,
|
|
||||||
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
|
||||||
};
|
|
||||||
use critical_section::CriticalSection;
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
|
||||||
|
|
||||||
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
pub use time_driver::init;
|
||||||
use rtt_target::rprintln;
|
|
||||||
use va416xx_hal::{
|
|
||||||
clock::Clocks,
|
|
||||||
enable_interrupt,
|
|
||||||
irq_router::enable_and_init_irq_router,
|
|
||||||
pac::{self, interrupt},
|
|
||||||
pwm::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type TimekeeperClk = pac::Tim15;
|
|
||||||
pub type AlarmClk0 = pac::Tim14;
|
|
||||||
pub type AlarmClk1 = pac::Tim13;
|
|
||||||
pub type AlarmClk2 = pac::Tim12;
|
|
||||||
|
|
||||||
// Uses integer division to get a margin of 75 % of the base value added on the ticks
|
|
||||||
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,
|
|
||||||
irq_router: &pac::IrqRouter,
|
|
||||||
timekeeper: TimekeeperClk,
|
|
||||||
alarm: AlarmClk0,
|
|
||||||
clocks: &Clocks,
|
|
||||||
) {
|
|
||||||
enable_and_init_irq_router(syscfg, irq_router);
|
|
||||||
DRIVER.init(syscfg, timekeeper, alarm, clocks)
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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,
|
|
||||||
timekeeper: TimekeeperClk,
|
|
||||||
alarm_tim: AlarmClk0,
|
|
||||||
clocks: &Clocks,
|
|
||||||
) {
|
|
||||||
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
|
||||||
assert_tim_reset_for_two_cycles(syscfg, TimekeeperClk::TIM_ID);
|
|
||||||
|
|
||||||
let rst_val = (TimekeeperClk::clock(clocks).raw() / TICK_HZ as u32) - 1;
|
|
||||||
timekeeper.rst_value().write(|w| unsafe { w.bits(rst_val) });
|
|
||||||
// Decrementing counter.
|
|
||||||
timekeeper.cnt_value().write(|w| unsafe { w.bits(rst_val) });
|
|
||||||
// Switch on. Timekeeping should always be done.
|
|
||||||
unsafe {
|
|
||||||
enable_interrupt(TimekeeperClk::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);
|
|
||||||
assert_tim_reset_for_two_cycles(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(AlarmClk0::IRQ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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| self.trigger_alarm(idx, cs))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_period(&self) {
|
|
||||||
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| {
|
|
||||||
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 + three_quarters_of_period(alarm_tim.rst_value().read().bits() as u64) {
|
|
||||||
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
|
||||||
let rst_val = alarm_tim.rst_value().read().bits();
|
|
||||||
alarm_tim.cnt_value().write(|w| unsafe { w.bits(rst_val) });
|
|
||||||
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());
|
|
||||||
|
|
||||||
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 {
|
|
||||||
let mut period1: u32;
|
|
||||||
let mut period2: u32;
|
|
||||||
let mut counter_val: u32;
|
|
||||||
|
|
||||||
let rst_val = timekeeping_tim().rst_value().read().bits();
|
|
||||||
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 = rst_val - 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 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(period1 as u64 * rst_val as u64) + counter_val as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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 rst_val = timekeeping_tim().rst_value().read().bits();
|
|
||||||
let rst_val_alarm = (safe_timestamp % rst_val as u64) as u32;
|
|
||||||
alarm_tim
|
|
||||||
.rst_value()
|
|
||||||
.write(|w| unsafe { w.bits(rst_val_alarm) });
|
|
||||||
|
|
||||||
let diff = timestamp - t;
|
|
||||||
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.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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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])
|
|
||||||
});
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn TIM15() {
|
|
||||||
DRIVER.on_interrupt_timekeeping()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
fn TIM14() {
|
|
||||||
DRIVER.on_interrupt_alarm(0)
|
|
||||||
}
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::Timer;
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
use embedded_hal::digital::StatefulOutputPin;
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
@@ -37,8 +37,10 @@ async fn main(_spawner: Spawner) {
|
|||||||
};
|
};
|
||||||
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();
|
||||||
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
loop {
|
loop {
|
||||||
Timer::after_secs(1).await;
|
ticker.next().await;
|
||||||
|
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||||
led.toggle().ok();
|
led.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
323
examples/embassy/src/time_driver.rs
Normal file
323
examples/embassy/src/time_driver.rs
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
//! This is a sample time driver implementation for the VA416xx family of devices, supporting
|
||||||
|
//! one alarm and requiring/reserving 2 TIM peripherals. You could adapt this implementation to
|
||||||
|
//! support more alarms.
|
||||||
|
use core::{
|
||||||
|
cell::Cell,
|
||||||
|
mem, ptr,
|
||||||
|
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
||||||
|
};
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
|
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use va416xx_hal::{
|
||||||
|
clock::Clocks,
|
||||||
|
enable_interrupt,
|
||||||
|
irq_router::enable_and_init_irq_router,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
pwm::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type TimekeeperClk = pac::Tim15;
|
||||||
|
pub type AlarmClk0 = pac::Tim14;
|
||||||
|
pub type AlarmClk1 = pac::Tim13;
|
||||||
|
pub type AlarmClk2 = pac::Tim12;
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
irq_router: &pac::IrqRouter,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm: AlarmClk0,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) {
|
||||||
|
enable_and_init_irq_router(syscfg, irq_router);
|
||||||
|
DRIVER.init(syscfg, timekeeper, alarm, clocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm_tim: AlarmClk0,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) {
|
||||||
|
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
assert_tim_reset_for_two_cycles(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
|
||||||
|
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||||
|
SCALE
|
||||||
|
.set((TimekeeperClk::clock(clocks).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.
|
||||||
|
unsafe {
|
||||||
|
enable_interrupt(TimekeeperClk::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);
|
||||||
|
assert_tim_reset_for_two_cycles(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(AlarmClk0::IRQ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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])
|
||||||
|
});
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM15() {
|
||||||
|
DRIVER.on_interrupt_timekeeping()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM14() {
|
||||||
|
DRIVER.on_interrupt_alarm(0)
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rtic"
|
name = "rtic-example"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
# [v0.2.0]
|
# [v0.2.0] 2024-09-18
|
||||||
|
|
||||||
- Documentation improvements
|
- Documentation improvements
|
||||||
- Improved UART typing support: Validity of passed pins is now checked properly
|
- Improved UART typing support: Validity of passed pins is now checked properly
|
||||||
@@ -25,6 +25,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||||
correctly
|
correctly
|
||||||
- Fixes for UART IRQ handler implementation
|
- Fixes for UART IRQ handler implementation
|
||||||
|
- Add new IRQ router initialization method `irq_router::enable_and_init_irq_router`. This method
|
||||||
|
also sets the initial values of some registers to 0 where the datasheet and the actual reset
|
||||||
|
value are inconsistent, which can lead to weird bugs like IRQs not being triggered properly.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
@@ -311,6 +311,12 @@ impl ClkgenCfgr {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pll_cfg(mut self, pll_cfg: PllCfg) -> Self {
|
||||||
|
self.pll_cfg = Some(pll_cfg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
|
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
|
||||||
self.ref_clk_sel = ref_clk_sel;
|
self.ref_clk_sel = ref_clk_sel;
|
||||||
@@ -318,7 +324,7 @@ impl ClkgenCfgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configures all clocks and return a clock configuration structure containing the final
|
/// Configures all clocks and return a clock configuration structure containing the final
|
||||||
/// frozen clock.
|
/// frozen clocks.
|
||||||
///
|
///
|
||||||
/// Internal implementation details: This implementation is based on the HAL implementation
|
/// Internal implementation details: This implementation is based on the HAL implementation
|
||||||
/// which performs a lot of delays. I do not know if all of those are necessary, but
|
/// which performs a lot of delays. I do not know if all of those are necessary, but
|
||||||
@@ -499,7 +505,7 @@ impl Clocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the frequency of the APB0 which is equal to the system clock.
|
/// Returns the frequency of the APB0 which is equal to the system clock.
|
||||||
pub fn apb0(&self) -> Hertz {
|
pub const fn apb0(&self) -> Hertz {
|
||||||
self.sysclk()
|
self.sysclk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
@@ -1,8 +1,16 @@
|
|||||||
|
//! IRQ Router peripheral support.
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::{PeripheralSelect, SyscfgExt},
|
clock::{PeripheralSelect, SyscfgExt},
|
||||||
pac,
|
pac,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This enables and initiates the peripheral.
|
||||||
|
///
|
||||||
|
/// Please note that this method also writes 0 to the registers which do not have 0 as the default
|
||||||
|
/// reset value. The programmers guide v1.2 and the actual values inspected using a SVD viewer
|
||||||
|
/// are inconsistent here, and the registers being non-zero can actually lead to weird bugs
|
||||||
|
/// when working with interrupts. Registers DMASELx and ADCSEL/DMASELx will reset to 0x7f and 0x1f
|
||||||
|
/// respectively instead of 0x00.
|
||||||
pub fn enable_and_init_irq_router(sysconfig: &mut pac::Sysconfig, irq_router: &pac::IrqRouter) {
|
pub fn enable_and_init_irq_router(sysconfig: &mut pac::Sysconfig, irq_router: &pac::IrqRouter) {
|
||||||
sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter);
|
sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter);
|
||||||
sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter);
|
sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter);
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
//! is not very accurate. You can use the [crate::clock] module for this. If you are working
|
//! is not very accurate. You can use the [crate::clock] module for this. If you are working
|
||||||
//! with interrupts, it is strongly recommended to set up the IRQ router with the
|
//! with interrupts, it is strongly recommended to set up the IRQ router with the
|
||||||
//! [crate::irq_router] module at the very least because that peripheral has confusing and/or
|
//! [crate::irq_router] module at the very least because that peripheral has confusing and/or
|
||||||
//! faulty register reset values which might leads to weird bugs and glitches.
|
//! faulty register reset values which might lead to weird bugs and glitches.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -46,7 +46,6 @@ pub mod edac;
|
|||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod irq_router;
|
pub mod irq_router;
|
||||||
pub mod nvm;
|
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
@@ -55,6 +54,9 @@ pub mod typelevel;
|
|||||||
pub mod uart;
|
pub mod uart;
|
||||||
pub mod wdt;
|
pub mod wdt;
|
||||||
|
|
||||||
|
#[cfg(feature = "va41630")]
|
||||||
|
pub mod nvm;
|
||||||
|
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
pub mod adc;
|
pub mod adc;
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
|
@@ -1,3 +1,10 @@
|
|||||||
|
//! Non-volatile memory (NVM) driver.
|
||||||
|
//!
|
||||||
|
//! Provides a basic API to work with the internal NVM of the VA41630 MCU.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [Flashloader application](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
use embedded_hal::spi::MODE_0;
|
use embedded_hal::spi::MODE_0;
|
||||||
|
|
||||||
use crate::clock::{Clocks, SyscfgExt};
|
use crate::clock::{Clocks, SyscfgExt};
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
||||||
|
//! - [Flashloader app using UART with IRQs](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use embedded_hal_nb::serial::Read;
|
use embedded_hal_nb::serial::Read;
|
||||||
|
@@ -410,5 +410,65 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Embassy Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "embassy-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/embassy-example",
|
||||||
|
"interface": "swd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "RTIC Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "embassy-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/rtic-example",
|
||||||
|
"interface": "swd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -186,5 +186,18 @@
|
|||||||
"kind": "build",
|
"kind": "build",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "rtic-example",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"rtic-example"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
Reference in New Issue
Block a user