Compare commits
12 Commits
7a525b0107
...
va416xx-ha
Author | SHA1 | Date | |
---|---|---|---|
dfab81a813 | |||
2c0728102a
|
|||
5ce2dae236 | |||
19c64a8257
|
|||
4ed0a806a6 | |||
a1a83700f8 | |||
ed175a03fc | |||
2465248223 | |||
1603de11bf | |||
78dd7ee5c3 | |||
cad968342a | |||
d386e5f6a1 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -39,7 +39,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features va41630
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features va41630
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
|
"va416xx",
|
||||||
|
"va416xx-hal",
|
||||||
|
"vorago-peb1",
|
||||||
"bootloader",
|
"bootloader",
|
||||||
"flashloader",
|
"flashloader",
|
||||||
"examples/simple",
|
"examples/simple",
|
||||||
"va416xx",
|
"examples/embassy",
|
||||||
"va416xx-hal",
|
"examples/rtic",
|
||||||
"vorago-peb1"
|
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"flashloader/slot-a-blinky",
|
"flashloader/slot-a-blinky",
|
||||||
|
13
README.md
13
README.md
@ -24,7 +24,11 @@ It also contains the following helper crates:
|
|||||||
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
||||||
is compatible to the provided bootloader as well.
|
is compatible to the provided bootloader as well.
|
||||||
- The `examples` folder contains various example applications crates for the HAL and the PAC.
|
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples)
|
||||||
|
folder contains various example applications crates using the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
|
native Rust RTOSes.
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
@ -96,7 +100,9 @@ example.
|
|||||||
### Using VS Code
|
### Using VS Code
|
||||||
|
|
||||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
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).
|
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.
|
||||||
|
|
||||||
Some sample configuration files for VS code were provided and can be used by running
|
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`
|
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||||
@ -111,4 +117,5 @@ configuration variables in your `settings.json`:
|
|||||||
- `"cortex-debug.gdbPath.osx"`
|
- `"cortex-debug.gdbPath.osx"`
|
||||||
|
|
||||||
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||||
via the terminal at `RTT Ch:0 console`.
|
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.
|
||||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@ -25,7 +25,9 @@ pipeline {
|
|||||||
stage('Docs') {
|
stage('Docs') {
|
||||||
steps {
|
steps {
|
||||||
sh """
|
sh """
|
||||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features va41630
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
examples/README.md
Normal file
25
examples/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
VA416xx Example Applications
|
||||||
|
========
|
||||||
|
|
||||||
|
This folder contains various examples
|
||||||
|
Consult the main README first for setup of the repository.
|
||||||
|
|
||||||
|
## Simple examples
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
You can have a look at the `simple/examples` folder to see all available simple examples
|
||||||
|
|
||||||
|
## RTIC example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin rtic-example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Embassy example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example
|
||||||
|
```
|
40
examples/embassy/Cargo.toml
Normal file
40
examples/embassy/Cargo.toml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
[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"]
|
4
examples/embassy/src/lib.rs
Normal file
4
examples/embassy/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![no_std]
|
||||||
|
pub mod time_driver;
|
||||||
|
|
||||||
|
pub use time_driver::init;
|
46
examples/embassy/src/main.rs
Normal file
46
examples/embassy/src/main.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#![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();
|
||||||
|
}
|
||||||
|
}
|
323
examples/embassy/src/time_driver.rs
Normal file
323
examples/embassy/src/time_driver.rs
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
//! This is a sample time driver implementation for the VA416xx family of devices, supporting
|
||||||
|
//! one alarm and requiring/reserving 2 TIM peripherals. You could adapt this implementation to
|
||||||
|
//! support more alarms.
|
||||||
|
use core::{
|
||||||
|
cell::Cell,
|
||||||
|
mem, ptr,
|
||||||
|
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
||||||
|
};
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
|
||||||
|
use embassy_time_driver::{time_driver_impl, AlarmHandle, Driver, TICK_HZ};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use va416xx_hal::{
|
||||||
|
clock::Clocks,
|
||||||
|
enable_interrupt,
|
||||||
|
irq_router::enable_and_init_irq_router,
|
||||||
|
pac::{self, interrupt},
|
||||||
|
pwm::{assert_tim_reset_for_two_cycles, enable_tim_clk, ValidTim},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type TimekeeperClk = pac::Tim15;
|
||||||
|
pub type AlarmClk0 = pac::Tim14;
|
||||||
|
pub type AlarmClk1 = pac::Tim13;
|
||||||
|
pub type AlarmClk2 = pac::Tim12;
|
||||||
|
|
||||||
|
/// Initialization method for embassy
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// This has to be called once at initialization time to initiate the time driver for
|
||||||
|
/// embassy.
|
||||||
|
pub unsafe fn init(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
irq_router: &pac::IrqRouter,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm: AlarmClk0,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) {
|
||||||
|
enable_and_init_irq_router(syscfg, irq_router);
|
||||||
|
DRIVER.init(syscfg, timekeeper, alarm, clocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn alarm_tim(idx: usize) -> &'static pac::tim0::RegisterBlock {
|
||||||
|
// Safety: This is a static memory-mapped peripheral.
|
||||||
|
match idx {
|
||||||
|
0 => unsafe { &*AlarmClk0::ptr() },
|
||||||
|
1 => unsafe { &*AlarmClk1::ptr() },
|
||||||
|
2 => unsafe { &*AlarmClk2::ptr() },
|
||||||
|
_ => {
|
||||||
|
panic!("invalid alarm timer index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn timekeeping_tim() -> &'static pac::tim0::RegisterBlock {
|
||||||
|
// Safety: This is a memory-mapped peripheral.
|
||||||
|
unsafe { &*TimekeeperClk::ptr() }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AlarmState {
|
||||||
|
timestamp: Cell<u64>,
|
||||||
|
|
||||||
|
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||||
|
// but fn pointers aren't allowed in const yet
|
||||||
|
callback: Cell<*const ()>,
|
||||||
|
ctx: Cell<*mut ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlarmState {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Cell::new(u64::MAX),
|
||||||
|
callback: Cell::new(ptr::null()),
|
||||||
|
ctx: Cell::new(ptr::null_mut()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for AlarmState {}
|
||||||
|
|
||||||
|
const ALARM_COUNT: usize = 1;
|
||||||
|
|
||||||
|
static SCALE: OnceCell<u64> = OnceCell::new();
|
||||||
|
|
||||||
|
pub struct TimerDriverEmbassy {
|
||||||
|
periods: AtomicU32,
|
||||||
|
alarm_count: AtomicU8,
|
||||||
|
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||||
|
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimerDriverEmbassy {
|
||||||
|
fn init(
|
||||||
|
&self,
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
timekeeper: TimekeeperClk,
|
||||||
|
alarm_tim: AlarmClk0,
|
||||||
|
clocks: &Clocks,
|
||||||
|
) {
|
||||||
|
enable_tim_clk(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
assert_tim_reset_for_two_cycles(syscfg, TimekeeperClk::TIM_ID);
|
||||||
|
|
||||||
|
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||||
|
SCALE
|
||||||
|
.set((TimekeeperClk::clock(clocks).raw() / TICK_HZ as u32) as u64)
|
||||||
|
.unwrap();
|
||||||
|
timekeeper
|
||||||
|
.rst_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Decrementing counter.
|
||||||
|
timekeeper
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
// Switch on. Timekeeping should always be done.
|
||||||
|
unsafe {
|
||||||
|
enable_interrupt(TimekeeperClk::IRQ);
|
||||||
|
}
|
||||||
|
timekeeper.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
timekeeper.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
enable_tim_clk(syscfg, AlarmClk0::TIM_ID);
|
||||||
|
assert_tim_reset_for_two_cycles(syscfg, AlarmClk0::TIM_ID);
|
||||||
|
|
||||||
|
// Explicitely disable alarm timer until needed.
|
||||||
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
|
||||||
|
unsafe {
|
||||||
|
enable_interrupt(AlarmClk0::IRQ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the timekeeper timer.
|
||||||
|
fn on_interrupt_timekeeping(&self) {
|
||||||
|
self.next_period();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called inside the IRQ of the alarm timer.
|
||||||
|
fn on_interrupt_alarm(&self, idx: usize) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
if self.alarms.borrow(cs)[idx].timestamp.get() <= self.now() {
|
||||||
|
self.trigger_alarm(idx, cs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_period(&self) {
|
||||||
|
let period = self.periods.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
let t = (period as u64) << 32;
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
for i in 0..ALARM_COUNT {
|
||||||
|
let alarm = &self.alarms.borrow(cs)[i];
|
||||||
|
let at = alarm.timestamp.get();
|
||||||
|
let alarm_tim = alarm_tim(0);
|
||||||
|
if at < t {
|
||||||
|
self.trigger_alarm(i, cs);
|
||||||
|
} else {
|
||||||
|
let remaining_ticks = (at - t) * *SCALE.get().unwrap();
|
||||||
|
if remaining_ticks <= u32::MAX as u64 {
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(0) });
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(remaining_ticks as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
||||||
|
// safety: we're allowed to assume the AlarmState is created by us, and
|
||||||
|
// we never create one that's out of bounds.
|
||||||
|
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||||
|
alarm_tim(n).ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
|
// Setting the maximum value disables the alarm.
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
|
||||||
|
// safety:
|
||||||
|
// - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
|
||||||
|
// - other than that we only store valid function pointers into alarm.callback
|
||||||
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
|
f(alarm.ctx.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Driver for TimerDriverEmbassy {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let mut period1: u32;
|
||||||
|
let mut period2: u32;
|
||||||
|
let mut counter_val: u32;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Acquire ensures that we get the latest value of `periods` and
|
||||||
|
// no instructions can be reordered before the load.
|
||||||
|
period1 = self.periods.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
counter_val = u32::MAX - timekeeping_tim().cnt_value().read().bits();
|
||||||
|
|
||||||
|
// Double read to protect against race conditions when the counter is overflowing.
|
||||||
|
period2 = self.periods.load(Ordering::Relaxed);
|
||||||
|
if period1 == period2 {
|
||||||
|
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
|
let id = self
|
||||||
|
.alarm_count
|
||||||
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
|
if x < ALARM_COUNT as u8 {
|
||||||
|
Some(x + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match id {
|
||||||
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm_callback(
|
||||||
|
&self,
|
||||||
|
alarm: embassy_time_driver::AlarmHandle,
|
||||||
|
callback: fn(*mut ()),
|
||||||
|
ctx: *mut (),
|
||||||
|
) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
|
||||||
|
alarm.callback.set(callback as *const ());
|
||||||
|
alarm.ctx.set(ctx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_alarm(&self, alarm: embassy_time_driver::AlarmHandle, timestamp: u64) -> bool {
|
||||||
|
if SCALE.get().is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let n = alarm.id();
|
||||||
|
let alarm_tim = alarm_tim(n.into());
|
||||||
|
alarm_tim.ctrl().modify(|_, w| {
|
||||||
|
w.irq_enb().clear_bit();
|
||||||
|
w.enable().clear_bit()
|
||||||
|
});
|
||||||
|
|
||||||
|
let alarm = self.get_alarm(cs, alarm);
|
||||||
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
|
let t = self.now();
|
||||||
|
if timestamp <= t {
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it hasn't triggered yet, setup the relevant reset value, regardless of whether
|
||||||
|
// the interrupts are enabled or not. When they are enabled at a later point, the
|
||||||
|
// right value is already set.
|
||||||
|
|
||||||
|
// If the timestamp is in the next few ticks, add a bit of buffer to be sure the alarm
|
||||||
|
// is not missed.
|
||||||
|
//
|
||||||
|
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||||
|
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||||
|
// and we don't do that here.
|
||||||
|
let safe_timestamp = timestamp.max(t + 3);
|
||||||
|
let timer_ticks = (safe_timestamp - t) * *SCALE.get().unwrap();
|
||||||
|
alarm_tim.rst_value().write(|w| unsafe { w.bits(u32::MAX) });
|
||||||
|
if timer_ticks <= u32::MAX as u64 {
|
||||||
|
alarm_tim
|
||||||
|
.cnt_value()
|
||||||
|
.write(|w| unsafe { w.bits(timer_ticks as u32) });
|
||||||
|
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
|
||||||
|
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
// If it's too far in the future, don't enable timer yet.
|
||||||
|
// It will be enabled later by `next_period`.
|
||||||
|
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time_driver_impl!(
|
||||||
|
static DRIVER: TimerDriverEmbassy = TimerDriverEmbassy {
|
||||||
|
periods: AtomicU32::new(0),
|
||||||
|
alarm_count: AtomicU8::new(0),
|
||||||
|
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [AlarmState::new(); ALARM_COUNT])
|
||||||
|
});
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM15() {
|
||||||
|
DRIVER.on_interrupt_timekeeping()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn TIM14() {
|
||||||
|
DRIVER.on_interrupt_alarm(0)
|
||||||
|
}
|
24
examples/rtic/Cargo.toml
Normal file
24
examples/rtic/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[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"]
|
57
examples/rtic/src/main.rs
Normal file
57
examples/rtic/src/main.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//! 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"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
|
critical-section = "1"
|
||||||
panic-rtt-target = { version = "0.1.3" }
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
rtt-target = { version = "0.5" }
|
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 = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
@ -23,8 +23,16 @@ path = "../../va416xx-hal"
|
|||||||
path = "../../vorago-peb1"
|
path = "../../vorago-peb1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.rtic]
|
||||||
|
version = "2"
|
||||||
|
features = ["thumbv7-backend"]
|
||||||
|
|
||||||
|
[dependencies.rtic-monotonics]
|
||||||
|
version = "2"
|
||||||
|
features = ["cortex-m-systick"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["va41630"]
|
||||||
va41630 = ["va416xx-hal/va41630", "has-adc-dac"]
|
va41630 = ["va416xx-hal/va41630", "has-adc-dac"]
|
||||||
va41629 = ["va416xx-hal/va41629", "has-adc-dac"]
|
va41629 = ["va416xx-hal/va41629", "has-adc-dac"]
|
||||||
va41628 = ["va416xx-hal/va41628"]
|
va41628 = ["va416xx-hal/va41628"]
|
||||||
|
@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
|
||||||
use cortex_m::interrupt::Mutex;
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use critical_section::Mutex;
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::dma::{Dma, DmaCfg, DmaChannel, DmaCtrlBlock};
|
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::pwm::CountdownTimer;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
@ -45,6 +46,7 @@ fn main() -> ! {
|
|||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze(&mut dp.sysconfig)
|
||||||
.unwrap();
|
.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
|
// Safety: The DMA control block has an alignment rule of 128 and we constructed it directly
|
||||||
// statically.
|
// statically.
|
||||||
let dma = Dma::new(&mut dp.sysconfig, dp.dma, DmaCfg::default(), unsafe {
|
let dma = Dma::new(&mut dp.sysconfig, dp.dma, DmaCfg::default(), unsafe {
|
||||||
@ -88,10 +90,10 @@ fn transfer_example_8_bit(
|
|||||||
(0..64).for_each(|i| {
|
(0..64).for_each(|i| {
|
||||||
src_buf[i] = i as u8;
|
src_buf[i] = i as u8;
|
||||||
});
|
});
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||||
@ -112,7 +114,7 @@ fn transfer_example_8_bit(
|
|||||||
// Use polling for completion status.
|
// Use polling for completion status.
|
||||||
loop {
|
loop {
|
||||||
let mut dma_done = false;
|
let mut dma_done = false;
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||||
rprintln!("DMA0 is active with 8 bit transfer");
|
rprintln!("DMA0 is active with 8 bit transfer");
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
@ -143,10 +145,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;
|
DMA_SRC_BUF[i] = (i as u32 * u16::MAX as u32 / (dest_buf_ref.len() as u32 - 1)) as u16;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||||
@ -170,7 +172,7 @@ fn transfer_example_16_bit(dma0: &mut DmaChannel, delay_ms: &mut CountdownTimer<
|
|||||||
// Use polling for completion status.
|
// Use polling for completion status.
|
||||||
loop {
|
loop {
|
||||||
let mut dma_done = false;
|
let mut dma_done = false;
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||||
rprintln!("DMA0 is active with 16-bit transfer");
|
rprintln!("DMA0 is active with 16-bit transfer");
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
@ -206,10 +208,10 @@ fn transfer_example_32_bit(
|
|||||||
(0..16).for_each(|i| {
|
(0..16).for_each(|i| {
|
||||||
src_buf[i] = (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32;
|
src_buf[i] = (i as u64 * u32::MAX as u64 / (src_buf.len() - 1) as u64) as u32;
|
||||||
});
|
});
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_DONE_FLAG.borrow(cs).set(false);
|
DMA_DONE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
});
|
});
|
||||||
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
// Safety: The source and destination buffer are valid for the duration of the DMA transfer.
|
||||||
@ -230,7 +232,7 @@ fn transfer_example_32_bit(
|
|||||||
// Use polling for completion status.
|
// Use polling for completion status.
|
||||||
loop {
|
loop {
|
||||||
let mut dma_done = false;
|
let mut dma_done = false;
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
if DMA_ACTIVE_FLAG.borrow(cs).get() {
|
||||||
rprintln!("DMA0 is active with 32-bit transfer");
|
rprintln!("DMA0 is active with 32-bit transfer");
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
DMA_ACTIVE_FLAG.borrow(cs).set(false);
|
||||||
@ -260,7 +262,7 @@ fn transfer_example_32_bit(
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn DMA_DONE0() {
|
fn DMA_DONE0() {
|
||||||
// Notify the main loop that the DMA transfer is finished.
|
// Notify the main loop that the DMA transfer is finished.
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_DONE_FLAG.borrow(cs).set(true);
|
DMA_DONE_FLAG.borrow(cs).set(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -269,7 +271,7 @@ fn DMA_DONE0() {
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn DMA_ACTIVE0() {
|
fn DMA_ACTIVE0() {
|
||||||
// Notify the main loop that the DMA 0 is active now.
|
// Notify the main loop that the DMA 0 is active now.
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
DMA_ACTIVE_FLAG.borrow(cs).set(true);
|
DMA_ACTIVE_FLAG.borrow(cs).set(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,14 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use cortex_m::interrupt::Mutex;
|
use cortex_m::asm;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use critical_section::Mutex;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
|
irq_router::enable_and_init_irq_router,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
|
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, MS_COUNTER},
|
||||||
@ -35,19 +37,21 @@ fn main() -> ! {
|
|||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze(&mut dp.sysconfig)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
enable_and_init_irq_router(&mut dp.sysconfig, &dp.irq_router);
|
||||||
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
|
let _ = set_up_ms_tick(&mut dp.sysconfig, dp.tim0, &clocks);
|
||||||
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
let mut second_timer = CountdownTimer::new(&mut dp.sysconfig, dp.tim1, &clocks);
|
||||||
second_timer.start(1.Hz());
|
|
||||||
second_timer.listen();
|
second_timer.listen();
|
||||||
|
second_timer.start(1.Hz());
|
||||||
loop {
|
loop {
|
||||||
let current_ms = cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get());
|
let current_ms = critical_section::with(|cs| MS_COUNTER.borrow(cs).get());
|
||||||
if current_ms - last_ms >= 1000 {
|
if current_ms >= last_ms + 1000 {
|
||||||
last_ms = current_ms;
|
// To prevent drift.
|
||||||
|
last_ms += 1000;
|
||||||
rprintln!("MS counter: {}", current_ms);
|
rprintln!("MS counter: {}", current_ms);
|
||||||
let second = cortex_m::interrupt::free(|cs| SEC_COUNTER.borrow(cs).get());
|
let second = critical_section::with(|cs| SEC_COUNTER.borrow(cs).get());
|
||||||
rprintln!("Second counter: {}", second);
|
rprintln!("Second counter: {}", second);
|
||||||
}
|
}
|
||||||
cortex_m::asm::delay(10000);
|
asm::delay(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +64,7 @@ fn TIM0() {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn TIM1() {
|
fn TIM1() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut sec = SEC_COUNTER.borrow(cs).get();
|
let mut sec = SEC_COUNTER.borrow(cs).get();
|
||||||
sec += 1;
|
sec += 1;
|
||||||
SEC_COUNTER.borrow(cs).set(sec);
|
SEC_COUNTER.borrow(cs).set(sec);
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use cortex_m::interrupt::Mutex;
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use critical_section::Mutex;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use simple_examples::peb1;
|
use simple_examples::peb1;
|
||||||
|
use va416xx_hal::irq_router::enable_and_init_irq_router;
|
||||||
use va416xx_hal::pac::{self, interrupt};
|
use va416xx_hal::pac::{self, interrupt};
|
||||||
use va416xx_hal::prelude::*;
|
use va416xx_hal::prelude::*;
|
||||||
use va416xx_hal::wdt::Wdt;
|
use va416xx_hal::wdt::Wdt;
|
||||||
@ -40,6 +41,7 @@ fn main() -> ! {
|
|||||||
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
.xtal_n_clk_with_src_freq(peb1::EXTCLK_FREQ)
|
||||||
.freeze(&mut dp.sysconfig)
|
.freeze(&mut dp.sysconfig)
|
||||||
.unwrap();
|
.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 delay_sysclk = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
|
||||||
|
|
||||||
let mut last_interrupt_counter = 0;
|
let mut last_interrupt_counter = 0;
|
||||||
@ -49,7 +51,7 @@ fn main() -> ! {
|
|||||||
if TEST_MODE != TestMode::AllowReset {
|
if TEST_MODE != TestMode::AllowReset {
|
||||||
wdt_ctrl.feed();
|
wdt_ctrl.feed();
|
||||||
}
|
}
|
||||||
let interrupt_counter = cortex_m::interrupt::free(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
|
let interrupt_counter = critical_section::with(|cs| WDT_INTRPT_COUNT.borrow(cs).get());
|
||||||
if interrupt_counter > last_interrupt_counter {
|
if interrupt_counter > last_interrupt_counter {
|
||||||
rprintln!("interrupt counter has increased to {}", interrupt_counter);
|
rprintln!("interrupt counter has increased to {}", interrupt_counter);
|
||||||
last_interrupt_counter = interrupt_counter;
|
last_interrupt_counter = interrupt_counter;
|
||||||
@ -65,7 +67,7 @@ fn main() -> ! {
|
|||||||
#[interrupt]
|
#[interrupt]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn WATCHDOG() {
|
fn WATCHDOG() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
WDT_INTRPT_COUNT
|
WDT_INTRPT_COUNT
|
||||||
.borrow(cs)
|
.borrow(cs)
|
||||||
.set(WDT_INTRPT_COUNT.borrow(cs).get() + 1);
|
.set(WDT_INTRPT_COUNT.borrow(cs).get() + 1);
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from spacepackets.ecss import RequestId
|
|
||||||
from spacepackets.ecss.defs import PusService
|
from spacepackets.ecss.defs import PusService
|
||||||
from spacepackets.ecss.tm import PusTm
|
from spacepackets.ecss.tm import PusTm
|
||||||
import toml
|
import toml
|
||||||
import struct
|
import struct
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
import enum
|
import enum
|
||||||
from tmtccmd.com.serial_base import SerialCfg
|
from tmtccmd.com.serial_base import SerialCfg
|
||||||
@ -45,6 +43,7 @@ ACTION_SERVICE = 8
|
|||||||
|
|
||||||
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||||
BOOT_NVM_MEMORY_ID = 1
|
BOOT_NVM_MEMORY_ID = 1
|
||||||
|
PING_PAYLOAD_SIZE = 0
|
||||||
|
|
||||||
|
|
||||||
class ActionId(enum.IntEnum):
|
class ActionId(enum.IntEnum):
|
||||||
@ -104,6 +103,29 @@ def main() -> int:
|
|||||||
com_if = SerialCobsComIF(serial_cfg)
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
com_if.open()
|
com_if.open()
|
||||||
file_path = None
|
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 args.target:
|
||||||
if not args.corrupt:
|
if not args.corrupt:
|
||||||
if not args.path:
|
if not args.path:
|
||||||
@ -113,15 +135,6 @@ def main() -> int:
|
|||||||
if not file_path.exists():
|
if not file_path.exists():
|
||||||
_LOGGER.error("File does not exist")
|
_LOGGER.error("File does not exist")
|
||||||
return -1
|
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 args.corrupt:
|
||||||
if not args.target:
|
if not args.target:
|
||||||
_LOGGER.error("target for corruption command required")
|
_LOGGER.error("target for corruption command required")
|
||||||
@ -254,7 +267,7 @@ def main() -> int:
|
|||||||
):
|
):
|
||||||
done = True
|
done = True
|
||||||
# Still keep a small delay
|
# Still keep a small delay
|
||||||
time.sleep(0.01)
|
# time.sleep(0.05)
|
||||||
verificator.remove_completed_entries()
|
verificator.remove_completed_entries()
|
||||||
if done:
|
if done:
|
||||||
break
|
break
|
||||||
|
@ -18,13 +18,11 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use embedded_hal_nb::serial::Read;
|
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
use va416xx_hal::{clock::Clocks, edac, pac, time::Hertz, wdt::Wdt};
|
||||||
|
|
||||||
const EXTCLK_FREQ: u32 = 40_000_000;
|
const EXTCLK_FREQ: u32 = 40_000_000;
|
||||||
const COBS_FRAME_SEPARATOR: u8 = 0x0;
|
|
||||||
|
|
||||||
const MAX_TC_SIZE: usize = 1024;
|
const MAX_TC_SIZE: usize = 1024;
|
||||||
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||||
@ -33,10 +31,8 @@ const MAX_TM_SIZE: usize = 128;
|
|||||||
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||||
|
|
||||||
const UART_BAUDRATE: u32 = 115200;
|
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 BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||||
|
const RX_DEBUGGING: bool = false;
|
||||||
|
|
||||||
pub enum ActionId {
|
pub enum ActionId {
|
||||||
CorruptImageA = 128,
|
CorruptImageA = 128,
|
||||||
@ -62,13 +58,24 @@ use ringbuf::{
|
|||||||
CachingCons, StaticProd, StaticRb,
|
CachingCons, StaticProd, StaticRb,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BUF_RB_SIZE_TX: usize = 1024;
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
const SIZES_RB_SIZE_TX: usize = 16;
|
const BUF_RB_SIZE_TC: usize = 2048;
|
||||||
|
const SIZES_RB_SIZE_TC: usize = 16;
|
||||||
|
|
||||||
static mut BUF_RB_TX: Lazy<StaticRb<u8, BUF_RB_SIZE_TX>> =
|
const BUF_RB_SIZE_TM: usize = 512;
|
||||||
Lazy::new(StaticRb::<u8, BUF_RB_SIZE_TX>::default);
|
const SIZES_RB_SIZE_TM: usize = 16;
|
||||||
static mut SIZES_RB_TX: Lazy<StaticRb<usize, SIZES_RB_SIZE_TX>> =
|
|
||||||
Lazy::new(StaticRb::<usize, SIZES_RB_SIZE_TX>::default);
|
// 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);
|
||||||
|
|
||||||
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
pub struct DataProducer<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||||
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
pub buf_prod: StaticProd<'static, u8, BUF_SIZE>,
|
||||||
@ -91,21 +98,17 @@ pub const APP_B_END_ADDR: u32 = 0x40000;
|
|||||||
mod app {
|
mod app {
|
||||||
use super::*;
|
use super::*;
|
||||||
use cortex_m::asm;
|
use cortex_m::asm;
|
||||||
use embedded_hal_nb::nb;
|
|
||||||
use embedded_io::Write;
|
use embedded_io::Write;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtic::Mutex;
|
use rtic::Mutex;
|
||||||
use rtic_monotonics::systick::prelude::*;
|
use rtic_monotonics::systick::prelude::*;
|
||||||
use rtic_sync::{
|
|
||||||
channel::{Receiver, Sender},
|
|
||||||
make_channel,
|
|
||||||
};
|
|
||||||
use rtt_target::rprintln;
|
use rtt_target::rprintln;
|
||||||
use satrs::pus::verification::VerificationReportCreator;
|
use satrs::pus::verification::VerificationReportCreator;
|
||||||
use spacepackets::ecss::PusServiceId;
|
use spacepackets::ecss::PusServiceId;
|
||||||
use spacepackets::ecss::{
|
use spacepackets::ecss::{
|
||||||
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
||||||
};
|
};
|
||||||
|
use va416xx_hal::irq_router::enable_and_init_irq_router;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{
|
||||||
clock::ClkgenExt,
|
clock::ClkgenExt,
|
||||||
edac,
|
edac,
|
||||||
@ -127,26 +130,24 @@ mod app {
|
|||||||
|
|
||||||
#[local]
|
#[local]
|
||||||
struct Local {
|
struct Local {
|
||||||
uart_rx: uart::Rx<pac::Uart0>,
|
uart_rx: uart::RxWithIrq<pac::Uart0>,
|
||||||
uart_tx: uart::Tx<pac::Uart0>,
|
uart_tx: uart::Tx<pac::Uart0>,
|
||||||
cobs_reader_state: CobsReaderStates,
|
|
||||||
tc_tx: TcTx,
|
|
||||||
tc_rx: TcRx,
|
|
||||||
rom_spi: Option<pac::Spi3>,
|
rom_spi: Option<pac::Spi3>,
|
||||||
tx_cons: DataConsumer<BUF_RB_SIZE_TX, SIZES_RB_SIZE_TX>,
|
// 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>,
|
||||||
verif_reporter: VerificationReportCreator,
|
verif_reporter: VerificationReportCreator,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[shared]
|
#[shared]
|
||||||
struct Shared {
|
struct Shared {
|
||||||
decode_buffer_busy: bool,
|
// Having this shared allows multiple tasks to generate telemetry.
|
||||||
decode_buf: [u8; MAX_TC_SIZE],
|
tm_prod: DataProducer<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||||
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);
|
rtic_monotonics::systick_monotonic!(Mono, 10_000);
|
||||||
|
|
||||||
#[init]
|
#[init]
|
||||||
@ -163,6 +164,7 @@ mod app {
|
|||||||
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
.xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ))
|
||||||
.freeze(&mut cx.device.sysconfig)
|
.freeze(&mut cx.device.sysconfig)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
enable_and_init_irq_router(&mut cx.device.sysconfig, &cx.device.irq_router);
|
||||||
setup_edac(&mut cx.device.sysconfig);
|
setup_edac(&mut cx.device.sysconfig);
|
||||||
|
|
||||||
let gpiob = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
let gpiob = PinsG::new(&mut cx.device.sysconfig, cx.device.portg);
|
||||||
@ -176,38 +178,45 @@ mod app {
|
|||||||
&mut cx.device.sysconfig,
|
&mut cx.device.sysconfig,
|
||||||
&clocks,
|
&clocks,
|
||||||
);
|
);
|
||||||
let (tx, rx) = uart0.split();
|
let (tx, mut rx, _) = uart0.split_with_irq();
|
||||||
let (tc_tx, tc_rx) = make_channel!(usize, 2);
|
|
||||||
|
|
||||||
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
let (buf_prod, buf_cons) = unsafe { BUF_RB_TX.split_ref() };
|
let (buf_prod_tm, buf_cons_tm) = unsafe { BUF_RB_TM.split_ref() };
|
||||||
let (sizes_prod, sizes_cons) = unsafe { SIZES_RB_TX.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() };
|
||||||
|
|
||||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||||
CLOCKS.set(clocks).unwrap();
|
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();
|
pus_tc_handler::spawn().unwrap();
|
||||||
uart_reader_task::spawn().unwrap();
|
|
||||||
pus_tm_tx_handler::spawn().unwrap();
|
pus_tm_tx_handler::spawn().unwrap();
|
||||||
(
|
(
|
||||||
Shared {
|
Shared {
|
||||||
decode_buffer_busy: false,
|
tm_prod: DataProducer {
|
||||||
decode_buf: [0; MAX_TC_SIZE],
|
buf_prod: buf_prod_tm,
|
||||||
tx_prod: DataProducer {
|
sizes_prod: sizes_prod_tm,
|
||||||
buf_prod,
|
|
||||||
sizes_prod,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Local {
|
Local {
|
||||||
uart_rx: rx,
|
uart_rx: rx,
|
||||||
uart_tx: tx,
|
uart_tx: tx,
|
||||||
cobs_reader_state: CobsReaderStates::default(),
|
|
||||||
tc_tx,
|
|
||||||
tc_rx,
|
|
||||||
rom_spi: Some(cx.device.spi3),
|
rom_spi: Some(cx.device.spi3),
|
||||||
tx_cons: DataConsumer {
|
tm_cons: DataConsumer {
|
||||||
buf_cons,
|
buf_cons: buf_cons_tm,
|
||||||
sizes_cons,
|
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,
|
||||||
},
|
},
|
||||||
verif_reporter,
|
verif_reporter,
|
||||||
},
|
},
|
||||||
@ -223,120 +232,62 @@ mod app {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[task(
|
#[task(
|
||||||
priority = 4,
|
binds = UART0_RX,
|
||||||
local=[
|
local = [
|
||||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
cnt: u32 = 0,
|
||||||
|
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||||
uart_rx,
|
uart_rx,
|
||||||
cobs_reader_state,
|
tc_prod
|
||||||
tc_tx
|
|
||||||
],
|
],
|
||||||
shared=[decode_buffer_busy, decode_buf]
|
|
||||||
)]
|
)]
|
||||||
async fn uart_reader_task(mut cx: uart_reader_task::Context) {
|
fn uart_rx_irq(cx: uart_rx_irq::Context) {
|
||||||
let mut current_idx = 0;
|
match cx.local.uart_rx.irq_handler(cx.local.rx_buf) {
|
||||||
loop {
|
Ok(result) => {
|
||||||
match cx.local.uart_rx.read() {
|
if RX_DEBUGGING {
|
||||||
Ok(byte) => {
|
log::debug!("RX Info: {:?}", cx.local.uart_rx.irq_info());
|
||||||
if SERIAL_RX_WIRETAPPING {
|
log::debug!("RX Result: {:?}", result);
|
||||||
log::debug!("RX Byte: 0x{:x?}", byte);
|
|
||||||
}
|
|
||||||
handle_single_rx_byte(&mut cx, byte, &mut current_idx)
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
if result.complete() {
|
||||||
match e {
|
// Check frame validity (must have COBS format) and decode the frame.
|
||||||
nb::Error::Other(e) => {
|
// Currently, we expect a full frame or a frame received through a timeout
|
||||||
log::warn!("UART error: {:?}", e);
|
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||||
match e {
|
// frame, but the additional complexity is not necessary here..
|
||||||
uart::Error::Overrun => {
|
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||||
cx.local.uart_rx.clear_fifo();
|
let decoded_size =
|
||||||
}
|
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||||
uart::Error::FramingError => (),
|
if decoded_size.is_err() {
|
||||||
uart::Error::ParityError => (),
|
log::warn!("COBS decoding failed");
|
||||||
uart::Error::BreakCondition => (),
|
} else {
|
||||||
uart::Error::TransferPending => (),
|
let decoded_size = decoded_size.unwrap();
|
||||||
uart::Error::BufferTooShort => (),
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nb::Error::WouldBlock => {
|
} else {
|
||||||
// Delay for a short period before polling again.
|
log::warn!("COBS frame with invalid format, start and end bytes are not 0");
|
||||||
Mono::delay(400.micros()).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_single_rx_byte(
|
// Initiate next transfer.
|
||||||
cx: &mut uart_reader_task::Context,
|
cx.local
|
||||||
byte: u8,
|
.uart_rx
|
||||||
current_idx: &mut usize,
|
.read_fixed_len_using_irq(MAX_TC_FRAME_SIZE, true)
|
||||||
) {
|
.expect("read operation failed");
|
||||||
match cx.local.cobs_reader_state {
|
}
|
||||||
CobsReaderStates::WaitingForStart => {
|
if result.error() {
|
||||||
if byte == COBS_FRAME_SEPARATOR {
|
log::warn!("UART error: {:?}", result.error());
|
||||||
if COBS_RX_DEBUGGING {
|
|
||||||
log::debug!("COBS start marker detected");
|
|
||||||
}
|
|
||||||
*cx.local.cobs_reader_state = CobsReaderStates::WatingForEnd;
|
|
||||||
*current_idx = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CobsReaderStates::WatingForEnd => {
|
Err(e) => {
|
||||||
if byte == COBS_FRAME_SEPARATOR {
|
log::warn!("UART error: {:?}", e);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,149 +295,167 @@ mod app {
|
|||||||
#[task(
|
#[task(
|
||||||
priority = 2,
|
priority = 2,
|
||||||
local=[
|
local=[
|
||||||
read_buf: [u8;MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
src_data_buf: [u8; 16] = [0; 16],
|
src_data_buf: [u8; 16] = [0; 16],
|
||||||
verif_buf: [u8; 32] = [0; 32],
|
verif_buf: [u8; 32] = [0; 32],
|
||||||
tc_rx,
|
tc_cons,
|
||||||
rom_spi,
|
rom_spi,
|
||||||
verif_reporter
|
verif_reporter
|
||||||
],
|
],
|
||||||
shared=[decode_buffer_busy, decode_buf, tx_prod]
|
shared=[tm_prod]
|
||||||
)]
|
)]
|
||||||
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||||
loop {
|
loop {
|
||||||
let packet_len = cx.local.tc_rx.recv().await.expect("all senders down");
|
// 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();
|
||||||
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
log::info!(target: "TC Handler", "received packet with length {}", packet_len);
|
||||||
// We still copy the data to a local buffer, so the exchange buffer can already be used
|
assert_eq!(
|
||||||
// for the next packet / decode process.
|
cx.local
|
||||||
cx.shared
|
.tc_cons
|
||||||
.decode_buf
|
.buf_cons
|
||||||
.lock(|buf| cx.local.read_buf[0..buf.len()].copy_from_slice(buf));
|
.pop_slice(&mut cx.local.tc_buf[0..packet_len]),
|
||||||
cx.shared.decode_buffer_busy.lock(|busy| *busy = false);
|
packet_len
|
||||||
match PusTcReader::new(cx.local.read_buf) {
|
);
|
||||||
Ok((pus_tc, _)) => {
|
// Read a telecommand, now handle it.
|
||||||
let mut write_and_send = |tm: &PusTmCreator| {
|
handle_valid_pus_tc(&mut cx);
|
||||||
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);
|
|
||||||
|
|
||||||
let (tm, started_token) = cx
|
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||||
.local
|
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||||
.verif_reporter
|
if pus_tc.is_err() {
|
||||||
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
log::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
||||||
.expect("acceptance success failed");
|
return;
|
||||||
write_and_send(&tm);
|
}
|
||||||
|
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);
|
||||||
|
|
||||||
if pus_tc.service() == PusServiceId::Action as u8 {
|
let (tm, started_token) = cx
|
||||||
let mut corrupt_image = |base_addr: u32| {
|
.local
|
||||||
// Safety: We only use this for NVM handling and we only do NVM
|
.verif_reporter
|
||||||
// handling here.
|
.start_success(cx.local.src_data_buf, accepted_token, 0, 0, &[])
|
||||||
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
.expect("acceptance success failed");
|
||||||
let nvm = Nvm::new(
|
write_and_send(&tm);
|
||||||
&mut sys_cfg,
|
|
||||||
cx.local.rom_spi.take().unwrap(),
|
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||||
CLOCKS.get().as_ref().unwrap(),
|
let mut corrupt_image = |base_addr: u32| {
|
||||||
);
|
// Safety: We only use this for NVM handling and we only do NVM
|
||||||
let mut buf = [0u8; 4];
|
// handling here.
|
||||||
nvm.read_data(base_addr + 32, &mut buf);
|
let mut sys_cfg = unsafe { pac::Sysconfig::steal() };
|
||||||
buf[0] += 1;
|
let nvm = Nvm::new(
|
||||||
nvm.write_data(base_addr + 32, &buf);
|
&mut sys_cfg,
|
||||||
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
cx.local.rom_spi.take().unwrap(),
|
||||||
let tm = cx
|
CLOCKS.get().as_ref().unwrap(),
|
||||||
.local
|
);
|
||||||
.verif_reporter
|
let mut buf = [0u8; 4];
|
||||||
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
nvm.read_data(base_addr + 32, &mut buf);
|
||||||
.expect("completion success failed");
|
buf[0] += 1;
|
||||||
write_and_send(&tm);
|
nvm.write_data(base_addr + 32, &buf);
|
||||||
};
|
*cx.local.rom_spi = Some(nvm.release(&mut sys_cfg));
|
||||||
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
let tm = cx
|
||||||
rprintln!("corrupting App Image A");
|
.local
|
||||||
corrupt_image(APP_A_START_ADDR);
|
.verif_reporter
|
||||||
}
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
.expect("completion success failed");
|
||||||
rprintln!("corrupting App Image B");
|
write_and_send(&tm);
|
||||||
corrupt_image(APP_B_START_ADDR);
|
};
|
||||||
}
|
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||||
}
|
rprintln!("corrupting App Image A");
|
||||||
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
corrupt_image(APP_A_START_ADDR);
|
||||||
log::info!(target: "TC Handler", "received ping TC");
|
}
|
||||||
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||||
let tm = cx
|
rprintln!("corrupting App Image B");
|
||||||
.local
|
corrupt_image(APP_B_START_ADDR);
|
||||||
.verif_reporter
|
}
|
||||||
.step_success(
|
}
|
||||||
cx.local.src_data_buf,
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
&started_token,
|
log::info!(target: "TC Handler", "received ping TC");
|
||||||
0,
|
let tm = cx
|
||||||
0,
|
.local
|
||||||
&[],
|
.verif_reporter
|
||||||
EcssEnumU8::new(0),
|
.completion_success(cx.local.src_data_buf, started_token, 0, 0, &[])
|
||||||
)
|
.expect("completion success failed");
|
||||||
.expect("step success failed");
|
write_and_send(&tm);
|
||||||
write_and_send(&tm);
|
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||||
// Raw memory write TC
|
let tm = cx
|
||||||
if pus_tc.subservice() == 2 {
|
.local
|
||||||
let app_data = pus_tc.app_data();
|
.verif_reporter
|
||||||
if app_data.len() < 10 {
|
.step_success(
|
||||||
log::warn!(
|
cx.local.src_data_buf,
|
||||||
target: "TC Handler",
|
&started_token,
|
||||||
"app data for raw memory write is too short: {}",
|
0,
|
||||||
app_data.len()
|
0,
|
||||||
);
|
&[],
|
||||||
}
|
EcssEnumU8::new(0),
|
||||||
let memory_id = app_data[0];
|
)
|
||||||
if memory_id != BOOT_NVM_MEMORY_ID {
|
.expect("step success failed");
|
||||||
log::warn!(target: "TC Handler", "memory ID {} not supported", memory_id);
|
write_and_send(&tm);
|
||||||
// TODO: Error reporting
|
// Raw memory write TC
|
||||||
return;
|
if pus_tc.subservice() == 2 {
|
||||||
}
|
let app_data = pus_tc.app_data();
|
||||||
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
if app_data.len() < 10 {
|
||||||
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
log::warn!(
|
||||||
if 10 + data_len as usize > app_data.len() {
|
target: "TC Handler",
|
||||||
log::warn!(
|
"app data for raw memory write is too short: {}",
|
||||||
target: "TC Handler",
|
app_data.len()
|
||||||
"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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
let memory_id = app_data[0];
|
||||||
log::warn!("PUS TC error: {}", e);
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -497,16 +466,16 @@ mod app {
|
|||||||
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||||
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||||
uart_tx,
|
uart_tx,
|
||||||
tx_cons,
|
tm_cons
|
||||||
],
|
],
|
||||||
shared=[]
|
shared=[]
|
||||||
)]
|
)]
|
||||||
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
async fn pus_tm_tx_handler(cx: pus_tm_tx_handler::Context) {
|
||||||
loop {
|
loop {
|
||||||
while cx.local.tx_cons.sizes_cons.occupied_len() > 0 {
|
while cx.local.tm_cons.sizes_cons.occupied_len() > 0 {
|
||||||
let next_size = cx.local.tx_cons.sizes_cons.try_pop().unwrap();
|
let next_size = cx.local.tm_cons.sizes_cons.try_pop().unwrap();
|
||||||
cx.local
|
cx.local
|
||||||
.tx_cons
|
.tm_cons
|
||||||
.buf_cons
|
.buf_cons
|
||||||
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||||
cx.local.encoded_buf[0] = 0;
|
cx.local.encoded_buf[0] = 0;
|
||||||
@ -521,7 +490,7 @@ mod app {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
Mono::delay(2.millis()).await;
|
Mono::delay(2.millis()).await;
|
||||||
}
|
}
|
||||||
Mono::delay(30.millis()).await;
|
Mono::delay(50.millis()).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
# [v0.2.0]
|
# [v0.2.0] 2024-09-18
|
||||||
|
|
||||||
- Documentation improvements
|
- Documentation improvements
|
||||||
- Improved UART typing support: Validity of passed pins is now checked properly
|
- Improved UART typing support: Validity of passed pins is now checked properly
|
||||||
@ -17,12 +17,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
- Added `va41620`, `va41630`, `va41628` and `va41629` device features. A device now has to be
|
||||||
selected for HAL compilation to work properly
|
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
|
## Fixed
|
||||||
|
|
||||||
- Small fixes and improvements for ADC drivers
|
- Small fixes and improvements for ADC drivers
|
||||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||||
correctly
|
correctly
|
||||||
|
- Fixes for UART IRQ handler implementation
|
||||||
|
- Add new IRQ router initialization method `irq_router::enable_and_init_irq_router`. This method
|
||||||
|
also sets the initial values of some registers to 0 where the datasheet and the actual reset
|
||||||
|
value are inconsistent, which can lead to weird bugs like IRQs not being triggered properly.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
critical-section = "1"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
embedded-hal-nb = "1"
|
embedded-hal-nb = "1"
|
||||||
@ -20,7 +21,7 @@ embedded-io = "0.6"
|
|||||||
num_enum = { version = "0.7", default-features = false }
|
num_enum = { version = "0.7", default-features = false }
|
||||||
typenum = "1"
|
typenum = "1"
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bitfield = "0.15"
|
bitfield = "0.17"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
fugit = "0.3"
|
fugit = "0.3"
|
||||||
delegate = "0.12"
|
delegate = "0.12"
|
||||||
|
@ -311,6 +311,12 @@ impl ClkgenCfgr {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pll_cfg(mut self, pll_cfg: PllCfg) -> Self {
|
||||||
|
self.pll_cfg = Some(pll_cfg);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
|
pub fn ref_clk_sel(mut self, ref_clk_sel: RefClkSel) -> Self {
|
||||||
self.ref_clk_sel = ref_clk_sel;
|
self.ref_clk_sel = ref_clk_sel;
|
||||||
@ -318,7 +324,7 @@ impl ClkgenCfgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Configures all clocks and return a clock configuration structure containing the final
|
/// Configures all clocks and return a clock configuration structure containing the final
|
||||||
/// frozen clock.
|
/// frozen clocks.
|
||||||
///
|
///
|
||||||
/// Internal implementation details: This implementation is based on the HAL implementation
|
/// Internal implementation details: This implementation is based on the HAL implementation
|
||||||
/// which performs a lot of delays. I do not know if all of those are necessary, but
|
/// which performs a lot of delays. I do not know if all of those are necessary, but
|
||||||
@ -494,33 +500,33 @@ pub struct Clocks {
|
|||||||
|
|
||||||
impl Clocks {
|
impl Clocks {
|
||||||
/// Returns the frequency of the HBO clock
|
/// Returns the frequency of the HBO clock
|
||||||
pub fn hbo(&self) -> Hertz {
|
pub const fn hbo(&self) -> Hertz {
|
||||||
HBO_FREQ
|
HBO_FREQ
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the frequency of the APB0 which is equal to the system clock.
|
/// Returns the frequency of the APB0 which is equal to the system clock.
|
||||||
pub fn apb0(&self) -> Hertz {
|
pub const fn apb0(&self) -> Hertz {
|
||||||
self.sysclk()
|
self.sysclk()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns system clock divied by 2.
|
/// Returns system clock divied by 2.
|
||||||
pub fn apb1(&self) -> Hertz {
|
pub const fn apb1(&self) -> Hertz {
|
||||||
self.apb1
|
self.apb1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns system clock divied by 4.
|
/// Returns system clock divied by 4.
|
||||||
pub fn apb2(&self) -> Hertz {
|
pub const fn apb2(&self) -> Hertz {
|
||||||
self.apb2
|
self.apb2
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the system (core) frequency
|
/// Returns the system (core) frequency
|
||||||
pub fn sysclk(&self) -> Hertz {
|
pub const fn sysclk(&self) -> Hertz {
|
||||||
self.sysclk
|
self.sysclk
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ADC clock frequency which has a separate divider.
|
/// Returns the ADC clock frequency which has a separate divider.
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
pub fn adc_clk(&self) -> Hertz {
|
pub const fn adc_clk(&self) -> Hertz {
|
||||||
self.adc_clk
|
self.adc_clk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ pub fn enable_ram1_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function enables the SBE related interrupts. The user should also provide a
|
/// This function enables the SBE related interrupts. The user should also provide a
|
||||||
/// [pac::EDAC_SBE] ISR and use [clear_sbe_irq] inside that ISR at the very least.
|
/// `EDAC_SBE` ISR and use [clear_sbe_irq] inside that ISR at the very least.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_sbe_irq() {
|
pub fn enable_sbe_irq() {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -31,7 +31,7 @@ pub fn enable_sbe_irq() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This function enables the SBE related interrupts. The user should also provide a
|
/// This function enables the SBE related interrupts. The user should also provide a
|
||||||
/// [pac::EDAC_MBE] ISR and use [clear_mbe_irq] inside that ISR at the very least.
|
/// `EDAC_MBE` ISR and use [clear_mbe_irq] inside that ISR at the very least.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable_mbe_irq() {
|
pub fn enable_mbe_irq() {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -39,7 +39,7 @@ pub fn enable_mbe_irq() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function should be called in the user provided [pac::EDAC_SBE] interrupt-service routine
|
/// This function should be called in the user provided `EDAC_SBE` interrupt-service routine
|
||||||
/// to clear the SBE related interrupts.
|
/// to clear the SBE related interrupts.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_sbe_irq() {
|
pub fn clear_sbe_irq() {
|
||||||
@ -52,7 +52,7 @@ pub fn clear_sbe_irq() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function should be called in the user provided [pac::EDAC_MBE] interrupt-service routine
|
/// This function should be called in the user provided `EDAC_MBE` interrupt-service routine
|
||||||
/// to clear the MBE related interrupts.
|
/// to clear the MBE related interrupts.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_mbe_irq() {
|
pub fn clear_mbe_irq() {
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct IsMaskedError;
|
pub struct IsMaskedError;
|
||||||
|
26
va416xx-hal/src/irq_router.rs
Normal file
26
va416xx-hal/src/irq_router.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//! 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,3 +1,26 @@
|
|||||||
|
//! 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]
|
#![no_std]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -22,7 +45,7 @@ pub mod dma;
|
|||||||
pub mod edac;
|
pub mod edac;
|
||||||
pub mod gpio;
|
pub mod gpio;
|
||||||
pub mod i2c;
|
pub mod i2c;
|
||||||
pub mod nvm;
|
pub mod irq_router;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
@ -31,6 +54,9 @@ pub mod typelevel;
|
|||||||
pub mod uart;
|
pub mod uart;
|
||||||
pub mod wdt;
|
pub mod wdt;
|
||||||
|
|
||||||
|
#[cfg(feature = "va41630")]
|
||||||
|
pub mod nvm;
|
||||||
|
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
pub mod adc;
|
pub mod adc;
|
||||||
#[cfg(not(feature = "va41628"))]
|
#[cfg(not(feature = "va41628"))]
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
//! Non-volatile memory (NVM) driver.
|
||||||
|
//!
|
||||||
|
//! Provides a basic API to work with the internal NVM of the VA41630 MCU.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! - [Flashloader application](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
use embedded_hal::spi::MODE_0;
|
use embedded_hal::spi::MODE_0;
|
||||||
|
|
||||||
use crate::clock::{Clocks, SyscfgExt};
|
use crate::clock::{Clocks, SyscfgExt};
|
||||||
@ -50,7 +57,7 @@ pub struct Nvm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", defmt::Format)]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct VerifyError {
|
pub struct VerifyError {
|
||||||
addr: u32,
|
addr: u32,
|
||||||
found: u8,
|
found: u8,
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
//! - [Timer MS and Second Tick Example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/timer-ticks.rs)
|
//! - [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 core::cell::Cell;
|
||||||
|
|
||||||
use cortex_m::interrupt::Mutex;
|
use cortex_m::asm;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
|
||||||
use crate::clock::Clocks;
|
use crate::clock::Clocks;
|
||||||
use crate::gpio::{
|
use crate::gpio::{
|
||||||
@ -169,6 +170,14 @@ 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!(
|
tim_markers!(
|
||||||
(pac::Tim0, 0, pac::Interrupt::TIM0),
|
(pac::Tim0, 0, pac::Interrupt::TIM0),
|
||||||
(pac::Tim1, 1, pac::Interrupt::TIM1),
|
(pac::Tim1, 1, pac::Interrupt::TIM1),
|
||||||
@ -328,19 +337,27 @@ valid_pin_and_tims!(
|
|||||||
///
|
///
|
||||||
/// Only the bit related to the corresponding TIM peripheral is modified
|
/// Only the bit related to the corresponding TIM peripheral is modified
|
||||||
#[inline]
|
#[inline]
|
||||||
fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
pub fn assert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||||
syscfg
|
syscfg
|
||||||
.tim_reset()
|
.tim_reset()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) })
|
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << tim_id as u32)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
pub fn deassert_tim_reset(syscfg: &mut pac::Sysconfig, tim_id: u8) {
|
||||||
syscfg
|
syscfg
|
||||||
.tim_reset()
|
.tim_reset()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << tim_id as u32)) })
|
.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;
|
pub type TimRegBlock = pac::tim0::RegisterBlock;
|
||||||
|
|
||||||
/// Register interface.
|
/// Register interface.
|
||||||
@ -481,7 +498,7 @@ pub struct CountdownTimer<TIM: ValidTim> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
pub fn enable_tim_clk(syscfg: &mut pac::Sysconfig, idx: u8) {
|
||||||
syscfg
|
syscfg
|
||||||
.tim_clk_enable()
|
.tim_clk_enable()
|
||||||
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << idx)) });
|
||||||
@ -579,9 +596,10 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
pub fn load(&mut self, timeout: impl Into<Hertz>) {
|
||||||
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
self.tim.reg().ctrl().modify(|_, w| w.enable().clear_bit());
|
||||||
self.curr_freq = timeout.into();
|
self.curr_freq = timeout.into();
|
||||||
self.rst_val = self.clock.raw() / self.curr_freq.raw();
|
self.rst_val = (self.clock.raw() / self.curr_freq.raw()) - 1;
|
||||||
self.set_reload(self.rst_val);
|
self.set_reload(self.rst_val);
|
||||||
self.set_count(0);
|
// Decrementing counter, to set the reset value.
|
||||||
|
self.set_count(self.rst_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -601,7 +619,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn enable(&mut self) {
|
pub fn enable(&mut self) {
|
||||||
self.tim.reg().ctrl().modify(|_, w| w.enable().set_bit());
|
self.tim.reg().enable().write(|w| unsafe { w.bits(1) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -778,7 +796,7 @@ pub fn set_up_ms_tick<Tim: ValidTim>(
|
|||||||
/// This function can be called in a specified interrupt handler to increment
|
/// This function can be called in a specified interrupt handler to increment
|
||||||
/// the MS counter
|
/// the MS counter
|
||||||
pub fn default_ms_irq_handler() {
|
pub fn default_ms_irq_handler() {
|
||||||
cortex_m::interrupt::free(|cs| {
|
critical_section::with(|cs| {
|
||||||
let mut ms = MS_COUNTER.borrow(cs).get();
|
let mut ms = MS_COUNTER.borrow(cs).get();
|
||||||
ms += 1;
|
ms += 1;
|
||||||
MS_COUNTER.borrow(cs).set(ms);
|
MS_COUNTER.borrow(cs).set(ms);
|
||||||
@ -787,7 +805,7 @@ pub fn default_ms_irq_handler() {
|
|||||||
|
|
||||||
/// Get the current MS tick count
|
/// Get the current MS tick count
|
||||||
pub fn get_ms_ticks() -> u32 {
|
pub fn get_ms_ticks() -> u32 {
|
||||||
cortex_m::interrupt::free(|cs| MS_COUNTER.borrow(cs).get())
|
critical_section::with(|cs| MS_COUNTER.borrow(cs).get())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
|
pub struct DelayMs<Tim: ValidTim = pac::Tim0>(CountdownTimer<Tim>);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/uart.rs)
|
||||||
|
//! - [Flashloader app using UART with IRQs](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/flashloader)
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use embedded_hal_nb::serial::Read;
|
use embedded_hal_nb::serial::Read;
|
||||||
@ -197,66 +198,36 @@ impl From<Hertz> for Config {
|
|||||||
// IRQ Definitions
|
// IRQ Definitions
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
struct IrqInfo {
|
#[derive(Debug)]
|
||||||
|
pub struct IrqInfo {
|
||||||
rx_len: usize,
|
rx_len: usize,
|
||||||
rx_idx: usize,
|
rx_idx: usize,
|
||||||
mode: IrqReceptionMode,
|
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
|
/// This struct is used to return the default IRQ handler result to the user
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct IrqResult {
|
pub struct IrqResult {
|
||||||
raw_res: u32,
|
complete: bool,
|
||||||
|
timeout: bool,
|
||||||
|
pub errors: IrqUartError,
|
||||||
pub bytes_read: usize,
|
pub bytes_read: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqResult {
|
impl IrqResult {
|
||||||
pub const fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
IrqResult {
|
IrqResult {
|
||||||
raw_res: 0,
|
complete: false,
|
||||||
|
timeout: false,
|
||||||
|
errors: IrqUartError::default(),
|
||||||
bytes_read: 0,
|
bytes_read: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqResult {
|
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]
|
#[inline]
|
||||||
pub fn error(&self) -> bool {
|
pub fn error(&self) -> bool {
|
||||||
if self.overflow_error() || self.framing_error() || self.parity_error() {
|
if self.errors.overflow || self.errors.parity || self.errors.framing {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -264,34 +235,27 @@ impl IrqResult {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn overflow_error(&self) -> bool {
|
pub fn overflow_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::Overflow as u32) & 0x01) == 0x01 {
|
self.errors.overflow
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn framing_error(&self) -> bool {
|
pub fn framing_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::FramingError as u32) & 0x01) == 0x01 {
|
self.errors.framing
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parity_error(&self) -> bool {
|
pub fn parity_error(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::ParityError as u32) & 0x01) == 0x01 {
|
self.errors.parity
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn timeout(&self) -> bool {
|
pub fn timeout(&self) -> bool {
|
||||||
if ((self.raw_res >> IrqResultMask::Timeout as u32) & 0x01) == 0x01 {
|
self.timeout
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
false
|
#[inline]
|
||||||
|
pub fn complete(&self) -> bool {
|
||||||
|
self.complete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,41 +281,27 @@ pub struct Uart<UartInstance, Pins> {
|
|||||||
pins: Pins,
|
pins: Pins,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UART using the IRQ capabilities of the peripheral. Can be created with the
|
/// Serial receiver
|
||||||
/// [`Uart::into_uart_with_irq`] function. Currently, only the RX side for IRQ based reception
|
pub struct Rx<Uart>(Uart);
|
||||||
/// is implemented.
|
|
||||||
pub struct UartWithIrq<Uart, Pins> {
|
|
||||||
base: UartWithIrqBase<Uart>,
|
|
||||||
pins: Pins,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-erased UART using the IRQ capabilities of the peripheral. Can be created with the
|
// Serial receiver, using interrupts to offload reading to the hardware.
|
||||||
/// [`UartWithIrq::downgrade`] function. Currently, only the RX side for IRQ based reception
|
pub struct RxWithIrq<Uart> {
|
||||||
/// is implemented.
|
inner: Rx<Uart>,
|
||||||
pub struct UartWithIrqBase<UART> {
|
|
||||||
pub inner: UartBase<UART>,
|
|
||||||
irq_info: IrqInfo,
|
irq_info: IrqInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serial receiver
|
|
||||||
pub struct Rx<Uart> {
|
|
||||||
uart: Uart,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serial transmitter
|
/// Serial transmitter
|
||||||
pub struct Tx<Uart> {
|
pub struct Tx<Uart>(Uart);
|
||||||
uart: Uart,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Uart: Instance> Rx<Uart> {
|
impl<Uart: Instance> Rx<Uart> {
|
||||||
fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self { uart }
|
Self(uart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart> Tx<Uart> {
|
impl<Uart> Tx<Uart> {
|
||||||
fn new(uart: Uart) -> Self {
|
fn new(uart: Uart) -> Self {
|
||||||
Self { uart }
|
Self(uart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,20 +552,30 @@ impl<TxPinInst: TxPin<UartInstance>, RxPinInst: RxPin<UartInstance>, UartInstanc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
/// If the IRQ capabilities of the peripheral are used, the UART needs to be converted
|
||||||
/// with this function
|
/// with this function. Currently, IRQ abstractions are only implemented for the RX part
|
||||||
pub fn into_uart_with_irq(self) -> UartWithIrq<UartInstance, (TxPinInst, RxPinInst)> {
|
/// 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),
|
||||||
|
) {
|
||||||
let (inner, pins) = self.downgrade_internal();
|
let (inner, pins) = self.downgrade_internal();
|
||||||
UartWithIrq {
|
let (tx, rx) = inner.split();
|
||||||
pins,
|
(
|
||||||
base: UartWithIrqBase {
|
tx,
|
||||||
inner,
|
RxWithIrq {
|
||||||
|
inner: rx,
|
||||||
irq_info: IrqInfo {
|
irq_info: IrqInfo {
|
||||||
rx_len: 0,
|
rx_len: 0,
|
||||||
rx_idx: 0,
|
rx_idx: 0,
|
||||||
mode: IrqReceptionMode::Idle,
|
mode: IrqReceptionMode::Idle,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
pins,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate::delegate! {
|
delegate::delegate! {
|
||||||
@ -673,11 +633,26 @@ impl<Uart: Instance> Rx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the RX side are used.
|
/// You must ensure that only registers related to the operation of the RX side are used.
|
||||||
pub unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,11 +663,22 @@ impl<Uart: Instance> Tx<Uart> {
|
|||||||
///
|
///
|
||||||
/// You must ensure that only registers related to the operation of the TX side are used.
|
/// You must ensure that only registers related to the operation of the TX side are used.
|
||||||
pub unsafe fn uart(&self) -> &Uart {
|
pub unsafe fn uart(&self) -> &Uart {
|
||||||
&self.uart
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn clear_fifo(&self) {
|
pub fn clear_fifo(&self) {
|
||||||
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,6 +687,7 @@ pub struct IrqUartError {
|
|||||||
overflow: bool,
|
overflow: bool,
|
||||||
framing: bool,
|
framing: bool,
|
||||||
parity: bool,
|
parity: bool,
|
||||||
|
other: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IrqUartError {
|
impl IrqUartError {
|
||||||
@ -715,7 +702,7 @@ pub enum IrqError {
|
|||||||
Uart(IrqUartError),
|
Uart(IrqUartError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Uart: Instance> UartWithIrqBase<Uart> {
|
impl<Uart: Instance> RxWithIrq<Uart> {
|
||||||
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
/// This initializes a non-blocking read transfer using the IRQ capabilities of the UART
|
||||||
/// peripheral.
|
/// peripheral.
|
||||||
///
|
///
|
||||||
@ -735,8 +722,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
self.irq_info.mode = IrqReceptionMode::Pending;
|
self.irq_info.mode = IrqReceptionMode::Pending;
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = max_len;
|
self.irq_info.rx_len = max_len;
|
||||||
self.inner.enable_rx();
|
self.inner.enable();
|
||||||
self.inner.enable_tx();
|
|
||||||
self.enable_rx_irq_sources(enb_timeout_irq);
|
self.enable_rx_irq_sources(enb_timeout_irq);
|
||||||
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
unsafe { enable_interrupt(Uart::IRQ_RX) };
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -744,7 +730,7 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
fn enable_rx_irq_sources(&mut self, timeout: bool) {
|
||||||
self.inner.uart.irq_enb().modify(|_, w| {
|
self.inner.0.irq_enb().modify(|_, w| {
|
||||||
if timeout {
|
if timeout {
|
||||||
w.irq_rx_to().set_bit();
|
w.irq_rx_to().set_bit();
|
||||||
}
|
}
|
||||||
@ -755,30 +741,24 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn disable_rx_irq_sources(&mut self) {
|
fn disable_rx_irq_sources(&mut self) {
|
||||||
self.inner.uart.irq_enb().modify(|_, w| {
|
self.inner.0.irq_enb().modify(|_, w| {
|
||||||
w.irq_rx_to().clear_bit();
|
w.irq_rx_to().clear_bit();
|
||||||
w.irq_rx_status().clear_bit();
|
w.irq_rx_status().clear_bit();
|
||||||
w.irq_rx().clear_bit()
|
w.irq_rx().clear_bit()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn enable_tx(&mut self) {
|
|
||||||
self.inner.enable_tx()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn disable_tx(&mut self) {
|
|
||||||
self.inner.disable_tx()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cancel_transfer(&mut self) {
|
pub fn cancel_transfer(&mut self) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.inner.clear_tx_fifo();
|
self.inner.clear_fifo();
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = 0;
|
self.irq_info.rx_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uart(&self) -> &Uart {
|
||||||
|
&self.inner.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
/// Default IRQ handler which can be used to read the packets arriving on the UART peripheral.
|
||||||
///
|
///
|
||||||
/// If passed buffer is equal to or larger than the specified maximum length, an
|
/// If passed buffer is equal to or larger than the specified maximum length, an
|
||||||
@ -791,104 +771,102 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut res = IrqResult::default();
|
let mut res = IrqResult::default();
|
||||||
let mut possible_error = IrqUartError::default();
|
|
||||||
|
|
||||||
let rx_status = self.inner.uart.rxstatus().read();
|
let irq_end = self.inner.0.irq_end().read();
|
||||||
res.raw_res = rx_status.bits();
|
let enb_status = self.inner.0.enable().read();
|
||||||
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();
|
let rx_enabled = enb_status.rxenable().bit_is_set();
|
||||||
let _tx_enabled = enb_status.txenable().bit_is_set();
|
|
||||||
let read_handler = |res: &mut IrqResult,
|
// Half-Full interrupt. We have a guaranteed amount of data we can read.
|
||||||
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() {
|
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.
|
// If this interrupt bit is set, the trigger level is available at the very least.
|
||||||
// Read everything as fast as possible
|
// Read everything as fast as possible
|
||||||
for _ in 0..core::cmp::min(
|
for _ in 0..bytes_to_read {
|
||||||
self.inner.uart.rxfifoirqtrg().read().bits() as usize,
|
buf[self.irq_info.rx_idx] = (self.inner.0.data().read().bits() & 0xff) as u8;
|
||||||
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;
|
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
|
// While there is data in the FIFO, write it into the reception buffer
|
||||||
loop {
|
loop {
|
||||||
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
if self.irq_info.rx_idx == self.irq_info.rx_len {
|
||||||
self.irq_completion_handler(&mut res);
|
break;
|
||||||
return Ok(res);
|
|
||||||
}
|
}
|
||||||
if let Some(byte) = read_handler(&mut res, &mut possible_error, self.inner.read()) {
|
if let Some(byte) = read_handler(&mut res.errors, self.inner.read()) {
|
||||||
buf[self.irq_info.rx_idx] = byte;
|
buf[self.irq_info.rx_idx] = byte;
|
||||||
self.irq_info.rx_idx += 1;
|
self.irq_info.rx_idx += 1;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.irq_completion_handler(&mut res);
|
||||||
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// RX transfer not complete, check for RX errors
|
// RX transfer not complete, check for RX errors
|
||||||
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
if (self.irq_info.rx_idx < self.irq_info.rx_len) && rx_enabled {
|
||||||
// Read status register again, might have changed since reading received data
|
// Read status register again, might have changed since reading received data
|
||||||
let rx_status = self.inner.uart.rxstatus().read();
|
let rx_status = self.inner.0.rxstatus().read();
|
||||||
res.raw_res = rx_status.bits();
|
|
||||||
if rx_status.rxovr().bit_is_set() {
|
if rx_status.rxovr().bit_is_set() {
|
||||||
possible_error.overflow = true;
|
res.errors.overflow = true;
|
||||||
}
|
}
|
||||||
if rx_status.rxfrm().bit_is_set() {
|
if rx_status.rxfrm().bit_is_set() {
|
||||||
possible_error.framing = true;
|
res.errors.framing = true;
|
||||||
}
|
}
|
||||||
if rx_status.rxpar().bit_is_set() {
|
if rx_status.rxpar().bit_is_set() {
|
||||||
possible_error.parity = true;
|
res.errors.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 it is not a timeout, it's an error
|
||||||
if possible_error.error() {
|
if res.error() {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
return Err(IrqError::Uart(possible_error));
|
return Err(IrqError::Uart(res.errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the interrupt status bits
|
// Clear the interrupt status bits
|
||||||
self.inner
|
self.inner
|
||||||
.uart
|
.0
|
||||||
.irq_clr()
|
.irq_clr()
|
||||||
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
.write(|w| unsafe { w.bits(irq_end.bits()) });
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@ -896,48 +874,23 @@ impl<Uart: Instance> UartWithIrqBase<Uart> {
|
|||||||
|
|
||||||
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
fn irq_completion_handler(&mut self, res: &mut IrqResult) {
|
||||||
self.disable_rx_irq_sources();
|
self.disable_rx_irq_sources();
|
||||||
self.inner.disable_rx();
|
self.inner.disable();
|
||||||
res.bytes_read = self.irq_info.rx_idx;
|
res.bytes_read = self.irq_info.rx_idx;
|
||||||
res.clear_result();
|
res.complete = true;
|
||||||
res.set_result(IrqResultMask::Complete);
|
|
||||||
self.irq_info.mode = IrqReceptionMode::Idle;
|
self.irq_info.mode = IrqReceptionMode::Idle;
|
||||||
self.irq_info.rx_idx = 0;
|
self.irq_info.rx_idx = 0;
|
||||||
self.irq_info.rx_len = 0;
|
self.irq_info.rx_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn irq_info(&self) -> &IrqInfo {
|
||||||
|
&self.irq_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn release(self) -> Uart {
|
pub fn release(self) -> Uart {
|
||||||
self.inner.release()
|
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 {
|
impl embedded_io::Error for Error {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io::ErrorKind {
|
||||||
embedded_io::ErrorKind::Other
|
embedded_io::ErrorKind::Other
|
||||||
|
@ -74,8 +74,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -104,8 +103,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -134,8 +132,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -164,11 +161,11 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
"type": "console"
|
"type": "console"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -194,8 +191,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -224,8 +220,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -255,8 +250,7 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -286,8 +280,187 @@
|
|||||||
"runToEntryPoint": "main",
|
"runToEntryPoint": "main",
|
||||||
"rttConfig": {
|
"rttConfig": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Have to use exact address unfortunately. "auto" does not work for some reason..
|
"address": "auto",
|
||||||
"address": "0x1fff8000",
|
"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",
|
||||||
"decoders": [
|
"decoders": [
|
||||||
{
|
{
|
||||||
"port": 0,
|
"port": 0,
|
||||||
@ -298,4 +471,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,32 @@
|
|||||||
// for the documentation about the tasks.json format
|
// for the documentation about the tasks.json format
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"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",
|
"label": "blinky-pac-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
@ -29,6 +55,19 @@
|
|||||||
"kind": "build",
|
"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",
|
"label": "blinky-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
@ -56,6 +95,19 @@
|
|||||||
"kind": "build",
|
"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",
|
"label": "wdt-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
@ -108,5 +160,44 @@
|
|||||||
"kind": "build",
|
"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