Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1baea3ccb2 | |||
| 027161d45d | |||
| 0d1bd3aa47 | |||
| 09ba2d3675 | |||
| 6225b8d183 | |||
| 1138b65203 | |||
| d36282d34b | |||
| 15fed28281 | |||
| adad36db89 | |||
| e1b3abcba9 | |||
| 682655c036 | |||
| 79b6f6124b | |||
| 2424fbf075 | |||
| eb41170fa2 | |||
| 8108beb70f | |||
| 4493cddb32 | |||
| f8114ba3f3 | |||
| 6f0890c6b2 | |||
| 3fcc13a179 | |||
| 427862d54b | |||
| db26553ed4 | |||
| 232b3e8c39 | |||
| cb61ff7ae0 | |||
| 5cf1e679af | |||
| 97f656ddde | |||
| 97a66f22e2 | |||
| 73c8d30078 | |||
| a1489285c9 | |||
| 2ac6c2f002 | |||
| 6d67953b3b | |||
| 08ba36966e | |||
| 732e4d5e73 | |||
| 7c984feba3 | |||
| 66b2506c7d | |||
| d1bb378bfa | |||
| 684db0a57b | |||
| 35b4a73368 | |||
| 0994837251 |
@@ -36,7 +36,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install nextest
|
||||
uses: taiki-e/install-action@nextest
|
||||
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
|
||||
- run: cargo nextest run -p va108xx-hal --no-tests=pass
|
||||
# I think we can skip those on an embedded crate..
|
||||
# - run: cargo test --doc -p va108xx-hal
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features --no-deps
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --features "defmt, embassy-oc30-oc31" --no-deps
|
||||
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1 --no-deps
|
||||
|
||||
clippy:
|
||||
|
||||
@@ -70,7 +70,7 @@ clippy-shared-hal:
|
||||
[working-directory: 'va108xx']
|
||||
docs-va108xx:
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features --no-deps
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --features "defmt, embassy-oc30-oc31" --no-deps
|
||||
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1 --no-deps
|
||||
|
||||
[working-directory: 'va416xx']
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "uart-clock-calc"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
arbitrary-int = "2"
|
||||
@@ -0,0 +1,50 @@
|
||||
use arbitrary_int::{u6, u18};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ClockConfig {
|
||||
pub frac: u6,
|
||||
pub int: u18,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum BaudMultiplier {
|
||||
_8 = 8,
|
||||
_16 = 16,
|
||||
}
|
||||
|
||||
pub fn uart_clock_calc(ref_clk: u32, baudrate: u32, baud_mult: BaudMultiplier) -> ClockConfig {
|
||||
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
|
||||
// point calculations.
|
||||
let multiplier = baud_mult as u32;
|
||||
let frac = ((ref_clk % (baudrate * multiplier)) * 64 + (baudrate * (multiplier / 2)))
|
||||
/ (baudrate * multiplier);
|
||||
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
|
||||
let integer_part = ref_clk / (baudrate * multiplier);
|
||||
ClockConfig {
|
||||
frac: u6::new(frac as u8),
|
||||
int: u18::new(integer_part),
|
||||
}
|
||||
}
|
||||
|
||||
const SYS_CLK_50_MHZ: u32 = 50_000_000;
|
||||
|
||||
fn main() {
|
||||
println!("UART Clock Configuration App");
|
||||
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_16);
|
||||
println!(
|
||||
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
|
||||
SYS_CLK_50_MHZ,
|
||||
38400,
|
||||
BaudMultiplier::_16 as u32,
|
||||
clock_config
|
||||
);
|
||||
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_8);
|
||||
println!(
|
||||
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
|
||||
SYS_CLK_50_MHZ,
|
||||
38400,
|
||||
BaudMultiplier::_8 as u32,
|
||||
clock_config
|
||||
);
|
||||
()
|
||||
}
|
||||
@@ -4,7 +4,6 @@ members = [
|
||||
"vorago-reb1",
|
||||
"va108xx",
|
||||
"va108xx-hal",
|
||||
"va108xx-embassy",
|
||||
"examples/simple",
|
||||
"examples/rtic",
|
||||
"examples/embassy",
|
||||
|
||||
+10
-3
@@ -90,15 +90,22 @@ work yet.
|
||||
After installation, you can run the following command
|
||||
|
||||
```sh
|
||||
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
|
||||
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/blinky
|
||||
```
|
||||
|
||||
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
||||
available for persistent flashing.
|
||||
available for persistent flashing (see note below!).
|
||||
|
||||
Runner configuration is available in the `.cargo/def-config.toml` file to use `probe-rs` for
|
||||
Runner configuration is available in the `.cargo/config.toml.template` file to use `probe-rs` for
|
||||
convenience. `probe-rs` is also able to process and display `defmt` strings directly.
|
||||
|
||||
Special note on the `VA108xx` target: This target allows flashing the NVM, but doing a soft reset
|
||||
with a tool like `probe-rs` can only perform a soft reset where the code already running in RAM
|
||||
is reset. If you want to immediately run the code flashed to the NVM and get `defmt` printouts,
|
||||
use `probe-rs download` to flash to NVM, then flash a binary which issues the system reset
|
||||
(e.g. the `reset` app inside the example folder), and then attach with `probe-rs attach`, passing
|
||||
the image downloaded to NVM to the attach command.
|
||||
|
||||
### Using VS Code
|
||||
|
||||
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||
|
||||
@@ -12,7 +12,7 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
||||
embedded-hal = "1"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.12"
|
||||
version = "0.13"
|
||||
features = ["rt"]
|
||||
path = "../va108xx-hal"
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ fn main() -> ! {
|
||||
delay_timer.delay_ms(500);
|
||||
}
|
||||
let ahb_freq: Hertz = 50.MHz();
|
||||
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.raw());
|
||||
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.to_raw());
|
||||
// Release image should be used to verify timings for pin PA0
|
||||
for _ in 0..5 {
|
||||
pa0.toggle();
|
||||
|
||||
@@ -15,7 +15,7 @@ num_enum = { version = "0.7", default-features = false }
|
||||
static_assertions = "1"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.12"
|
||||
version = "0.13"
|
||||
path = "../va108xx-hal"
|
||||
features = ["defmt"]
|
||||
|
||||
|
||||
@@ -181,7 +181,8 @@ fn check_own_crc(
|
||||
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
||||
// panics.
|
||||
#[allow(clippy::zero_ptr)]
|
||||
let first_four_bytes = unsafe { core::ptr::read_volatile(0x0 as *const u32) }.to_ne_bytes();
|
||||
let first_four_bytes =
|
||||
unsafe { core::ptr::read_volatile(BOOTLOADER_START_ADDR as *const u32) }.to_ne_bytes();
|
||||
let mut digest = CRC_ALGO.digest();
|
||||
digest.update(&first_four_bytes);
|
||||
digest.update(unsafe {
|
||||
@@ -270,14 +271,10 @@ fn boot_app(
|
||||
APP_B_START_ADDR
|
||||
};
|
||||
unsafe {
|
||||
// First 4 bytes done with inline assembly, writing to the physical address 0x0 can not
|
||||
// be done without it. See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/2.
|
||||
let first_four_bytes = core::ptr::read(base_addr as *const u32);
|
||||
core::arch::asm!(
|
||||
"str {0}, [{1}]",
|
||||
in(reg) first_four_bytes, // Input: App vector table.
|
||||
in(reg) BOOTLOADER_START_ADDR as *mut u32, // Input: destination pointer
|
||||
);
|
||||
let first_four_bytes = core::ptr::read_volatile(base_addr as *const u32);
|
||||
#[allow(clippy::zero_ptr)]
|
||||
core::ptr::write_volatile(BOOTLOADER_START_ADDR as *mut u32, first_four_bytes);
|
||||
|
||||
core::slice::from_raw_parts_mut(
|
||||
(BOOTLOADER_START_ADDR + 4) as *mut u8,
|
||||
(VECTOR_TABLE_LEN - 4) as usize,
|
||||
|
||||
@@ -4,8 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1"
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
cfg-if = "1"
|
||||
embedded-hal-async = "1"
|
||||
embedded-io = "0.7"
|
||||
embedded-io-async = "0.7"
|
||||
@@ -18,19 +19,18 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
||||
|
||||
critical-section = "1"
|
||||
|
||||
embassy-sync = "0.7"
|
||||
embassy-sync = "0.8"
|
||||
embassy-time = "0.5"
|
||||
embassy-executor = { version = "0.9", features = [
|
||||
"arch-cortex-m",
|
||||
embassy-executor = { version = "0.10", features = [
|
||||
"platform-cortex-m",
|
||||
"executor-thread",
|
||||
"executor-interrupt"
|
||||
]}
|
||||
|
||||
va108xx-hal = { version = "0.12", path = "../../va108xx-hal", features = ["defmt"] }
|
||||
va108xx-embassy = { version = "0.3", path = "../../va108xx-embassy" }
|
||||
va108xx-hal = { version = "0.13", path = "../../va108xx-hal", features = ["defmt"] }
|
||||
|
||||
[features]
|
||||
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
||||
default = ["ticks-hz-1_000", "va108xx-hal/embassy-oc30-oc31"]
|
||||
custom-irqs = []
|
||||
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||
|
||||
@@ -61,7 +61,10 @@ async fn main(spawner: Spawner) {
|
||||
let dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
va108xx_hal::embassy_time::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
unsafe {
|
||||
cortex_m::interrupt::enable();
|
||||
}
|
||||
|
||||
let porta = PinsA::new(dp.porta);
|
||||
let portb = PinsB::new(dp.portb);
|
||||
@@ -71,36 +74,35 @@ async fn main(spawner: Spawner) {
|
||||
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
||||
let in_pb23 = Input::new_floating(portb.pb23);
|
||||
|
||||
let mut in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
||||
let mut in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
||||
let mut in_pa1_async = InputPinAsync::new(
|
||||
in_pa1,
|
||||
va108xx_hal::InterruptConfig::new(pac::Interrupt::OC10, true, true),
|
||||
);
|
||||
let mut in_pb23_async = InputPinAsync::new(
|
||||
in_pb23,
|
||||
va108xx_hal::InterruptConfig::new(PB22_TO_PB23_IRQ, true, true),
|
||||
);
|
||||
|
||||
spawner
|
||||
.spawn(output_task(
|
||||
"PA0 to PA1",
|
||||
out_pa0,
|
||||
CHANNEL_PA0_PA1.receiver(),
|
||||
))
|
||||
.unwrap();
|
||||
spawner
|
||||
.spawn(output_task(
|
||||
"PB22 to PB23",
|
||||
out_pb22,
|
||||
CHANNEL_PB22_TO_PB23.receiver(),
|
||||
))
|
||||
.unwrap();
|
||||
spawner.spawn(output_task("PA0 to PA1", out_pa0, CHANNEL_PA0_PA1.receiver()).unwrap());
|
||||
spawner.spawn(output_task("PB22 to PB23", out_pb22, CHANNEL_PB22_TO_PB23.receiver()).unwrap());
|
||||
|
||||
if CHECK_PA0_TO_PA1 {
|
||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async).await;
|
||||
defmt::info!("Example PA0 to PA1 done");
|
||||
}
|
||||
if CHECK_PB22_TO_PB23 {
|
||||
check_pin_to_pin_async_ops(
|
||||
"PB22 to PB23",
|
||||
CHANNEL_PB22_TO_PB23.sender(),
|
||||
&mut in_pb23_async,
|
||||
)
|
||||
.await;
|
||||
defmt::info!("Example PB22 to PB23 done");
|
||||
for i in 0..3 {
|
||||
defmt::info!("Starting async GPIO operations check {}", i);
|
||||
if CHECK_PA0_TO_PA1 {
|
||||
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), &mut in_pa1_async)
|
||||
.await;
|
||||
defmt::info!("Example PA0 to PA1 done");
|
||||
}
|
||||
if CHECK_PB22_TO_PB23 {
|
||||
check_pin_to_pin_async_ops(
|
||||
"PB22 to PB23",
|
||||
CHANNEL_PB22_TO_PB23.sender(),
|
||||
&mut in_pb23_async,
|
||||
)
|
||||
.await;
|
||||
defmt::info!("Example PB22 to PB23 done");
|
||||
}
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
defmt::info!("Example done, toggling LED0");
|
||||
|
||||
@@ -55,7 +55,7 @@ async fn main(spawner: Spawner) {
|
||||
let dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
va108xx_hal::embassy_time::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
|
||||
let porta = PinsA::new(dp.porta);
|
||||
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
||||
@@ -65,12 +65,13 @@ async fn main(spawner: Spawner) {
|
||||
let tx_uart_a = porta.pa9;
|
||||
let rx_uart_a = porta.pa8;
|
||||
|
||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uarta = uart::Uart::new_with_interrupt_uart0(
|
||||
dp.uarta,
|
||||
tx_uart_a,
|
||||
rx_uart_a,
|
||||
50.MHz(),
|
||||
115200.Hz().into(),
|
||||
uart_config,
|
||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||
);
|
||||
|
||||
@@ -81,8 +82,7 @@ async fn main(spawner: Spawner) {
|
||||
dp.uartb,
|
||||
tx_uart_b,
|
||||
rx_uart_b,
|
||||
50.MHz(),
|
||||
115200.Hz().into(),
|
||||
uart_config,
|
||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||
);
|
||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||
@@ -97,9 +97,7 @@ async fn main(spawner: Spawner) {
|
||||
});
|
||||
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
||||
spawner
|
||||
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||
.unwrap();
|
||||
spawner.spawn(uart_b_task(async_rx_uart_b, tx_uart_b).unwrap());
|
||||
let mut buf = [0u8; 256];
|
||||
loop {
|
||||
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
||||
|
||||
@@ -41,7 +41,7 @@ async fn main(_spawner: Spawner) {
|
||||
let dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
// Safety: Only called once here.
|
||||
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
va108xx_hal::embassy_time::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||
|
||||
let porta = PinsA::new(dp.porta);
|
||||
|
||||
@@ -52,12 +52,13 @@ async fn main(_spawner: Spawner) {
|
||||
let tx = porta.pa9;
|
||||
let rx = porta.pa8;
|
||||
|
||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uarta = uart::Uart::new_with_interrupt_uart0(
|
||||
dp.uarta,
|
||||
tx,
|
||||
rx,
|
||||
50.MHz(),
|
||||
115200.Hz().into(),
|
||||
uart_config,
|
||||
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||
);
|
||||
let (tx, _rx) = uarta.split();
|
||||
|
||||
@@ -6,7 +6,7 @@ use embassy_time::{Duration, Instant, Ticker};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "custom-irqs")] {
|
||||
use va108xx_embassy::embassy_time_driver_irqs;
|
||||
use va108xx_hal::embassy_time_driver_irqs;
|
||||
use va108xx_hal::pac::interrupt;
|
||||
embassy_time_driver_irqs!(timekeeper_irq = OC23, alarm_irq = OC24);
|
||||
}
|
||||
@@ -31,13 +31,13 @@ async fn main(_spawner: Spawner) {
|
||||
// Safety: Only called once here.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(feature = "custom-irqs"))] {
|
||||
va108xx_embassy::init(
|
||||
va108xx_hal::embassy_time::init(
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
SYSCLK_FREQ,
|
||||
);
|
||||
} else {
|
||||
va108xx_embassy::init_with_custom_irqs(
|
||||
va108xx_hal::embassy_time::init_with_custom_irqs(
|
||||
dp.tim23,
|
||||
dp.tim22,
|
||||
SYSCLK_FREQ,
|
||||
|
||||
@@ -9,9 +9,10 @@ embedded-io = "0.7"
|
||||
defmt-rtt = "1"
|
||||
defmt = "1"
|
||||
panic-probe = { version = "1", features = ["defmt"] }
|
||||
fugit = "0.4"
|
||||
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||
|
||||
va108xx-hal = { version = "0.12", path = "../../va108xx-hal" }
|
||||
va108xx-hal = { version = "0.13", path = "../../va108xx-hal" }
|
||||
vorago-reb1 = { version = "0.9", path = "../../vorago-reb1" }
|
||||
|
||||
@@ -43,7 +43,7 @@ mod app {
|
||||
#[init]
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
defmt::println!("-- Vorago Button IRQ Example --");
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.to_raw());
|
||||
|
||||
let mode = DEFAULT_MODE;
|
||||
defmt::info!("Using {:?} mode", mode);
|
||||
|
||||
@@ -20,11 +20,11 @@ mod app {
|
||||
use panic_probe as _;
|
||||
// Import global logger.
|
||||
use defmt_rtt as _;
|
||||
use rtic_monotonics::fugit::ExtU32 as _;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use va108xx_hal::{
|
||||
pac,
|
||||
pins::PinsA,
|
||||
prelude::*,
|
||||
uart::{self, RxWithInterrupt, Tx},
|
||||
InterruptConfig,
|
||||
};
|
||||
@@ -46,19 +46,24 @@ mod app {
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
defmt::println!("-- VA108xx UART Echo with IRQ example application--");
|
||||
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.to_raw());
|
||||
|
||||
let dp = cx.device;
|
||||
let gpioa = PinsA::new(dp.porta);
|
||||
let tx = gpioa.pa9;
|
||||
let rx = gpioa.pa8;
|
||||
|
||||
let clock_config = uart::ClockConfig::calculate(
|
||||
SYSCLK_FREQ,
|
||||
fugit::HertzU32::from_raw(115200),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
||||
dp.uarta,
|
||||
tx,
|
||||
rx,
|
||||
SYSCLK_FREQ,
|
||||
115200.Hz().into(),
|
||||
uart_config,
|
||||
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||
);
|
||||
let (tx, rx) = irq_uart.split();
|
||||
|
||||
@@ -34,7 +34,7 @@ mod app {
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
defmt::println!("-- Vorago VA108xx RTIC template --");
|
||||
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.to_raw());
|
||||
|
||||
let porta = PinsA::new(cx.device.porta);
|
||||
let led0 = Output::new(porta.pa10, PinState::Low);
|
||||
|
||||
@@ -16,6 +16,6 @@ embedded-io = "0.7"
|
||||
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.12"
|
||||
version = "0.13"
|
||||
path = "../../va108xx-hal"
|
||||
features = ["defmt"]
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//! Dummy app which does not do anything.
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_halt as _;
|
||||
use va108xx_hal as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
cortex_m::peripheral::SCB::sys_reset();
|
||||
}
|
||||
@@ -87,7 +87,7 @@ fn main() -> ! {
|
||||
bmstall: true,
|
||||
hw_cs: None,
|
||||
};
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
spi.configure_transfer(&transfer_cfg);
|
||||
}
|
||||
SpiBusSelect::SpiBPortB => {
|
||||
let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2);
|
||||
@@ -99,7 +99,7 @@ fn main() -> ! {
|
||||
bmstall: true,
|
||||
hw_cs: Some(hw_cs_pin),
|
||||
};
|
||||
spi.cfg_transfer(&transfer_cfg);
|
||||
spi.configure_transfer(&transfer_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -46,8 +46,8 @@ fn main() -> ! {
|
||||
}
|
||||
|
||||
let sys_clk: Hertz = 50.MHz();
|
||||
let cnt_ms = sys_clk.raw() / 1000 - 1;
|
||||
let cnt_sec = sys_clk.raw() - 1;
|
||||
let cnt_ms = sys_clk.to_raw() / 1000 - 1;
|
||||
let cnt_sec = sys_clk.to_raw() - 1;
|
||||
unsafe {
|
||||
dp.tim0.cnt_value().write(|w| w.bits(cnt_ms));
|
||||
dp.tim0.rst_value().write(|w| w.bits(cnt_ms));
|
||||
@@ -28,8 +28,9 @@ fn main() -> ! {
|
||||
let gpioa = PinsA::new(dp.porta);
|
||||
let tx = gpioa.pa9;
|
||||
let rx = gpioa.pa8;
|
||||
let uart =
|
||||
uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, 50.MHz(), 115200.Hz().into());
|
||||
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uart = uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, uart_config);
|
||||
|
||||
let (mut tx, mut rx) = uart.split();
|
||||
writeln!(tx, "Hello World\r").unwrap();
|
||||
@@ -13,6 +13,7 @@ panic-probe = { version = "1", features = ["print-defmt"] }
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
cobs = { version = "0.5", default-features = false }
|
||||
satrs = { version = "0.3.0-alpha.3", default-features = false, features = ["defmt"] }
|
||||
fugit = "0.4"
|
||||
arbitrary-int = "2"
|
||||
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||
# spacepackets = { version = "0.17", path = "https://egit.irs.uni-stuttgart.de/rust/spacepackets.git", default-features = false, features = ["defmt"] }
|
||||
@@ -24,7 +25,7 @@ rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.12"
|
||||
version = "0.13"
|
||||
path = "../va108xx-hal"
|
||||
features = ["defmt"]
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ use ringbuf::{
|
||||
traits::{Consumer, Observer, Producer},
|
||||
StaticRb,
|
||||
};
|
||||
use va108xx_hal::prelude::*;
|
||||
use rtic_monotonics::fugit::ExtU32;
|
||||
use va108xx_hal::time::Hertz;
|
||||
|
||||
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||
|
||||
@@ -64,7 +65,7 @@ mod app {
|
||||
use cortex_m::asm;
|
||||
use embedded_io::Write;
|
||||
use rtic::Mutex;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use rtic_monotonics::Monotonic;
|
||||
use satrs::pus::verification::{FailParams, VerificationReportCreator};
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs::spacepackets::ecss::{
|
||||
@@ -106,7 +107,7 @@ mod app {
|
||||
fn init(cx: init::Context) -> (Shared, Local) {
|
||||
defmt::println!("-- Vorago flashloader --");
|
||||
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||
Mono::start(cx.core.SYST, SYSCLK_FREQ.to_raw());
|
||||
|
||||
let dp = cx.device;
|
||||
let spi_clock_config = SpiClockConfig::new(2, 4);
|
||||
@@ -116,12 +117,17 @@ mod app {
|
||||
let tx = gpioa.pa9;
|
||||
let rx = gpioa.pa8;
|
||||
|
||||
let clock_config = uart::ClockConfig::calculate(
|
||||
SYSCLK_FREQ,
|
||||
fugit::HertzU32::from_raw(UART_BAUDRATE),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let irq_uart = uart::Uart::new_with_interrupt_uart0(
|
||||
dp.uarta,
|
||||
tx,
|
||||
rx,
|
||||
SYSCLK_FREQ,
|
||||
UART_BAUDRATE.Hz().into(),
|
||||
uart_config,
|
||||
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||
);
|
||||
let (tx, rx) = irq_uart.split();
|
||||
@@ -252,7 +258,7 @@ mod app {
|
||||
let packet_len = cx.shared.tc_rb.lock(|rb| rb.sizes.try_pop());
|
||||
if packet_len.is_none() {
|
||||
// Small delay, TCs might arrive very quickly.
|
||||
Mono::delay(20.millis()).await;
|
||||
Mono::delay(20_u32.millis()).await;
|
||||
continue;
|
||||
}
|
||||
let packet_len = packet_len.unwrap();
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
Change Log
|
||||
=======
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.3.0] 2025-09-03
|
||||
|
||||
Bumped allowed va108xx-hal to v0.12
|
||||
|
||||
## [v0.2.1] 2025-03-07
|
||||
|
||||
- Bumped allowed va108xx-hal to v0.11
|
||||
|
||||
## [v0.2.0] 2025-02-17
|
||||
|
||||
- Bumped va108xx-hal to v0.10.0
|
||||
- Remove `embassy` module, expose public functions in library root directly
|
||||
|
||||
|
||||
## [v0.1.2] and [v0.1.1] 2025-02-13
|
||||
|
||||
Docs patch
|
||||
|
||||
## [v0.1.0] 2025-02-13
|
||||
|
||||
Initial release
|
||||
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.3.0...HEAD
|
||||
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.1...va10xx-embassy-v0.3.0
|
||||
[v0.2.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.0...va10xx-embassy-v0.2.1
|
||||
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.1.2...va10xx-embassy-v0.2.0
|
||||
@@ -1,28 +0,0 @@
|
||||
[package]
|
||||
name = "va108xx-embassy"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/vorago-rs"
|
||||
license = "Apache-2.0"
|
||||
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor1x"] }
|
||||
va108xx-hal = { version = "0.12", path = "../va108xx-hal" }
|
||||
|
||||
[features]
|
||||
default = ["irq-oc30-oc31"]
|
||||
irqs-in-lib = []
|
||||
# This determines the reserved interrupt functions for the embassy time drivers. Only one
|
||||
# is allowed to be selected!
|
||||
irq-oc28-oc29 = ["irqs-in-lib"]
|
||||
irq-oc29-oc30 = ["irqs-in-lib"]
|
||||
irq-oc30-oc31 = ["irqs-in-lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["thumbv6m-none-eabi"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
@@ -1,10 +0,0 @@
|
||||
[](https://crates.io/crates/va108xx-embassy)
|
||||
[](https://docs.rs/va108xx-embassy)
|
||||
|
||||
# Embassy-rs support for the Vorago VA108xx MCU family
|
||||
|
||||
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
|
||||
VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
|
||||
peripherals provided by the VA108xx family for this purpose.
|
||||
|
||||
The documentation contains more information on how to use this crate.
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||
cargo +nightly doc --open
|
||||
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.13.0] 2026-05-18
|
||||
|
||||
- Bump `vorago-shared-hal` dependency to v0.3.
|
||||
- Integrate `va108xx-embassy` as a `embassy-time` module.
|
||||
|
||||
## [v0.12.0] 2025-09-03
|
||||
|
||||
## Changed
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-rs"
|
||||
repository = "https://egit.irs.uni-stuttgart.de/rust/vorago-rs"
|
||||
@@ -12,10 +12,10 @@ categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor1x"] }
|
||||
fugit = "0.3"
|
||||
vorago-shared-hal = { version = "0.3", path = "../../vorago-shared-hal", features = ["vor1x"] }
|
||||
fugit = "0.4"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
va108xx = { version = "0.6", path = "../va108xx", default-features = false, features = ["critical-section", "defmt"] }
|
||||
va108xx = { version = "0.6", path = "../va108xx", default-features = false, features = ["critical-section"] }
|
||||
defmt = { version = "1", optional = true }
|
||||
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||
@@ -26,7 +26,15 @@ portable-atomic = "1"
|
||||
[features]
|
||||
default = ["rt"]
|
||||
rt = ["va108xx/rt"]
|
||||
defmt = ["dep:defmt", "vorago-shared-hal/defmt"]
|
||||
defmt = ["dep:defmt", "vorago-shared-hal/defmt", "va108xx/defmt"]
|
||||
|
||||
# Embassy time features
|
||||
_irqs-in-lib = []
|
||||
# This determines the reserved interrupt functions for the embassy time drivers. Only one
|
||||
# is allowed to be selected!
|
||||
embassy-oc30-oc31 = ["_irqs-in-lib"]
|
||||
embassy-oc29-oc30 = ["_irqs-in-lib"]
|
||||
embassy-oc28-oc29 = ["_irqs-in-lib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -23,21 +23,20 @@
|
||||
//!
|
||||
//! You can disable the default features and then specify one of the features above to use the
|
||||
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
||||
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
||||
//! using the [crate::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
||||
//! application code. If this is done, [init_with_custom_irqs] must be used
|
||||
//! method to pass the IRQ numbers to the library.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy)
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[cfg(feature = "irqs-in-lib")]
|
||||
use va108xx_hal::pac::{self, interrupt};
|
||||
use va108xx_hal::time::Hertz;
|
||||
use va108xx_hal::timer::TimInstance;
|
||||
use vorago_shared_hal::embassy::time_driver;
|
||||
#[cfg(feature = "_irqs-in-lib")]
|
||||
use crate::pac::{self, interrupt};
|
||||
use crate::time::Hertz;
|
||||
use crate::timer::TimInstance;
|
||||
|
||||
pub use vorago_shared_hal::embassy::time_driver;
|
||||
|
||||
/// Macro to define the IRQ handlers for the time driver.
|
||||
///
|
||||
@@ -45,7 +44,7 @@ use vorago_shared_hal::embassy::time_driver;
|
||||
/// the feature flags specified. However, the macro is exported to allow users to specify the
|
||||
/// interrupt handlers themselves.
|
||||
///
|
||||
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
|
||||
/// Please note that you have to explicitely import the [macro@crate::pac::interrupt]
|
||||
/// macro in the application code in case this macro is used there.
|
||||
#[macro_export]
|
||||
macro_rules! embassy_time_driver_irqs {
|
||||
@@ -59,7 +58,7 @@ macro_rules! embassy_time_driver_irqs {
|
||||
#[allow(non_snake_case)]
|
||||
fn $timekeeper_irq() {
|
||||
// Safety: We call it once here.
|
||||
unsafe { $crate::time_driver().on_interrupt_timekeeping() }
|
||||
unsafe { $crate::embassy_time::time_driver().on_interrupt_timekeeping() }
|
||||
}
|
||||
|
||||
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
|
||||
@@ -68,25 +67,25 @@ macro_rules! embassy_time_driver_irqs {
|
||||
#[allow(non_snake_case)]
|
||||
fn $alarm_irq() {
|
||||
// Safety: We call it once here.
|
||||
unsafe { $crate::time_driver().on_interrupt_alarm() }
|
||||
unsafe { $crate::embassy_time::time_driver().on_interrupt_alarm() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Provide three combinations of IRQs for the time driver by default.
|
||||
|
||||
#[cfg(feature = "irq-oc30-oc31")]
|
||||
#[cfg(feature = "embassy-oc30-oc31")]
|
||||
embassy_time_driver_irqs!(timekeeper_irq = OC31, alarm_irq = OC30);
|
||||
#[cfg(feature = "irq-oc29-oc30")]
|
||||
#[cfg(feature = "embassy-oc29-oc30")]
|
||||
embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
|
||||
#[cfg(feature = "irq-oc28-oc29")]
|
||||
#[cfg(feature = "embassy-oc28-oc29")]
|
||||
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
|
||||
|
||||
/// Initialization method for embassy.
|
||||
///
|
||||
/// This should be used if the interrupt handler is provided by the library, which is the
|
||||
/// default case.
|
||||
#[cfg(feature = "irqs-in-lib")]
|
||||
#[cfg(feature = "_irqs-in-lib")]
|
||||
pub fn init<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
|
||||
timekeeper_tim: TimekeeperTim,
|
||||
alarm_tim: AlarmTim,
|
||||
@@ -102,8 +101,8 @@ pub fn init_with_custom_irqs<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
|
||||
timekeeper_tim: TimekeeperTim,
|
||||
alarm_tim: AlarmTim,
|
||||
sysclk: Hertz,
|
||||
timekeeper_irq: pac::Interrupt,
|
||||
alarm_irq: pac::Interrupt,
|
||||
timekeeper_irq: crate::pac::Interrupt,
|
||||
alarm_irq: crate::pac::Interrupt,
|
||||
) {
|
||||
time_driver().__init(sysclk, timekeeper_tim, alarm_tim, timekeeper_irq, alarm_irq)
|
||||
}
|
||||
@@ -12,9 +12,4 @@
|
||||
//!
|
||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||
//! in a type-safe way.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/blinky.rs)
|
||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-gpio.rs)
|
||||
pub use vorago_shared_hal::gpio::*;
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
//! API for the I2C peripheral
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/examples/adt75-temp-sensor.rs)
|
||||
pub use vorago_shared_hal::i2c::*;
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! The [examples folder](https://github.com/us-irs/vorago-rs/tree/main/va108xx/examples) contains
|
||||
//! various example applications using the HAL.
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
@@ -26,9 +29,11 @@ pub mod time;
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
|
||||
pub mod embassy_time;
|
||||
|
||||
pub use vorago_shared_hal::{
|
||||
disable_nvic_interrupt, enable_nvic_interrupt, FunctionSelect, InterruptConfig,
|
||||
PeripheralSelect,
|
||||
FunctionSelect, InterruptConfig, PeripheralSelect, disable_nvic_interrupt,
|
||||
enable_nvic_interrupt,
|
||||
};
|
||||
|
||||
/// This is the NONE destination reigster value for the IRQSEL peripheral.
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
//! API for Pulse-Width Modulation (PWM)
|
||||
//!
|
||||
//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/pwm.rs)
|
||||
pub use vorago_shared_hal::pwm::*;
|
||||
|
||||
@@ -3,10 +3,4 @@
|
||||
//! The main abstraction provided by this module is the [Spi] an structure.
|
||||
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
||||
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/spi.rs)
|
||||
//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/examples/max11519-adc.rs)
|
||||
//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/vorago-reb1/src/m95m01.rs)
|
||||
pub use vorago_shared_hal::spi::*;
|
||||
|
||||
@@ -39,5 +39,6 @@ pub fn disable_ram_scrubbing() {
|
||||
}
|
||||
|
||||
pub use vorago_shared_hal::sysconfig::{
|
||||
assert_peripheral_reset, disable_peripheral_clock, enable_peripheral_clock,
|
||||
assert_peripheral_reset, deassert_peripheral_reset, disable_peripheral_clock,
|
||||
enable_peripheral_clock, reset_peripheral_for_cycles,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
//! API for the TIM peripherals
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/timer-ticks.rs)
|
||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/cascade.rs)
|
||||
pub use vorago_shared_hal::timer::*;
|
||||
|
||||
@@ -6,12 +6,4 @@
|
||||
//!
|
||||
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
||||
//! peripheral.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/simple/examples/uart.rs)
|
||||
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/rtic/src/bin/uart-echo-rtic.rs)
|
||||
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/flashloader)
|
||||
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-uart-rx.rs)
|
||||
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va108xx/examples/embassy/src/bin/async-uart-tx.rs)
|
||||
pub use vorago_shared_hal::uart::*;
|
||||
|
||||
@@ -15,11 +15,11 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-hal = "1"
|
||||
nb = "1"
|
||||
bitbybit = "1.3"
|
||||
bitbybit = "2"
|
||||
arbitrary-int = "2"
|
||||
max116xx-10bit = "0.3"
|
||||
|
||||
va108xx-hal = { version = "0.12", path = "../va108xx-hal", features = ["rt"] }
|
||||
va108xx-hal = { version = "0.13", path = "../va108xx-hal", features = ["rt"] }
|
||||
|
||||
[features]
|
||||
rt = ["va108xx-hal/rt"]
|
||||
|
||||
@@ -47,7 +47,7 @@ fn main() -> ! {
|
||||
.mode(MODE_3)
|
||||
.slave_output_disable(true);
|
||||
let mut spi = Spi::new_for_spi1(dp.spib, (sck, miso, mosi), spi_cfg);
|
||||
spi.cfg_hw_cs(hw_cs_id);
|
||||
spi.configure_hw_cs(hw_cs_id);
|
||||
|
||||
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
||||
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
||||
|
||||
@@ -83,7 +83,7 @@ impl<Delay: DelayNs> SpiDevice for SpiWithHwCs<Delay> {
|
||||
) -> Result<(), Self::Error> {
|
||||
// Only the HW CS is configured here. This is not really necessary, but showcases
|
||||
// that we could scale this multiple SPI devices.
|
||||
self.inner.cfg_hw_cs(self.hw_cs_id);
|
||||
self.inner.configure_hw_cs(self.hw_cs_id);
|
||||
for operation in operations {
|
||||
match operation {
|
||||
spi::Operation::Read(buf) => self.inner.read(buf),
|
||||
@@ -93,7 +93,7 @@ impl<Delay: DelayNs> SpiDevice for SpiWithHwCs<Delay> {
|
||||
spi::Operation::DelayNs(delay) => self.delay_provider.delay_ns(*delay),
|
||||
};
|
||||
}
|
||||
self.inner.cfg_hw_cs_disable();
|
||||
self.inner.disable_hw_cs();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ impl Button {
|
||||
irq_cfg: InterruptConfig,
|
||||
) {
|
||||
self.0.configure_edge_interrupt(edge_type);
|
||||
self.0.enable_interrupt(irq_cfg);
|
||||
self.0.enable_interrupt(irq_cfg, true);
|
||||
}
|
||||
|
||||
/// Configures an IRQ on level.
|
||||
@@ -46,7 +46,7 @@ impl Button {
|
||||
irq_cfg: InterruptConfig,
|
||||
) {
|
||||
self.0.configure_level_interrupt(level);
|
||||
self.0.enable_interrupt(irq_cfg);
|
||||
self.0.enable_interrupt(irq_cfg, true);
|
||||
}
|
||||
|
||||
/// Configures a filter on the button. This can be useful for debouncing the switch.
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ work yet.
|
||||
After installation, you can run the following command
|
||||
|
||||
```sh
|
||||
probe-rs run --chip VA416xx_RAM --protocol jtag target/thumbv7em-none-eabihf/debug/examples/blinky
|
||||
probe-rs run --chip VA416xx_RAM --protocol jtag target/thumbv7em-none-eabihf/debug/blinky
|
||||
```
|
||||
|
||||
to flash and run the blinky program on the RAM. There is also a `VA416xx` chip target
|
||||
|
||||
@@ -61,8 +61,14 @@ async fn main(_spawner: Spawner) {
|
||||
let portg = PinsG::new(dp.portg);
|
||||
let mut led = Output::new(portg.pg5, PinState::Low);
|
||||
|
||||
let uarta =
|
||||
uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into());
|
||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
||||
uart::Bank::Uart0,
|
||||
&clocks,
|
||||
115200.Hz(),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uarta = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
|
||||
|
||||
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
||||
|
||||
@@ -59,8 +59,14 @@ async fn main(_spawner: Spawner) {
|
||||
let pinsg = PinsG::new(dp.portg);
|
||||
let mut led = Output::new(pinsg.pg5, PinState::Low);
|
||||
|
||||
let uarta =
|
||||
uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into());
|
||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
||||
uart::Bank::Uart0,
|
||||
&clocks,
|
||||
115200.Hz(),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uarta = uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, uart_config);
|
||||
let (tx, _rx) = uarta.split();
|
||||
let mut async_tx = TxAsync::new(tx);
|
||||
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||
|
||||
@@ -67,13 +67,14 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
let portg = PinsG::new(dp.portg);
|
||||
|
||||
let uart0 = uart::Uart::new_for_uart0(
|
||||
dp.uart0,
|
||||
portg.pg0,
|
||||
portg.pg1,
|
||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
||||
uart::Bank::Uart0,
|
||||
&clocks,
|
||||
Hertz::from_raw(BAUDRATE).into(),
|
||||
Hertz::from_raw(BAUDRATE),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uart0 = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
|
||||
let (mut tx, rx) = uart0.split();
|
||||
let mut rx = rx.into_rx_with_irq();
|
||||
rx.start();
|
||||
|
||||
@@ -41,7 +41,7 @@ mod app {
|
||||
.xtal_n_clk_with_src_freq(EXTCLK_FREQ)
|
||||
.freeze()
|
||||
.unwrap();
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().to_raw());
|
||||
let pinsg = PinsG::new(cx.device.portg);
|
||||
let led = Output::new(pinsg.pg5, PinState::Low);
|
||||
blinky::spawn().ok();
|
||||
|
||||
@@ -22,23 +22,9 @@ va416xx = { version = "0.5", path = "../../va416xx" }
|
||||
|
||||
[dependencies.vorago-peb1]
|
||||
path = "../../vorago-peb1"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["va41630"]
|
||||
va41630 = ["va416xx-hal/va41630", "has-adc-dac"]
|
||||
va41629 = ["va416xx-hal/va41629", "has-adc-dac"]
|
||||
va41630 = ["va416xx-hal/va41630"]
|
||||
va41629 = ["va416xx-hal/va41629"]
|
||||
va41628 = ["va416xx-hal/va41628"]
|
||||
has-adc-dac = []
|
||||
|
||||
[[example]]
|
||||
name = "peb1-accelerometer"
|
||||
required-features = ["vorago-peb1"]
|
||||
|
||||
[[example]]
|
||||
name = "dac-adc"
|
||||
required-features = ["has-adc-dac"]
|
||||
|
||||
[[example]]
|
||||
name = "adc"
|
||||
required-features = ["has-adc-dac"]
|
||||
|
||||
+1
-2
@@ -15,7 +15,6 @@ use va416xx_hal::{
|
||||
clock::ClockConfigurator,
|
||||
i2c,
|
||||
pac::{self},
|
||||
prelude::*,
|
||||
timer::CountdownTimer,
|
||||
};
|
||||
use vorago_peb1::lis2dh12::{self, detect_i2c_addr, FullScale, Odr};
|
||||
@@ -29,7 +28,7 @@ const DISPLAY_MODE: DisplayMode = DisplayMode::Normalized;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let dp = pac::Peripherals::take().unwrap();
|
||||
defmt::println!("-- Vorago PEB1 accelerometer example --");
|
||||
// Use the external clock connected to XTAL_N.
|
||||
let clocks = ClockConfigurator::new(dp.clkgen)
|
||||
@@ -30,13 +30,14 @@ fn main() -> ! {
|
||||
|
||||
let gpiog = PinsG::new(dp.portg);
|
||||
|
||||
let uart0 = uart::Uart::new_for_uart0(
|
||||
dp.uart0,
|
||||
gpiog.pg0,
|
||||
gpiog.pg1,
|
||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
||||
uart::Bank::Uart0,
|
||||
&clocks,
|
||||
Hertz::from_raw(115200).into(),
|
||||
Hertz::from_raw(115200),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
let uart0 = uart::Uart::new_for_uart0(dp.uart0, gpiog.pg0, gpiog.pg1, uart_config);
|
||||
let (mut tx, mut rx) = uart0.split();
|
||||
writeln!(tx, "Hello World\n\r").unwrap();
|
||||
loop {
|
||||
@@ -40,7 +40,7 @@ fn main() -> ! {
|
||||
.freeze()
|
||||
.unwrap();
|
||||
enable_and_init_irq_router();
|
||||
let mut delay = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().raw());
|
||||
let mut delay = cortex_m::delay::Delay::new(cp.SYST, clocks.apb0().to_raw());
|
||||
|
||||
let mut last_interrupt_counter = 0;
|
||||
let mut wdt_ctrl = Wdt::start(dp.watch_dog, &clocks, WDT_ROLLOVER_MS);
|
||||
@@ -49,7 +49,7 @@ fn main() -> ! {
|
||||
let mut counter: u32 = 0;
|
||||
loop {
|
||||
counter = counter.wrapping_add(1);
|
||||
if counter % log_divisor == 0 {
|
||||
if counter.is_multiple_of(log_divisor) {
|
||||
defmt::info!("wdt example main loop alive");
|
||||
}
|
||||
if TEST_MODE != TestMode::AllowReset {
|
||||
@@ -10,6 +10,7 @@ defmt-rtt = "1"
|
||||
defmt = "1"
|
||||
panic-probe = { version = "1", features = ["defmt"] }
|
||||
static_cell = "2"
|
||||
fugit = "0.4"
|
||||
ringbuf = { version = "0.4", default-features = false }
|
||||
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
|
||||
satrs = { version = "0.3.0-alpha.3", default-features = false, features = ["defmt"] }
|
||||
|
||||
@@ -97,12 +97,12 @@ mod app {
|
||||
use arbitrary_int::{u11, u14};
|
||||
use cortex_m::asm;
|
||||
use embedded_io::Write;
|
||||
use rtic_monotonics::{fugit::ExtU32, Monotonic};
|
||||
// Import panic provider.
|
||||
use panic_probe as _;
|
||||
// Import logger.
|
||||
use defmt_rtt as _;
|
||||
use rtic::Mutex;
|
||||
use rtic_monotonics::systick::prelude::*;
|
||||
use satrs::pus::verification::VerificationReportCreator;
|
||||
use satrs::spacepackets::ecss::PusServiceId;
|
||||
use satrs::spacepackets::ecss::{
|
||||
@@ -167,13 +167,15 @@ mod app {
|
||||
|
||||
let gpiog = PinsG::new(cx.device.portg);
|
||||
|
||||
let uart0 = Uart::new_for_uart0(
|
||||
cx.device.uart0,
|
||||
gpiog.pg0,
|
||||
gpiog.pg1,
|
||||
let clock_config = uart::ClockConfig::calculate_with_clocks(
|
||||
uart::Bank::Uart0,
|
||||
&clocks,
|
||||
Hertz::from_raw(UART_BAUDRATE).into(),
|
||||
fugit::HertzU32::from_raw(UART_BAUDRATE),
|
||||
uart::BaudMode::_16,
|
||||
);
|
||||
let uart_config = uart::Config::new_with_clock_config(clock_config);
|
||||
|
||||
let uart0 = Uart::new_for_uart0(cx.device.uart0, gpiog.pg0, gpiog.pg1, uart_config);
|
||||
let (tx, rx) = uart0.split();
|
||||
|
||||
let verif_reporter = VerificationReportCreator::new(u11::new(0));
|
||||
@@ -192,7 +194,7 @@ mod app {
|
||||
.init(StaticRb::<usize, SIZES_RB_SIZE_TC>::default())
|
||||
.split_ref();
|
||||
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().raw());
|
||||
Mono::start(cx.core.SYST, clocks.sysclk().to_raw());
|
||||
CLOCKS.set(clocks).unwrap();
|
||||
|
||||
let mut rx = rx.into_rx_with_irq();
|
||||
|
||||
@@ -11,7 +11,7 @@ keywords = ["no-std", "hal", "cortex-m", "vorago", "va416xx"]
|
||||
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||
|
||||
[dependencies]
|
||||
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor4x"] }
|
||||
vorago-shared-hal = { version = "0.3", path = "../../vorago-shared-hal", features = ["vor4x"] }
|
||||
va416xx-hal = { version = "0.6", path = "../va416xx-hal" }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -15,18 +15,18 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
va416xx = { version = "0.5", path = "../va416xx", features = ["critical-section"], default-features = false }
|
||||
derive-mmio = "0.6.1"
|
||||
static_assertions = "1.1"
|
||||
vorago-shared-hal = { version = "0.2", path = "../../vorago-shared-hal", features = ["vor4x"] }
|
||||
vorago-shared-hal = { version = "0.3", path = "../../vorago-shared-hal", features = ["vor4x"] }
|
||||
|
||||
libm = "0.2"
|
||||
nb = "1"
|
||||
embedded-hal = "1"
|
||||
num_enum = { version = "0.7", default-features = false }
|
||||
bitflags = "2"
|
||||
bitbybit = "1.3"
|
||||
bitbybit = "2"
|
||||
arbitrary-int = "2"
|
||||
fugit = "0.3"
|
||||
fugit = "0.4"
|
||||
embedded-can = "0.4"
|
||||
embassy-sync = "0.7"
|
||||
embassy-sync = "0.8"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
|
||||
defmt = { version = "1", optional = true }
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
//! Analog to Digital Converter (ADC) driver.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [ADC and DAC example](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/dac-adc.rs)
|
||||
//! - [ADC](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/adc.rs)
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::clock::Clocks;
|
||||
|
||||
@@ -3,6 +3,8 @@ use core::{
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use arbitrary_int::u4;
|
||||
|
||||
use crate::can::regs::BufferState;
|
||||
|
||||
use super::{
|
||||
@@ -58,7 +60,7 @@ pub enum InterruptResult {
|
||||
pub enum InterruptError {
|
||||
UnexpectedError,
|
||||
InvalidInterruptId(StatusPending),
|
||||
InvalidStatus(u8),
|
||||
InvalidStatus(u4),
|
||||
UnexpectedState(BufferState),
|
||||
CanError(DiagnosticRegister),
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ impl CanChannelLowLevel {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_state(&self) -> Result<BufferState, u8> {
|
||||
pub fn read_state(&self) -> Result<BufferState, u4> {
|
||||
self.msg_buf.read_stat_ctrl().state()
|
||||
}
|
||||
|
||||
|
||||
@@ -150,18 +150,19 @@ impl ClockConfig {
|
||||
tseg2: u8,
|
||||
sjw: u8,
|
||||
) -> Result<ClockConfig, ClockConfigError> {
|
||||
if bitrate.raw() == 0 {
|
||||
if bitrate.to_raw() == 0 {
|
||||
return Err(ClockConfigError::BitrateIsZero);
|
||||
}
|
||||
let nominal_bit_time = 1 + tseg1 as u32 + tseg2 as u32;
|
||||
let prescaler =
|
||||
roundf(clocks.apb1().raw() as f32 / (bitrate.raw() as f32 * nominal_bit_time as f32))
|
||||
as u32;
|
||||
let prescaler = roundf(
|
||||
clocks.apb1().to_raw() as f32 / (bitrate.to_raw() as f32 * nominal_bit_time as f32),
|
||||
) as u32;
|
||||
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
|
||||
return Err(ClockConfigError::CanNotFindPrescaler);
|
||||
}
|
||||
|
||||
let actual_bitrate = (clocks.apb1().raw() as f32) / (prescaler * nominal_bit_time) as f32;
|
||||
let actual_bitrate =
|
||||
(clocks.apb1().to_raw() as f32) / (prescaler * nominal_bit_time) as f32;
|
||||
let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
|
||||
if bitrate_deviation > MAX_BITRATE_DEVIATION {
|
||||
return Err(ClockConfigError::BitrateErrorTooLarge);
|
||||
@@ -269,17 +270,17 @@ pub const fn calculate_nominal_bit_time(
|
||||
target_bitrate: Hertz,
|
||||
prescaler: u8,
|
||||
) -> u32 {
|
||||
apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32)
|
||||
apb1_clock.to_raw() / (target_bitrate.to_raw() * prescaler as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 {
|
||||
apb1_clock.raw() as f32 / (prescaler as u32 * nom_bit_time) as f32
|
||||
apb1_clock.to_raw() as f32 / (prescaler as u32 * nom_bit_time) as f32
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn calculate_bitrate_deviation(actual_bitrate: f32, target_bitrate: Hertz) -> f32 {
|
||||
(actual_bitrate - target_bitrate.raw() as f32).abs() / target_bitrate.raw() as f32
|
||||
(actual_bitrate - target_bitrate.to_raw() as f32).abs() / target_bitrate.to_raw() as f32
|
||||
}
|
||||
|
||||
pub trait CanInstance {
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
//!
|
||||
//! Calling [ClockConfigurator::freeze] returns the frozen clock configuration inside the [Clocks]
|
||||
//! structure. This structure can also be used to configure other structures provided by this HAL.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! - [UART example on the PEB1 board](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/uart.rs)
|
||||
#[cfg(not(feature = "va41628"))]
|
||||
use crate::adc::ADC_MAX_CLK;
|
||||
use crate::pac;
|
||||
@@ -98,9 +94,9 @@ pub struct PllConfig {
|
||||
pub const fn clock_after_division(clk: Hertz, div_sel: ClockDivisorSelect) -> Hertz {
|
||||
match div_sel {
|
||||
ClockDivisorSelect::Div1 => clk,
|
||||
ClockDivisorSelect::Div2 => Hertz::from_raw(clk.raw() / 2),
|
||||
ClockDivisorSelect::Div4 => Hertz::from_raw(clk.raw() / 4),
|
||||
ClockDivisorSelect::Div8 => Hertz::from_raw(clk.raw() / 8),
|
||||
ClockDivisorSelect::Div2 => Hertz::from_raw(clk.to_raw() / 2),
|
||||
ClockDivisorSelect::Div4 => Hertz::from_raw(clk.to_raw() / 4),
|
||||
ClockDivisorSelect::Div8 => Hertz::from_raw(clk.to_raw() / 8),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +382,7 @@ impl ClockConfigurator {
|
||||
// ADC clock (must be 2-12.5 MHz)
|
||||
// NOTE: Not using divide by 1 or /2 ratio in REVA silicon because of triggering issue
|
||||
// For this reason, keep SYSCLK above 8MHz to have the ADC /4 ratio in range)
|
||||
if final_sysclk.raw() <= ADC_MAX_CLK.raw() * 4 {
|
||||
if final_sysclk.to_raw() <= ADC_MAX_CLK.to_raw() * 4 {
|
||||
self.clkgen.ctrl1().modify(|_, w| unsafe {
|
||||
w.adc_clk_div_sel().bits(AdcClockDivisorSelect::Div4 as u8)
|
||||
});
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
//! Digital to Analog Converter (DAC) driver.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [ADC and DAC example](https://github.com/us-irs/vorago-rs/blob/main/va416xx/examples/simple/examples/dac-adc.rs)
|
||||
use core::ops::Deref;
|
||||
|
||||
use vorago_shared_hal::{
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
//! API for the DMA peripheral
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/dma.rs)
|
||||
use arbitrary_int::{u10, u3};
|
||||
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
||||
|
||||
|
||||
@@ -12,9 +12,4 @@
|
||||
//!
|
||||
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||
//! in a type-safe way.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/blinky.rs)
|
||||
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-gpio.rs)
|
||||
pub use vorago_shared_hal::gpio::*;
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
//! API for the I2C peripheral
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [PEB1 accelerometer example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/peb1-accelerometer.rs)
|
||||
pub use vorago_shared_hal::i2c::*;
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! The [examples folder](https://github.com/us-irs/vorago-rs/tree/main/va416xx/examples) contains
|
||||
//! various example applications using the HAL.
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#[cfg(feature = "alloc")]
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
//! API for Pulse-Width Modulation (PWM)
|
||||
//!
|
||||
//! The Vorago devices use the TIM peripherals to perform PWM related tasks
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/pwm.rs)
|
||||
pub use vorago_shared_hal::pwm::*;
|
||||
|
||||
@@ -3,9 +3,4 @@
|
||||
//! The main abstraction provided by this module is the [Spi] an structure.
|
||||
//! It provides the [SpiBus trait](https://docs.rs/embedded-hal/latest/embedded_hal/spi/trait.SpiBus.html),
|
||||
//! but also offer a low level interface via the [SpiLowLevel] trait.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/spi.rs)
|
||||
//! - [NVM library][crate::nvm]
|
||||
pub use vorago_shared_hal::spi::*;
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
//! API for the TIM peripherals
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/timer-ticks.rs)
|
||||
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/cascade.rs)
|
||||
pub use vorago_shared_hal::timer::*;
|
||||
|
||||
pub const TIM_IRQ_OFFSET: usize = 48;
|
||||
|
||||
@@ -6,12 +6,4 @@
|
||||
//!
|
||||
//! The [rx_async] and [tx_async] modules provide an asynchronous non-blocking API for the UART
|
||||
//! peripheral.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [UART simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/uart.rs)
|
||||
//! - [UART with IRQ and RTIC](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/rtic/src/bin/uart-echo-rtic.rs)
|
||||
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/flashloader)
|
||||
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-uart-rx.rs)
|
||||
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/embassy/src/bin/async-uart-tx.rs)
|
||||
pub use vorago_shared_hal::uart::*;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
//! # API for the Watchdog peripheral
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Watchdog simple example](https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/branch/main/va416xx/examples/simple/examples/wdt.rs)
|
||||
use vorago_shared_hal::{enable_peripheral_clock, reset_peripheral_for_cycles, PeripheralSelect};
|
||||
|
||||
use crate::time::Hertz;
|
||||
@@ -62,7 +58,7 @@ impl Wdt {
|
||||
|
||||
#[inline]
|
||||
pub fn set_freq(&mut self, freq_ms: u32) {
|
||||
let counter = (self.clock_freq.raw() / 1000) * freq_ms;
|
||||
let counter = (self.clock_freq.to_raw() / 1000) * freq_ms;
|
||||
self.wdt.wdogload().write(|w| unsafe { w.bits(counter) });
|
||||
}
|
||||
|
||||
|
||||
@@ -8,23 +8,34 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [unreleased]
|
||||
|
||||
## [v0.3.0] 2026-05-18
|
||||
|
||||
### Added
|
||||
|
||||
- Add `is_high` and `is_low` for `InputPinAsync`.
|
||||
- Add `InputPin` impl for `InputPinAsync`.
|
||||
- `HwCsPin` in SPI module for easer usage of HW CS pins as `Output` CS pins
|
||||
|
||||
### Changed
|
||||
|
||||
- Bumped `fugit` from v0.3 to v0.4
|
||||
- Added `RxWithInterrupt::steal`.
|
||||
- Renamed UART `Data` register `value` field to `data`
|
||||
- Improved type level support for resource management for SPI, PWM, UART.
|
||||
- Renamed `tx_asynch` and `rx_asynch` module name to `*_async`
|
||||
- Naming improvements in SPI module: replaced `cfg` by `config*`
|
||||
- UART configuration now expects an explicit clock configuration structure and does not
|
||||
calculate it itself anymore.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Pull::Up` and `Pull::High` were inverted.
|
||||
- Removed HW CS pin provider implementation for PA23, PA22 and PA21, which are multi HW CS pins.
|
||||
- Added missing `AnyPin` trait impl for Multi HW CS pins.
|
||||
- Expose inner `Input` pin for `InputPinAsync`.
|
||||
- Bugfix for UART clock calculation with 8x baud mode.
|
||||
- Possible bugfix for Asynch GPIO where the interrupt handler could become stuck in a loop.
|
||||
- Robustness improvements for the Asynch GPIO driver code.
|
||||
|
||||
## [v0.2.0] 2025-09-03
|
||||
|
||||
@@ -46,6 +57,7 @@ Renamed to `vorago-shared-hal`
|
||||
|
||||
Init commit.
|
||||
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/vorago-shared-hal/compare/v0.2.0...HEAD
|
||||
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/vorago-rs/vorago-shared-hal/compare/v0.3.0...HEAD
|
||||
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/vorago-rs/src/tag/vorago-shared-hal-v0.3.0
|
||||
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/vorago-shared-hal/compare/v0.1.0...v0.2.0
|
||||
[v0.1.0]: https://egit.irs.uni-stuttgart.de/rust/vorago-shared-hal/src/tag/v0.1.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vorago-shared-hal"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Peripheral HAL components shared between Vorago families"
|
||||
edition = "2024"
|
||||
homepage = "https://egit.irs.uni-stuttgart.de/rust/vorago-rs"
|
||||
@@ -25,11 +25,11 @@ embedded-io-async = "0.7"
|
||||
raw-slicee = "0.1"
|
||||
thiserror = { version = "2", default-features = false }
|
||||
paste = "1"
|
||||
fugit = "0.3"
|
||||
fugit = "0.4"
|
||||
defmt = { version = "1", optional = true }
|
||||
va108xx = { version = "0.6", path = "../va108xx/va108xx", default-features = false, optional = true }
|
||||
va416xx = { version = "0.5", path = "../va416xx/va416xx", default-features = false, optional = true }
|
||||
embassy-sync = "0.7"
|
||||
embassy-sync = "0.8"
|
||||
embassy-time-driver = "0.2"
|
||||
embassy-time-queue-utils = "0.3"
|
||||
once_cell = { version = "1", default-features = false, features = [
|
||||
|
||||
@@ -79,7 +79,9 @@ impl TimerDriver {
|
||||
let mut timekeeper_reg_block = unsafe { TimekeeperTim::ID.steal_regs() };
|
||||
let mut alarm_tim_reg_block = unsafe { AlarmTim::ID.steal_regs() };
|
||||
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||
SCALE.set((sysclk.raw() / TICK_HZ as u32) as u64).unwrap();
|
||||
SCALE
|
||||
.set((sysclk.to_raw() / TICK_HZ as u32) as u64)
|
||||
.unwrap();
|
||||
timekeeper_reg_block.write_reset_value(u32::MAX);
|
||||
// Decrementing counter.
|
||||
timekeeper_reg_block.write_count_value(u32::MAX);
|
||||
@@ -137,7 +139,7 @@ impl TimerDriver {
|
||||
// Initiate scale value here. This is required to convert timer ticks back to a timestamp.
|
||||
|
||||
SCALE
|
||||
.set((TimekeeperTim::clock(clocks).raw() / TICK_HZ as u32) as u64)
|
||||
.set((TimekeeperTim::clock(clocks).to_raw() / TICK_HZ as u32) as u64)
|
||||
.unwrap();
|
||||
timekeeper_regs.write_reset_value(u32::MAX);
|
||||
// Decrementing counter.
|
||||
|
||||
@@ -21,9 +21,6 @@ use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
|
||||
#[cfg(feature = "vor4x")]
|
||||
use super::ll::PortDoesNotSupportInterrupts;
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
use va108xx as pac;
|
||||
|
||||
pub use super::ll::InterruptEdge;
|
||||
use super::{
|
||||
Input, Port,
|
||||
@@ -118,7 +115,7 @@ pub fn on_interrupt_for_async_gpio_for_port(
|
||||
}
|
||||
|
||||
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
||||
let gpio = unsafe { port.steal_gpio() };
|
||||
let mut gpio = unsafe { port.steal_regs() };
|
||||
|
||||
let irq_enb = gpio.read_irq_enable();
|
||||
let edge_status = gpio.read_edge_status();
|
||||
@@ -134,18 +131,19 @@ fn on_interrupt_for_port(
|
||||
wakers: &'static [AtomicWaker],
|
||||
edge_detection: &'static [AtomicBool],
|
||||
) {
|
||||
// Check all enabled interrupts.
|
||||
while irq_enb != 0 {
|
||||
// For all enabled interrupts, check whether the corresponding edge detection has
|
||||
// triggered.
|
||||
let bit_pos = irq_enb.trailing_zeros() as usize;
|
||||
let bit_mask = 1 << bit_pos;
|
||||
|
||||
wakers[bit_pos].wake();
|
||||
|
||||
if edge_status & bit_mask != 0 {
|
||||
edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
wakers[bit_pos].wake();
|
||||
}
|
||||
// Clear the processed bit
|
||||
irq_enb &= !bit_mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,13 +161,12 @@ pub struct InputPinFuture {
|
||||
impl InputPinFuture {
|
||||
/// Create a new input pin future from mutable reference to an [Input] pin.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
|
||||
pub fn new_with_input_pin(pin: &mut Input, edge: InterruptEdge) -> Self {
|
||||
let (waker_group, edge_detection_group) =
|
||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge);
|
||||
#[cfg(feature = "vor1x")]
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
pin.enable_interrupt_gpio_only();
|
||||
Self {
|
||||
id: pin.id(),
|
||||
waker_group,
|
||||
@@ -186,7 +183,7 @@ impl InputPinFuture {
|
||||
let (waker_group, edge_detection_group) =
|
||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
pin.configure_edge_interrupt(edge);
|
||||
pin.enable_interrupt(true)?;
|
||||
pin.enable_interrupt_gpio_only();
|
||||
Ok(Self {
|
||||
id: pin.id(),
|
||||
waker_group,
|
||||
@@ -223,8 +220,6 @@ impl Future for InputPinFuture {
|
||||
/// Input pin which has additional asynchronous support.
|
||||
pub struct InputPinAsync {
|
||||
pin: Input,
|
||||
#[cfg(feature = "vor1x")]
|
||||
irq: va108xx::Interrupt,
|
||||
}
|
||||
|
||||
impl InputPinAsync {
|
||||
@@ -235,8 +230,10 @@ impl InputPinAsync {
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
pub fn new(mut pin: Input, irq_config: InterruptConfig) -> Self {
|
||||
// Do not enable GPIO interrupt bit yet.
|
||||
pin.enable_interrupt(irq_config, false);
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Create a new asynchronous input pin from an [Input] pin. The interrupt ID to be used must be
|
||||
@@ -246,10 +243,12 @@ impl InputPinAsync {
|
||||
/// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function
|
||||
/// for the asynchronous functionality to work.
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||
pub fn new(mut pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||
if pin.id().port() == Port::G {
|
||||
return Err(PortDoesNotSupportInterrupts);
|
||||
}
|
||||
// Do not enable GPIO interrupt bit yet.
|
||||
pin.enable_interrupt(true, false)?;
|
||||
Ok(Self { pin })
|
||||
}
|
||||
|
||||
@@ -259,8 +258,7 @@ impl InputPinAsync {
|
||||
pub async fn wait_for_high(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh);
|
||||
#[cfg(feature = "vor4x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap();
|
||||
@@ -300,8 +298,7 @@ impl InputPinAsync {
|
||||
pub async fn wait_for_low(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
let fut = InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow);
|
||||
#[cfg(feature = "vor4x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||
@@ -315,7 +312,7 @@ impl InputPinAsync {
|
||||
pub async fn wait_for_falling_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).await;
|
||||
#[cfg(feature = "vor4x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow)
|
||||
.unwrap()
|
||||
@@ -326,14 +323,14 @@ impl InputPinAsync {
|
||||
pub async fn wait_for_rising_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await;
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::LowToHigh).await;
|
||||
}
|
||||
|
||||
/// Asynchronously wait until the pin sees any edge (either rising or falling).
|
||||
pub async fn wait_for_any_edge(&mut self) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges).await;
|
||||
#[cfg(feature = "vor4x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
|
||||
@@ -384,32 +384,53 @@ impl LowLevelGpio {
|
||||
self.gpio.write_tog_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
if irq_cfg.route {
|
||||
self.configure_irqsel(irq_cfg.id);
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
/// Only enabled GPIO peripheral interrupt bit without enabling the interrupt in NVIC
|
||||
/// or routing it in the IRQSEL peripheral for VA108xx devices.
|
||||
#[inline]
|
||||
pub fn enable_interrupt_gpio_only(&mut self) {
|
||||
self.gpio.modify_irq_enable(|mut value| {
|
||||
value |= 1 << self.id.offset;
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
/// Depending on the configuration parameters, does the following:
|
||||
///
|
||||
/// - Routes the interrupt in the IRQSEL peripheral
|
||||
/// - Enables the interrupt in the NVIC,
|
||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
||||
if irq_cfg.route {
|
||||
self.configure_irqsel(irq_cfg.id);
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
if gpio {
|
||||
self.enable_interrupt_gpio_only();
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on the configuration parameters, does the following:
|
||||
///
|
||||
/// - Enables the interrupt in the NVIC,
|
||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn enable_interrupt(
|
||||
&mut self,
|
||||
enable_in_nvic: bool,
|
||||
gpio: bool,
|
||||
) -> Result<(), PortDoesNotSupportInterrupts> {
|
||||
if self.id().port() == Port::G {
|
||||
return Err(PortDoesNotSupportInterrupts);
|
||||
}
|
||||
if enable_in_nvic {
|
||||
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
||||
}
|
||||
self.gpio.modify_irq_enable(|mut value| {
|
||||
value |= 1 << self.id.offset;
|
||||
value
|
||||
});
|
||||
if gpio {
|
||||
self.enable_interrupt_gpio_only();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -132,19 +132,34 @@ impl Input {
|
||||
self.0.id()
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
self.0.enable_interrupt(irq_cfg);
|
||||
pub fn enable_interrupt_gpio_only(&mut self) {
|
||||
self.0.enable_interrupt_gpio_only();
|
||||
}
|
||||
|
||||
/// Depending on the configuration parameters, does the following:
|
||||
///
|
||||
/// - Routes the interrupt in the IRQSEL peripheral
|
||||
/// - Enables the interrupt in the NVIC,
|
||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||
#[cfg(feature = "vor1x")]
|
||||
#[inline]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig, gpio: bool) {
|
||||
self.0.enable_interrupt(irq_cfg, gpio);
|
||||
}
|
||||
|
||||
/// Depending on the configuration parameters, does the following:
|
||||
///
|
||||
/// - Enables the interrupt in the NVIC,
|
||||
/// - Enable the GPIO peripheral interrupt bit for this pin if configured.
|
||||
#[cfg(feature = "vor4x")]
|
||||
#[inline]
|
||||
pub fn enable_interrupt(
|
||||
&mut self,
|
||||
enable_in_nvic: bool,
|
||||
gpio: bool,
|
||||
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
||||
self.0.enable_interrupt(enable_in_nvic)
|
||||
self.0.enable_interrupt(enable_in_nvic, gpio)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -58,7 +58,8 @@ pub struct Gpio {
|
||||
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
||||
#[mmio(PureRead)]
|
||||
irq_status: u32,
|
||||
#[mmio(PureRead)]
|
||||
// Reading this register clears it.
|
||||
#[mmio(Read)]
|
||||
edge_status: u32,
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
|
||||
@@ -149,12 +149,12 @@ fn calc_clk_div_generic(
|
||||
speed_mode: I2cSpeed,
|
||||
) -> Result<u8, ClockTooSlowForFastI2cError> {
|
||||
if speed_mode == I2cSpeed::Regular100khz {
|
||||
Ok(((ref_clk.raw() / CLK_100K.raw() / 20) - 1) as u8)
|
||||
Ok(((ref_clk.to_raw() / CLK_100K.to_raw() / 20) - 1) as u8)
|
||||
} else {
|
||||
if ref_clk.raw() < MIN_CLK_400K.raw() {
|
||||
if ref_clk.to_raw() < MIN_CLK_400K.to_raw() {
|
||||
return Err(ClockTooSlowForFastI2cError);
|
||||
}
|
||||
Ok(((ref_clk.raw() / CLK_400K.raw() / 25) - 1) as u8)
|
||||
Ok(((ref_clk.to_raw() / CLK_400K.to_raw() / 25) - 1) as u8)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ pub enum FilterClockSelect {
|
||||
#[bitbybit::bitenum(u1, exhaustive = true)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Pull {
|
||||
Up = 0,
|
||||
Down = 1,
|
||||
Down = 0,
|
||||
Up = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
||||
@@ -137,7 +137,7 @@ impl Port {
|
||||
/// # Safety
|
||||
///
|
||||
/// Circumvents ownership and safety guarantees by the HAL.
|
||||
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
|
||||
pub unsafe fn steal_regs(&self) -> gpio::regs::MmioGpio<'static> {
|
||||
gpio::regs::Gpio::new_mmio(*self)
|
||||
}
|
||||
}
|
||||
@@ -225,4 +225,11 @@ pub(crate) mod shared {
|
||||
#[bit(0, w)]
|
||||
rx_fifo: bool,
|
||||
}
|
||||
|
||||
impl FifoClear {
|
||||
pub const ALL: Self = Self::builder()
|
||||
.with_tx_fifo(true)
|
||||
.with_rx_fifo(true)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,10 +164,10 @@ impl<Mode> PwmPin<Mode> {
|
||||
pub fn set_period(&mut self, period: impl Into<Hertz>) {
|
||||
self.current_period = period.into();
|
||||
// Avoid division by 0
|
||||
if self.current_period.raw() == 0 {
|
||||
if self.current_period.to_raw() == 0 {
|
||||
return;
|
||||
}
|
||||
self.current_rst_val = self.ref_clk.raw() / self.current_period.raw();
|
||||
self.current_rst_val = self.ref_clk.to_raw() / self.current_period.to_raw();
|
||||
self.regs.write_reset_value(self.current_rst_val);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,661 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use arbitrary_int::u5;
|
||||
use critical_section::Mutex;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use portable_atomic::AtomicBool;
|
||||
use raw_slice::{RawBufSlice, RawBufSliceMut};
|
||||
|
||||
use crate::{
|
||||
shared::{FifoClear, TriggerLevel},
|
||||
spi::{
|
||||
FIFO_DEPTH,
|
||||
regs::{Data, InterruptClear, InterruptControl},
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub const NUM_SPIS: usize = 3;
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub const NUM_SPIS: usize = 4;
|
||||
|
||||
static WAKERS: [AtomicWaker; NUM_SPIS] = [const { AtomicWaker::new() }; NUM_SPIS];
|
||||
static TRANSFER_CONTEXTS: [Mutex<RefCell<TransferContext>>; NUM_SPIS] =
|
||||
[const { Mutex::new(RefCell::new(TransferContext::new())) }; NUM_SPIS];
|
||||
// Completion flag. Kept outside of the context structure as an atomic to avoid
|
||||
// critical section.
|
||||
static DONE: [AtomicBool; NUM_SPIS] = [const { AtomicBool::new(false) }; NUM_SPIS];
|
||||
|
||||
#[derive(Debug, Clone, Copy, thiserror::Error)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("SPI RX FIFO overrun")]
|
||||
pub struct RxOverrunError;
|
||||
|
||||
impl embedded_hal_async::spi::Error for RxOverrunError {
|
||||
fn kind(&self) -> embedded_hal::spi::ErrorKind {
|
||||
embedded_hal::spi::ErrorKind::Overrun
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a generic interrupt handler to handle asynchronous SPI operations for a given
|
||||
/// SPI peripheral.
|
||||
///
|
||||
/// The user has to call this once in the interrupt handler responsible for the SPI interrupts on
|
||||
/// the given SPI bank.
|
||||
pub fn on_interrupt(peripheral: super::Bank) {
|
||||
let mut spi = unsafe { peripheral.steal_regs() };
|
||||
let index = peripheral as usize;
|
||||
let enabled_irqs = spi.read_interrupt_control();
|
||||
let interrupt_status = spi.read_interrupt_status();
|
||||
spi.write_interrupt_clear(InterruptClear::ALL);
|
||||
// Prevent spurious interrupts from messing with out logic here.
|
||||
spi.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||
// IRQ is not related.
|
||||
if enabled_irqs.raw_value() == 0 {
|
||||
reset_trigger_levels(&mut spi);
|
||||
spi.write_fifo_clear(FifoClear::ALL);
|
||||
return;
|
||||
}
|
||||
if interrupt_status.rx_overrun() {
|
||||
// Not sure how to otherwise handle this cleanly..
|
||||
return handle_rx_overrun(&mut spi, index);
|
||||
}
|
||||
let mut context = critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||
*context_ref.borrow()
|
||||
});
|
||||
// No transfer active.
|
||||
if context.transfer_type.is_none() {
|
||||
return;
|
||||
}
|
||||
let transfer_type = context.transfer_type.unwrap();
|
||||
match transfer_type {
|
||||
TransferType::Read => on_interrupt_read(index, &mut context, &mut spi, enabled_irqs),
|
||||
TransferType::Write => on_interrupt_write(index, &mut context, &mut spi, enabled_irqs),
|
||||
TransferType::Transfer => {
|
||||
on_interrupt_transfer(index, &mut context, &mut spi, enabled_irqs)
|
||||
}
|
||||
TransferType::TransferInPlace => {
|
||||
on_interrupt_transfer_in_place(index, &mut context, &mut spi, enabled_irqs)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_rx_overrun(spi: &mut super::regs::MmioSpi<'static>, idx: usize) {
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
context_ref.borrow_mut().rx_overrun = true;
|
||||
});
|
||||
// Clean up, restore clean state.
|
||||
reset_trigger_levels(spi);
|
||||
spi.write_fifo_clear(FifoClear::ALL);
|
||||
// Interrupts were already disabled and cleared.
|
||||
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
WAKERS[idx].wake();
|
||||
}
|
||||
|
||||
fn on_interrupt_read(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
enabled_irqs: InterruptControl,
|
||||
) {
|
||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let transfer_len = read_slice.len();
|
||||
|
||||
// Read data from RX FIFO first.
|
||||
while spi.read_status().rx_not_empty() {
|
||||
let data = spi.read_data();
|
||||
if context.rx_progress < transfer_len {
|
||||
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||
context.rx_progress += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The FIFO still needs to be pumped.
|
||||
while context.tx_progress < read_slice.len() && spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(0));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||
}
|
||||
|
||||
fn on_interrupt_write(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
enabled_irqs: InterruptControl,
|
||||
) {
|
||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||
let transfer_len = write_slice.len();
|
||||
|
||||
// Read data from RX FIFO first.
|
||||
while spi.read_status().rx_not_empty() {
|
||||
spi.read_data();
|
||||
if context.rx_progress < transfer_len {
|
||||
context.rx_progress += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Data still needs to be sent
|
||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
write_slice[context.tx_progress] as u32,
|
||||
));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||
}
|
||||
|
||||
fn on_interrupt_transfer(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
enabled_irqs: InterruptControl,
|
||||
) {
|
||||
let read_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let read_len = read_slice.len();
|
||||
let write_slice = unsafe { context.tx_slice.get().unwrap() };
|
||||
let write_len = write_slice.len();
|
||||
let transfer_len = core::cmp::max(read_len, write_len);
|
||||
|
||||
// Send data first to avoid overwriting data that still needs to be sent.
|
||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
write_slice.get(context.tx_progress).copied().unwrap_or(0) as u32,
|
||||
));
|
||||
// Always increment this.
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
|
||||
// Read data from RX FIFO.
|
||||
while spi.read_status().rx_not_empty() {
|
||||
let data = spi.read_data();
|
||||
if context.rx_progress < read_len {
|
||||
read_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||
}
|
||||
// Always increment this.
|
||||
context.rx_progress += 1;
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||
}
|
||||
|
||||
fn on_interrupt_transfer_in_place(
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
enabled_irqs: InterruptControl,
|
||||
) {
|
||||
let transfer_slice = unsafe { context.rx_slice.get_mut().unwrap() };
|
||||
let transfer_len = transfer_slice.len();
|
||||
// Send data first to avoid overwriting data that still needs to be sent.
|
||||
while context.tx_progress < transfer_len && spi.read_status().tx_not_full() {
|
||||
spi.write_data(Data::new_with_raw_value(
|
||||
transfer_slice[context.tx_progress] as u32,
|
||||
));
|
||||
context.tx_progress += 1;
|
||||
}
|
||||
// Read data from RX FIFO.
|
||||
while spi.read_status().rx_not_empty() {
|
||||
let data = spi.read_data();
|
||||
if context.rx_progress < transfer_len {
|
||||
transfer_slice[context.rx_progress] = (data.data() & 0xFF) as u8;
|
||||
context.rx_progress += 1;
|
||||
}
|
||||
}
|
||||
|
||||
isr_finish_handler(idx, spi, context, transfer_len, enabled_irqs)
|
||||
}
|
||||
|
||||
/// Generic handler after RX FIFO and TX FIFO were handled. Checks and handles finished
|
||||
/// and unfinished conditions.
|
||||
fn isr_finish_handler(
|
||||
idx: usize,
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
context: &mut TransferContext,
|
||||
transfer_len: usize,
|
||||
enabled: InterruptControl,
|
||||
) {
|
||||
// Transfer finish condition.
|
||||
if context.rx_progress == context.tx_progress && context.rx_progress == transfer_len {
|
||||
finish_transfer(spi, idx, context);
|
||||
return;
|
||||
}
|
||||
// If the transfer is done, the context structure was already written back.
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = *context;
|
||||
});
|
||||
unfinished_transfer(spi, transfer_len, context, enabled);
|
||||
}
|
||||
|
||||
fn finish_transfer(
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
idx: usize,
|
||||
context: &mut TransferContext,
|
||||
) {
|
||||
// Write back updated context structure.
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[idx].borrow(cs);
|
||||
*context_ref.borrow_mut() = *context;
|
||||
});
|
||||
// Clean up, restore clean state.
|
||||
reset_trigger_levels(spi);
|
||||
spi.write_fifo_clear(FifoClear::ALL);
|
||||
// Interrupts were already disabled and cleared.
|
||||
DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed);
|
||||
WAKERS[idx].wake();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn unfinished_transfer(
|
||||
spi: &mut super::regs::MmioSpi<'static>,
|
||||
transfer_len: usize,
|
||||
context: &TransferContext,
|
||||
enabled_irqs: InterruptControl,
|
||||
) {
|
||||
// Take 8 as a conservative value to make sure that the FIFO does not overflow even if there
|
||||
// is a significant delay between the interrupt being triggered and the handler being executed.
|
||||
let new_trig_level = core::cmp::min(8, transfer_len - context.rx_progress);
|
||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(new_trig_level as u8)));
|
||||
|
||||
// If TX was already enabled and the transfer is finished, stop enabling it. Otherwise, we can
|
||||
// become stuck in an interrupt loop. In any other case, enable it. I am not fully sure
|
||||
// why this is necessary and why we can not stop interrupts as soon as we have the full
|
||||
// TX progress, but tests with ADCs have shown that not doing this causes timeouts.
|
||||
let enable_tx = !(enabled_irqs.tx() && context.tx_progress == transfer_len);
|
||||
|
||||
// Re-enable interrupts with the new RX FIFO trigger level.
|
||||
spi.write_interrupt_control(
|
||||
InterruptControl::builder()
|
||||
.with_tx(enable_tx)
|
||||
.with_rx(true)
|
||||
.with_rx_timeout(true)
|
||||
.with_rx_overrun(true)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset_trigger_levels(spi: &mut super::regs::MmioSpi<'static>) {
|
||||
spi.write_rx_fifo_trigger(TriggerLevel::new(u5::new(0x08)));
|
||||
spi.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0x00)));
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TransferType {
|
||||
Read,
|
||||
Write,
|
||||
Transfer,
|
||||
TransferInPlace,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct TransferContext {
|
||||
transfer_type: Option<TransferType>,
|
||||
tx_progress: usize,
|
||||
rx_progress: usize,
|
||||
tx_slice: RawBufSlice,
|
||||
rx_slice: RawBufSliceMut,
|
||||
rx_overrun: bool,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl TransferContext {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
transfer_type: None,
|
||||
tx_progress: 0,
|
||||
rx_progress: 0,
|
||||
tx_slice: RawBufSlice::new_nulled(),
|
||||
rx_slice: RawBufSliceMut::new_nulled(),
|
||||
rx_overrun: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpiFuture<'spi> {
|
||||
bank: super::Bank,
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
finished_regularly: core::cell::Cell<bool>,
|
||||
}
|
||||
|
||||
impl<'spi> SpiFuture<'spi> {
|
||||
fn new_for_read(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &mut [u8]) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("words length unexpectedly 0");
|
||||
}
|
||||
Self::generic_init_transfer(spi, bank);
|
||||
|
||||
let write_index = core::cmp::min(super::FIFO_DEPTH, words.len());
|
||||
// Send dummy bytes.
|
||||
(0..write_index).for_each(|_| {
|
||||
spi.regs.write_data(Data::new_with_raw_value(0));
|
||||
});
|
||||
|
||||
Self::set_triggers(spi, write_index, words.len());
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Read);
|
||||
unsafe {
|
||||
context.rx_slice.set(words);
|
||||
}
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_index;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs.write_interrupt_control(
|
||||
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||
);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_write(spi: &'spi mut super::Spi<u8>, bank: super::Bank, words: &[u8]) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("words length unexpectedly 0");
|
||||
}
|
||||
let index = bank as usize;
|
||||
let write_index = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Write);
|
||||
unsafe {
|
||||
context.tx_slice.set(words);
|
||||
}
|
||||
context.rx_slice.set_null();
|
||||
context.tx_progress = write_index;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs.write_interrupt_control(
|
||||
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||
);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_transfer(
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
bank: super::Bank,
|
||||
read: &mut [u8],
|
||||
write: &[u8],
|
||||
) -> Self {
|
||||
if read.is_empty() || write.is_empty() {
|
||||
panic!("read or write buffer unexpectedly empty");
|
||||
}
|
||||
let index = bank as usize;
|
||||
let full_write_len = core::cmp::max(read.len(), write.len());
|
||||
let fifo_prefill = core::cmp::min(super::FIFO_DEPTH, full_write_len);
|
||||
|
||||
Self::generic_init_transfer(spi, bank);
|
||||
|
||||
for write_index in 0..fifo_prefill {
|
||||
let value = write.get(write_index).copied().unwrap_or(0);
|
||||
spi.regs.write_data(Data::new_with_raw_value(value as u32));
|
||||
}
|
||||
|
||||
Self::set_triggers(spi, fifo_prefill, full_write_len);
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[index].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::Transfer);
|
||||
unsafe {
|
||||
context.tx_slice.set(write);
|
||||
context.rx_slice.set(read);
|
||||
}
|
||||
context.tx_progress = fifo_prefill;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs.write_interrupt_control(
|
||||
InterruptControl::ENABLE_ALL.with_tx(fifo_prefill > FIFO_DEPTH),
|
||||
);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_for_transfer_in_place(
|
||||
spi: &'spi mut super::Spi<u8>,
|
||||
bank: super::Bank,
|
||||
words: &mut [u8],
|
||||
) -> Self {
|
||||
if words.is_empty() {
|
||||
panic!("read and write buffer unexpectedly empty");
|
||||
}
|
||||
let write_idx = Self::generic_init_transfer_write_transfer_in_place(spi, bank, words);
|
||||
critical_section::with(|cs| {
|
||||
let context_ref = TRANSFER_CONTEXTS[bank as usize].borrow(cs);
|
||||
let mut context = context_ref.borrow_mut();
|
||||
context.transfer_type = Some(TransferType::TransferInPlace);
|
||||
unsafe {
|
||||
context.rx_slice.set(words);
|
||||
}
|
||||
context.tx_slice.set_null();
|
||||
context.tx_progress = write_idx;
|
||||
context.rx_progress = 0;
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
spi.regs.write_interrupt_control(
|
||||
InterruptControl::ENABLE_ALL.with_tx(words.len() > FIFO_DEPTH),
|
||||
);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(false));
|
||||
});
|
||||
Self {
|
||||
bank,
|
||||
spi,
|
||||
finished_regularly: core::cell::Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
fn generic_init_transfer(spi: &mut super::Spi<u8>, bank: super::Bank) {
|
||||
let idx = bank as usize;
|
||||
DONE[idx].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
spi.regs
|
||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||
spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||
spi.regs.modify_ctrl1(|v| v.with_mtxpause(true));
|
||||
}
|
||||
|
||||
// Returns amount of bytes written to FIFO.
|
||||
fn generic_init_transfer_write_transfer_in_place(
|
||||
spi: &mut super::Spi<u8>,
|
||||
bank: super::Bank,
|
||||
write: &[u8],
|
||||
) -> usize {
|
||||
Self::generic_init_transfer(spi, bank);
|
||||
|
||||
let write_idx = core::cmp::min(super::FIFO_DEPTH, write.len());
|
||||
(0..write_idx).for_each(|idx| {
|
||||
spi.regs
|
||||
.write_data(Data::new_with_raw_value(write[idx] as u32));
|
||||
});
|
||||
|
||||
Self::set_triggers(spi, write_idx, write.len());
|
||||
write_idx
|
||||
}
|
||||
|
||||
fn set_triggers(spi: &mut super::Spi<u8>, fifo_prefill: usize, write_len: usize) {
|
||||
spi.regs
|
||||
.write_rx_fifo_trigger(TriggerLevel::new(u5::new(core::cmp::min(
|
||||
fifo_prefill,
|
||||
FIFO_DEPTH / 2,
|
||||
) as u8)));
|
||||
// We want to re-fill the TX FIFO before it is completely empty if the full transfer size
|
||||
// is larger than the FIFO depth. Otherwise, set it to 0. Not exactly sure what that does,
|
||||
// but we do not enable interrupts anyway.
|
||||
if write_len > super::FIFO_DEPTH {
|
||||
spi.regs
|
||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(8)));
|
||||
} else {
|
||||
spi.regs
|
||||
.write_tx_fifo_trigger(TriggerLevel::new(u5::new(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'spi> Future for SpiFuture<'spi> {
|
||||
type Output = Result<(), RxOverrunError>;
|
||||
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
WAKERS[self.bank as usize].register(cx.waker());
|
||||
if DONE[self.bank as usize].swap(false, core::sync::atomic::Ordering::Relaxed) {
|
||||
let rx_overrun = critical_section::with(|cs| {
|
||||
let mut ctx = TRANSFER_CONTEXTS[self.bank as usize]
|
||||
.borrow(cs)
|
||||
.borrow_mut();
|
||||
let overrun = ctx.rx_overrun;
|
||||
*ctx = TransferContext::default();
|
||||
overrun
|
||||
});
|
||||
self.finished_regularly.set(true);
|
||||
if rx_overrun {
|
||||
return core::task::Poll::Ready(Err(RxOverrunError));
|
||||
}
|
||||
return core::task::Poll::Ready(Ok(()));
|
||||
}
|
||||
core::task::Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
impl<'spi> Drop for SpiFuture<'spi> {
|
||||
fn drop(&mut self) {
|
||||
if !self.finished_regularly.get() {
|
||||
// It might be sufficient to disable and enable the SPI.. But this definitely
|
||||
// ensures the SPI is fully reset.
|
||||
self.spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
self.spi
|
||||
.regs
|
||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||
self.spi.regs.write_fifo_clear(FifoClear::ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronous SPI driver.
|
||||
///
|
||||
/// This is the primary data structure used to perform non-blocking SPI operations.
|
||||
/// It implements the [embedded_hal_async::spi::SpiBus] as well.
|
||||
pub struct SpiAsync(pub super::Spi<u8>);
|
||||
|
||||
impl SpiAsync {
|
||||
pub fn new(
|
||||
mut spi: super::Spi<u8>,
|
||||
#[cfg(feature = "vor1x")] opt_irq_cfg: Option<crate::InterruptConfig>,
|
||||
) -> Self {
|
||||
#[cfg(feature = "vor1x")]
|
||||
if let Some(irq_cfg) = opt_irq_cfg {
|
||||
spi.regs
|
||||
.write_interrupt_control(InterruptControl::DISABLE_ALL);
|
||||
spi.regs.write_interrupt_clear(InterruptClear::ALL);
|
||||
if irq_cfg.route {
|
||||
crate::enable_peripheral_clock(crate::PeripheralSelect::Irqsel);
|
||||
unsafe { va108xx::Irqsel::steal() }
|
||||
.spi(spi.id as usize)
|
||||
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
// Safety: User has specifically configured this.
|
||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
}
|
||||
// Disable blockmode for asynchronous mode.
|
||||
spi.regs
|
||||
.modify_ctrl1(|v| v.with_bm_stall(false).with_blockmode(false));
|
||||
Self(spi)
|
||||
}
|
||||
|
||||
fn read(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_read(&mut self.0, id, words))
|
||||
}
|
||||
|
||||
fn write(&mut self, words: &[u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_write(&mut self.0, id, words))
|
||||
}
|
||||
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Option<SpiFuture<'_>> {
|
||||
if read.is_empty() || write.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_transfer(&mut self.0, id, read, write))
|
||||
}
|
||||
|
||||
fn transfer_in_place(&mut self, words: &mut [u8]) -> Option<SpiFuture<'_>> {
|
||||
if words.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let id = self.0.id;
|
||||
Some(SpiFuture::new_for_transfer_in_place(&mut self.0, id, words))
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal_async::spi::ErrorType for SpiAsync {
|
||||
type Error = RxOverrunError;
|
||||
}
|
||||
|
||||
impl embedded_hal_async::spi::SpiBus for SpiAsync {
|
||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
self.read(words).unwrap().await
|
||||
}
|
||||
|
||||
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
self.write(words).unwrap().await
|
||||
}
|
||||
|
||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
if read.is_empty() && write.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
self.transfer(read, write).unwrap().await
|
||||
}
|
||||
|
||||
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
if words.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
self.transfer_in_place(words).unwrap().await
|
||||
}
|
||||
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,11 @@ use va416xx as pac;
|
||||
|
||||
pub use regs::{Bank, HwChipSelectId};
|
||||
|
||||
pub mod asynch;
|
||||
pub mod regs;
|
||||
|
||||
pub const FIFO_DEPTH: usize = 16;
|
||||
|
||||
pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
|
||||
IoPeriphPin::new(P::ID, P::FUN_SEL, None);
|
||||
P::CS_ID
|
||||
@@ -496,11 +499,11 @@ pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
|
||||
}
|
||||
|
||||
// Step 1: Calculate raw divider.
|
||||
let raw_div = sys_clk.raw() / spi_clk.raw();
|
||||
let remainder = sys_clk.raw() % spi_clk.raw();
|
||||
let raw_div = sys_clk.to_raw() / spi_clk.to_raw();
|
||||
let remainder = sys_clk.to_raw() % spi_clk.to_raw();
|
||||
|
||||
// Step 2: Round up if necessary.
|
||||
let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
|
||||
let mut rounded_div = if remainder * 2 >= spi_clk.to_raw() {
|
||||
raw_div + 1
|
||||
} else {
|
||||
raw_div
|
||||
@@ -520,7 +523,6 @@ pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
|
||||
pub struct Spi<Word = u8> {
|
||||
id: Bank,
|
||||
regs: regs::MmioSpi<'static>,
|
||||
cfg: SpiConfig,
|
||||
/// Fill word for read-only SPI transactions.
|
||||
fill_word: Word,
|
||||
blockmode: bool,
|
||||
@@ -653,7 +655,6 @@ where
|
||||
Spi {
|
||||
id: spi_sel,
|
||||
regs: regs::Spi::new_mmio(spi_sel),
|
||||
cfg: spi_cfg,
|
||||
fill_word: Default::default(),
|
||||
bmstall: spi_cfg.bmstall,
|
||||
blockmode: spi_cfg.blockmode,
|
||||
@@ -676,14 +677,14 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
|
||||
pub fn configure_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
|
||||
let val = spi_clk_config_from_div(div)?;
|
||||
self.cfg_clock(val);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cfg_mode(&mut self, mode: Mode) {
|
||||
pub fn configure_mode(&mut self, mode: Mode) {
|
||||
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
|
||||
self.regs.modify_ctrl0(|mut value| {
|
||||
value.set_spo(cpo_bit);
|
||||
@@ -718,7 +719,7 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn perid(&self) -> u32 {
|
||||
pub fn peripheral_id(&self) -> u32 {
|
||||
self.regs.read_perid()
|
||||
}
|
||||
|
||||
@@ -728,7 +729,7 @@ where
|
||||
/// by using the [configure_pin_as_hw_cs_pin] function which also returns the
|
||||
/// corresponding [HwChipSelectId].
|
||||
#[inline]
|
||||
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId) {
|
||||
pub fn configure_hw_cs(&mut self, hw_cs: HwChipSelectId) {
|
||||
self.regs.modify_ctrl1(|mut value| {
|
||||
value.set_sod(false);
|
||||
value.set_ss(hw_cs);
|
||||
@@ -739,7 +740,7 @@ where
|
||||
/// Disables the hardware chip select functionality. This can be used when performing
|
||||
/// external chip select handling, for example with GPIO pins.
|
||||
#[inline]
|
||||
pub fn cfg_hw_cs_disable(&mut self) {
|
||||
pub fn disable_hw_cs(&mut self) {
|
||||
self.regs.modify_ctrl1(|mut value| {
|
||||
value.set_sod(true);
|
||||
value
|
||||
@@ -749,12 +750,12 @@ where
|
||||
/// Utility function to configure all relevant transfer parameters in one go.
|
||||
/// This is useful if multiple devices with different clock and mode configurations
|
||||
/// are connected to one bus.
|
||||
pub fn cfg_transfer(&mut self, transfer_cfg: &TransferConfig) {
|
||||
pub fn configure_transfer(&mut self, transfer_cfg: &TransferConfig) {
|
||||
if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
|
||||
self.cfg_clock(trans_clk_div);
|
||||
}
|
||||
if let Some(mode) = transfer_cfg.mode {
|
||||
self.cfg_mode(mode);
|
||||
self.configure_mode(mode);
|
||||
}
|
||||
self.blockmode = transfer_cfg.blockmode;
|
||||
self.regs.modify_ctrl1(|mut value| {
|
||||
@@ -1031,7 +1032,6 @@ impl From<Spi<u8>> for Spi<u16> {
|
||||
Spi {
|
||||
id: old_spi.id,
|
||||
regs: old_spi.regs,
|
||||
cfg: old_spi.cfg,
|
||||
blockmode: old_spi.blockmode,
|
||||
fill_word: Default::default(),
|
||||
bmstall: old_spi.bmstall,
|
||||
@@ -1049,7 +1049,6 @@ impl From<Spi<u16>> for Spi<u8> {
|
||||
Spi {
|
||||
id: old_spi.id,
|
||||
regs: old_spi.regs,
|
||||
cfg: old_spi.cfg,
|
||||
blockmode: old_spi.blockmode,
|
||||
fill_word: Default::default(),
|
||||
bmstall: old_spi.bmstall,
|
||||
@@ -1057,3 +1056,39 @@ impl From<Spi<u16>> for Spi<u8> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This abstraction which can be used to map a hardware chip select pin
|
||||
/// to [embedded_hal::digital::OutputPin]. This is useful for creating physical chip select
|
||||
/// pins required by the [embedded_hal_bus](https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/)
|
||||
/// API.
|
||||
pub struct HwCsPin {
|
||||
regs: regs::MmioSpi<'static>,
|
||||
id: HwChipSelectId,
|
||||
}
|
||||
|
||||
impl HwCsPin {
|
||||
pub fn new<P: HwCsProvider + AnyPin>(pin: P) -> Self {
|
||||
configure_pin_as_hw_cs_pin(pin);
|
||||
Self {
|
||||
regs: unsafe { P::SPI_ID.steal_regs() },
|
||||
id: P::CS_ID,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for HwCsPin {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::OutputPin for HwCsPin {
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.regs
|
||||
.modify_ctrl1(|value| value.with_sod(false).with_ss(self.id));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.regs.modify_ctrl1(|value| value.with_sod(true));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user