36 Commits

Author SHA1 Message Date
b4f1512463 docs patch 2 2025-02-13 15:47:50 +01:00
e9ec01fc60 Merge pull request 'doc fixes' (#48) from va108xx-embassy-doc-fix into main
Reviewed-on: #48
2025-02-13 15:38:50 +01:00
d57cd383cd doc fixes 2025-02-13 15:38:38 +01:00
df4e943f48 Merge pull request 'use released package versions' (#47) from use-released-package-versions into main
Reviewed-on: #47
2025-02-13 15:26:41 +01:00
93b67a4795 README update 2025-02-13 15:26:24 +01:00
a9f2e6dcee use released package versions 2025-02-13 15:25:01 +01:00
271c853df1 Merge pull request 'prepare va108xx-embassy release' (#46) from prepare-va108xx-embassy-release into main
Reviewed-on: #46
2025-02-13 15:22:36 +01:00
107189b166 prepare va108xx-embassy release 2025-02-13 15:22:18 +01:00
872944bebf Merge pull request 'prepare BSP release' (#45) from vorago-reb1-release into main
Reviewed-on: #45
2025-02-13 15:08:22 +01:00
d077bb6210 prepare BSP release 2025-02-13 15:06:02 +01:00
bd286bdb2a Merge pull request 'small README tweak' (#44) from readme-tweak into main
Reviewed-on: #44
2025-02-13 14:56:48 +01:00
d3cc00a4a5 small README tweak 2025-02-13 14:56:24 +01:00
1018a65447 Merge pull request 'add more defmt features' (#42) from add-more-defmt-features into main
Reviewed-on: #42
2025-02-13 14:52:03 +01:00
0a31b637e6 add more defmt features 2025-02-13 14:51:54 +01:00
6e1ae70054 Merge pull request 'add various debug impls' (#43) from add-various-debug-impls into main
Reviewed-on: #43
2025-02-13 14:51:27 +01:00
8ae2d6189a add various debug impls 2025-02-13 14:50:00 +01:00
549a98dbaf Merge pull request 'all doc fixes' (#41) from doc-fixes into main
Reviewed-on: #41
2025-02-13 12:23:56 +01:00
e24fc608a3 all doc fixes 2025-02-13 11:44:14 +01:00
7b74312013 Merge pull request 'completed async RX support as well' (#39) from async-uart-rx into main
Reviewed-on: #39
2025-02-13 11:33:36 +01:00
417f5b7f67 completed async RX support as well 2025-02-13 11:31:17 +01:00
3e796ef22b Merge pull request 'UART B hotfix' (#40) from uart-b-hotfix into main
Reviewed-on: #40
2025-02-13 11:30:37 +01:00
b145047b95 UART B hotfix 2025-02-13 11:14:39 +01:00
82b4c16f8e Merge pull request 'bump PAC version' (#38) from bump-va108xx-version into main
Reviewed-on: #38
2025-02-12 14:57:53 +01:00
189ac2d256 bump PAC version 2025-02-12 14:47:18 +01:00
c5543d8606 Merge pull request 'add back doc attribute' (#37) from prep-pac-release into main
Reviewed-on: #37
2025-02-12 14:25:28 +01:00
691911d087 add back doc attribute 2025-02-12 14:23:25 +01:00
3953897c48 Merge pull request 'Async UART support' (#33) from async-uart into main
Reviewed-on: #33
2025-02-12 14:15:10 +01:00
6e0d417a5c Async UART TX support 2025-02-12 14:13:35 +01:00
4edba63b02 Merge pull request 'docs fix' (#36) from docs-fix into main
Reviewed-on: #36
2025-02-12 14:11:21 +01:00
bcd79f0f20 docs fix 2025-02-12 14:07:18 +01:00
77608da74e Merge pull request 'dynpin defmt bugfix' (#35) from dynpin-fix-2 into main
Reviewed-on: #35
2025-02-12 14:06:51 +01:00
066d91aee5 dynpin defmt bugfix 2025-02-12 14:06:41 +01:00
e869355960 Merge pull request 'Regenerate PAC' (#29) from regenerate-pac into main
Reviewed-on: #29
2025-02-12 14:05:06 +01:00
99631dbd03 va108xx v0.4.0: Regnerate PAC 2025-02-12 14:04:56 +01:00
698ed3a700 Merge pull request 'dynpin bugfix' (#34) from bugfix-dynpin into main
Reviewed-on: #34
2025-02-12 13:41:34 +01:00
f3d840ace7 dynpin bugfix 2025-02-12 13:41:06 +01:00
53 changed files with 1663 additions and 304 deletions

View File

@ -21,9 +21,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- name: Install nextest
uses: taiki-e/install-action@nextest
# There is not a single test, and the tests are brittle because some dependencies do not
# like being run on general purpose machines..
# - run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
# I think we can skip those on an embedded crate..
# - run: cargo test --doc -p va108xx-hal

View File

@ -14,6 +14,8 @@ This workspace contains the following released crates:
crate containing basic low-level register definition.
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
HAL crate containing higher-level abstractions on top of the PAC register crate.
- The [`va108xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-embassy)
crate containing support for running the embassy-rs RTOS.
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
BSP crate containing support for the REB1 development board.

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
pac::{self, interrupt},
prelude::*,
time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, IrqCfg},
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, InterruptConfig},
};
#[allow(dead_code)]
@ -155,7 +155,7 @@ fn main() -> ! {
}
TestCase::DelayMs => {
let mut ms_timer = set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),

View File

@ -15,10 +15,12 @@ num_enum = { version = "0.7", default-features = false }
static_assertions = "1"
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"
[features]
default = []

View File

@ -9,6 +9,10 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7"
embedded-hal = "1"
embedded-hal-async = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
heapless = "0.8"
static_cell = "2"
rtt-target = "0.6"
panic-rtt-target = "0.2"
@ -23,8 +27,8 @@ embassy-executor = { version = "0.7", features = [
"executor-interrupt"
]}
va108xx-hal = { path = "../../va108xx-hal" }
va108xx-embassy = { path = "../../va108xx-embassy", default-features = false }
va108xx-hal = "0.9"
va108xx-embassy = "0.1"
[features]
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]

View File

@ -14,7 +14,7 @@ use embedded_hal_async::digital::Wait;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::gpio::{handle_interrupt_for_async_gpio, InputDynPinAsync, InputPinAsync, PinsB};
use va108xx_hal::gpio::{on_interrupt_for_asynch_gpio, InputDynPinAsync, InputPinAsync, PinsB};
use va108xx_hal::{
gpio::{DynPin, PinsA},
pac::{self, interrupt},
@ -248,11 +248,11 @@ async fn output_task(
#[interrupt]
#[allow(non_snake_case)]
fn OC10() {
handle_interrupt_for_async_gpio();
on_interrupt_for_asynch_gpio();
}
#[interrupt]
#[allow(non_snake_case)]
fn OC11() {
handle_interrupt_for_async_gpio();
on_interrupt_for_asynch_gpio();
}

View File

@ -0,0 +1,171 @@
//! Asynchronous UART reception example application.
//!
//! This application receives data on two UARTs permanently using a ring buffer.
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
//! UART B is received on ports PA2 and PA3.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! type something in the terminal and check if the data is echoed back. You can also check the
//! RTT logs to see received data.
#![no_std]
#![no_main]
use core::cell::RefCell;
use critical_section::Mutex;
use embassy_executor::Spawner;
use embassy_time::Instant;
use embedded_hal::digital::StatefulOutputPin;
use embedded_io::Write;
use embedded_io_async::Read;
use heapless::spsc::{Consumer, Producer, Queue};
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
prelude::*,
uart::{
self, on_interrupt_uart_b_overwriting,
rx_asynch::{on_interrupt_uart_a, RxAsync},
RxAsyncSharedConsumer, Tx,
},
InterruptConfig,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
static_cell::ConstStaticCell::new(Queue::new());
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8, 256>>>> = Mutex::new(RefCell::new(None));
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8, 256>>>> = Mutex::new(RefCell::new(None));
// main is itself an async function.
#[embassy_executor::main]
async fn main(spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART RX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx_uart_a = porta.pa9.into_funsel_2();
let rx_uart_a = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx_uart_a, rx_uart_a),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let tx_uart_b = porta.pa3.into_funsel_2();
let rx_uart_b = porta.pa2.into_funsel_2();
let uartb = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uartb,
(tx_uart_b, rx_uart_b),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (tx_uart_b, rx_uart_b) = uartb.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
// Pass the producer to the interrupt handler.
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
critical_section::with(|cs| {
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_uart_b);
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
});
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B);
spawner
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
.unwrap();
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART A: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART A: {:?}",
read_bytes,
read_str
);
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
}
}
#[embassy_executor::task]
async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer<pac::Uartb, 256>, mut tx: Tx<pac::Uartb>) {
let mut buf = [0u8; 256];
loop {
rprintln!("Current time UART B: {}", Instant::now().as_secs());
// Infallible asynchronous operation.
let read_bytes = async_rx.read(&mut buf).await.unwrap();
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
rprintln!(
"Read {} bytes asynchronously on UART B: {:?}",
read_bytes,
read_str
);
tx.write_all(read_str.as_bytes()).unwrap();
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_uart_a(&mut prod);
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
// In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors {
rprintln!("UART A errors: {:?}", errors);
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC3() {
let mut prod =
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B);
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
// In a production app, we could use a channel to send the errors to the main task.
if let Err(errors) = errors {
rprintln!("UART B errors: {:?}", errors);
}
}

View File

@ -0,0 +1,97 @@
//! Asynchronous UART transmission example application.
//!
//! This application receives sends 4 strings with different sizes permanently using UART A.
//! Ports PA8 and PA9 are used for this.
//!
//! Instructions:
//!
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
//! can verify the correctness of the sent strings.
#![no_std]
#![no_main]
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::Write;
use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy;
use va108xx_hal::{
gpio::PinsA,
pac::{self, interrupt},
prelude::*,
uart::{self, on_interrupt_uart_a_tx, TxAsync},
InterruptConfig,
};
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
const STR_LIST: &[&str] = &[
"Hello World\r\n",
"Smoll\r\n",
"A string which is larger than the FIFO size\r\n",
"A really large string which is significantly larger than the FIFO size\r\n",
];
// main is itself an async function.
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
rtt_init_print!();
rprintln!("-- VA108xx Async UART TX Demo --");
let mut dp = pac::Peripherals::take().unwrap();
// Safety: Only called once here.
unsafe {
embassy::init(
&mut dp.sysconfig,
&dp.irqsel,
SYSCLK_FREQ,
dp.tim23,
dp.tim22,
);
}
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.into_readable_push_pull_output();
let mut led2 = porta.pa6.into_readable_push_pull_output();
let tx = porta.pa9.into_funsel_2();
let rx = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let (tx, _rx) = uarta.split();
let mut async_tx = TxAsync::new(tx);
let mut ticker = Ticker::every(Duration::from_secs(1));
let mut idx = 0;
loop {
rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok();
led1.toggle().ok();
led2.toggle().ok();
let _written = async_tx
.write(STR_LIST[idx].as_bytes())
.await
.expect("writing failed");
idx += 1;
if idx == STR_LIST.len() {
idx = 0;
}
ticker.next().await;
}
}
#[interrupt]
#[allow(non_snake_case)]
fn OC2() {
on_interrupt_uart_a_tx();
}

View File

@ -22,5 +22,5 @@ rtic-sync = { version = "1.3", features = ["defmt-03"] }
once_cell = {version = "1", default-features = false, features = ["critical-section"]}
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
va108xx-hal = "0.8"
vorago-reb1 = { path = "../../vorago-reb1" }
va108xx-hal = "0.9"
vorago-reb1 = "0.7"

View File

@ -12,7 +12,7 @@ mod app {
gpio::{FilterType, InterruptEdge, PinsA},
pac,
prelude::*,
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
};
use vorago_reb1::button::Button;
use vorago_reb1::leds::Leds;
@ -61,23 +61,24 @@ mod app {
rprintln!("Using {:?} mode", mode);
let mut dp = cx.device;
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let edge_irq = match mode {
PressMode::Toggle => InterruptEdge::HighToLow,
PressMode::Keep => InterruptEdge::BothEdges,
};
// Configure an edge interrupt on the button and route it to interrupt vector 15
let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq(
let mut button = Button::new(pinsa.pa11.into_floating_input());
button.configure_edge_interrupt(
edge_irq,
IrqCfg::new(pac::interrupt::OC15, true, true),
InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig),
Some(&mut dp.irqsel),
);
if mode == PressMode::Toggle {
// This filter debounces the switch for edge based interrupts
button = button.filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
}
let mut leds = Leds::new(
@ -89,7 +90,7 @@ mod app {
led.off();
}
set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),

View File

@ -23,12 +23,13 @@ mod app {
gpio::PinsA,
pac,
prelude::*,
uart::{self, RxWithIrq, Tx},
uart::{self, RxWithInterrupt, Tx},
InterruptConfig,
};
#[local]
struct Local {
rx: RxWithIrq<pac::Uarta>,
rx: RxWithInterrupt<pac::Uarta>,
tx: Tx<pac::Uarta>,
}
@ -47,19 +48,20 @@ mod app {
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let mut dp = cx.device;
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2();
let irq_uart = uart::Uart::new(
let irq_uart = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
SYSCLK_FREQ,
dp.uarta,
(tx, rx),
115200.Hz(),
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (tx, rx) = irq_uart.split();
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC3);
let mut rx = rx.into_rx_with_irq();
rx.start();
@ -90,7 +92,7 @@ mod app {
fn reception_task(mut cx: reception_task::Context) {
let mut buf: [u8; 16] = [0; 16];
let mut ringbuf_full = false;
let result = cx.local.rx.irq_handler(&mut buf);
let result = cx.local.rx.on_interrupt(&mut buf);
if result.bytes_read > 0 && result.errors.is_none() {
cx.shared.rb.lock(|rb| {
if rb.vacant_len() < result.bytes_read {

View File

@ -35,11 +35,7 @@ mod app {
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
let porta = PinsA::new(
&mut cx.device.sysconfig,
Some(cx.device.ioconfig),
cx.device.porta,
);
let porta = PinsA::new(&mut cx.device.sysconfig, cx.device.porta);
let led0 = porta.pa10.into_readable_push_pull_output();
let led1 = porta.pa7.into_readable_push_pull_output();
let led2 = porta.pa6.into_readable_push_pull_output();

View File

@ -16,8 +16,8 @@ embedded-io = "0.6"
cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal]
version = "0.8"
version = "0.9"
features = ["rt", "defmt"]
[dependencies.vorago-reb1]
path = "../../vorago-reb1"
version = "0.7"

View File

@ -18,14 +18,14 @@ use va108xx_hal::{
prelude::*,
timer::DelayMs,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
IrqCfg,
InterruptConfig,
};
#[entry]
fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap();
let mut delay_ms = DelayMs::new(set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true),
InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),
@ -33,7 +33,7 @@ fn main() -> ! {
))
.unwrap();
let mut delay_tim1 = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1);
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led1 = porta.pa10.into_readable_push_pull_output();
let mut led2 = porta.pa7.into_readable_push_pull_output();
let mut led3 = porta.pa6.into_readable_push_pull_output();

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
prelude::*,
timer::{
default_ms_irq_handler, set_up_ms_delay_provider, CascadeCtrl, CascadeSource,
CountdownTimer, Event, IrqCfg,
CountdownTimer, Event, InterruptConfig,
},
};
@ -39,7 +39,7 @@ fn main() -> ! {
CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim3).auto_disable(true);
cascade_triggerer.listen(
Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC1, true, false),
InterruptConfig::new(pac::Interrupt::OC1, true, false),
Some(&mut dp.irqsel),
Some(&mut dp.sysconfig),
);
@ -62,7 +62,7 @@ fn main() -> ! {
// the timer expires
cascade_target_1.listen(
Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC2, true, false),
InterruptConfig::new(pac::Interrupt::OC2, true, false),
Some(&mut dp.irqsel),
Some(&mut dp.sysconfig),
);
@ -88,7 +88,7 @@ fn main() -> ! {
// the timer expires
cascade_target_2.listen(
Event::TimeOut,
IrqCfg::new(pac::Interrupt::OC3, true, false),
InterruptConfig::new(pac::Interrupt::OC3, true, false),
Some(&mut dp.irqsel),
Some(&mut dp.sysconfig),
);

View File

@ -19,7 +19,7 @@ fn main() -> ! {
rtt_init_print!();
rprintln!("-- VA108xx PWM example application--");
let mut dp = pac::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut pwm = pwm::PwmPin::new(
&mut dp.sysconfig,
50.MHz(),

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
prelude::*,
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
timer::{default_ms_irq_handler, set_up_ms_tick},
IrqCfg,
InterruptConfig,
};
#[derive(PartialEq, Debug)]
@ -47,7 +47,7 @@ fn main() -> ! {
rprintln!("-- VA108xx SPI example application--");
let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true),
InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),
@ -58,8 +58,8 @@ fn main() -> ! {
.expect("creating SPI clock config failed");
let spia_ref: RefCell<Option<SpiBase<pac::Spia, u8>>> = RefCell::new(None);
let spib_ref: RefCell<Option<SpiBase<pac::Spib, u8>>> = RefCell::new(None);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let pinsb = PinsB::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portb);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let pinsb = PinsB::new(&mut dp.sysconfig, dp.portb);
let mut spi_cfg = spi::SpiConfig::default();
if EXAMPLE_SEL == ExampleSelect::Loopback {

View File

@ -12,7 +12,9 @@ use va108xx_hal::{
pac::{self, interrupt},
prelude::*,
time::Hertz,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, IrqCfg, MS_COUNTER},
timer::{
default_ms_irq_handler, set_up_ms_tick, CountdownTimer, Event, InterruptConfig, MS_COUNTER,
},
};
#[allow(dead_code)]
@ -65,7 +67,7 @@ fn main() -> ! {
}
LibType::Hal => {
set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true),
InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),
@ -75,7 +77,7 @@ fn main() -> ! {
CountdownTimer::new(&mut dp.sysconfig, get_sys_clock().unwrap(), dp.tim1);
second_timer.listen(
Event::TimeOut,
IrqCfg::new(interrupt::OC1, true, true),
InterruptConfig::new(interrupt::OC1, true, true),
Some(&mut dp.irqsel),
Some(&mut dp.sysconfig),
);

View File

@ -24,12 +24,18 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap();
let gpioa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let gpioa = PinsA::new(&mut dp.sysconfig, dp.porta);
let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2();
let uart = uart::Uart::new_without_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
);
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz());
let (mut tx, mut rx) = uarta.split();
let (mut tx, mut rx) = uart.split();
writeln!(tx, "Hello World\r").unwrap();
loop {
// Echo what is received on the serial link.

View File

@ -29,7 +29,9 @@ rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
rtic-sync = {version = "1", features = ["defmt-03"]}
[dependencies.va108xx-hal]
path = "../va108xx-hal"
version = "0.9"
# path = "../va108xx-hal"
[dependencies.vorago-reb1]
path = "../vorago-reb1"
version = "0.7"
# path = "../vorago-reb1"

View File

@ -71,7 +71,7 @@ mod app {
};
use va108xx_hal::gpio::PinsA;
use va108xx_hal::uart::IrqContextTimeoutOrMaxSize;
use va108xx_hal::{pac, uart};
use va108xx_hal::{pac, uart, InterruptConfig};
use vorago_reb1::m95m01::M95M01;
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
@ -84,7 +84,7 @@ mod app {
#[local]
struct Local {
uart_rx: uart::RxWithIrq<pac::Uarta>,
uart_rx: uart::RxWithInterrupt<pac::Uarta>,
uart_tx: uart::Tx<pac::Uarta>,
rx_context: IrqContextTimeoutOrMaxSize,
verif_reporter: VerificationReportCreator,
@ -114,15 +114,17 @@ mod app {
let tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2();
let irq_uart = uart::Uart::new(
let irq_uart = uart::Uart::new_with_interrupt(
&mut dp.sysconfig,
SYSCLK_FREQ,
dp.uarta,
(tx, rx),
UART_BAUDRATE.Hz(),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
);
let (tx, rx) = irq_uart.split();
let mut rx = rx.into_rx_with_irq(&mut dp.sysconfig, &mut dp.irqsel, pac::interrupt::OC0);
// Unwrap is okay, we explicitely set the interrupt ID.
let mut rx = rx.into_rx_with_irq();
let verif_reporter = VerificationReportCreator::new(0).unwrap();
@ -175,7 +177,7 @@ mod app {
match cx
.local
.uart_rx
.irq_handler_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
{
Ok(result) => {
if RX_DEBUGGING {

View File

@ -0,0 +1,17 @@
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.1.2] and [v0.1.1] 2025-02-13
Docs patch
## [v0.1.0] 2025-02-13
Initial release

View File

@ -1,11 +1,17 @@
[package]
name = "va108xx-embassy"
version = "0.1.0"
version = "0.1.2"
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/va108xx-rs"
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
license = "Apache-2.0"
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
[dependencies]
critical-section = "1"
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
embassy-sync = "0.6"
embassy-executor = "0.7"
@ -14,8 +20,12 @@ embassy-time-queue-utils = "0.1"
once_cell = { version = "1", default-features = false, features = ["critical-section"] }
[dependencies.va108xx-hal]
path = "../va108xx-hal"
va108xx-hal = "0.9"
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features]
default = ["irq-oc30-oc31"]
@ -25,3 +35,6 @@ irqs-in-lib = []
irq-oc28-oc29 = ["irqs-in-lib"]
irq-oc29-oc30 = ["irqs-in-lib"]
irq-oc30-oc31 = ["irqs-in-lib"]
[package.metadata.docs.rs]
rustdoc-args = ["--generate-link-to-definition"]

3
va108xx-embassy/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --open

View File

@ -23,14 +23,15 @@
//!
//! 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::embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
//! application code. If this is done, [embassy::init_with_custom_irqs] must be used
//! method to pass the IRQ numbers to the library.
//!
//! ## Examples
//!
//! [embassy example project](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::cell::{Cell, RefCell};
use critical_section::CriticalSection;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
@ -43,7 +44,7 @@ use once_cell::sync::OnceCell;
use va108xx_hal::pac::interrupt;
use va108xx_hal::{
clock::enable_peripheral_clock,
enable_interrupt, pac,
enable_nvic_interrupt, pac,
prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
PeripheralSelect,
@ -62,7 +63,7 @@ time_driver_impl!(
/// 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 [va108xx_hal::pac::interrupt]
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
/// macro in the application code in case this macro is used there.
#[macro_export]
macro_rules! embassy_time_driver_irqs {
@ -221,7 +222,7 @@ impl TimerDriver {
.tim0(timekeeper_tim.tim_id() as usize)
.write(|w| unsafe { w.bits(timekeeper_irq as u32) });
unsafe {
enable_interrupt(timekeeper_irq);
enable_nvic_interrupt(timekeeper_irq);
}
timekeeper_reg_block
.ctrl()
@ -239,7 +240,7 @@ impl TimerDriver {
});
// Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe {
enable_interrupt(alarm_irq);
enable_nvic_interrupt(alarm_irq);
}
irqsel
.tim0(alarm_tim.tim_id() as usize)
@ -299,7 +300,7 @@ impl TimerDriver {
.cnt_value()
.write(|w| unsafe { w.bits(remaining_ticks.unwrap() as u32) });
alarm_tim.ctrl().modify(|_, w| w.irq_enb().set_bit());
alarm_tim.enable().write(|w| unsafe { w.bits(1) })
alarm_tim.enable().write(|w| unsafe { w.bits(1) });
}
}
})

View File

@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [v0.9.0]
## Fixed
- Important bugfix for UART driver which causes UART B drivers not to work.
## Removed
- Deleted some HAL re-exports in the PWM module
@ -24,12 +28,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- I2C `TimingCfg` constructor now returns explicit error instead of generic Error.
Removed the timing configuration error type from the generic I2C error enumeration.
- `PinsA` and `PinsB` constructor do not expect an optional `pac::Ioconfig` argument anymore.
- `IrqCfg` renamed to `InterruptConfig`, kept alias for old name.
- All library provided interrupt handlers now start with common prefix `on_interrupt_*`
- `RxWithIrq` renamed to `RxWithInterrupt`
- `Rx::into_rx_with_irq` does not expect any arguments any more.
- `filter_type` renamed to `configure_filter_type`.
- `level_irq` renamed to `configure_level_interrupt`.
- `edge_irq` renamed to `configure_edge_interrupt`.
- `PinsA` and `PinsB` constructor do not expect an optional IOCONFIG argument anymore.
- UART interrupt management is now handled by the main constructor instead of later stages to
statically ensure one interrupt vector for the UART peripheral. `Uart::new` expects an
optional `InterruptConfig` argument.
- `enable_interrupt` and `disable_interrupt` renamed to `enable_nvic_interrupt` and
`disable_nvic_interrupt` to distinguish them from peripheral interrupts more clearly.
- `port_mux` renamed to `port_function_select`
- Renamed `IrqUartErrors` to `UartErrors`.
## Added
- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion
methods.
- Asynchronous GPIO support.
- Asynchronous UART TX support.
- Asynchronous UART RX support.
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
## [v0.8.0] 2024-09-30

View File

@ -1,6 +1,6 @@
[package]
name = "va108xx-hal"
version = "0.8.0"
version = "0.9.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers"
@ -19,19 +19,26 @@ embedded-hal = "1"
embedded-hal-async = "1"
embedded-hal-nb = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
fugit = "0.3"
typenum = "1"
critical-section = "1"
delegate = ">=0.12, <=0.13"
heapless = "0.8"
static_cell = "2"
thiserror = { version = "2", default-features = false }
void = { version = "1", default-features = false }
once_cell = {version = "1", default-features = false }
va108xx = { version = "0.3", default-features = false, features = ["critical-section"]}
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"]}
va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true }
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
portable-atomic = "1"
[features]
default = ["rt"]
rt = ["va108xx/rt"]

View File

@ -25,12 +25,6 @@ rustup target add thumbv6m-none-eabi
After that, you can use `cargo build` to build the development version of the crate.
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
## Setting up your own binary crate
If you have a custom board, you might be interested in setting up a new binary crate for your
@ -65,3 +59,11 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions in primary README.
## Embedded Rust
If you have not done this yet, it is recommended to read some of the excellent resources available
to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)

View File

@ -11,6 +11,7 @@ static SYS_CLOCK: Mutex<OnceCell<Hertz>> = Mutex::new(OnceCell::new());
pub type PeripheralClocks = PeripheralSelect;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FilterClkSel {
SysClk = 0,
Clk1 = 1,
@ -39,13 +40,27 @@ pub fn get_sys_clock() -> Option<Hertz> {
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClkSel, div: u32) {
match clk_sel {
FilterClkSel::SysClk => (),
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk1 => {
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk2 => {
syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk3 => {
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk4 => {
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk5 => {
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk6 => {
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
}
FilterClkSel::Clk7 => {
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
}
}
}

View File

@ -4,12 +4,12 @@
//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers
//! which must be provided for async support to work. However, it provides one generic
//! [handler][handle_interrupt_for_async_gpio] which should be called in ALL user interrupt handlers
//! for which handle GPIO interrupts.
//! [handler][on_interrupt_for_asynch_gpio] which should be called in ALL user interrupt handlers
//! which handle GPIO interrupts.
//!
//! # Example
//!
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/async-gpio/examples/embassy/src/bin/async-gpio.rs)
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
use core::future::Future;
use embassy_sync::waitqueue::AtomicWaker;
@ -18,7 +18,7 @@ use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool;
use va108xx::{self as pac, Irqsel, Sysconfig};
use crate::IrqCfg;
use crate::InterruptConfig;
use super::{
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId,
@ -44,7 +44,7 @@ fn pin_id_to_offset(dyn_pin_id: DynPinId) -> usize {
/// complete async operations. The user should call this function in ALL interrupt handlers
/// which handle any GPIO interrupts.
#[inline]
pub fn handle_interrupt_for_async_gpio() {
pub fn on_interrupt_for_asynch_gpio() {
let periphs = unsafe { pac::Peripherals::steal() };
handle_interrupt_for_gpio_and_port(
@ -90,7 +90,7 @@ pub struct InputPinFuture {
impl InputPinFuture {
/// # Safety
///
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
/// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
/// related to this input pin are not being used elsewhere concurrently.
pub unsafe fn new_unchecked_with_dyn_pin(
@ -117,7 +117,7 @@ impl InputPinFuture {
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
edge,
IrqCfg::new(irq, true, true),
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
)
@ -127,7 +127,7 @@ impl InputPinFuture {
/// # Safety
///
/// This calls [Self::new] but uses [pac::Peripherals::steal] to get the system configuration
/// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration
/// and IRQ selection peripherals. Users must ensure that the registers and configuration
/// related to this input pin are not being used elsewhere concurrently.
pub unsafe fn new_unchecked_with_pin<I: PinId, C: InputConfig>(
@ -148,9 +148,9 @@ impl InputPinFuture {
) -> Self {
EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
pin.configure_edge_interrupt(
edge,
IrqCfg::new(irq, true, true),
InterruptConfig::new(irq, true, true),
Some(sys_cfg),
Some(irq_sel),
);
@ -200,7 +200,7 @@ impl InputDynPinAsync {
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [handle_interrupt_for_async_gpio] function must be called inside that function for
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: DynPin, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
@ -335,7 +335,7 @@ impl<I: PinId, C: InputConfig> InputPinAsync<I, C> {
/// passed as well and is used to route and enable the interrupt.
///
/// Please note that the interrupt handler itself must be provided by the user and the
/// generic [handle_interrupt_for_async_gpio] function must be called inside that function for
/// generic [on_interrupt_for_asynch_gpio] function must be called inside that function for
/// the asynchronous functionality to work.
pub fn new(pin: Pin<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
Self { pin, irq }

View File

@ -61,7 +61,7 @@ use super::{
reg::RegisterInterface,
InputDynPinAsync,
};
use crate::{clock::FilterClkSel, enable_interrupt, pac, FunSel, IrqCfg};
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
//==================================================================================================
// DynPinMode configurations
@ -69,6 +69,7 @@ use crate::{clock::FilterClkSel, enable_interrupt, pac, FunSel, IrqCfg};
/// Value-level `enum` for disabled configurations
#[derive(PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynDisabled {
Floating,
PullDown,
@ -77,6 +78,7 @@ pub enum DynDisabled {
/// Value-level `enum` for input configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynInput {
Floating,
PullDown,
@ -85,6 +87,7 @@ pub enum DynInput {
/// Value-level `enum` for output configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynOutput {
PushPull,
OpenDrain,
@ -119,6 +122,7 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
/// Value-level `enum` representing pin modes
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynPinMode {
Input(DynInput),
Output(DynOutput),
@ -153,14 +157,16 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
//==================================================================================================
/// Value-level `enum` for pin groups
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynGroup {
A,
B,
}
/// Value-level `struct` representing pin IDs
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId {
pub group: DynGroup,
pub num: u8,
@ -174,6 +180,7 @@ pub struct DynPinId {
///
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters.
#[derive(Debug)]
pub(crate) struct DynRegisters(DynPinId);
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
@ -206,6 +213,7 @@ impl DynRegisters {
///
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
/// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)]
pub struct DynPin {
pub(crate) regs: DynRegisters,
mode: DynPinMode,
@ -348,7 +356,7 @@ impl DynPin {
pub(crate) fn irq_enb(
&mut self,
irq_cfg: crate::IrqCfg,
irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>,
) {
@ -363,18 +371,18 @@ impl DynPin {
DynGroup::A => {
irqsel
.porta0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
DynGroup::B => {
irqsel
.portb0(self.regs.id().num as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
}
}
}
if irq_cfg.enable {
unsafe { enable_interrupt(irq_cfg.irq) };
if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
}
@ -432,7 +440,7 @@ impl DynPin {
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> {
@ -450,7 +458,7 @@ impl DynPin {
pub fn interrupt_level(
&mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> {

View File

@ -76,7 +76,7 @@ use super::{DynPin, InputPinAsync};
use crate::{
pac::{Irqsel, Porta, Portb, Sysconfig},
typelevel::Sealed,
IrqCfg,
InterruptConfig,
};
use core::convert::Infallible;
use core::marker::PhantomData;
@ -119,8 +119,11 @@ pub trait InputConfig: Sealed {
const DYN: DynInput;
}
#[derive(Debug)]
pub enum Floating {}
#[derive(Debug)]
pub enum PullDown {}
#[derive(Debug)]
pub enum PullUp {}
impl InputConfig for Floating {
@ -148,6 +151,7 @@ pub type InputPullUp = Input<PullUp>;
///
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
/// [`PullUp`]
#[derive(Debug)]
pub struct Input<C: InputConfig> {
cfg: PhantomData<C>,
}
@ -177,13 +181,17 @@ pub trait OutputConfig: Sealed {
pub trait ReadableOutput: Sealed {}
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
#[derive(Debug)]
pub enum PushPull {}
/// Type-level variant of [`OutputConfig`] for an open drain configuration
#[derive(Debug)]
pub enum OpenDrain {}
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
#[derive(Debug)]
pub enum ReadablePushPull {}
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
#[derive(Debug)]
pub enum ReadableOpenDrain {}
impl Sealed for PushPull {}
@ -210,6 +218,7 @@ impl OutputConfig for ReadableOpenDrain {
///
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
/// their respective readable versions
#[derive(Debug)]
pub struct Output<C: OutputConfig> {
cfg: PhantomData<C>,
}
@ -304,6 +313,7 @@ macro_rules! pin_id {
// Need paste macro to use ident in doc attribute
paste! {
#[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {}
impl Sealed for $Id {}
impl PinId for $Id {
@ -321,6 +331,7 @@ macro_rules! pin_id {
//==================================================================================================
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> {
inner: DynPin,
phantom: PhantomData<(I, M)>,
@ -454,7 +465,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
fn irq_enb(
&mut self,
irq_cfg: crate::IrqCfg,
irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>,
) {
@ -581,10 +592,10 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
InputPinAsync::new(self, irq)
}
pub fn interrupt_edge(
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
@ -592,10 +603,10 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
self.irq_enb(irq_cfg, syscfg, irqsel);
}
pub fn interrupt_level(
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
@ -631,7 +642,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
@ -642,7 +653,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_level(
&mut self,
level_type: InterruptLevel,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
@ -654,7 +665,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.inner.regs.filter_type(filter, clksel);
}
}
@ -741,6 +752,7 @@ macro_rules! pins {
) => {
paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
#[derive(Debug)]
pub struct $PinsName {
port: $Port,
$(

View File

@ -304,7 +304,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
@ -316,7 +316,7 @@ pub(super) unsafe trait RegisterInterface {
unsafe {
portreg
.datamask()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}

View File

@ -18,6 +18,7 @@ const CLK_400K: Hertz = Hertz::from_raw(400_000);
const MIN_CLK_400K: Hertz = Hertz::from_raw(8_000_000);
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FifoEmptyMode {
Stall = 0,
EndTransaction = 1,
@ -89,18 +90,21 @@ enum I2cCmd {
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cSpeed {
Regular100khz = 0,
Fast400khz = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cDirection {
Send = 0,
Read = 1,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum I2cAddress {
Regular(u8),
TenBit(u16),
@ -141,9 +145,12 @@ impl Instance for pac::I2cb {
// Config
//==================================================================================================
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TrTfThighTlow(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8);
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimingCfg {
// 4 bit max width
tr: u8,
@ -218,6 +225,7 @@ impl Default for TimingCfg {
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MasterConfig {
pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode,
@ -384,12 +392,12 @@ impl<I2c: Instance> I2cBase<I2c> {
let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b);
self.i2c
.s0_addressb()
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) })
.write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) });
}
if let Some(addr_b_mask) = sl_cfg.addr_b_mask {
self.i2c
.s0_addressmaskb()
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) })
.write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) });
}
}

View File

@ -17,6 +17,7 @@ pub mod typelevel;
pub mod uart;
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FunSel {
Sel1 = 0b01,
Sel2 = 0b10,
@ -24,12 +25,14 @@ pub enum FunSel {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PortSel {
PortA,
PortB,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
PortB = 1,
@ -46,31 +49,39 @@ pub enum PeripheralSelect {
Gpio = 24,
}
/// Generic IRQ config which can be used to specify whether the HAL driver will
/// Generic interrupt config which can be used to specify whether the HAL driver will
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
/// this steps themselves
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct IrqCfg {
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub irq: pac::Interrupt,
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
pub id: pac::Interrupt,
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral.
pub route: bool,
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
pub enable: bool,
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC. If an interrupt is used for
/// multiple purposes, the user can enable the interrupts themselves.
pub enable_in_nvic: bool,
}
impl IrqCfg {
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
IrqCfg { irq, route, enable }
impl InterruptConfig {
pub fn new(id: pac::Interrupt, route: bool, enable_in_nvic: bool) -> Self {
InterruptConfig {
id,
route,
enable_in_nvic,
}
}
}
pub type IrqCfg = InterruptConfig;
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPin(pub(crate) ());
/// Can be used to manually manipulate the function select of port pins
pub fn port_mux(
pub fn port_function_select(
ioconfig: &mut pac::Ioconfig,
port: PortSel,
pin: u8,
@ -104,7 +115,7 @@ pub fn port_mux(
///
/// This function is `unsafe` because it can break mask-based critical sections.
#[inline]
pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) {
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
}
@ -112,6 +123,6 @@ pub unsafe fn enable_interrupt(irq: pac::Interrupt) {
/// Disable a specific interrupt using the NVIC peripheral.
#[inline]
pub fn disable_interrupt(irq: pac::Interrupt) {
pub fn disable_nvic_interrupt(irq: pac::Interrupt) {
cortex_m::peripheral::NVIC::mask(irq);
}

View File

@ -15,7 +15,9 @@ use crate::{clock::enable_peripheral_clock, gpio::DynPinId};
const DUTY_MAX: u16 = u16::MAX;
pub struct PwmCommon {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct PwmCommon {
sys_clk: Hertz,
/// For PWMB, this is the upper limit
current_duty: u16,
@ -80,7 +82,7 @@ where
pin
}
pub fn reduce(self) -> ReducedPwmPin<Mode> {
pub fn downgrade(self) -> ReducedPwmPin<Mode> {
self.inner
}
@ -277,6 +279,24 @@ impl<Mode> ReducedPwmPin<Mode> {
}
}
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmA>> for ReducedPwmPin<PwmA>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
fn from(value: PwmPin<Pin, Tim, PwmA>) -> Self {
value.downgrade()
}
}
impl<Pin: TimPin, Tim: ValidTim> From<PwmPin<Pin, Tim, PwmB>> for ReducedPwmPin<PwmB>
where
(Pin, Tim): ValidTimAndPin<Pin, Tim>,
{
fn from(value: PwmPin<Pin, Tim, PwmB>) -> Self {
value.downgrade()
}
}
impl From<ReducedPwmPin<PwmA>> for ReducedPwmPin<PwmB> {
fn from(other: ReducedPwmPin<PwmA>) -> Self {
let mut pwmb = Self {

View File

@ -37,6 +37,7 @@ pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const DEFAULT_CLK_DIV: u16 = 2;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HwChipSelectId {
Id0 = 0,
Id1 = 1,
@ -50,6 +51,7 @@ pub enum HwChipSelectId {
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiPort {
Porta = 0,
Portb = 1,
@ -58,6 +60,7 @@ pub enum SpiPort {
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum WordSize {
OneBit = 0x00,
FourBits = 0x03,
@ -789,7 +792,7 @@ where
// initialization. Returns the amount of written bytes.
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
@ -803,7 +806,7 @@ where
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
}
current_write_idx
}
@ -812,7 +815,7 @@ where
// initialization.
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit());
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
@ -826,7 +829,7 @@ where
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit());
}
current_write_idx
}

View File

@ -20,7 +20,7 @@ pub fn enable_rom_scrubbing(
}
pub fn disable_rom_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) })
syscfg.rom_scrub().write(|w| unsafe { w.bits(0) });
}
/// Enable scrubbing for the RAM
@ -39,7 +39,7 @@ pub fn enable_ram_scrubbing(
}
pub fn disable_ram_scrubbing(syscfg: &mut pac::Sysconfig) {
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) })
syscfg.ram_scrub().write(|w| unsafe { w.bits(0) });
}
/// Clear the reset bit. This register is active low, so doing this will hold the peripheral

View File

@ -4,10 +4,10 @@
//!
//! - [MS and second tick implementation](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/timer-ticks.rs)
//! - [Cascade feature example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/cascade.rs)
pub use crate::IrqCfg;
pub use crate::InterruptConfig;
use crate::{
clock::{enable_peripheral_clock, PeripheralClocks},
enable_interrupt,
enable_nvic_interrupt,
gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14,
PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
@ -79,6 +79,7 @@ pub enum Event {
}
#[derive(Default, Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CascadeCtrl {
/// Enable Cascade 0 signal active as a requirement for counting
pub enb_start_src_csd0: bool,
@ -108,6 +109,7 @@ pub struct CascadeCtrl {
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CascadeSel {
Csd0 = 0,
Csd1 = 1,
@ -319,7 +321,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal()
.sysconfig
.tim_reset()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
@ -330,7 +332,7 @@ pub unsafe trait TimRegInterface {
va108xx::Peripherals::steal()
.sysconfig
.tim_reset()
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
@ -362,7 +364,7 @@ unsafe impl TimRegInterface for TimDynRegister {
pub struct CountdownTimer<Tim: ValidTim> {
tim: Tim,
curr_freq: Hertz,
irq_cfg: Option<IrqCfg>,
irq_cfg: Option<InterruptConfig>,
sys_clk: Hertz,
rst_val: u32,
last_cnt: u32,
@ -415,13 +417,13 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn listen(
&mut self,
event: Event,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
irq_sel: Option<&mut pac::Irqsel>,
sys_cfg: Option<&mut pac::Sysconfig>,
) {
match event {
Event::TimeOut => {
cortex_m::peripheral::NVIC::mask(irq_cfg.irq);
cortex_m::peripheral::NVIC::mask(irq_cfg.id);
self.irq_cfg = Some(irq_cfg);
if irq_cfg.route {
if let Some(sys_cfg) = sys_cfg {
@ -430,7 +432,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
if let Some(irq_sel) = irq_sel {
irq_sel
.tim0(Tim::TIM_ID as usize)
.write(|w| unsafe { w.bits(irq_cfg.irq as u32) });
.write(|w| unsafe { w.bits(irq_cfg.id as u32) });
}
}
self.listening = true;
@ -520,8 +522,8 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn enable(&mut self) {
if let Some(irq_cfg) = self.irq_cfg {
self.enable_interrupt();
if irq_cfg.enable {
unsafe { enable_interrupt(irq_cfg.irq) };
if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
}
self.tim
@ -719,7 +721,7 @@ impl<TIM: ValidTim> embedded_hal::delay::DelayNs for CountdownTimer<TIM> {
// Set up a millisecond timer on TIM0. Please note that the user still has to provide an IRQ handler
// which should call [default_ms_irq_handler].
pub fn set_up_ms_tick<TIM: ValidTim>(
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
sys_cfg: &mut pac::Sysconfig,
irq_sel: Option<&mut pac::Irqsel>,
sys_clk: impl Into<Hertz>,

View File

@ -7,12 +7,11 @@
//! - [Flashloader exposing a CCSDS interface via UART](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
use core::{convert::Infallible, ops::Deref};
use fugit::RateExtU32;
use va108xx::Uarta;
pub use crate::IrqCfg;
pub use crate::InterruptConfig;
use crate::{
clock::enable_peripheral_clock,
enable_interrupt,
enable_nvic_interrupt,
gpio::pin::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30,
PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
@ -23,6 +22,13 @@ use crate::{
};
use embedded_hal_nb::serial::Read;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Bank {
A = 0,
B = 1,
}
//==================================================================================================
// Type-Level support
//==================================================================================================
@ -48,6 +54,11 @@ impl Pins<pac::Uartb> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
// Regular Definitions
//==================================================================================================
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("no interrupt ID was set")]
pub struct NoInterruptIdWasSet;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("transer is pending")]
@ -227,6 +238,7 @@ impl From<Hertz> for Config {
//==================================================================================================
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqContextTimeoutOrMaxSize {
rx_idx: usize,
mode: IrqReceptionMode,
@ -252,17 +264,19 @@ impl IrqContextTimeoutOrMaxSize {
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResult {
pub bytes_read: usize,
pub errors: Option<IrqUartError>,
pub errors: Option<UartErrors>,
}
/// This struct is used to return the default IRQ handler result to the user
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IrqResultMaxSizeOrTimeout {
complete: bool,
timeout: bool,
pub errors: Option<IrqUartError>,
pub errors: Option<UartErrors>,
pub bytes_read: usize,
}
@ -309,20 +323,22 @@ impl IrqResultMaxSizeOrTimeout {
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum IrqReceptionMode {
Idle,
Pending,
}
#[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UartErrors {
overflow: bool,
framing: bool,
parity: bool,
other: bool,
}
impl IrqUartError {
impl UartErrors {
#[inline(always)]
pub fn overflow(&self) -> bool {
self.overflow
@ -344,7 +360,7 @@ impl IrqUartError {
}
}
impl IrqUartError {
impl UartErrors {
#[inline(always)]
pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity
@ -373,6 +389,16 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
/// This circumvents the safety guarantees of the HAL.
unsafe fn steal() -> Self;
fn ptr() -> *const uart_base::RegisterBlock;
/// Retrieve the type erased peripheral register block.
///
/// # Safety
///
/// This circumvents the safety guarantees of the HAL.
#[inline(always)]
unsafe fn reg_block() -> &'static uart_base::RegisterBlock {
unsafe { &(*Self::ptr()) }
}
}
impl Instance for pac::Uarta {
@ -380,11 +406,13 @@ impl Instance for pac::Uarta {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
#[inline(always)]
unsafe fn steal() -> Self {
pac::Peripherals::steal().uarta
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _
Self::ptr() as *const _
}
}
@ -393,11 +421,13 @@ impl Instance for pac::Uartb {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
#[inline(always)]
unsafe fn steal() -> Self {
pac::Peripherals::steal().uartb
}
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _
Self::ptr() as *const _
}
}
@ -592,15 +622,51 @@ where
UartInstance: Instance,
PinsInstance: Pins<UartInstance>,
{
pub fn new(
/// Calls [Self::new] with the interrupt configuration to some valid value.
pub fn new_with_interrupt(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
irq_cfg: InterruptConfig,
) -> Self {
Self::new(syscfg, sys_clk, uart, pins, config, Some(irq_cfg))
}
/// Calls [Self::new] with the interrupt configuration to [None].
pub fn new_without_interrupt(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
) -> Self {
Self::new(syscfg, sys_clk, uart, pins, config, None)
}
/// Create a new UART peripheral with an interrupt configuration.
///
/// # Arguments
///
/// - `syscfg`: The system configuration register block
/// - `sys_clk`: The system clock frequency
/// - `uart`: The concrete UART peripheral instance.
/// - `pins`: UART TX and RX pin tuple.
/// - `config`: UART specific configuration parameters like baudrate.
/// - `irq_cfg`: Optional interrupt configuration. This should be a valid value if the plan
/// is to use TX or RX functionality relying on interrupts. If only the blocking API without
/// any interrupt support is used, this can be [None].
pub fn new(
syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>,
uart: UartInstance,
pins: PinsInstance,
config: impl Into<Config>,
opt_irq_cfg: Option<InterruptConfig>,
) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
Uart {
let uart = Uart {
inner: UartBase {
uart,
tx: Tx::new(unsafe { UartInstance::steal() }),
@ -608,7 +674,21 @@ where
},
pins,
}
.init(config.into(), sys_clk.into())
.init(config.into(), sys_clk.into());
if let Some(irq_cfg) = opt_irq_cfg {
if irq_cfg.route {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
unsafe { pac::Irqsel::steal() }
.uart0(UartInstance::IDX 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 { enable_nvic_interrupt(irq_cfg.id) };
}
}
uart
}
/// This function assumes that the peripheral clock was alredy enabled
@ -683,14 +763,45 @@ where
}
}
#[inline(always)]
pub fn enable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().set_bit());
}
#[inline(always)]
pub fn disable_rx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.rxenable().clear_bit());
}
#[inline(always)]
pub fn enable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().set_bit();
w.irq_rx_to().set_bit();
w.irq_rx_status().set_bit()
});
}
#[inline(always)]
pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_rx().clear_bit();
w.irq_rx_to().clear_bit();
w.irq_rx_status().clear_bit()
});
}
/// Serial receiver.
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Rx<Uart>(Uart);
pub struct Rx<Uart> {
uart: Uart,
}
impl<Uart: Instance> Rx<Uart> {
#[inline(always)]
fn new(uart: Uart) -> Self {
Self(uart)
Self { uart }
}
/// Direct access to the peripheral structure.
@ -698,23 +809,33 @@ impl<Uart: Instance> Rx<Uart> {
/// # Safety
///
/// You must ensure that only registers related to the operation of the RX side are used.
#[inline(always)]
pub unsafe fn uart(&self) -> &Uart {
&self.0
&self.uart
}
#[inline]
pub fn clear_fifo(&self) {
self.0.fifo_clr().write(|w| w.rxfifo().set_bit());
self.uart.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn disable_interrupts(&mut self) {
disable_rx_interrupts(unsafe { Uart::reg_block() });
}
#[inline]
pub fn enable_interrupts(&mut self) {
enable_rx_interrupts(unsafe { Uart::reg_block() });
}
#[inline]
pub fn enable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().set_bit());
enable_rx(unsafe { Uart::reg_block() });
}
#[inline]
pub fn disable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().clear_bit());
disable_rx(unsafe { Uart::reg_block() });
}
/// Low level function to read a word from the UART FIFO.
@ -725,7 +846,7 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)]
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> {
if self.0.rxstatus().read().rdavl().bit_is_clear() {
if self.uart.rxstatus().read().rdavl().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
Ok(self.read_fifo_unchecked())
@ -741,20 +862,16 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)]
pub fn read_fifo_unchecked(&self) -> u32 {
self.0.data().read().bits()
self.uart.data().read().bits()
}
pub fn into_rx_with_irq(
self,
sysconfig: &mut pac::Sysconfig,
irqsel: &mut pac::Irqsel,
interrupt: pac::Interrupt,
) -> RxWithIrq<Uart> {
RxWithIrq::new(self, sysconfig, irqsel, interrupt)
pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
RxWithInterrupt::new(self)
}
#[inline(always)]
pub fn release(self) -> Uart {
self.0
self.uart
}
}
@ -811,14 +928,57 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
}
}
#[inline(always)]
pub fn enable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().set_bit());
}
#[inline(always)]
pub fn disable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
}
#[inline(always)]
pub fn enable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().set_bit();
w.irq_tx_status().set_bit();
w.irq_tx_empty().set_bit()
});
}
#[inline(always)]
pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_status().clear_bit();
w.irq_tx_empty().clear_bit()
});
}
/// Serial transmitter
///
/// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Tx<Uart>(Uart);
pub struct Tx<Uart> {
uart: Uart,
}
impl<Uart: Instance> Tx<Uart> {
/// Retrieve a TX pin without expecting an explicit UART structure
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
#[inline(always)]
pub unsafe fn steal() -> Self {
Self {
uart: Uart::steal(),
}
}
#[inline(always)]
fn new(uart: Uart) -> Self {
Self(uart)
Self { uart }
}
/// Direct access to the peripheral structure.
@ -826,23 +986,47 @@ impl<Uart: Instance> Tx<Uart> {
/// # Safety
///
/// You must ensure that only registers related to the operation of the TX side are used.
#[inline(always)]
pub unsafe fn uart(&self) -> &Uart {
&self.0
&self.uart
}
#[inline]
pub fn clear_fifo(&self) {
self.0.fifo_clr().write(|w| w.txfifo().set_bit());
self.uart.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn enable(&mut self) {
self.0.enable().modify(|_, w| w.txenable().set_bit());
// Safety: We own the UART structure
enable_tx(unsafe { Uart::reg_block() });
}
#[inline]
pub fn disable(&mut self) {
self.0.enable().modify(|_, w| w.txenable().clear_bit());
// Safety: We own the UART structure
disable_tx(unsafe { Uart::reg_block() });
}
/// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
///
/// - The IRQ_TX interrupt is generated when the TX FIFO is at least half empty.
/// - The IRQ_TX_STATUS interrupt is generated when write data is lost due to a FIFO overflow
/// - The IRQ_TX_EMPTY interrupt is generated when the TX FIFO is empty and the TXBUSY signal
/// is 0
#[inline]
pub fn enable_interrupts(&self) {
// Safety: We own the UART structure
enable_tx_interrupts(unsafe { Uart::reg_block() });
}
/// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
///
/// [Self::enable_interrupts] documents the interrupts.
#[inline]
pub fn disable_interrupts(&self) {
// Safety: We own the UART structure
disable_tx_interrupts(unsafe { Uart::reg_block() });
}
/// Low level function to write a word to the UART FIFO.
@ -853,7 +1037,7 @@ impl<Uart: Instance> Tx<Uart> {
/// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)]
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
if self.0.txstatus().read().wrrdy().bit_is_clear() {
if self.uart.txstatus().read().wrrdy().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
self.write_fifo_unchecked(data);
@ -868,7 +1052,11 @@ impl<Uart: Instance> Tx<Uart> {
/// API.
#[inline(always)]
pub fn write_fifo_unchecked(&self, data: u32) {
self.0.data().write(|w| unsafe { w.bits(data) });
self.uart.data().write(|w| unsafe { w.bits(data) });
}
pub fn into_async(self) -> TxAsync<Uart> {
TxAsync::new(self)
}
}
@ -925,51 +1113,38 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
///
/// 1. The first way simply empties the FIFO on an interrupt into a user provided buffer. You
/// can simply use [Self::start] to prepare the peripheral and then call the
/// [Self::irq_handler] in the interrupt service routine.
/// [Self::on_interrupt] in the interrupt service routine.
/// 2. The second way reads packets bounded by a maximum size or a baudtick based timeout. You
/// can use [Self::read_fixed_len_or_timeout_based_using_irq] to prepare the peripheral and
/// then call the [Self::irq_handler_max_size_or_timeout_based] in the interrupt service
/// then call the [Self::on_interrupt_max_size_or_timeout_based] in the interrupt service
/// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to
/// start reading the next packet.
pub struct RxWithIrq<Uart> {
pub rx: Rx<Uart>,
pub interrupt: pac::Interrupt,
}
pub struct RxWithInterrupt<Uart>(Rx<Uart>);
impl<Uart: Instance> RxWithIrq<Uart> {
pub fn new(
rx: Rx<Uart>,
syscfg: &mut pac::Sysconfig,
irqsel: &mut pac::Irqsel,
interrupt: pac::Interrupt,
) -> Self {
enable_peripheral_clock(syscfg, PeripheralSelect::Irqsel);
irqsel
.uart0(Uart::IDX as usize)
.write(|w| unsafe { w.bits(interrupt as u32) });
Self { rx, interrupt }
impl<Uart: Instance> RxWithInterrupt<Uart> {
pub fn new(rx: Rx<Uart>) -> Self {
Self(rx)
}
/// This function should be called once at initialization time if the regular
/// [Self::irq_handler] is used to read the UART receiver to enable and start the receiver.
/// [Self::on_interrupt] is used to read the UART receiver to enable and start the receiver.
pub fn start(&mut self) {
self.rx.enable();
self.0.enable();
self.enable_rx_irq_sources(true);
unsafe { enable_interrupt(self.interrupt) };
}
#[inline(always)]
pub fn uart(&self) -> &Uart {
&self.rx.0
&self.0.uart
}
/// This function is used together with the [Self::irq_handler_max_size_or_timeout_based]
/// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based]
/// function to read packets with a maximum size or variable sized packets by using the
/// receive timeout of the hardware.
///
/// This function should be called once at initialization to initiate the context state
/// and to [Self::start] the receiver. After that, it should be called after each
/// completed [Self::irq_handler_max_size_or_timeout_based] call to restart the reception
/// completed [Self::on_interrupt_max_size_or_timeout_based] call to restart the reception
/// of a packet.
pub fn read_fixed_len_or_timeout_based_using_irq(
&mut self,
@ -1006,7 +1181,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
pub fn cancel_transfer(&mut self) {
self.disable_rx_irq_sources();
self.rx.clear_fifo();
self.0.clear_fifo();
}
/// This function should be called in the user provided UART interrupt handler.
@ -1017,7 +1192,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// This function will not disable the RX interrupts, so you don't need to call any other
/// API after calling this function to continue emptying the FIFO. RX errors are handled
/// as partial errors and are returned as part of the [IrqResult].
pub fn irq_handler(&mut self, buf: &mut [u8; 16]) -> IrqResult {
pub fn on_interrupt(&mut self, buf: &mut [u8; 16]) -> IrqResult {
let mut result = IrqResult::default();
let irq_end = self.uart().irq_end().read();
@ -1040,7 +1215,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if irq_end.irq_rx_to().bit_is_set() {
loop {
// While there is data in the FIFO, write it into the reception buffer
let read_result = self.rx.read();
let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[result.bytes_read] = byte;
result.bytes_read += 1;
@ -1074,7 +1249,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// If passed buffer is equal to or larger than the specified maximum length, an
/// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors
/// and returned inside the [IrqResultMaxSizeOrTimeout] structure.
pub fn irq_handler_max_size_or_timeout_based(
pub fn on_interrupt_max_size_or_timeout_based(
&mut self,
context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8],
@ -1123,7 +1298,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if context.rx_idx == context.max_len {
break;
}
let read_result = self.rx.read();
let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[context.rx_idx] = byte;
context.rx_idx += 1;
@ -1149,7 +1324,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
fn read_handler(
&self,
errors: &mut Option<IrqUartError>,
errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>,
) -> Option<u8> {
match read_res {
@ -1157,7 +1332,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(IrqUartError::default());
let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err`
match e {
@ -1170,14 +1345,14 @@ impl<Uart: Instance> RxWithIrq<Uart> {
}
}
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) {
fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read();
if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set()
{
let err = errors.get_or_insert(IrqUartError::default());
let err = errors.get_or_insert(UartErrors::default());
if rx_status.rxovr().bit_is_set() {
err.overflow = true;
@ -1197,7 +1372,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
context: &mut IrqContextTimeoutOrMaxSize,
) {
self.disable_rx_irq_sources();
self.rx.disable();
self.0.disable();
res.bytes_read = context.rx_idx;
res.complete = true;
context.mode = IrqReceptionMode::Idle;
@ -1210,6 +1385,12 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// The user must ensure that these instances are not used to create multiple overlapping
/// UART drivers.
pub unsafe fn release(self) -> Uart {
self.rx.release()
self.0.release()
}
}
pub mod tx_asynch;
pub use tx_asynch::*;
pub mod rx_asynch;
pub use rx_asynch::*;

View File

@ -0,0 +1,419 @@
//! # Async UART reception functionality for the VA108xx family.
//!
//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the
//! [embedded_io_async::Read] trait.
//! This trait allows for asynchronous reception of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides four interrupt handlers:
//!
//! - [on_interrupt_uart_a]
//! - [on_interrupt_uart_b]
//! - [on_interrupt_uart_a_overwriting]
//! - [on_interrupt_uart_b_overwriting]
//!
//! The first two are used for the [RxAsync] struct, while the latter two are used with the
//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer.
//!
//! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors]
//! structure returned by the interrupt handlers.
//!
//! # Example
//!
//! - [Async UART RX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-rx.rs)
use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ordering};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io::ErrorType;
use heapless::spsc::Consumer;
use portable_atomic::AtomicBool;
use va108xx as pac;
use super::{Instance, Rx, RxError, UartErrors};
static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
static RX_HAS_DATA: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
struct RxFuture {
uart_idx: usize,
}
impl RxFuture {
pub fn new<Uart: Instance>(_rx: &mut Rx<Uart>) -> Self {
RX_READ_ACTIVE[Uart::IDX as usize].store(true, Ordering::Relaxed);
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for RxFuture {
type Output = Result<(), RxError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_RX_WAKERS[self.uart_idx].register(cx.waker());
if RX_HAS_DATA[self.uart_idx].load(Ordering::Relaxed) {
return core::task::Poll::Ready(Ok(()));
}
core::task::Poll::Pending
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AsyncUartErrors {
/// Queue has overflowed, data might have been lost.
pub queue_overflow: bool,
/// UART errors.
pub uart_errors: UartErrors,
}
fn on_interrupt_handle_rx_errors<Uart: Instance>(uart: &Uart) -> Option<UartErrors> {
let rx_status = uart.rxstatus().read();
if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set()
{
let mut errors_val = UartErrors::default();
if rx_status.rxovr().bit_is_set() {
errors_val.overflow = true;
}
if rx_status.rxfrm().bit_is_set() {
errors_val.framing = true;
}
if rx_status.rxpar().bit_is_set() {
errors_val.parity = true;
}
return Some(errors_val);
}
None
}
fn on_interrupt_rx_common_post_processing<Uart: Instance>(
uart: &Uart,
rx_enabled: bool,
read_some_data: bool,
irq_end: u32,
) -> Option<UartErrors> {
if read_some_data {
RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed);
if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) {
UART_RX_WAKERS[Uart::IDX as usize].wake();
}
}
let mut errors = None;
// Check for RX errors
if rx_enabled {
errors = on_interrupt_handle_rx_errors(uart);
}
// Clear the interrupt status bits
uart.irq_clr().write(|w| unsafe { w.bits(irq_end) });
errors
}
/// Interrupt handler for UART A.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_uart_a_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uarta::steal() },
prod,
shared_consumer,
)
}
/// Interrupt handler for UART B.
///
/// Should be called in the user interrupt handler to enable
/// asynchronous reception. This variant will overwrite old data in the ring buffer in case
/// the ring buffer is full.
pub fn on_interrupt_uart_b_overwriting<const N: usize>(
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue_overwriting(
unsafe { pac::Uartb::steal() },
prod,
shared_consumer,
)
}
pub fn on_interrupt_rx_async_heapless_queue_overwriting<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<u8, N>,
shared_consumer: &Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Result<(), AsyncUartErrors> {
let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
critical_section::with(|cs| {
let mut cons_ref = shared_consumer.borrow(cs).borrow_mut();
cons_ref.as_mut().unwrap().dequeue();
});
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
/// Interrupt handler for UART A.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_a<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod)
}
/// Interrupt handler for UART B.
///
/// Should be called in the user interrupt handler to enable asynchronous reception.
pub fn on_interrupt_uart_b<const N: usize>(
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod)
}
pub fn on_interrupt_rx_async_heapless_queue<Uart: Instance, const N: usize>(
uart: Uart,
prod: &mut heapless::spsc::Producer<'_, u8, N>,
) -> Result<(), AsyncUartErrors> {
//let uart = unsafe { Uart::steal() };
let irq_end = uart.irq_end().read();
let enb_status = uart.enable().read();
let rx_enabled = enb_status.rxenable().bit_is_set();
let mut read_some_data = false;
let mut queue_overflow = false;
// Half-Full interrupt. We have a guaranteed amount of data we can read.
if irq_end.irq_rx().bit_is_set() {
let available_bytes = uart.rxfifoirqtrg().read().bits() as usize;
// If this interrupt bit is set, the trigger level is available at the very least.
// Read everything as fast as possible
for _ in 0..available_bytes {
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
// Timeout, empty the FIFO completely.
if irq_end.irq_rx_to().bit_is_set() {
while uart.rxstatus().read().rdavl().bit_is_set() {
// While there is data in the FIFO, write it into the reception buffer
let byte = uart.data().read().bits();
if !prod.ready() {
queue_overflow = true;
}
prod.enqueue(byte as u8).ok();
}
read_some_data = true;
}
let uart_errors =
on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits());
if uart_errors.is_some() || queue_overflow {
return Err(AsyncUartErrors {
queue_overflow,
uart_errors: uart_errors.unwrap_or_default(),
});
}
Ok(())
}
struct ActiveReadGuard(usize);
impl Drop for ActiveReadGuard {
fn drop(&mut self) {
RX_READ_ACTIVE[self.0].store(false, Ordering::Relaxed);
}
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, data will be lost.
pub struct RxAsync<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
pub queue: heapless::spsc::Consumer<'static, u8, N>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsync<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsync<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which
/// is filled by the interrupt handler.
pub fn new(mut rx: Rx<Uart>, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsync<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
if self.queue.len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = |consumer: &mut heapless::spsc::Consumer<'static, u8, N>| {
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
};
let fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue(&mut self.queue);
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
Ok(handle_data_in_queue(&mut self.queue))
}
}
/// Core data structure to allow asynchronous UART reception.
///
/// If the ring buffer becomes full, the oldest data will be overwritten when using the
/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers.
pub struct RxAsyncSharedConsumer<Uart: Instance, const N: usize> {
rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<Consumer<'static, u8, N>>>>,
}
impl<Uart: Instance, const N: usize> ErrorType for RxAsyncSharedConsumer<Uart, N> {
/// Error reporting is done using the result of the interrupt functions.
type Error = Infallible;
}
impl<Uart: Instance, const N: usize> RxAsyncSharedConsumer<Uart, N> {
/// Create a new asynchronous receiver.
///
/// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data
/// which is filled by the interrupt handler. The shared property allows using it in the
/// interrupt handler to overwrite old data.
pub fn new(
mut rx: Rx<Uart>,
queue: &'static Mutex<RefCell<Option<heapless::spsc::Consumer<'static, u8, N>>>>,
) -> Self {
rx.disable_interrupts();
rx.disable();
rx.clear_fifo();
// Enable those together.
critical_section::with(|_| {
rx.enable_interrupts();
rx.enable();
});
Self { rx, queue }
}
}
impl<Uart: Instance, const N: usize> embedded_io_async::Read for RxAsyncSharedConsumer<Uart, N> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
// Need to wait for the IRQ to read data and set this flag. If the queue is not
// empty, we can read data immediately.
critical_section::with(|cs| {
let queue = self.queue.borrow(cs);
if queue.borrow().as_ref().unwrap().len() == 0 {
RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed);
}
});
let _guard = ActiveReadGuard(Uart::IDX as usize);
let mut handle_data_in_queue = || {
critical_section::with(|cs| {
let mut consumer_ref = self.queue.borrow(cs).borrow_mut();
let consumer = consumer_ref.as_mut().unwrap();
let data_to_read = consumer.len().min(buf.len());
for byte in buf.iter_mut().take(data_to_read) {
// We own the consumer and we checked that the amount of data is guaranteed to be available.
*byte = unsafe { consumer.dequeue_unchecked() };
}
data_to_read
})
};
let fut = RxFuture::new(&mut self.rx);
// Data is available, so read that data immediately.
let read_data = handle_data_in_queue();
if read_data > 0 {
return Ok(read_data);
}
// Await data.
let _ = fut.await;
let read_data = handle_data_in_queue();
Ok(read_data)
}
}

View File

@ -0,0 +1,264 @@
//! # Async UART transmission functionality for the VA108xx family.
//!
//! This module provides the [TxAsync] struct which implements the [embedded_io_async::Write] trait.
//! This trait allows for asynchronous sending of data streams. Please note that this module does
//! not specify/declare the interrupt handlers which must be provided for async support to work.
//! However, it provides two interrupt handlers:
//!
//! - [on_interrupt_uart_a_tx]
//! - [on_interrupt_uart_b_tx]
//!
//! Those should be called in ALL user interrupt handlers which handle UART TX interrupts,
//! depending on which UARTs are used.
//!
//! # Example
//!
//! - [Async UART TX example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-uart-tx.rs)
use core::{cell::RefCell, future::Future};
use critical_section::Mutex;
use embassy_sync::waitqueue::AtomicWaker;
use embedded_io_async::Write;
use portable_atomic::AtomicBool;
use super::*;
static UART_TX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2];
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] =
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2];
// Completion flag. Kept outside of the context structure as an atomic to avoid
// critical section.
static TX_DONE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2];
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
/// has to call this once in the interrupt handler responsible for UART A TX interrupts for
/// asynchronous operations to work.
pub fn on_interrupt_uart_a_tx() {
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
}
/// This is a generic interrupt handler to handle asynchronous UART TX operations. The user
/// has to call this once in the interrupt handler responsible for UART B TX interrupts for
/// asynchronous operations to work.
pub fn on_interrupt_uart_b_tx() {
on_interrupt_uart_tx(unsafe { pac::Uartb::steal() });
}
fn on_interrupt_uart_tx<Uart: Instance>(uart: Uart) {
let irq_enb = uart.irq_enb().read();
// IRQ is not related to TX.
if irq_enb.irq_tx().bit_is_clear() || irq_enb.irq_tx_empty().bit_is_clear() {
return;
}
let tx_status = uart.txstatus().read();
let unexpected_overrun = tx_status.wrlost().bit_is_set();
let mut context = critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow()
});
context.tx_overrun = unexpected_overrun;
if context.progress >= context.slice.len && !tx_status.wrbusy().bit_is_set() {
uart.irq_enb().modify(|_, w| {
w.irq_tx().clear_bit();
w.irq_tx_empty().clear_bit();
w.irq_tx_status().clear_bit()
});
uart.enable().modify(|_, w| w.txenable().clear_bit());
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context;
});
// Transfer is done.
TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed);
UART_TX_WAKERS[Uart::IDX as usize].wake();
return;
}
// Safety: We documented that the user provided slice must outlive the future, so we convert
// the raw pointer back to the slice here.
let slice = unsafe { core::slice::from_raw_parts(context.slice.data, context.slice.len) };
while context.progress < context.slice.len {
let wrrdy = uart.txstatus().read().wrrdy().bit_is_set();
if !wrrdy {
break;
}
// Safety: TX structure is owned by the future which does not write into the the data
// register, so we can assume we are the only one writing to the data register.
uart.data()
.write(|w| unsafe { w.bits(slice[context.progress] as u32) });
context.progress += 1;
}
// Write back updated context structure.
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
*context_ref.borrow_mut() = context;
});
}
#[derive(Debug, Copy, Clone)]
pub struct TxContext {
progress: usize,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
tx_overrun: false,
slice: RawBufSlice::new_empty(),
}
}
}
#[derive(Debug, Copy, Clone)]
struct RawBufSlice {
data: *const u8,
len: usize,
}
/// Safety: This type MUST be used with mutex to ensure concurrent access is valid.
unsafe impl Send for RawBufSlice {}
impl RawBufSlice {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
#[allow(dead_code)]
const unsafe fn new(data: &[u8]) -> Self {
Self {
data: data.as_ptr(),
len: data.len(),
}
}
const fn new_empty() -> Self {
Self {
data: core::ptr::null(),
len: 0,
}
}
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn set(&mut self, data: &[u8]) {
self.data = data.as_ptr();
self.len = data.len();
}
}
pub struct TxFuture {
uart_idx: usize,
}
impl TxFuture {
/// # Safety
///
/// This function stores the raw pointer of the passed data slice. The user MUST ensure
/// that the slice outlives the data structure.
pub unsafe fn new<Uart: Instance>(tx: &mut Tx<Uart>, data: &[u8]) -> Self {
TX_DONE[Uart::IDX as usize].store(false, core::sync::atomic::Ordering::Relaxed);
tx.disable_interrupts();
tx.disable();
tx.clear_fifo();
let uart_tx = unsafe { tx.uart() };
let init_fill_count = core::cmp::min(data.len(), 16);
// We fill the FIFO.
for data in data.iter().take(init_fill_count) {
uart_tx.data().write(|w| unsafe { w.bits(*data as u32) });
}
critical_section::with(|cs| {
let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs);
let mut context = context_ref.borrow_mut();
context.slice.set(data);
context.progress = init_fill_count;
// Ensure those are enabled inside a critical section at the same time. Can lead to
// weird glitches otherwise.
tx.enable_interrupts();
tx.enable();
});
Self {
uart_idx: Uart::IDX as usize,
}
}
}
impl Future for TxFuture {
type Output = Result<usize, TxOverrunError>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
UART_TX_WAKERS[self.uart_idx].register(cx.waker());
if TX_DONE[self.uart_idx].swap(false, core::sync::atomic::Ordering::Relaxed) {
let progress = critical_section::with(|cs| {
TX_CONTEXTS[self.uart_idx].borrow(cs).borrow().progress
});
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
impl Drop for TxFuture {
fn drop(&mut self) {
let reg_block = match self.uart_idx {
0 => unsafe { pac::Uarta::reg_block() },
1 => unsafe { pac::Uartb::reg_block() },
_ => unreachable!(),
};
disable_tx_interrupts(reg_block);
disable_tx(reg_block);
}
}
pub struct TxAsync<Uart: Instance> {
tx: Tx<Uart>,
}
impl<Uart: Instance> TxAsync<Uart> {
pub fn new(tx: Tx<Uart>) -> Self {
Self { tx }
}
pub fn release(self) -> Tx<Uart> {
self.tx
}
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("TX overrun error")]
pub struct TxOverrunError;
impl embedded_io_async::Error for TxOverrunError {
fn kind(&self) -> embedded_io_async::ErrorKind {
embedded_io_async::ErrorKind::Other
}
}
impl<Uart: Instance> embedded_io::ErrorType for TxAsync<Uart> {
type Error = TxOverrunError;
}
impl<Uart: Instance> Write for TxAsync<Uart> {
/// Write a buffer asynchronously.
///
/// This implementation is not side effect free, and a started future might have already
/// written part of the passed buffer.
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
}
}

View File

@ -3,6 +3,8 @@ svd2rust release can be generated by cloning the svd2rust [repository], checking
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![no_std]
// Manually inserted.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
use core::marker::PhantomData;
use core::ops::Deref;
#[doc = r"Number available in the NVIC for configuring priority"]

View File

@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.7.0] 2025-02-13
- Bumped `va108xx-hal` dependency to 0.9
- Minor adjustments to `Button` API.
- `Button`, `Led` and `Leds` now simply wrap a type using a tuple struct.
## [v0.6.0] 2024-09-30
- Added M95M01 EEPROM module/API

