Add embassy example
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
This commit is contained in:
parent
ed175a03fc
commit
2223a2459c
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
|
||||||
|
- 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",
|
||||||
|
@ -25,6 +25,9 @@ It also contains the following helper crates:
|
|||||||
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` folder contains various example applications crates for the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
|
native Rust RTOSes.
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
|
4
automation/Jenkinsfile
vendored
4
automation/Jenkinsfile
vendored
@ -25,7 +25,9 @@ pipeline {
|
|||||||
stage('Docs') {
|
stage('Docs') {
|
||||||
steps {
|
steps {
|
||||||
sh """
|
sh """
|
||||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features -p vorago-peb1
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features -p va416xx-hal
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features -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"
|
||||||
|
@ -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);
|
||||||
|
@ -108,6 +108,7 @@ mod app {
|
|||||||
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,
|
||||||
@ -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);
|
||||||
@ -488,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Fixes for the SPI implementation where the clock divider values were not calculated
|
- Fixes for the SPI implementation where the clock divider values were not calculated
|
||||||
correctly
|
correctly
|
||||||
- Fixes for UART IRQ handler implementation
|
- Fixes for UART IRQ handler implementation
|
||||||
|
- Add new IRQ router initialization method `irq_router::enable_and_init_irq_router`. This method
|
||||||
|
also sets the initial values of some registers to 0 where the datasheet and the actual reset
|
||||||
|
value are inconsistent, which can lead to weird bugs like IRQs not being triggered properly.
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -494,7 +494,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,23 +504,23 @@ impl Clocks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
va416xx-hal/src/irq_router.rs
Normal file
18
va416xx-hal/src/irq_router.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use crate::{
|
||||||
|
clock::{PeripheralSelect, SyscfgExt},
|
||||||
|
pac,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 leads 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,6 +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 irq_router;
|
||||||
pub mod nvm;
|
pub mod nvm;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
|
@ -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>);
|
||||||
|
@ -410,5 +410,65 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Embassy Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "embassy-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/embassy-example",
|
||||||
|
"interface": "swd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "RTIC Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"jlinkscript": "${workspaceFolder}/jlink/JLinkSettings.JLinkScript",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M4",
|
||||||
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
|
"preLaunchTask": "embassy-example",
|
||||||
|
"overrideLaunchCommands": [
|
||||||
|
"monitor halt",
|
||||||
|
"monitor reset",
|
||||||
|
"load",
|
||||||
|
],
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/rtic-example",
|
||||||
|
"interface": "swd",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -186,5 +186,18 @@
|
|||||||
"kind": "build",
|
"kind": "build",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "rtic-example",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin",
|
||||||
|
"rtic-example"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user