Compare commits
1 Commits
va416xx-ha
...
2b9f0a7d53
Author | SHA1 | Date | |
---|---|---|---|
2b9f0a7d53 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -39,9 +39,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- 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
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features va41630
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
|
@ -1,14 +1,12 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"va416xx",
|
||||
"va416xx-hal",
|
||||
"vorago-peb1",
|
||||
"bootloader",
|
||||
"flashloader",
|
||||
"examples/simple",
|
||||
"examples/embassy",
|
||||
"examples/rtic",
|
||||
"va416xx",
|
||||
"va416xx-hal",
|
||||
"vorago-peb1"
|
||||
]
|
||||
exclude = [
|
||||
"flashloader/slot-a-blinky",
|
||||
|
13
README.md
13
README.md
@ -24,11 +24,7 @@ It also contains the following helper crates:
|
||||
- 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
|
||||
is compatible to the provided bootloader as well.
|
||||
- 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.
|
||||
- The `examples` folder contains various example applications crates for the HAL and the PAC.
|
||||
|
||||
## Using the `.cargo/config.toml` file
|
||||
|
||||
@ -100,9 +96,7 @@ example.
|
||||
### Using VS Code
|
||||
|
||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug). Please make sure that
|
||||
[`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||
are installed as well.
|
||||
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||
|
||||
Some sample configuration files for VS code were provided and can be used by running
|
||||
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||
@ -117,5 +111,4 @@ configuration variables in your `settings.json`:
|
||||
- `"cortex-debug.gdbPath.osx"`
|
||||
|
||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||
via the terminal at `RTT Ch:0 console`.
|
||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@ -25,9 +25,7 @@ pipeline {
|
||||
stage('Docs') {
|
||||
steps {
|
||||
sh """
|
||||
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
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
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
|
||||
```
|
@ -1,40 +0,0 @@
|
||||
[package]
|
||||
name = "embassy-example"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
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" }
|
||||
critical-section = "1"
|
||||
|
||||
embassy-sync = { version = "0.6.0" }
|
||||
embassy-time = { version = "0.3.2" }
|
||||
embassy-time-driver = { version = "0.1" }
|
||||
|
||||
[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.va416xx-hal]
|
||||
path = "../../va416xx-hal"
|
||||
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,4 +0,0 @@
|
||||
#![no_std]
|
||||
pub mod time_driver;
|
||||
|
||||
pub use time_driver::init;
|
@ -1,46 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Instant, Ticker};
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz};
|
||||
|
||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||
|
||||
// main is itself an async function.
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
rtt_init_print!();
|
||||
rprintln!("VA416xx Embassy Demo");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Initialize the systick interrupt & obtain the token to prove that we did
|
||||
// Use the external clock connected to XTAL_N.
|
||||
let clocks = dp
|
||||
.clkgen
|
||||
.constrain()
|
||||
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||
.freeze(&mut dp.sysconfig)
|
||||
.unwrap();
|
||||
// Safety: Only called once here.
|
||||
unsafe {
|
||||
embassy_example::init(
|
||||
&mut dp.sysconfig,
|
||||
&dp.irq_router,
|
||||
dp.tim15,
|
||||
dp.tim14,
|
||||
&clocks,
|
||||
)
|
||||
};
|
||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
loop {
|
||||
ticker.next().await;
|
||||
rprintln!("Current time: {}", Instant::now().as_secs());
|
||||
led.toggle().ok();
|
||||
}
|
||||
}
|
@ -1,323 +0,0 @@
|
||||
//! 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,24 +0,0 @@
|
||||
[package]
|
||||
name = "rtic-example"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
rtt-target = { version = "0.5" }
|
||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
panic-rtt-target = { version = "0.1.3" }
|
||||
|
||||
[dependencies.va416xx-hal]
|
||||
path = "../../va416xx-hal"
|
||||
features = ["va41630"]
|
||||
|
||||
[dependencies.rtic]
|
||||
version = "2"
|
||||
features = ["thumbv7-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
@ -1,57 +0,0 @@
|
||||
//! RTIC minimal blinky
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[rtic::app(device = pac, dispatchers = [U1, U2, U3])]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use embedded_hal::digital::StatefulOutputPin;
|
||||
use panic_rtt_target as _;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use rtt_target::{rprintln, rtt_init_default};
|
||||
use va416xx_hal::{
|
||||
gpio::{OutputReadablePushPull, Pin, PinsG, PG5},
|
||||
pac,
|
||||
};
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
led: Pin<PG5, OutputReadablePushPull>,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {}
|
||||
|
||||
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
||||
|
||||
#[init]
|
||||
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||
rtt_init_default!();
|
||||
rprintln!("-- Vorago RTIC template --");
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let portg = PinsG::new(&mut dp.sysconfig, dp.portg);
|
||||
let led = portg.pg5.into_readable_push_pull_output();
|
||||
blinky::spawn().ok();
|
||||
(Shared {}, Local { led })
|
||||
}
|
||||
|
||||
// `shared` cannot be accessed from this context
|
||||
#[idle]
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
loop {
|
||||
asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
#[task(
|
||||
priority = 3,
|
||||
local=[led],
|
||||
)]
|
||||
async fn blinky(cx: blinky::Context) {
|
||||
loop {
|
||||
cx.local.led.toggle().ok();
|
||||
Mono::delay(200.millis()).await;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,11 +4,11 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
critical-section = "1"
|
||||
panic-rtt-target = { version = "0.1.3" }
|
||||
rtt-target = { version = "0.5" }
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
rtic-sync = { version = "1.3", features = ["defmt-03"] }
|
||||
embedded-hal = "1"
|
||||
embedded-hal-nb = "1"
|
||||
nb = "1"
|
||||
@ -23,16 +23,8 @@ path = "../../va416xx-hal"
|
||||
path = "../../vorago-peb1"
|
||||
optional = true
|
||||
|
||||
[dependencies.rtic]
|
||||
version = "2"
|
||||
features = ["thumbv7-backend"]
|
||||
|
||||
[dependencies.rtic-monotonics]
|
||||
version = "2"
|
||||
features = ["cortex-m-systick"]
|
||||
|
||||
[features]
|
||||
default = ["va41630"]
|
||||
default = []
|
||||
va41630 = ["va416xx-hal/va41630", "has-adc-dac"]
|
||||
va41629 = ["va416xx-hal/va41629", "has-adc-dac"]
|
||||
va41628 = ["va416xx-hal/va41628"]
|
||||
|
@ -4,14 +4,13 @@
|
||||
|
||||
use core::cell::Cell;
|
||||
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use critical_section::Mutex;
|
||||
use embedded_hal::delay::DelayNs;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use simple_examples::peb1;
|
||||
use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock};
|
||||
use va416xx_hal::irq_router::enable_and_init_irq_router;
|
||||
use va416xx_hal::pwm::CountdownTimer;
|
||||
use va416xx_hal::{
|
||||
pac::{self, interrupt},
|
||||
@ -46,7 +45,6 @@ fn main() -> ! {
|
||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||
.freeze(&mut dp.sysconfig)
|
||||
.unwrap();
|
||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
||||
// Safety: The DMA control block has an alignment rule of 128 and we constructed it directly
|
||||
// statically.
|
||||
let dma = Dma::new(&mut dp.sysconfig, dp.dma, DmaCfg::default(), unsafe {
|
||||
@ -90,10 +88,10 @@ fn transfer_example_8_bit(
|
||||
(0..64).for_each(|i| {
|
||||
src_buf[i] = i as u8;
|
||||
});
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||
@ -114,7 +112,7 @@ fn transfer_example_8_bit(
|
||||
// Use polling for completion status.
|
||||
loop {
|
||||
let mut dma_done = false;
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||
rprintln!("DMA0 is active with 8 bit transfer");
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
@ -145,10 +143,10 @@ fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer<
|
||||
DMA_SRC_BUF[i] = (i as u32 * u16::MAX as u32 / (dest_buf_ref.len() as u32 - 1)) as u16;
|
||||
});
|
||||
}
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||
@ -172,7 +170,7 @@ fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer<
|
||||
// Use polling for completion status.
|
||||
loop {
|
||||
let mut dma_done = false;
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||
rprintln!("DMA0 is active with 16-bit transfer");
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
@ -208,10 +206,10 @@ fn transfer_example_32_bit(
|
||||
(0..16).for_each(|i| {
|
||||
src_buf[i] = (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32;
|
||||
});
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
});
|
||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||
@ -232,7 +230,7 @@ fn transfer_example_32_bit(
|
||||
// Use polling for completion status.
|
||||
loop {
|
||||
let mut dma_done = false;
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||
rprintln!("DMA0 is active with 32-bit transfer");
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||
@ -262,7 +260,7 @@ fn transfer_example_32_bit(
|
||||
#[allow(non_snake_case)]
|
||||
fn DMA_DONE0() {
|
||||
// Notify the main loop that the DMA transfer is finished.
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_DONE_FLAG.borrow(cs).set(true);
|
||||
});
|
||||
}
|
||||
@ -271,7 +269,7 @@ fn DMA_DONE0() {
|
||||
#[allow(non_snake_case)]
|
||||
fn DMA_ACTIVE0() {
|
||||
// Notify the main loop that the DMA 0 is active now.
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
DMA_ACTIVE_FLAG.borrow(cs).set(true);
|
||||
});
|
||||
}
|
||||
|
@ -3,14 +3,12 @@
|
||||
#![no_std]
|
||||
|
||||
use core::cell::Cell;
|
||||
use cortex_m::asm;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use critical_section::Mutex;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use simple_examples::peb1;
|
||||
use va416xx_hal::{
|
||||
irq_router::enable_and_init_irq_router,
|
||||
pac::{self, interrupt},
|
||||
prelude::*,
|
||||
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
|
||||
@ -37,21 +35,19 @@ fn main() -> ! {
|
||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||
.freeze(&mut dp.sysconfig)
|
||||
.unwrap();
|
||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
||||
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
||||
second_timer.listen();
|
||||
second_timer.start(1.Hz());
|
||||
second_timer.listen();
|
||||
loop {
|
||||
let current_ms = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
|
||||
if current_ms >= last_ms + 1000 {
|
||||
// To prevent drift.
|
||||
last_ms += 1000;
|
||||
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
||||
if current_ms - last_ms >= 1000 {
|
||||
last_ms = current_ms;
|
||||
rprintln!("MS counter: {}", current_ms);
|
||||
let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
|
||||
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
||||
rprintln!("Second counter: {}", second);
|
||||
}
|
||||
asm::delay(1000);
|
||||
cortex_m::asm::delay(10000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +60,7 @@ fn TIM0() {
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn TIM1() {
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||
sec += 1;
|
||||
SEC_COUNTER.borrow(cs).set(sec);
|
||||
|
@ -3,12 +3,11 @@
|
||||
#![no_std]
|
||||
|
||||
use core::cell::Cell;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
use cortex_m_rt::entry;
|
||||
use critical_section::Mutex;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use simple_examples::peb1;
|
||||
use va416xx_hal::irq_router::enable_and_init_irq_router;
|
||||
use va416xx_hal::pac::{self, interrupt};
|
||||
use va416xx_hal::prelude::*;
|
||||
use va416xx_hal::wdt::Wdt;
|
||||
@ -41,7 +40,6 @@ fn main() -> ! {
|
||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||
.freeze(&mut dp.sysconfig)
|
||||
.unwrap();
|
||||
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
||||
let mut delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
|
||||
|
||||
let mut last_interrupt_counter = 0;
|
||||
@ -51,7 +49,7 @@ fn main() -> ! {
|
||||
if TEST_MODE != TestMode::AllowReset {
|
||||
wdt_ctrl.feed();
|
||||
}
|
||||
let interrupt_counter = critical_section::with(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
|
||||
let interrupt_counter = cortex_m::interrupt::free(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
|
||||
if interrupt_counter > last_interrupt_counter {
|
||||
rprintln!("interrupt counter has increased to {}", interrupt_counter);
|
||||
last_interrupt_counter = interrupt_counter;
|
||||
@ -67,7 +65,7 @@ fn main() -> ! {
|
||||
#[interrupt]
|
||||
#[allow(non_snake_case)]
|
||||
fn WATCHDOG() {
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
WDT_INTRPT_COUNT
|
||||
.borrow(cs)
|
||||
.set(WDT_INTRPT_COUNT.borrow(cs).get() + 1);
|
||||
|
@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
from spacepackets.ecss import RequestId
|
||||
from spacepackets.ecss.defs import PusService
|
||||
from spacepackets.ecss.tm import PusTm
|
||||
import toml
|
||||
import struct
|
||||
import logging
|
||||
import argparse
|
||||
import threading
|
||||
import time
|
||||
import enum
|
||||
from tmtccmd.com.serial_base import SerialCfg
|
||||
@ -43,7 +45,6 @@ ACTION_SERVICE = 8
|
||||
|
||||
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||
BOOT_NVM_MEMORY_ID = 1
|
||||
PING_PAYLOAD_SIZE = 0
|
||||
|
||||
|
||||
class ActionId(enum.IntEnum):
|
||||
@ -103,29 +104,6 @@ def main() -> int:
|
||||
com_if = SerialCobsComIF(serial_cfg)
|
||||
com_if.open()
|
||||
file_path = None
|
||||
if args.ping:
|
||||
_LOGGER.info("Sending ping command")
|
||||
ping_tc = PusTc(
|
||||
apid=0x00,
|
||||
service=PusService.S17_TEST,
|
||||
subservice=1,
|
||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||
)
|
||||
verificator.add_tc(ping_tc)
|
||||
com_if.send(ping_tc.pack())
|
||||
|
||||
data_available = com_if.data_available(0.4)
|
||||
if not data_available:
|
||||
_LOGGER.warning("no ping reply received")
|
||||
for reply in com_if.receive():
|
||||
result = verificator.add_tm(
|
||||
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||
)
|
||||
if result is not None and result.completed:
|
||||
_LOGGER.info("received ping completion reply")
|
||||
if not args.target:
|
||||
return 0
|
||||
if args.target:
|
||||
if not args.corrupt:
|
||||
if not args.path:
|
||||
@ -135,6 +113,15 @@ def main() -> int:
|
||||
if not file_path.exists():
|
||||
_LOGGER.error("File does not exist")
|
||||
return -1
|
||||
if args.ping:
|
||||
_LOGGER.info("Sending ping command")
|
||||
ping_tc = PusTc(
|
||||
apid=0x00,
|
||||
service=PusService.S17_TEST,
|
||||
subservice=1,
|
||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||
)
|
||||
com_if.send(ping_tc.pack())
|
||||
if args.corrupt:
|
||||
if not args.target:
|
||||
_LOGGER.error("target for corruption command required")
|
||||
@ -267,7 +254,7 @@ def main() -> int:
|
||||
):
|
||||
done = True
|
||||
# Still keep a small delay
|
||||
# time.sleep(0.05)
|
||||
time.sleep(0.01)
|
||||
verificator.remove_completed_entries()
|
||||
if done:
|
||||
break
|
||||
|
@ -18,11 +18,13 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use embedded_hal_nb::serial::Read;
|
||||
use once_cell::sync::OnceCell;
|
||||
use panic_rtt_target as _;
|
||||
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
||||
|
||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||
const COBS_FRAME_SEPARATOR: u8 = 0x0;
|
||||
|
||||
const MAX_TC_SIZE: usize = 1024;
|
||||
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||
@ -31,8 +33,10 @@ const MAX_TM_SIZE: usize = 128;
|
||||
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||
|
||||
const UART_BAUDRATE: u32 = 115200;
|
||||
const SERIAL_RX_WIRETAPPING: bool = false;
|
||||
const COBS_RX_DEBUGGING: bool = false;
|
||||
|
||||
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||
const RX_DEBUGGING: bool = false;
|
||||
|
||||
pub enum ActionId {
|
||||
CorruptImageA = 128,
|
||||
@ -58,24 +62,13 @@ use ringbuf::{
|
||||
CachingCons, StaticProd, StaticRb,
|
||||
};
|
||||
|
||||
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||
const BUF_RB_SIZE_TC: usize = 2048;
|
||||
const SIZES_RB_SIZE_TC: usize = 16;
|
||||
const BUF_RB_SIZE_TX: usize = 1024;
|
||||
const SIZES_RB_SIZE_TX: usize = 16;
|
||||
|
||||
const BUF_RB_SIZE_TM: usize = 512;
|
||||
const SIZES_RB_SIZE_TM: usize = 16;
|
||||
|
||||
// Ring buffers to handling variable sized telemetry
|
||||
static mut BUF_RB_TM: Lazy<StaticRb<u8, BUF_RB_SIZE_TM>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TM>::default);
|
||||
static mut SIZES_RB_TM: Lazy<StaticRb<usize, SIZES_RB_SIZE_TM>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TM>::default);
|
||||
|
||||
// Ring buffers to handling variable sized telecommands
|
||||
static mut BUF_RB_TC: Lazy<StaticRb<u8, BUF_RB_SIZE_TC>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TC>::default);
|
||||
static mut SIZES_RB_TC: Lazy<StaticRb<usize, SIZES_RB_SIZE_TC>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TC>::default);
|
||||
static mut BUF_RB_TX: Lazy<StaticRb<u8, BUF_RB_SIZE_TX>> =
|
||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TX>::default);
|
||||
static mut SIZES_RB_TX: Lazy<StaticRb<usize, SIZES_RB_SIZE_TX>> =
|
||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TX>::default);
|
||||
|
||||
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
||||
@ -98,17 +91,21 @@ pub const APP_B_END_ADDR: u32 = 0x40000;
|
||||
mod app {
|
||||
use super::*;
|
||||
use cortex_m::asm;
|
||||
use embedded_hal_nb::nb;
|
||||
use embedded_io::Write;
|
||||
use panic_rtt_target as _;
|
||||
use rtic::Mutex;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_sync::{
|
||||
channel::{Receiver, Sender},
|
||||
make_channel,
|
||||
};
|
||||
use rtt_target::rprintln;
|
||||
use satrs::pus::verification::VerificationReportCreator;
|
||||
use spacepackets::ecss::PusServiceId;
|
||||
use spacepackets::ecss::{
|
||||
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
||||
};
|
||||
use va416xx_hal::irq_router::enable_and_init_irq_router;
|
||||
use va416xx_hal::{
|
||||
clock::ClkgenExt,
|
||||
edac,
|
||||
@ -130,24 +127,26 @@ mod app {
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
uart_rx: uart::RxWithIrq<pac::Uart0>,
|
||||
uart_rx: uart::Rx<pac::Uart0>,
|
||||
uart_tx: uart::Tx<pac::Uart0>,
|
||||
cobs_reader_state: CobsReaderStates,
|
||||
tc_tx: TcTx,
|
||||
tc_rx: TcRx,
|
||||
rom_spi: Option<pac::Spi3>,
|
||||
// We handle all TM in one task.
|
||||
tm_cons: DataConsumer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||
// We consume all TC in one task.
|
||||
tc_cons: DataConsumer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||
// We produce all TC in one task.
|
||||
tc_prod: DataProducer<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||
tx_cons: DataConsumer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
||||
verif_reporter: VerificationReportCreator,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
// Having this shared allows multiple tasks to generate telemetry.
|
||||
tm_prod: DataProducer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||
decode_buffer_busy: bool,
|
||||
decode_buf: [u8; MAX_TC_SIZE],
|
||||
tx_prod: DataProducer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
||||
}
|
||||
|
||||
pub type TcTx = Sender<'static, usize, 2>;
|
||||
pub type TcRx = Receiver<'static, usize, 2>;
|
||||
|
||||
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
||||
|
||||
#[init]
|
||||
@ -164,7 +163,6 @@ mod app {
|
||||
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||
.freeze(&mut cx.device.sysconfig)
|
||||
.unwrap();
|
||||
enable_and_init_irq_router(&mut cx.device.sysconfig, &cx.device.irq_router);
|
||||
setup_edac(&mut cx.device.sysconfig);
|
||||
|
||||
let gpiob = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
||||
@ -178,45 +176,38 @@ mod app {
|
||||
&mut cx.device.sysconfig,
|
||||
&clocks,
|
||||
);
|
||||
let (tx, mut rx, _) = uart0.split_with_irq();
|
||||
let (tx, rx) = uart0.split();
|
||||
let (tc_tx, tc_rx) = make_channel!(usize, 2);
|
||||
|
||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||
|
||||
let (buf_prod_tm, buf_cons_tm) = unsafe { BUF_RB_TM.split_ref() };
|
||||
let (sizes_prod_tm, sizes_cons_tm) = unsafe { SIZES_RB_TM.split_ref() };
|
||||
|
||||
let (buf_prod_tc, buf_cons_tc) = unsafe { BUF_RB_TC.split_ref() };
|
||||
let (sizes_prod_tc, sizes_cons_tc) = unsafe { SIZES_RB_TC.split_ref() };
|
||||
let (buf_prod, buf_cons) = unsafe { BUF_RB_TX.split_ref() };
|
||||
let (sizes_prod, sizes_cons) = unsafe { SIZES_RB_TX.split_ref() };
|
||||
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||
CLOCKS.set(clocks).unwrap();
|
||||
|
||||
rx.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||
.expect("initiating UART RX failed");
|
||||
pus_tc_handler::spawn().unwrap();
|
||||
uart_reader_task::spawn().unwrap();
|
||||
pus_tm_tx_handler::spawn().unwrap();
|
||||
(
|
||||
Shared {
|
||||
tm_prod: DataProducer {
|
||||
buf_prod: buf_prod_tm,
|
||||
sizes_prod: sizes_prod_tm,
|
||||
decode_buffer_busy: false,
|
||||
decode_buf: [0; MAX_TC_SIZE],
|
||||
tx_prod: DataProducer {
|
||||
buf_prod,
|
||||
sizes_prod,
|
||||
},
|
||||
},
|
||||
Local {
|
||||
uart_rx: rx,
|
||||
uart_tx: tx,
|
||||
cobs_reader_state: CobsReaderStates::default(),
|
||||
tc_tx,
|
||||
tc_rx,
|
||||
rom_spi: Some(cx.device.spi3),
|
||||
tm_cons: DataConsumer {
|
||||
buf_cons: buf_cons_tm,
|
||||
sizes_cons: sizes_cons_tm,
|
||||
},
|
||||
tc_cons: DataConsumer {
|
||||
buf_cons: buf_cons_tc,
|
||||
sizes_cons: sizes_cons_tc,
|
||||
},
|
||||
tc_prod: DataProducer {
|
||||
buf_prod: buf_prod_tc,
|
||||
sizes_prod: sizes_prod_tc,
|
||||
tx_cons: DataConsumer {
|
||||
buf_cons,
|
||||
sizes_cons,
|
||||
},
|
||||
verif_reporter,
|
||||
},
|
||||
@ -232,62 +223,120 @@ mod app {
|
||||
}
|
||||
|
||||
#[task(
|
||||
binds = UART0_RX,
|
||||
local = [
|
||||
cnt: u32 = 0,
|
||||
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
priority = 4,
|
||||
local=[
|
||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
uart_rx,
|
||||
tc_prod
|
||||
cobs_reader_state,
|
||||
tc_tx
|
||||
],
|
||||
shared=[decode_buffer_busy, decode_buf]
|
||||
)]
|
||||
fn uart_rx_irq(cx: uart_rx_irq::Context) {
|
||||
match cx.local.uart_rx.irq_handler(cx.local.rx_buf) {
|
||||
Ok(result) => {
|
||||
if RX_DEBUGGING {
|
||||
log::debug!("RX Info: {:?}", cx.local.uart_rx.irq_info());
|
||||
log::debug!("RX Result: {:?}", result);
|
||||
async fn uart_reader_task(mut cx: uart_reader_task::Context) {
|
||||
let mut current_idx = 0;
|
||||
loop {
|
||||
match cx.local.uart_rx.read() {
|
||||
Ok(byte) => {
|
||||
if SERIAL_RX_WIRETAPPING {
|
||||
log::debug!("RX Byte: 0x{:x?}", byte);
|
||||
}
|
||||
handle_single_rx_byte(&mut cx, byte, &mut current_idx)
|
||||
}
|
||||
if result.complete() {
|
||||
// Check frame validity (must have COBS format) and decode the frame.
|
||||
// Currently, we expect a full frame or a frame received through a timeout
|
||||
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||
// frame, but the additional complexity is not necessary here..
|
||||
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||
let decoded_size =
|
||||
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||
if decoded_size.is_err() {
|
||||
log::warn!("COBS decoding failed");
|
||||
} else {
|
||||
let decoded_size = decoded_size.unwrap();
|
||||
if cx.local.tc_prod.sizes_prod.vacant_len() >= 1
|
||||
&& cx.local.tc_prod.buf_prod.vacant_len() >= decoded_size
|
||||
{
|
||||
// Should never fail, we checked there is enough space.
|
||||
cx.local.tc_prod.sizes_prod.try_push(decoded_size).unwrap();
|
||||
cx.local
|
||||
.tc_prod
|
||||
.buf_prod
|
||||
.push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
|
||||
} else {
|
||||
log::warn!("COBS TC queue full");
|
||||
Err(e) => {
|
||||
match e {
|
||||
nb::Error::Other(e) => {
|
||||
log::warn!("UART error: {:?}", e);
|
||||
match e {
|
||||
uart::Error::Overrun => {
|
||||
cx.local.uart_rx.clear_fifo();
|
||||
}
|
||||
uart::Error::FramingError => (),
|
||||
uart::Error::ParityError => (),
|
||||
uart::Error::BreakCondition => (),
|
||||
uart::Error::TransferPending => (),
|
||||
uart::Error::BufferTooShort => (),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||
nb::Error::WouldBlock => {
|
||||
// Delay for a short period before polling again.
|
||||
Mono::delay(400.micros()).await;
|
||||
}
|
||||
}
|
||||
|
||||
// Initiate next transfer.
|
||||
cx.local
|
||||
.uart_rx
|
||||
.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||
.expect("read operation failed");
|
||||
}
|
||||
if result.error() {
|
||||
log::warn!("UART error: {:?}", result.error());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("UART error: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_single_rx_byte(
|
||||
cx: &mut uart_reader_task::Context,
|
||||
byte: u8,
|
||||
current_idx: &mut usize,
|
||||
) {
|
||||
match cx.local.cobs_reader_state {
|
||||
CobsReaderStates::WaitingForStart => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!("COBS start marker detected");
|
||||
}
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WatingForEnd;
|
||||
*current_idx = 0;
|
||||
}
|
||||
}
|
||||
CobsReaderStates::WatingForEnd => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!("COBS end marker detected");
|
||||
}
|
||||
let mut sending_failed = false;
|
||||
let mut decoding_error = false;
|
||||
let mut decode_buffer_busy = false;
|
||||
cx.shared.decode_buffer_busy.lock(|busy| {
|
||||
if *busy {
|
||||
decode_buffer_busy = true;
|
||||
} else {
|
||||
cx.shared.decode_buf.lock(|buf| {
|
||||
match cobs::decode(&cx.local.read_buf[..*current_idx], buf) {
|
||||
Ok(packet_len) => {
|
||||
if COBS_RX_DEBUGGING {
|
||||
log::debug!(
|
||||
"COBS decoded packet with length {}",
|
||||
packet_len
|
||||
);
|
||||
}
|
||||
if cx.local.tc_tx.try_send(packet_len).is_err() {
|
||||
sending_failed = true;
|
||||
}
|
||||
*busy = true;
|
||||
}
|
||||
Err(_) => {
|
||||
decoding_error = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if sending_failed {
|
||||
log::warn!("sending TC packet failed, queue full");
|
||||
}
|
||||
if decoding_error {
|
||||
log::warn!("decoding error");
|
||||
}
|
||||
if decode_buffer_busy {
|
||||
log::warn!("decode buffer busy. data arriving too fast");
|
||||
}
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
||||
} else if *current_idx >= cx.local.read_buf.len() {
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::FrameOverflow;
|
||||
} else {
|
||||
cx.local.read_buf[*current_idx] = byte;
|
||||
*current_idx += 1;
|
||||
}
|
||||
}
|
||||
CobsReaderStates::FrameOverflow => {
|
||||
if byte == COBS_FRAME_SEPARATOR {
|
||||
*cx.local.cobs_reader_state = CobsReaderStates::WaitingForStart;
|
||||
*current_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -295,167 +344,149 @@ mod app {
|
||||
#[task(
|
||||
priority = 2,
|
||||
local=[
|
||||
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||
src_data_buf: [u8; 16] = [0; 16],
|
||||
verif_buf: [u8; 32] = [0; 32],
|
||||
tc_cons,
|
||||
tc_rx,
|
||||
rom_spi,
|
||||
verif_reporter
|
||||
],
|
||||
shared=[tm_prod]
|
||||
shared=[decode_buffer_busy, decode_buf, tx_prod]
|
||||
)]
|
||||
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||
loop {
|
||||
// Try to read a TC from the ring buffer.
|
||||
let packet_len = cx.local.tc_cons.sizes_cons.try_pop();
|
||||
if packet_len.is_none() {
|
||||
// Small delay, TCs might arrive very quickly.
|
||||
Mono::delay(20.millis()).await;
|
||||
continue;
|
||||
}
|
||||
let packet_len = packet_len.unwrap();
|
||||
let packet_len = cx.local.tc_rx.recv().await.expect("all senders down");
|
||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||
assert_eq!(
|
||||
cx.local
|
||||
.tc_cons
|
||||
.buf_cons
|
||||
.pop_slice(&mut cx.local.tc_buf[0..packet_len]),
|
||||
packet_len
|
||||
);
|
||||
// Read a telecommand, now handle it.
|
||||
handle_valid_pus_tc(&mut cx);
|
||||
}
|
||||
}
|
||||
// We still copy the data to a local buffer, so the exchange buffer can already be used
|
||||
// for the next packet / decode process.
|
||||
cx.shared
|
||||
.decode_buf
|
||||
.lock(|buf| cx.local.read_buf[0..buf.len()].copy_from_slice(buf));
|
||||
cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
|
||||
match PusTcReader::new(cx.local.read_buf) {
|
||||
Ok((pus_tc, _)) => {
|
||||
let mut write_and_send = |tm: &PusTmCreator| {
|
||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||
cx.shared.tx_prod.lock(|prod| {
|
||||
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
||||
prod.buf_prod
|
||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
});
|
||||
};
|
||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||
let (tm, accepted_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.acceptance_success(cx.local.src_data_buf, token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
|
||||
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||
if pus_tc.is_err() {
|
||||
log::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
||||
return;
|
||||
}
|
||||
let (pus_tc, _) = pus_tc.unwrap();
|
||||
let mut write_and_send = |tm: &PusTmCreator| {
|
||||
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||
cx.shared.tm_prod.lock(|prod| {
|
||||
prod.sizes_prod.try_push(tm.len_written()).unwrap();
|
||||
prod.buf_prod
|
||||
.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||
});
|
||||
};
|
||||
let token = cx.local.verif_reporter.add_tc(&pus_tc);
|
||||
let (tm, accepted_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.acceptance_success(cx.local.src_data_buf, token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
let (tm, started_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
|
||||
let (tm, started_token) = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||
.expect("acceptance success failed");
|
||||
write_and_send(&tm);
|
||||
|
||||
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||
let mut corrupt_image = |base_addr: u32| {
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
let mut buf = [0u8; 4];
|
||||
nvm.read_data(base_addr + 32, &mut buf);
|
||||
buf[0] += 1;
|
||||
nvm.write_data(base_addr + 32, &buf);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
};
|
||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||
rprintln!("corrupting App Image A");
|
||||
corrupt_image(APP_A_START_ADDR);
|
||||
}
|
||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||
rprintln!("corrupting App Image B");
|
||||
corrupt_image(APP_B_START_ADDR);
|
||||
}
|
||||
}
|
||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||
log::info!(target: "TC Handler", "received ping TC");
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.step_success(
|
||||
cx.local.src_data_buf,
|
||||
&started_token,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.expect("step success failed");
|
||||
write_and_send(&tm);
|
||||
// Raw memory write TC
|
||||
if pus_tc.subservice() == 2 {
|
||||
let app_data = pus_tc.app_data();
|
||||
if app_data.len() < 10 {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"app data for raw memory write is too short: {}",
|
||||
app_data.len()
|
||||
);
|
||||
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||
let mut corrupt_image = |base_addr: u32| {
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
let mut buf = [0u8; 4];
|
||||
nvm.read_data(base_addr + 32, &mut buf);
|
||||
buf[0] += 1;
|
||||
nvm.write_data(base_addr + 32, &buf);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
};
|
||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||
rprintln!("corrupting App Image A");
|
||||
corrupt_image(APP_A_START_ADDR);
|
||||
}
|
||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||
rprintln!("corrupting App Image B");
|
||||
corrupt_image(APP_B_START_ADDR);
|
||||
}
|
||||
}
|
||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||
log::info!(target: "TC Handler", "received ping TC");
|
||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.step_success(
|
||||
cx.local.src_data_buf,
|
||||
&started_token,
|
||||
0,
|
||||
0,
|
||||
&[],
|
||||
EcssEnumU8::new(0),
|
||||
)
|
||||
.expect("step success failed");
|
||||
write_and_send(&tm);
|
||||
// Raw memory write TC
|
||||
if pus_tc.subservice() == 2 {
|
||||
let app_data = pus_tc.app_data();
|
||||
if app_data.len() < 10 {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"app data for raw memory write is too short: {}",
|
||||
app_data.len()
|
||||
);
|
||||
}
|
||||
let memory_id = app_data[0];
|
||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||
if 10 + data_len as usize > app_data.len() {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"invalid data length {} for raw mem write detected",
|
||||
data_len
|
||||
);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let data = &app_data[10..10 + data_len as usize];
|
||||
log::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
nvm.write_data(offset, data);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
log::info!("NVM operation done");
|
||||
}
|
||||
}
|
||||
}
|
||||
let memory_id = app_data[0];
|
||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
Err(e) => {
|
||||
log::warn!("PUS TC error: {}", e);
|
||||
}
|
||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||
if 10 + data_len as usize > app_data.len() {
|
||||
log::warn!(
|
||||
target: "TC Handler",
|
||||
"invalid data length {} for raw mem write detected",
|
||||
data_len
|
||||
);
|
||||
// TODO: Error reporting
|
||||
return;
|
||||
}
|
||||
let data = &app_data[10..10 + data_len as usize];
|
||||
log::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||
// Safety: We only use this for NVM handling and we only do NVM
|
||||
// handling here.
|
||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||
let nvm = Nvm::new(
|
||||
&mut sys_cfg,
|
||||
cx.local.rom_spi.take().unwrap(),
|
||||
CLOCKS.get().as_ref().unwrap(),
|
||||
);
|
||||
nvm.write_data(offset, data);
|
||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||
let tm = cx
|
||||
.local
|
||||
.verif_reporter
|
||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||
.expect("completion success failed");
|
||||
write_and_send(&tm);
|
||||
log::info!("NVM operation done");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -466,16 +497,16 @@ mod app {
|
||||
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||
uart_tx,
|
||||
tm_cons
|
||||
tx_cons,
|
||||
],
|
||||
shared=[]
|
||||
)]
|
||||
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
||||
loop {
|
||||
while cx.local.tm_cons.sizes_cons.occupied_len() > 0 {
|
||||
let next_size = cx.local.tm_cons.sizes_cons.try_pop().unwrap();
|
||||
while cx.local.tx_cons.sizes_cons.occupied_len() > 0 {
|
||||
let next_size = cx.local.tx_cons.sizes_cons.try_pop().unwrap();
|
||||
cx.local
|
||||
.tm_cons
|
||||
.tx_cons
|
||||
.buf_cons
|
||||
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||
cx.local.encoded_buf[0] = 0;
|
||||
@ -490,7 +521,7 @@ mod app {
|
||||
.unwrap();
|
||||
Mono::delay(2.millis()).await;
|
||||
}
|
||||
Mono::delay(50.millis()).await;
|
||||
Mono::delay(30.millis()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
# [unreleased]
|
||||
|
||||
# [v0.2.0] 2024-09-18
|
||||
# [v0.2.0]
|
||||
|
||||
- Documentation improvements
|
||||
- Improved UART typing support: Validity of passed pins is now checked properly
|
||||
@ -17,17 +17,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
||||
selected for HAL compilation to work properly
|
||||
- Adaptions for the UART IRQ feature which are now only implemented for the RX part of the UART.
|
||||
|
||||
## Fixed
|
||||
|
||||
- Small fixes and improvements for ADC drivers
|
||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||
correctly
|
||||
- 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
|
||||
|
||||
|
@ -12,7 +12,6 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
critical-section = "1"
|
||||
nb = "1"
|
||||
paste = "1"
|
||||
embedded-hal-nb = "1"
|
||||
@ -21,7 +20,7 @@ embedded-io = "0.6"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
typenum = "1"
|
||||
bitflags = "2"
|
||||
bitfield = "0.17"
|
||||
bitfield = "0.15"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
fugit = "0.3"
|
||||
delegate = "0.12"
|
||||
|
@ -311,12 +311,6 @@ impl ClkgenCfgr {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pll_cfg(mut self, pll_cfg: PllCfg) -> Self {
|
||||
self.pll_cfg = Some(pll_cfg);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
|
||||
self.ref_clk_sel = ref_clk_sel;
|
||||
@ -324,7 +318,7 @@ impl ClkgenCfgr {
|
||||
}
|
||||
|
||||
/// Configures all clocks and return a clock configuration structure containing the final
|
||||
/// frozen clocks.
|
||||
/// frozen clock.
|
||||
///
|
||||
/// 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
|
||||
@ -500,33 +494,33 @@ pub struct Clocks {
|
||||
|
||||
impl Clocks {
|
||||
/// Returns the frequency of the HBO clock
|
||||
pub const fn hbo(&self) -> Hertz {
|
||||
pub fn hbo(&self) -> Hertz {
|
||||
HBO_FREQ
|
||||
}
|
||||
|
||||
/// Returns the frequency of the APB0 which is equal to the system clock.
|
||||
pub const fn apb0(&self) -> Hertz {
|
||||
pub fn apb0(&self) -> Hertz {
|
||||
self.sysclk()
|
||||
}
|
||||
|
||||
/// Returns system clock divied by 2.
|
||||
pub const fn apb1(&self) -> Hertz {
|
||||
pub fn apb1(&self) -> Hertz {
|
||||
self.apb1
|
||||
}
|
||||
|
||||
/// Returns system clock divied by 4.
|
||||
pub const fn apb2(&self) -> Hertz {
|
||||
pub fn apb2(&self) -> Hertz {
|
||||
self.apb2
|
||||
}
|
||||
|
||||
/// Returns the system (core) frequency
|
||||
pub const fn sysclk(&self) -> Hertz {
|
||||
pub fn sysclk(&self) -> Hertz {
|
||||
self.sysclk
|
||||
}
|
||||
|
||||
/// Returns the ADC clock frequency which has a separate divider.
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
pub const fn adc_clk(&self) -> Hertz {
|
||||
pub fn adc_clk(&self) -> Hertz {
|
||||
self.adc_clk
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct IsMaskedError;
|
||||
|
@ -1,26 +0,0 @@
|
||||
//! IRQ Router peripheral support.
|
||||
use crate::{
|
||||
clock::{PeripheralSelect, SyscfgExt},
|
||||
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) {
|
||||
sysconfig.enable_peripheral_clock(PeripheralSelect::IrqRouter);
|
||||
sysconfig.assert_periph_reset_for_two_cycles(PeripheralSelect::IrqRouter);
|
||||
unsafe {
|
||||
irq_router.dmasel0().write_with_zero(|w| w);
|
||||
irq_router.dmasel1().write_with_zero(|w| w);
|
||||
irq_router.dmasel2().write_with_zero(|w| w);
|
||||
irq_router.dmasel3().write_with_zero(|w| w);
|
||||
irq_router.adcsel().write_with_zero(|w| w);
|
||||
irq_router.dacsel0().write_with_zero(|w| w);
|
||||
irq_router.dacsel1().write_with_zero(|w| w);
|
||||
}
|
||||
}
|
@ -1,26 +1,3 @@
|
||||
//! This is the **H**ardware **A**bstraction **L**ayer (HAL) for the VA416xx MCU family.
|
||||
//!
|
||||
//! It is an additional hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/va416xx).
|
||||
|
||||
//! It is the result of reading the datasheet for the device and encoding a type-safe layer over the
|
||||
//! raw PAC. This crate also implements traits specified by the
|
||||
//! [embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||
//! various drivers in the embedded rust ecosystem.
|
||||
|
||||
//! You have to enable one of the following device features to use this crate depending on
|
||||
//! which chip you are using:
|
||||
|
||||
//! - `va41630`
|
||||
//! - `va41629`
|
||||
//! - `va41628`
|
||||
//! - `va41620`
|
||||
//!
|
||||
//! When using this HAL and writing applications for the VA416xx family in general, it is strongly
|
||||
//! recommended that you set up the clock properly, because the default internal HBO clock
|
||||
//! 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
|
||||
//! [crate::irq_router] module at the very least because that peripheral has confusing and/or
|
||||
//! faulty register reset values which might lead to weird bugs and glitches.
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#[cfg(test)]
|
||||
@ -45,7 +22,7 @@ pub mod dma;
|
||||
pub mod edac;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod irq_router;
|
||||
pub mod nvm;
|
||||
pub mod pwm;
|
||||
pub mod spi;
|
||||
pub mod time;
|
||||
@ -54,9 +31,6 @@ pub mod typelevel;
|
||||
pub mod uart;
|
||||
pub mod wdt;
|
||||
|
||||
#[cfg(feature = "va41630")]
|
||||
pub mod nvm;
|
||||
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
pub mod adc;
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
|
@ -1,10 +1,3 @@
|
||||
//! 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 crate::clock::{Clocks, SyscfgExt};
|
||||
|
@ -5,8 +5,7 @@
|
||||
//! - [Timer MS and Second Tick Example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/timer-ticks.rs)
|
||||
use core::cell::Cell;
|
||||
|
||||
use cortex_m::asm;
|
||||
use critical_section::Mutex;
|
||||
use cortex_m::interrupt::Mutex;
|
||||
|
||||
use crate::clock::Clocks;
|
||||
use crate::gpio::{
|
||||
@ -170,14 +169,6 @@ macro_rules! tim_markers {
|
||||
};
|
||||
}
|
||||
|
||||
pub const fn const_clock<Tim: ValidTim + ?Sized>(_: &Tim, clocks: &Clocks) -> Hertz {
|
||||
if Tim::TIM_ID <= 15 {
|
||||
clocks.apb1()
|
||||
} else {
|
||||
clocks.apb2()
|
||||
}
|
||||
}
|
||||
|
||||
tim_markers!(
|
||||
(pac::Tim0, 0, pac::Interrupt::TIM0),
|
||||
(pac::Tim1, 1, pac::Interrupt::TIM1),
|
||||
@ -337,27 +328,19 @@ valid_pin_and_tims!(
|
||||
///
|
||||
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||
#[inline]
|
||||
pub fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||
fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||
syscfg
|
||||
.tim_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||
fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||
syscfg
|
||||
.tim_reset()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim_id as u32)) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn assert_tim_reset_for_two_cycles(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||
assert_tim_reset(syscfg, tim_id);
|
||||
asm::nop();
|
||||
asm::nop();
|
||||
deassert_tim_reset(syscfg, tim_id);
|
||||
}
|
||||
|
||||
pub type TimRegBlock = pac::tim0::RegisterBlock;
|
||||
|
||||
/// Register interface.
|
||||
@ -498,7 +481,7 @@ pub struct CountdownTimer<TIM: ValidTim> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||
syscfg
|
||||
.tim_clk_enable()
|
||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||
@ -596,10 +579,9 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
||||
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||
self.curr_freq = timeout.into();
|
||||
self.rst_val = (self.clock.raw() / self.curr_freq.raw()) - 1;
|
||||
self.rst_val = self.clock.raw() / self.curr_freq.raw();
|
||||
self.set_reload(self.rst_val);
|
||||
// Decrementing counter, to set the reset value.
|
||||
self.set_count(self.rst_val);
|
||||
self.set_count(0);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -619,7 +601,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
||||
|
||||
#[inline(always)]
|
||||
pub fn enable(&mut self) {
|
||||
self.tim.reg().enable().write(|w| unsafe { w.bits(1) });
|
||||
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -796,7 +778,7 @@ pub fn set_up_ms_tick<Tim: ValidTim>(
|
||||
/// This function can be called in a specified interrupt handler to increment
|
||||
/// the MS counter
|
||||
pub fn default_ms_irq_handler() {
|
||||
critical_section::with(|cs| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||
ms += 1;
|
||||
MS_COUNTER.borrow(cs).set(ms);
|
||||
@ -805,7 +787,7 @@ pub fn default_ms_irq_handler() {
|
||||
|
||||
/// Get the current MS tick count
|
||||
pub fn get_ms_ticks() -> u32 {
|
||||
critical_section::with(|cs| MS_COUNTER.borrow(cs).get())
|
||||
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
||||
}
|
||||
|
||||
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
|
||||
|
@ -3,7 +3,6 @@
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [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 embedded_hal_nb::serial::Read;
|
||||
@ -198,36 +197,66 @@ impl From<Hertz> for Config {
|
||||
// IRQ Definitions
|
||||
//==================================================================================================
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IrqInfo {
|
||||
struct IrqInfo {
|
||||
rx_len: usize,
|
||||
rx_idx: usize,
|
||||
mode: IrqReceptionMode,
|
||||
}
|
||||
|
||||
pub enum IrqResultMask {
|
||||
Complete = 0,
|
||||
Overflow = 1,
|
||||
FramingError = 2,
|
||||
ParityError = 3,
|
||||
Break = 4,
|
||||
Timeout = 5,
|
||||
Addr9 = 6,
|
||||
/// Should not happen
|
||||
Unknown = 7,
|
||||
}
|
||||
|
||||
/// This struct is used to return the default IRQ handler result to the user
|
||||
#[derive(Debug, Default)]
|
||||
pub struct IrqResult {
|
||||
complete: bool,
|
||||
timeout: bool,
|
||||
pub errors: IrqUartError,
|
||||
raw_res: u32,
|
||||
pub bytes_read: usize,
|
||||
}
|
||||
|
||||
impl IrqResult {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
IrqResult {
|
||||
complete: false,
|
||||
timeout: false,
|
||||
errors: IrqUartError::default(),
|
||||
raw_res: 0,
|
||||
bytes_read: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IrqResult {
|
||||
#[inline]
|
||||
pub fn raw_result(&self) -> u32 {
|
||||
self.raw_res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn clear_result(&mut self) {
|
||||
self.raw_res = 0;
|
||||
}
|
||||
#[inline]
|
||||
pub(crate) fn set_result(&mut self, flag: IrqResultMask) {
|
||||
self.raw_res |= 1 << flag as u32;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn complete(&self) -> bool {
|
||||
if ((self.raw_res >> IrqResultMask::Complete as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn error(&self) -> bool {
|
||||
if self.errors.overflow || self.errors.parity || self.errors.framing {
|
||||
if self.overflow_error() || self.framing_error() || self.parity_error() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
@ -235,27 +264,34 @@ impl IrqResult {
|
||||
|
||||
#[inline]
|
||||
pub fn overflow_error(&self) -> bool {
|
||||
self.errors.overflow
|
||||
if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn framing_error(&self) -> bool {
|
||||
self.errors.framing
|
||||
if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn parity_error(&self) -> bool {
|
||||
self.errors.parity
|
||||
if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn timeout(&self) -> bool {
|
||||
self.timeout
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn complete(&self) -> bool {
|
||||
self.complete
|
||||
if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,27 +317,41 @@ pub struct Uart<UartInstance, Pins> {
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<Uart>(Uart);
|
||||
/// UART using the IRQ capabilities of the peripheral. Can be created with the
|
||||
/// [`Uart::into_uart_with_irq`] function. Currently, only the RX side for IRQ based reception
|
||||
/// is implemented.
|
||||
pub struct UartWithIrq<Uart, Pins> {
|
||||
base: UartWithIrqBase<Uart>,
|
||||
pins: Pins,
|
||||
}
|
||||
|
||||
// Serial receiver, using interrupts to offload reading to the hardware.
|
||||
pub struct RxWithIrq<Uart> {
|
||||
inner: Rx<Uart>,
|
||||
/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the
|
||||
/// [`UartWithIrq::downgrade`] function. Currently, only the RX side for IRQ based reception
|
||||
/// is implemented.
|
||||
pub struct UartWithIrqBase<UART> {
|
||||
pub inner: UartBase<UART>,
|
||||
irq_info: IrqInfo,
|
||||
}
|
||||
|
||||
/// Serial receiver
|
||||
pub struct Rx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
|
||||
/// Serial transmitter
|
||||
pub struct Tx<Uart>(Uart);
|
||||
pub struct Tx<Uart> {
|
||||
uart: Uart,
|
||||
}
|
||||
|
||||
impl<Uart: Instance> Rx<Uart> {
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self(uart)
|
||||
Self { uart }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart> Tx<Uart> {
|
||||
fn new(uart: Uart) -> Self {
|
||||
Self(uart)
|
||||
Self { uart }
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,30 +602,20 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
||||
}
|
||||
|
||||
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
||||
/// with this function. Currently, IRQ abstractions are only implemented for the RX part
|
||||
/// of the UART, so this function will release a TX and RX handle as well as the pin
|
||||
/// instances.
|
||||
pub fn split_with_irq(
|
||||
self,
|
||||
) -> (
|
||||
Tx<UartInstance>,
|
||||
RxWithIrq<UartInstance>,
|
||||
(TxPinInst, RxPinInst),
|
||||
) {
|
||||
/// with this function
|
||||
pub fn into_uart_with_irq(self) -> UartWithIrq<UartInstance, (TxPinInst, RxPinInst)> {
|
||||
let (inner, pins) = self.downgrade_internal();
|
||||
let (tx, rx) = inner.split();
|
||||
(
|
||||
tx,
|
||||
RxWithIrq {
|
||||
inner: rx,
|
||||
UartWithIrq {
|
||||
pins,
|
||||
base: UartWithIrqBase {
|
||||
inner,
|
||||
irq_info: IrqInfo {
|
||||
rx_len: 0,
|
||||
rx_idx: 0,
|
||||
mode: IrqReceptionMode::Idle,
|
||||
},
|
||||
},
|
||||
pins,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
delegate::delegate! {
|
||||
@ -633,26 +673,11 @@ impl<Uart: Instance> Rx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
&self.uart
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.rxenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
|
||||
}
|
||||
|
||||
pub fn release(self) -> Uart {
|
||||
self.0
|
||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,22 +688,11 @@ impl<Uart: Instance> Tx<Uart> {
|
||||
///
|
||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||
pub unsafe fn uart(&self) -> &Uart {
|
||||
&self.0
|
||||
&self.uart
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_fifo(&self) {
|
||||
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.txenable().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.0.enable().modify(|_, w| w.txenable().clear_bit());
|
||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
@ -687,7 +701,6 @@ pub struct IrqUartError {
|
||||
overflow: bool,
|
||||
framing: bool,
|
||||
parity: bool,
|
||||
other: bool,
|
||||
}
|
||||
|
||||
impl IrqUartError {
|
||||
@ -702,7 +715,7 @@ pub enum IrqError {
|
||||
Uart(IrqUartError),
|
||||
}
|
||||
|
||||
impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
impl<Uart: Instance> UartWithIrqBase<Uart> {
|
||||
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
||||
/// peripheral.
|
||||
///
|
||||
@ -722,7 +735,8 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
self.irq_info.mode = IrqReceptionMode::Pending;
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = max_len;
|
||||
self.inner.enable();
|
||||
self.inner.enable_rx();
|
||||
self.inner.enable_tx();
|
||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
||||
Ok(())
|
||||
@ -730,7 +744,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
|
||||
#[inline]
|
||||
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
||||
self.inner.0.irq_enb().modify(|_, w| {
|
||||
self.inner.uart.irq_enb().modify(|_, w| {
|
||||
if timeout {
|
||||
w.irq_rx_to().set_bit();
|
||||
}
|
||||
@ -741,22 +755,28 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
|
||||
#[inline]
|
||||
fn disable_rx_irq_sources(&mut self) {
|
||||
self.inner.0.irq_enb().modify(|_, w| {
|
||||
self.inner.uart.irq_enb().modify(|_, w| {
|
||||
w.irq_rx_to().clear_bit();
|
||||
w.irq_rx_status().clear_bit();
|
||||
w.irq_rx().clear_bit()
|
||||
});
|
||||
}
|
||||
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.disable_rx_irq_sources();
|
||||
self.inner.clear_fifo();
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = 0;
|
||||
#[inline]
|
||||
pub fn enable_tx(&mut self) {
|
||||
self.inner.enable_tx()
|
||||
}
|
||||
|
||||
pub fn uart(&self) -> &Uart {
|
||||
&self.inner.0
|
||||
#[inline]
|
||||
pub fn disable_tx(&mut self) {
|
||||
self.inner.disable_tx()
|
||||
}
|
||||
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.disable_rx_irq_sources();
|
||||
self.inner.clear_tx_fifo();
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = 0;
|
||||
}
|
||||
|
||||
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
||||
@ -771,102 +791,104 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
});
|
||||
}
|
||||
let mut res = IrqResult::default();
|
||||
let mut possible_error = IrqUartError::default();
|
||||
|
||||
let irq_end = self.inner.0.irq_end().read();
|
||||
let enb_status = self.inner.0.enable().read();
|
||||
let rx_status = self.inner.uart.rxstatus().read();
|
||||
res.raw_res = rx_status.bits();
|
||||
let irq_end = self.inner.uart.irq_end().read();
|
||||
let enb_status = self.inner.uart.enable().read();
|
||||
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||
|
||||
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||
let _tx_enabled = enb_status.txenable().bit_is_set();
|
||||
let read_handler = |res: &mut IrqResult,
|
||||
possible_error: &mut IrqUartError,
|
||||
read_res: nb::Result<u8, Error>|
|
||||
-> Option<u8> {
|
||||
match read_res {
|
||||
Ok(byte) => Some(byte),
|
||||
Err(nb::Error::WouldBlock) => None,
|
||||
Err(nb::Error::Other(e)) => {
|
||||
match e {
|
||||
Error::Overrun => {
|
||||
possible_error.overflow = true;
|
||||
}
|
||||
Error::FramingError => {
|
||||
possible_error.framing = true;
|
||||
}
|
||||
Error::ParityError => {
|
||||
possible_error.parity = true;
|
||||
}
|
||||
_ => {
|
||||
res.set_result(IrqResultMask::Unknown);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
if irq_end.irq_rx().bit_is_set() {
|
||||
// Determine the number of bytes to read, ensuring we leave 1 byte in the FIFO.
|
||||
// We use this trick/hack because the timeout feature of the peripheral relies on data
|
||||
// being in the RX FIFO. If data continues arriving, another half-full IRQ will fire.
|
||||
// If not, the last byte(s) is/are emptied by the timeout interrupt.
|
||||
let available_bytes = self.inner.0.rxfifoirqtrg().read().bits() as usize;
|
||||
|
||||
let bytes_to_read = core::cmp::min(
|
||||
available_bytes.saturating_sub(1),
|
||||
self.irq_info.rx_len - self.irq_info.rx_idx,
|
||||
);
|
||||
|
||||
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||
// Read everything as fast as possible
|
||||
for _ in 0..bytes_to_read {
|
||||
buf[self.irq_info.rx_idx] = (self.inner.0.data().read().bits() & 0xff) as u8;
|
||||
for _ in 0..core::cmp::min(
|
||||
self.inner.uart.rxfifoirqtrg().read().bits() as usize,
|
||||
self.irq_info.rx_len,
|
||||
) {
|
||||
buf[self.irq_info.rx_idx] = (self.inner.uart.data().read().bits() & 0xff) as u8;
|
||||
self.irq_info.rx_idx += 1;
|
||||
}
|
||||
|
||||
// On high-baudrates, data might be available immediately, and we possible have to
|
||||
// read continuosly? Then again, the CPU should always be faster than that. I'd rather
|
||||
// rely on the hardware firing another IRQ. I have not tried baudrates higher than
|
||||
// 115200 so far.
|
||||
}
|
||||
let read_handler =
|
||||
|possible_error: &mut IrqUartError, read_res: nb::Result<u8, Error>| -> Option<u8> {
|
||||
match read_res {
|
||||
Ok(byte) => Some(byte),
|
||||
Err(nb::Error::WouldBlock) => None,
|
||||
Err(nb::Error::Other(e)) => {
|
||||
match e {
|
||||
Error::Overrun => {
|
||||
possible_error.overflow = true;
|
||||
}
|
||||
Error::FramingError => {
|
||||
possible_error.framing = true;
|
||||
}
|
||||
Error::ParityError => {
|
||||
possible_error.parity = true;
|
||||
}
|
||||
_ => {
|
||||
possible_error.other = true;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
// Timeout, empty the FIFO completely.
|
||||
if irq_end.irq_rx_to().bit_is_set() {
|
||||
// While there is data in the FIFO, write it into the reception buffer
|
||||
loop {
|
||||
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
||||
break;
|
||||
self.irq_completion_handler(&mut res);
|
||||
return Ok(res);
|
||||
}
|
||||
if let Some(byte) = read_handler(&mut res.errors, self.inner.read()) {
|
||||
if let Some(byte) = read_handler(&mut res, &mut possible_error, self.inner.read()) {
|
||||
buf[self.irq_info.rx_idx] = byte;
|
||||
self.irq_info.rx_idx += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
self.irq_completion_handler(&mut res);
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
// RX transfer not complete, check for RX errors
|
||||
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
||||
// Read status register again, might have changed since reading received data
|
||||
let rx_status = self.inner.0.rxstatus().read();
|
||||
let rx_status = self.inner.uart.rxstatus().read();
|
||||
res.raw_res = rx_status.bits();
|
||||
if rx_status.rxovr().bit_is_set() {
|
||||
res.errors.overflow = true;
|
||||
possible_error.overflow = true;
|
||||
}
|
||||
if rx_status.rxfrm().bit_is_set() {
|
||||
res.errors.framing = true;
|
||||
possible_error.framing = true;
|
||||
}
|
||||
if rx_status.rxpar().bit_is_set() {
|
||||
res.errors.parity = true;
|
||||
possible_error.parity = true;
|
||||
}
|
||||
if rx_status.rxto().bit_is_set() {
|
||||
// A timeout has occured but there might be some leftover data in the FIFO,
|
||||
// so read that data as well
|
||||
while let Some(byte) =
|
||||
read_handler(&mut res, &mut possible_error, self.inner.read())
|
||||
{
|
||||
buf[self.irq_info.rx_idx] = byte;
|
||||
self.irq_info.rx_idx += 1;
|
||||
}
|
||||
self.irq_completion_handler(&mut res);
|
||||
res.set_result(IrqResultMask::Timeout);
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
// If it is not a timeout, it's an error
|
||||
if res.error() {
|
||||
if possible_error.error() {
|
||||
self.disable_rx_irq_sources();
|
||||
return Err(IrqError::Uart(res.errors));
|
||||
return Err(IrqError::Uart(possible_error));
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the interrupt status bits
|
||||
self.inner
|
||||
.0
|
||||
.uart
|
||||
.irq_clr()
|
||||
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
||||
Ok(res)
|
||||
@ -874,23 +896,48 @@ impl<Uart: Instance> RxWithIrq<Uart> {
|
||||
|
||||
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
||||
self.disable_rx_irq_sources();
|
||||
self.inner.disable();
|
||||
self.inner.disable_rx();
|
||||
res.bytes_read = self.irq_info.rx_idx;
|
||||
res.complete = true;
|
||||
res.clear_result();
|
||||
res.set_result(IrqResultMask::Complete);
|
||||
self.irq_info.mode = IrqReceptionMode::Idle;
|
||||
self.irq_info.rx_idx = 0;
|
||||
self.irq_info.rx_len = 0;
|
||||
}
|
||||
|
||||
pub fn irq_info(&self) -> &IrqInfo {
|
||||
&self.irq_info
|
||||
}
|
||||
|
||||
pub fn release(self) -> Uart {
|
||||
self.inner.release()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Uart: Instance, Pins> UartWithIrq<Uart, Pins> {
|
||||
/// See [`UartWithIrqBase::read_fixed_len_using_irq`] doc
|
||||
pub fn read_fixed_len_using_irq(
|
||||
&mut self,
|
||||
max_len: usize,
|
||||
enb_timeout_irq: bool,
|
||||
) -> Result<(), Error> {
|
||||
self.base.read_fixed_len_using_irq(max_len, enb_timeout_irq)
|
||||
}
|
||||
|
||||
pub fn cancel_transfer(&mut self) {
|
||||
self.base.cancel_transfer()
|
||||
}
|
||||
|
||||
/// See [`UartWithIrqBase::irq_handler`] doc
|
||||
pub fn irq_handler(&mut self, buf: &mut [u8]) -> Result<IrqResult, IrqError> {
|
||||
self.base.irq_handler(buf)
|
||||
}
|
||||
|
||||
pub fn release(self) -> (Uart, Pins) {
|
||||
(self.base.release(), self.pins)
|
||||
}
|
||||
|
||||
pub fn downgrade(self) -> (UartWithIrqBase<Uart>, Pins) {
|
||||
(self.base, self.pins)
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
|
@ -74,7 +74,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -103,7 +104,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -132,7 +134,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -161,11 +164,11 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
@ -191,7 +194,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -220,7 +224,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -250,7 +255,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -280,187 +286,8 @@
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug PWM Example",
|
||||
"servertype": "jlink",
|
||||
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M4",
|
||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||
"preLaunchTask": "pwm-example",
|
||||
"overrideLaunchCommands": [
|
||||
"monitor halt",
|
||||
"monitor reset",
|
||||
"load",
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/pwm",
|
||||
"interface": "swd",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Debug DMA Example",
|
||||
"servertype": "jlink",
|
||||
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M4",
|
||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||
"preLaunchTask": "dma-example",
|
||||
"overrideLaunchCommands": [
|
||||
"monitor halt",
|
||||
"monitor reset",
|
||||
"load",
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/dma",
|
||||
"interface": "swd",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Bootloader",
|
||||
"servertype": "jlink",
|
||||
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M4",
|
||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||
"preLaunchTask": "bootloader",
|
||||
"overrideLaunchCommands": [
|
||||
"monitor halt",
|
||||
"monitor reset",
|
||||
"load",
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/bootloader",
|
||||
"interface": "swd",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cortex-debug",
|
||||
"request": "launch",
|
||||
"name": "Flashloader",
|
||||
"servertype": "jlink",
|
||||
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"device": "Cortex-M4",
|
||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||
"preLaunchTask": "flashloader",
|
||||
"overrideLaunchCommands": [
|
||||
"monitor halt",
|
||||
"monitor reset",
|
||||
"load",
|
||||
],
|
||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/flashloader",
|
||||
"interface": "swd",
|
||||
"runToEntryPoint": "main",
|
||||
"rttConfig": {
|
||||
"enabled": true,
|
||||
"address": "auto",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
"timestamp": true,
|
||||
"type": "console"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
||||
"address": "0x1fff8000",
|
||||
"decoders": [
|
||||
{
|
||||
"port": 0,
|
||||
@ -471,4 +298,4 @@
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
@ -3,32 +3,6 @@
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "bootloader",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--bin",
|
||||
"bootloader"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "flashloader",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--bin",
|
||||
"flashloader"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "blinky-pac-example",
|
||||
"type": "shell",
|
||||
@ -55,19 +29,6 @@
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "timer-ticks-example",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--example",
|
||||
"timer-ticks"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "blinky-example",
|
||||
"type": "shell",
|
||||
@ -95,19 +56,6 @@
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "pwm-example",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--example",
|
||||
"pwm"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "wdt-example",
|
||||
"type": "shell",
|
||||
@ -160,44 +108,5 @@
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "dma-example",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--example",
|
||||
"dma"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "embassy-example",
|
||||
"type": "shell",
|
||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||
"args": [
|
||||
"build",
|
||||
"--bin",
|
||||
"embassy-example"
|
||||
],
|
||||
"group": {
|
||||
"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