View File

@ -1,6 +1,6 @@
[package]
name = "vorago-reb1"
version = "0.6.0"
version = "0.7.0"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
edition = "2021"
description = "Board Support Crate for the Vorago REB1 development board"
@ -19,7 +19,7 @@ bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3"
[dependencies.va108xx-hal]
version = ">=0.8, <0.9"
version = "0.9"
features = ["rt"]
[features]

View File

@ -31,7 +31,7 @@ fn main() -> ! {
rprintln!("-- Vorago Accelerometer Example --");
let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let (sck, mosi, miso) = (
pinsa.pa20.into_funsel_2(),
pinsa.pa19.into_funsel_2(),

View File

@ -13,7 +13,7 @@ use va108xx_hal::{
gpio::{FilterType, InterruptEdge, PinsA},
pac::{self, interrupt},
prelude::*,
timer::{default_ms_irq_handler, set_up_ms_tick, IrqCfg},
timer::{default_ms_irq_handler, set_up_ms_tick, InterruptConfig},
};
use vorago_reb1::button::Button;
use vorago_reb1::leds::Leds;
@ -35,28 +35,29 @@ fn main() -> ! {
rtt_init_print!();
rprintln!("-- Vorago Button IRQ Example --");
let mut dp = pac::Peripherals::take().unwrap();
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let edge_irq = match PRESS_MODE {
PressMode::Toggle => InterruptEdge::HighToLow,
PressMode::Keep => InterruptEdge::BothEdges,
};
// Configure an edge interrupt on the button and route it to interrupt vector 15
let mut button = Button::new(pinsa.pa11.into_floating_input()).edge_irq(
let mut button = Button::new(pinsa.pa11.into_floating_input());
button.configure_edge_interrupt(
edge_irq,
IrqCfg::new(pac::interrupt::OC15, true, true),
InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig),
Some(&mut dp.irqsel),
);
if PRESS_MODE == PressMode::Toggle {
// This filter debounces the switch for edge based interrupts
button = button.filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
button.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1);
set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000);
}
set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
50.MHz(),

View File

@ -61,7 +61,7 @@ fn main() -> ! {
}
}
LibType::Hal => {
let pins = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let pins = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led1 = pins.pa10.into_readable_push_pull_output();
let mut led2 = pins.pa7.into_readable_push_pull_output();
let mut led3 = pins.pa6.into_readable_push_pull_output();
@ -87,27 +87,25 @@ fn main() -> ! {
}
}
LibType::Bsp => {
let pinsa = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut leds = Leds::new(
pinsa.pa10.into_push_pull_output(),
pinsa.pa7.into_push_pull_output(),
pinsa.pa6.into_push_pull_output(),
);
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
loop {
for _ in 0..10 {
// Blink all LEDs quickly
for led in leds.iter_mut() {
led.toggle();
}
delay.delay_ms(500);
for _ in 0..10 {
// Blink all LEDs quickly
for led in leds.iter_mut() {
led.toggle();
}
// Now use a wave pattern
loop {
for led in leds.iter_mut() {
led.toggle();
delay.delay_ms(200);
}
delay.delay_ms(500);
}
// Now use a wave pattern
loop {
for led in leds.iter_mut() {
led.toggle();
delay.delay_ms(200);
}
}
}

View File

@ -22,9 +22,9 @@ use va108xx_hal::{
pac::{self, interrupt},
prelude::*,
spi::{Spi, SpiBase, SpiConfig},
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig},
};
use va108xx_hal::{port_mux, FunSel, PortSel};
use va108xx_hal::{port_function_select, FunSel, PortSel};
use vorago_reb1::max11619::{
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
@ -112,7 +112,7 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap();
let tim0 = set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true),
InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig,
Some(&mut dp.irqsel),
SYS_CLK,
@ -123,7 +123,7 @@ fn main() -> ! {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
}
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
let pinsa = PinsA::new(&mut dp.sysconfig, dp.porta);
let spi_cfg = SpiConfig::default()
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
.mode(MODE_0)
@ -135,10 +135,10 @@ fn main() -> ! {
);
if MUX_MODE == MuxMode::PortB19to17 {
port_mux(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 16, FunSel::Sel1).ok();
port_function_select(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
port_function_select(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
port_function_select(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok();
port_function_select(&mut dp.ioconfig, PortSel::PortB, 16, FunSel::Sel1).ok();
}
// Set the accelerometer chip select low in case the board slot is populated
let mut accel_cs = pinsa.pa16.into_push_pull_output();

View File

@ -7,60 +7,56 @@
use embedded_hal::digital::InputPin;
use va108xx_hal::{
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
pac, IrqCfg,
pac, InterruptConfig,
};
pub struct Button {
button: Pin<PA11, InputFloating>,
}
#[derive(Debug)]
pub struct Button(pub Pin<PA11, InputFloating>);
impl Button {
pub fn new(pin: Pin<PA11, InputFloating>) -> Button {
Button { button: pin }
Button(pin)
}
#[inline]
pub fn pressed(&mut self) -> bool {
self.button.is_low().ok().unwrap()
self.0.is_low().ok().unwrap()
}
#[inline]
pub fn released(&mut self) -> bool {
self.button.is_high().ok().unwrap()
self.0.is_high().ok().unwrap()
}
/// Configures an IRQ on edge.
pub fn edge_irq(
mut self,
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Self {
self.button = self
.button
.interrupt_edge(edge_type, irq_cfg, syscfg, irqsel);
self
) {
self.0
.configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
}
/// Configures an IRQ on level.
pub fn level_irq(
mut self,
pub fn configure_level_interrupt(
&mut self,
level: InterruptLevel,
irq_cfg: IrqCfg,
irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>,
) -> Self {
self.button = self.button.interrupt_level(level, irq_cfg, syscfg, irqsel);
self
) {
self.0
.configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
}
/// Configures a filter on the button. This can be useful for debouncing the switch.
///
/// Please note that you still have to set a clock divisor yourself using the
/// [`va108xx_hal::clock::set_clk_div_register`] function in order for this to work.
pub fn filter_type(mut self, filter: FilterType, clksel: FilterClkSel) -> Self {
self.button = self.button.filter_type(filter, clksel);
self
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.0.configure_filter_type(filter, clksel);
}
}

View File

@ -15,15 +15,12 @@ pub type LD2 = Pin<PA10, PushPullOutput>;
pub type LD3 = Pin<PA7, PushPullOutput>;
pub type LD4 = Pin<PA6, PushPullOutput>;
pub struct Leds {
leds: [Led; 3],
}
#[derive(Debug)]
pub struct Leds(pub [Led; 3]);
impl Leds {
pub fn new(led_pin1: LD2, led_pin2: LD3, led_pin3: LD4) -> Leds {
Leds {
leds: [led_pin1.into(), led_pin2.into(), led_pin3.into()],
}
Leds([led_pin1.into(), led_pin2.into(), led_pin3.into()])
}
}
@ -31,13 +28,13 @@ impl core::ops::Deref for Leds {
type Target = [Led];
fn deref(&self) -> &[Led] {
&self.leds
&self.0
}
}
impl core::ops::DerefMut for Leds {
fn deref_mut(&mut self) -> &mut [Led] {
&mut self.leds
&mut self.0
}
}
@ -45,28 +42,25 @@ impl core::ops::Index<usize> for Leds {
type Output = Led;
fn index(&self, i: usize) -> &Led {
&self.leds[i]
&self.0[i]
}
}
impl core::ops::IndexMut<usize> for Leds {
fn index_mut(&mut self, i: usize) -> &mut Led {
&mut self.leds[i]
&mut self.0[i]
}
}
pub struct Led {
pin: DynPin,
}
#[derive(Debug)]
pub struct Led(pub DynPin);
macro_rules! ctor {
($($ldx:ident),+) => {
$(
impl From<$ldx> for Led {
fn from(led: $ldx) -> Self {
Led {
pin: led.into()
}
Led(led.into())
}
}
)+
@ -79,18 +73,18 @@ impl Led {
/// Turns the LED off. Setting the pin high actually turns the LED off
#[inline]
pub fn off(&mut self) {
self.pin.set_high().ok();
self.0.set_high().ok();
}
/// Turns the LED on. Setting the pin low actually turns the LED on
#[inline]
pub fn on(&mut self) {
self.pin.set_low().ok();
self.0.set_low().ok();
}
/// Toggles the LED
#[inline]
pub fn toggle(&mut self) {
self.pin.toggle_with_toggle_reg().ok();
self.0.toggle_with_toggle_reg().ok();
}
}

View File

@ -363,8 +363,8 @@
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "rust: cargo build uart irq",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-rtic",
"preLaunchTask": "uart-echo-rtic-example",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/uart-echo-rtic",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
@ -523,5 +523,53 @@
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART TX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart-tx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-tx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
{
"type": "cortex-debug",
"request": "launch",
"name": "Async UART RX",
"servertype": "jlink",
"cwd": "${workspaceRoot}",
"device": "Cortex-M0",
"svdFile": "./va108xx/svd/va108xx.svd.patched",
"preLaunchTask": "async-uart-rx",
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/async-uart-rx",
"interface": "jtag",
"runToEntryPoint": "main",
"rttConfig": {
"enabled": true,
"address": "auto",
"decoders": [
{
"port": 0,
"timestamp": true,
"type": "console"
}
]
}
},
]
}
}

View File

@ -276,6 +276,26 @@
"async-gpio"
]
},
{
"label": "async-uart-tx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-tx"
]
},
{
"label": "async-uart-rx",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"build",
"--bin",
"async-uart-rx"
]
},
{
"label": "bootloader",
"type": "shell",
@ -299,4 +319,4 @@
]
}
]
}
}