async UART now works

This commit is contained in:
Robin Müller 2025-02-12 11:55:25 +01:00
parent beb56efca6
commit 76b880915d
Signed by: muellerr
GPG Key ID: A649FB78196E3849
29 changed files with 619 additions and 405 deletions

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
time::Hertz, 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)] #[allow(dead_code)]
@ -155,7 +155,7 @@ fn main() -> ! {
} }
TestCase::DelayMs => { TestCase::DelayMs => {
let mut ms_timer = set_up_ms_tick( 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, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

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

View File

@ -1,228 +1,28 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use core::{
cell::RefCell,
future::Future,
ptr::{self},
};
use critical_section::Mutex;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_sync::waitqueue::AtomicWaker;
use embassy_time::{Duration, Instant, Ticker}; use embassy_time::{Duration, Instant, Ticker};
use embedded_hal::digital::StatefulOutputPin; use embedded_hal::digital::StatefulOutputPin;
use embedded_io_async::{ErrorType, Write}; use embedded_io_async::Write;
use panic_rtt_target as _; use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print}; use rtt_target::{rprintln, rtt_init_print};
use va108xx_embassy::embassy; use va108xx_embassy::embassy;
use va108xx_hal::{ use va108xx_hal::{
gpio::PinsA, gpio::PinsA,
pac, pac::{self, interrupt},
prelude::*, prelude::*,
uart::{self, Instance, Tx}, uart::{self, on_interrupt_uart_a_tx, TxAsync},
InterruptConfig,
}; };
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000); const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
static UART_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; const STR_LIST: &[&str] = &[
static TX_CONTEXTS: [Mutex<RefCell<TxContext>>; 2] = "Hello World\r\n",
[const { Mutex::new(RefCell::new(TxContext::new())) }; 2]; "Smoll\r\n",
"A string which is larger than the FIFO size\r\n",
#[derive(Debug, Copy, Clone)] "A really large string which is significantly larger than the FIFO size\r\n",
pub struct TxContext { ];
progress: usize,
done: bool,
tx_overrun: bool,
slice: RawBufSlice,
}
#[allow(clippy::new_without_default)]
impl TxContext {
pub const fn new() -> Self {
Self {
progress: 0,
done: false,
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: 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.disable_interrupts();
tx.disable();
tx.clear_fifo();
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 = 0;
context.done = false;
});
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.progress += init_fill_count;
});
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_WAKERS[self.uart_idx].register(cx.waker());
let (done, progress) = critical_section::with(|cs| {
let context = TX_CONTEXTS[self.uart_idx].borrow(cs).borrow();
(context.done, context.progress)
});
if done {
return core::task::Poll::Ready(Ok(progress));
}
core::task::Poll::Pending
}
}
pub fn on_interrupt_uart_a_tx() {
on_interrupt_uart_tx(unsafe { pac::Uarta::steal() });
}
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() {
// Transfer is done.
context.done = true;
// 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;
});
// TODO: Handle completion, disable TX interrupt and disable TX.
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;
});
}
pub struct TxUartAsync<Uart: Instance> {
tx: Tx<Uart>,
}
#[derive(Debug)]
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> ErrorType for TxUartAsync<Uart> {
type Error = TxOverrunError;
}
impl<Uart: Instance> Write for TxUartAsync<Uart> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let fut = unsafe { TxFuture::new(&mut self.tx, buf) };
fut.await
}
}
// main is itself an async function. // main is itself an async function.
#[embassy_executor::main] #[embassy_executor::main]
@ -243,7 +43,7 @@ async fn main(_spawner: Spawner) {
); );
} }
let porta = PinsA::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.porta); let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
let mut led0 = porta.pa10.into_readable_push_pull_output(); let mut led0 = porta.pa10.into_readable_push_pull_output();
let mut led1 = porta.pa7.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 mut led2 = porta.pa6.into_readable_push_pull_output();
@ -251,17 +51,37 @@ async fn main(_spawner: Spawner) {
let tx = porta.pa9.into_funsel_2(); let tx = porta.pa9.into_funsel_2();
let rx = porta.pa8.into_funsel_2(); let rx = porta.pa8.into_funsel_2();
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz()); 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 (tx, _rx) = uarta.split();
let mut async_tx = TxUartAsync { tx }; let mut async_tx = TxAsync::new(tx);
let send_data = b"Hello World\r\n";
async_tx.write(send_data).await.ok();
let mut ticker = Ticker::every(Duration::from_secs(1)); let mut ticker = Ticker::every(Duration::from_secs(1));
let mut idx = 0;
loop { loop {
ticker.next().await;
rprintln!("Current time: {}", Instant::now().as_secs()); rprintln!("Current time: {}", Instant::now().as_secs());
led0.toggle().ok(); led0.toggle().ok();
led1.toggle().ok(); led1.toggle().ok();
led2.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"]} once_cell = {version = "1", default-features = false, features = ["critical-section"]}
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] } ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
va108xx-hal = "0.8" va108xx-hal = { version = "0.8", path = "../../va108xx-hal" }
vorago-reb1 = { path = "../../vorago-reb1" } vorago-reb1 = { path = "../../vorago-reb1" }

View File

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

View File

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

View File

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

View File

@ -16,6 +16,7 @@ embedded-io = "0.6"
cortex-m-semihosting = "0.5.0" cortex-m-semihosting = "0.5.0"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
path = "../../va108xx-hal"
version = "0.8" version = "0.8"
features = ["rt", "defmt"] features = ["rt", "defmt"]

View File

@ -18,14 +18,14 @@ use va108xx_hal::{
prelude::*, prelude::*,
timer::DelayMs, timer::DelayMs,
timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer}, timer::{default_ms_irq_handler, set_up_ms_tick, CountdownTimer},
IrqCfg, InterruptConfig,
}; };
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay_ms = DelayMs::new(set_up_ms_tick( 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, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

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

View File

@ -17,7 +17,7 @@ use va108xx_hal::{
prelude::*, prelude::*,
spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs}, spi::{self, Spi, SpiBase, SpiClkConfig, TransferConfigWithHwcs},
timer::{default_ms_irq_handler, set_up_ms_tick}, timer::{default_ms_irq_handler, set_up_ms_tick},
IrqCfg, InterruptConfig,
}; };
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
@ -47,7 +47,7 @@ fn main() -> ! {
rprintln!("-- VA108xx SPI example application--"); rprintln!("-- VA108xx SPI example application--");
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let mut delay = set_up_ms_tick( let mut delay = set_up_ms_tick(
IrqCfg::new(interrupt::OC0, true, true), InterruptConfig::new(interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

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

View File

@ -24,11 +24,17 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); 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 tx = gpioa.pa9.into_funsel_2();
let rx = gpioa.pa8.into_funsel_2(); let rx = gpioa.pa8.into_funsel_2();
let uarta = uart::Uart::new(&mut dp.sysconfig, 50.MHz(), dp.uarta, (tx, rx), 115200.Hz()); let uarta = uart::Uart::new_without_interrupt(
&mut dp.sysconfig,
50.MHz(),
dp.uarta,
(tx, rx),
115200.Hz(),
);
let (mut tx, mut rx) = uarta.split(); let (mut tx, mut rx) = uarta.split();
writeln!(tx, "Hello World\r").unwrap(); writeln!(tx, "Hello World\r").unwrap();
loop { loop {

View File

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

View File

@ -43,7 +43,7 @@ use once_cell::sync::OnceCell;
use va108xx_hal::pac::interrupt; use va108xx_hal::pac::interrupt;
use va108xx_hal::{ use va108xx_hal::{
clock::enable_peripheral_clock, clock::enable_peripheral_clock,
enable_interrupt, pac, enable_nvic_interrupt, pac,
prelude::*, prelude::*,
timer::{enable_tim_clk, get_tim_raw, TimRegInterface}, timer::{enable_tim_clk, get_tim_raw, TimRegInterface},
PeripheralSelect, PeripheralSelect,
@ -221,7 +221,7 @@ impl TimerDriver {
.tim0(timekeeper_tim.tim_id() as usize) .tim0(timekeeper_tim.tim_id() as usize)
.write(|w| unsafe { w.bits(timekeeper_irq as u32) }); .write(|w| unsafe { w.bits(timekeeper_irq as u32) });
unsafe { unsafe {
enable_interrupt(timekeeper_irq); enable_nvic_interrupt(timekeeper_irq);
} }
timekeeper_reg_block timekeeper_reg_block
.ctrl() .ctrl()
@ -239,7 +239,7 @@ impl TimerDriver {
}); });
// Enable general interrupts. The IRQ enable of the peripheral remains cleared. // Enable general interrupts. The IRQ enable of the peripheral remains cleared.
unsafe { unsafe {
enable_interrupt(alarm_irq); enable_nvic_interrupt(alarm_irq);
} }
irqsel irqsel
.tim0(alarm_tim.tim_id() as usize) .tim0(alarm_tim.tim_id() as usize)

View File

@ -19,6 +19,7 @@ embedded-hal = "1"
embedded-hal-async = "1" embedded-hal-async = "1"
embedded-hal-nb = "1" embedded-hal-nb = "1"
embedded-io = "0.6" embedded-io = "0.6"
embedded-io-async = "0.6"
fugit = "0.3" fugit = "0.3"
typenum = "1" typenum = "1"
critical-section = "1" critical-section = "1"

View File

@ -5,11 +5,11 @@
//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers //! 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 //! 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 //! [handler][handle_interrupt_for_async_gpio] which should be called in ALL user interrupt handlers
//! for which handle GPIO interrupts. //! which handle GPIO interrupts.
//! //!
//! # Example //! # 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 core::future::Future;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
@ -18,7 +18,7 @@ use embedded_hal_async::digital::Wait;
use portable_atomic::AtomicBool; use portable_atomic::AtomicBool;
use va108xx::{self as pac, Irqsel, Sysconfig}; use va108xx::{self as pac, Irqsel, Sysconfig};
use crate::IrqCfg; use crate::InterruptConfig;
use super::{ use super::{
pin, DynGroup, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, 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 /// complete async operations. The user should call this function in ALL interrupt handlers
/// which handle any GPIO interrupts. /// which handle any GPIO interrupts.
#[inline] #[inline]
pub fn handle_interrupt_for_async_gpio() { pub fn on_interrupt_for_asynch_gpio() {
let periphs = unsafe { pac::Peripherals::steal() }; let periphs = unsafe { pac::Peripherals::steal() };
handle_interrupt_for_gpio_and_port( handle_interrupt_for_gpio_and_port(
@ -117,7 +117,7 @@ impl InputPinFuture {
.store(false, core::sync::atomic::Ordering::Relaxed); .store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge( pin.interrupt_edge(
edge, edge,
IrqCfg::new(irq, true, true), InterruptConfig::new(irq, true, true),
Some(sys_cfg), Some(sys_cfg),
Some(irq_sel), Some(irq_sel),
) )
@ -148,9 +148,9 @@ impl InputPinFuture {
) -> Self { ) -> Self {
EDGE_DETECTION[pin_id_to_offset(pin.id())] EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed); .store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge( pin.configure_edge_interrupt(
edge, edge,
IrqCfg::new(irq, true, true), InterruptConfig::new(irq, true, true),
Some(sys_cfg), Some(sys_cfg),
Some(irq_sel), Some(irq_sel),
); );

View File

@ -61,7 +61,7 @@ use super::{
reg::RegisterInterface, reg::RegisterInterface,
InputDynPinAsync, InputDynPinAsync,
}; };
use crate::{clock::FilterClkSel, enable_interrupt, pac, FunSel, IrqCfg}; use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig};
//================================================================================================== //==================================================================================================
// DynPinMode configurations // DynPinMode configurations
@ -77,6 +77,7 @@ pub enum DynDisabled {
/// Value-level `enum` for input configurations /// Value-level `enum` for input configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynInput { pub enum DynInput {
Floating, Floating,
PullDown, PullDown,
@ -85,6 +86,7 @@ pub enum DynInput {
/// Value-level `enum` for output configurations /// Value-level `enum` for output configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynOutput { pub enum DynOutput {
PushPull, PushPull,
OpenDrain, OpenDrain,
@ -119,6 +121,7 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
/// Value-level `enum` representing pin modes /// Value-level `enum` representing pin modes
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynPinMode { pub enum DynPinMode {
Input(DynInput), Input(DynInput),
Output(DynOutput), Output(DynOutput),
@ -346,14 +349,9 @@ impl DynPin {
self.regs.write_pin_masked(false) self.regs.write_pin_masked(false)
} }
#[inline]
pub fn edge_has_occurred(&mut self) -> bool {
self.regs.edge_has_occurred()
}
pub(crate) fn irq_enb( pub(crate) fn irq_enb(
&mut self, &mut self,
irq_cfg: crate::IrqCfg, irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>, syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>, irqsel: Option<&mut va108xx::Irqsel>,
) { ) {
@ -368,18 +366,18 @@ impl DynPin {
DynGroup::A => { DynGroup::A => {
irqsel irqsel
.porta0(self.regs.id().num as usize) .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 => { DynGroup::B => {
irqsel irqsel
.portb0(self.regs.id().num as usize) .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 { if irq_cfg.enable_in_nvic {
unsafe { enable_interrupt(irq_cfg.irq) }; unsafe { enable_nvic_interrupt(irq_cfg.id) };
} }
} }
@ -437,7 +435,7 @@ impl DynPin {
pub fn interrupt_edge( pub fn interrupt_edge(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
@ -455,7 +453,7 @@ impl DynPin {
pub fn interrupt_level( pub fn interrupt_level(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Result<(), InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {

View File

@ -76,7 +76,7 @@ use super::{DynPin, InputPinAsync};
use crate::{ use crate::{
pac::{Irqsel, Porta, Portb, Sysconfig}, pac::{Irqsel, Porta, Portb, Sysconfig},
typelevel::Sealed, typelevel::Sealed,
IrqCfg, InterruptConfig,
}; };
use core::convert::Infallible; use core::convert::Infallible;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -454,7 +454,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
fn irq_enb( fn irq_enb(
&mut self, &mut self,
irq_cfg: crate::IrqCfg, irq_cfg: crate::InterruptConfig,
syscfg: Option<&mut va108xx::Sysconfig>, syscfg: Option<&mut va108xx::Sysconfig>,
irqsel: Option<&mut va108xx::Irqsel>, irqsel: Option<&mut va108xx::Irqsel>,
) { ) {
@ -581,10 +581,10 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
InputPinAsync::new(self, irq) InputPinAsync::new(self, irq)
} }
pub fn interrupt_edge( pub fn configure_edge_interrupt(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -592,10 +592,10 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
self.irq_enb(irq_cfg, syscfg, irqsel); self.irq_enb(irq_cfg, syscfg, irqsel);
} }
pub fn interrupt_level( pub fn configure_level_interrupt(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -631,7 +631,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_edge( pub fn interrupt_edge(
&mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -642,7 +642,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
pub fn interrupt_level( pub fn interrupt_level(
&mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>, syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>, irqsel: Option<&mut Irqsel>,
) { ) {
@ -654,7 +654,7 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information. /// See p.37 and p.38 of the programmers guide for more information.
#[inline] #[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); self.inner.regs.filter_type(filter, clksel);
} }
} }

View File

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

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) //! - [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) //! - [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::{ use crate::{
clock::{enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
enable_interrupt, enable_nvic_interrupt,
gpio::{ gpio::{
AltFunc1, AltFunc2, AltFunc3, DynPinId, Pin, PinId, PA0, PA1, PA10, PA11, PA12, PA13, PA14, 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, PA15, PA2, PA24, PA25, PA26, PA27, PA28, PA29, PA3, PA30, PA31, PA4, PA5, PA6, PA7, PA8,
@ -362,7 +362,7 @@ unsafe impl TimRegInterface for TimDynRegister {
pub struct CountdownTimer<Tim: ValidTim> { pub struct CountdownTimer<Tim: ValidTim> {
tim: Tim, tim: Tim,
curr_freq: Hertz, curr_freq: Hertz,
irq_cfg: Option<IrqCfg>, irq_cfg: Option<InterruptConfig>,
sys_clk: Hertz, sys_clk: Hertz,
rst_val: u32, rst_val: u32,
last_cnt: u32, last_cnt: u32,
@ -415,13 +415,13 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn listen( pub fn listen(
&mut self, &mut self,
event: Event, event: Event,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
irq_sel: Option<&mut pac::Irqsel>, irq_sel: Option<&mut pac::Irqsel>,
sys_cfg: Option<&mut pac::Sysconfig>, sys_cfg: Option<&mut pac::Sysconfig>,
) { ) {
match event { match event {
Event::TimeOut => { Event::TimeOut => {
cortex_m::peripheral::NVIC::mask(irq_cfg.irq); cortex_m::peripheral::NVIC::mask(irq_cfg.id);
self.irq_cfg = Some(irq_cfg); self.irq_cfg = Some(irq_cfg);
if irq_cfg.route { if irq_cfg.route {
if let Some(sys_cfg) = sys_cfg { if let Some(sys_cfg) = sys_cfg {
@ -430,7 +430,7 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
if let Some(irq_sel) = irq_sel { if let Some(irq_sel) = irq_sel {
irq_sel irq_sel
.tim0(Tim::TIM_ID as usize) .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; self.listening = true;
@ -520,8 +520,8 @@ impl<Tim: ValidTim> CountdownTimer<Tim> {
pub fn enable(&mut self) { pub fn enable(&mut self) {
if let Some(irq_cfg) = self.irq_cfg { if let Some(irq_cfg) = self.irq_cfg {
self.enable_interrupt(); self.enable_interrupt();
if irq_cfg.enable { if irq_cfg.enable_in_nvic {
unsafe { enable_interrupt(irq_cfg.irq) }; unsafe { enable_nvic_interrupt(irq_cfg.id) };
} }
} }
self.tim self.tim
@ -719,7 +719,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 // 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]. // which should call [default_ms_irq_handler].
pub fn set_up_ms_tick<TIM: ValidTim>( pub fn set_up_ms_tick<TIM: ValidTim>(
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
sys_cfg: &mut pac::Sysconfig, sys_cfg: &mut pac::Sysconfig,
irq_sel: Option<&mut pac::Irqsel>, irq_sel: Option<&mut pac::Irqsel>,
sys_clk: impl Into<Hertz>, sys_clk: impl Into<Hertz>,

View File

@ -0,0 +1,264 @@
//! # Async GPIO 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 example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/async-gpio/examples/embassy/src/bin/async-uart.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_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_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_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

@ -9,10 +9,10 @@ use core::{convert::Infallible, ops::Deref};
use fugit::RateExtU32; use fugit::RateExtU32;
use va108xx::Uarta; use va108xx::Uarta;
pub use crate::IrqCfg; pub use crate::InterruptConfig;
use crate::{ use crate::{
clock::enable_peripheral_clock, clock::enable_peripheral_clock,
enable_interrupt, enable_nvic_interrupt,
gpio::pin::{ gpio::pin::{
AltFunc1, AltFunc2, AltFunc3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, 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, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
@ -48,6 +48,11 @@ impl Pins<pac::Uartb> for (Pin<PB21, AltFunc1>, Pin<PB20, AltFunc1>) {}
// Regular Definitions // 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)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("transer is pending")] #[error("transer is pending")]
@ -373,6 +378,16 @@ pub trait Instance: Deref<Target = uart_base::RegisterBlock> {
/// This circumvents the safety guarantees of the HAL. /// This circumvents the safety guarantees of the HAL.
unsafe fn steal() -> Self; unsafe fn steal() -> Self;
fn ptr() -> *const uart_base::RegisterBlock; 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 { impl Instance for pac::Uarta {
@ -380,9 +395,11 @@ impl Instance for pac::Uarta {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0;
#[inline(always)]
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uarta pac::Peripherals::steal().uarta
} }
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _ Uarta::ptr() as *const _
} }
@ -393,9 +410,11 @@ impl Instance for pac::Uartb {
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1;
#[inline(always)]
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uartb pac::Peripherals::steal().uartb
} }
#[inline(always)]
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uarta::ptr() as *const _ Uarta::ptr() as *const _
} }
@ -592,15 +611,36 @@ where
UartInstance: Instance, UartInstance: Instance,
PinsInstance: Pins<UartInstance>, PinsInstance: Pins<UartInstance>,
{ {
pub fn new( 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))
}
pub fn new_without_interrupt(
syscfg: &mut va108xx::Sysconfig, syscfg: &mut va108xx::Sysconfig,
sys_clk: impl Into<Hertz>, sys_clk: impl Into<Hertz>,
uart: UartInstance, uart: UartInstance,
pins: PinsInstance, pins: PinsInstance,
config: impl Into<Config>, config: impl Into<Config>,
) -> Self {
Self::new(syscfg, sys_clk, uart, pins, config, 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 { ) -> Self {
crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL); crate::clock::enable_peripheral_clock(syscfg, UartInstance::PERIPH_SEL);
Uart { let uart = Uart {
inner: UartBase { inner: UartBase {
uart, uart,
tx: Tx::new(unsafe { UartInstance::steal() }), tx: Tx::new(unsafe { UartInstance::steal() }),
@ -608,7 +648,21 @@ where
}, },
pins, 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 /// This function assumes that the peripheral clock was alredy enabled
@ -686,11 +740,14 @@ where
/// Serial receiver. /// Serial receiver.
/// ///
/// Can be created by using the [Uart::split] or [UartBase::split] API. /// Can be created by using the [Uart::split] or [UartBase::split] API.
pub struct Rx<Uart>(Uart); pub struct Rx<Uart> {
uart: Uart,
//opt_irq_id: Option<pac::Interrupt>,
}
impl<Uart: Instance> Rx<Uart> { impl<Uart: Instance> Rx<Uart> {
fn new(uart: Uart) -> Self { fn new(uart: Uart) -> Self {
Self(uart) Self { uart }
} }
/// Direct access to the peripheral structure. /// Direct access to the peripheral structure.
@ -699,22 +756,22 @@ impl<Uart: Instance> Rx<Uart> {
/// ///
/// You must ensure that only registers related to the operation of the RX side are used. /// You must ensure that only registers related to the operation of the RX side are used.
pub unsafe fn uart(&self) -> &Uart { pub unsafe fn uart(&self) -> &Uart {
&self.0 &self.uart
} }
#[inline] #[inline]
pub fn clear_fifo(&self) { 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] #[inline]
pub fn enable(&mut self) { pub fn enable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().set_bit()); self.uart.enable().modify(|_, w| w.rxenable().set_bit());
} }
#[inline] #[inline]
pub fn disable(&mut self) { pub fn disable(&mut self) {
self.0.enable().modify(|_, w| w.rxenable().clear_bit()); self.uart.enable().modify(|_, w| w.rxenable().clear_bit());
} }
/// Low level function to read a word from the UART FIFO. /// Low level function to read a word from the UART FIFO.
@ -725,7 +782,7 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information. /// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)] #[inline(always)]
pub fn read_fifo(&self) -> nb::Result<u32, Infallible> { 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); return Err(nb::Error::WouldBlock);
} }
Ok(self.read_fifo_unchecked()) Ok(self.read_fifo_unchecked())
@ -741,20 +798,15 @@ impl<Uart: Instance> Rx<Uart> {
/// value if you use the manual parity mode. See chapter 4.6.2 for more information. /// value if you use the manual parity mode. See chapter 4.6.2 for more information.
#[inline(always)] #[inline(always)]
pub fn read_fifo_unchecked(&self) -> u32 { pub fn read_fifo_unchecked(&self) -> u32 {
self.0.data().read().bits() self.uart.data().read().bits()
} }
pub fn into_rx_with_irq( pub fn into_rx_with_irq(self) -> RxWithInterrupt<Uart> {
self, RxWithInterrupt::new(self)
sysconfig: &mut pac::Sysconfig,
irqsel: &mut pac::Irqsel,
interrupt: pac::Interrupt,
) -> RxWithIrq<Uart> {
RxWithIrq::new(self, sysconfig, irqsel, interrupt)
} }
pub fn release(self) -> Uart { pub fn release(self) -> Uart {
self.0 self.uart
} }
} }
@ -811,14 +863,51 @@ impl<Uart: Instance> embedded_io::Read for Rx<Uart> {
} }
} }
pub fn enable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().set_bit());
}
pub fn disable_tx(uart: &uart_base::RegisterBlock) {
uart.enable().modify(|_, w| w.txenable().clear_bit());
}
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()
});
}
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 /// Serial transmitter
/// ///
/// Can be created by using the [Uart::split] or [UartBase::split] API. /// 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> { impl<Uart: Instance> Tx<Uart> {
/// Retrieve a TX pin without expecting an explicit UART structure
///
/// # Safety
///
/// Circumvents the HAL safety guarantees.
pub unsafe fn steal() -> Self {
Self {
uart: Uart::steal(),
}
}
fn new(uart: Uart) -> Self { fn new(uart: Uart) -> Self {
Self(uart) Self { uart }
} }
/// Direct access to the peripheral structure. /// Direct access to the peripheral structure.
@ -827,22 +916,24 @@ impl<Uart: Instance> Tx<Uart> {
/// ///
/// You must ensure that only registers related to the operation of the TX side are used. /// You must ensure that only registers related to the operation of the TX side are used.
pub unsafe fn uart(&self) -> &Uart { pub unsafe fn uart(&self) -> &Uart {
&self.0 &self.uart
} }
#[inline] #[inline]
pub fn clear_fifo(&self) { 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] #[inline]
pub fn enable(&mut self) { 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] #[inline]
pub fn disable(&mut self) { 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. /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
@ -853,11 +944,8 @@ impl<Uart: Instance> Tx<Uart> {
/// is 0 /// is 0
#[inline] #[inline]
pub fn enable_interrupts(&self) { pub fn enable_interrupts(&self) {
self.0.irq_enb().modify(|_, w| { // Safety: We own the UART structure
w.irq_tx().set_bit(); enable_tx_interrupts(unsafe { Uart::reg_block() });
w.irq_tx_status().set_bit();
w.irq_tx_empty().set_bit()
});
} }
/// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. /// Disables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts.
@ -865,11 +953,8 @@ impl<Uart: Instance> Tx<Uart> {
/// [Self::enable_interrupts] documents the interrupts. /// [Self::enable_interrupts] documents the interrupts.
#[inline] #[inline]
pub fn disable_interrupts(&self) { pub fn disable_interrupts(&self) {
self.0.irq_enb().modify(|_, w| { // Safety: We own the UART structure
w.irq_tx().clear_bit(); disable_tx_interrupts(unsafe { Uart::reg_block() });
w.irq_tx_empty().clear_bit();
w.irq_tx_empty().clear_bit()
});
} }
/// Low level function to write a word to the UART FIFO. /// Low level function to write a word to the UART FIFO.
@ -880,7 +965,7 @@ impl<Uart: Instance> Tx<Uart> {
/// value if you use the manual parity mode. See chapter 11.4.1 for more information. /// value if you use the manual parity mode. See chapter 11.4.1 for more information.
#[inline(always)] #[inline(always)]
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> { 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); return Err(nb::Error::WouldBlock);
} }
self.write_fifo_unchecked(data); self.write_fifo_unchecked(data);
@ -895,7 +980,11 @@ impl<Uart: Instance> Tx<Uart> {
/// API. /// API.
#[inline(always)] #[inline(always)]
pub fn write_fifo_unchecked(&self, data: u32) { 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)
} }
} }
@ -958,36 +1047,23 @@ impl<Uart: Instance> embedded_io::Write for Tx<Uart> {
/// then call the [Self::irq_handler_max_size_or_timeout_based] in the interrupt service /// then call the [Self::irq_handler_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 /// routine. You have to call [Self::read_fixed_len_or_timeout_based_using_irq] in the ISR to
/// start reading the next packet. /// start reading the next packet.
pub struct RxWithIrq<Uart> { pub struct RxWithInterrupt<Uart>(Rx<Uart>);
pub rx: Rx<Uart>,
pub interrupt: pac::Interrupt,
}
impl<Uart: Instance> RxWithIrq<Uart> { impl<Uart: Instance> RxWithInterrupt<Uart> {
pub fn new( pub fn new(rx: Rx<Uart>) -> Self {
rx: Rx<Uart>, Self(rx)
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 }
} }
/// This function should be called once at initialization time if the regular /// 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::irq_handler] is used to read the UART receiver to enable and start the receiver.
pub fn start(&mut self) { pub fn start(&mut self) {
self.rx.enable(); self.0.enable();
self.enable_rx_irq_sources(true); self.enable_rx_irq_sources(true);
unsafe { enable_interrupt(self.interrupt) };
} }
#[inline(always)] #[inline(always)]
pub fn uart(&self) -> &Uart { 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::irq_handler_max_size_or_timeout_based]
@ -1033,7 +1109,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
pub fn cancel_transfer(&mut self) { pub fn cancel_transfer(&mut self) {
self.disable_rx_irq_sources(); 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. /// This function should be called in the user provided UART interrupt handler.
@ -1044,7 +1120,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// This function will not disable the RX interrupts, so you don't need to call any other /// 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 /// 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]. /// 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 mut result = IrqResult::default();
let irq_end = self.uart().irq_end().read(); let irq_end = self.uart().irq_end().read();
@ -1067,7 +1143,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if irq_end.irq_rx_to().bit_is_set() { if irq_end.irq_rx_to().bit_is_set() {
loop { loop {
// While there is data in the FIFO, write it into the reception buffer // While there is data in the FIFO, write it into the reception buffer
let read_result = self.rx.read(); let read_result = self.0.read();
if let Some(byte) = self.read_handler(&mut result.errors, &read_result) { if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[result.bytes_read] = byte; buf[result.bytes_read] = byte;
result.bytes_read += 1; result.bytes_read += 1;
@ -1101,7 +1177,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// If passed buffer is equal to or larger than the specified maximum length, an /// If passed buffer is equal to or larger than the specified maximum length, an
/// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors /// [BufferTooShortError] will be returned. Other RX errors are treated as partial errors
/// and returned inside the [IrqResultMaxSizeOrTimeout] structure. /// 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, &mut self,
context: &mut IrqContextTimeoutOrMaxSize, context: &mut IrqContextTimeoutOrMaxSize,
buf: &mut [u8], buf: &mut [u8],
@ -1150,7 +1226,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
if context.rx_idx == context.max_len { if context.rx_idx == context.max_len {
break; 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) { if let Some(byte) = self.read_handler(&mut result.errors, &read_result) {
buf[context.rx_idx] = byte; buf[context.rx_idx] = byte;
context.rx_idx += 1; context.rx_idx += 1;
@ -1224,7 +1300,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
context: &mut IrqContextTimeoutOrMaxSize, context: &mut IrqContextTimeoutOrMaxSize,
) { ) {
self.disable_rx_irq_sources(); self.disable_rx_irq_sources();
self.rx.disable(); self.0.disable();
res.bytes_read = context.rx_idx; res.bytes_read = context.rx_idx;
res.complete = true; res.complete = true;
context.mode = IrqReceptionMode::Idle; context.mode = IrqReceptionMode::Idle;
@ -1237,6 +1313,9 @@ impl<Uart: Instance> RxWithIrq<Uart> {
/// The user must ensure that these instances are not used to create multiple overlapping /// The user must ensure that these instances are not used to create multiple overlapping
/// UART drivers. /// UART drivers.
pub unsafe fn release(self) -> Uart { pub unsafe fn release(self) -> Uart {
self.rx.release() self.0.release()
} }
} }
pub mod asynch;
pub use asynch::*;

View File

@ -19,6 +19,7 @@ bitfield = ">=0.17, <=0.18"
max116xx-10bit = "0.3" max116xx-10bit = "0.3"
[dependencies.va108xx-hal] [dependencies.va108xx-hal]
path = "../va108xx-hal"
version = ">=0.8, <0.9" version = ">=0.8, <0.9"
features = ["rt"] features = ["rt"]

View File

@ -13,7 +13,7 @@ use va108xx_hal::{
gpio::{FilterType, InterruptEdge, PinsA}, gpio::{FilterType, InterruptEdge, PinsA},
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, 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::button::Button;
use vorago_reb1::leds::Leds; use vorago_reb1::leds::Leds;
@ -42,9 +42,9 @@ fn main() -> ! {
}; };
// Configure an edge interrupt on the button and route it to interrupt vector 15 // 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()).configure_edge_interrupt(
edge_irq, edge_irq,
IrqCfg::new(pac::interrupt::OC15, true, true), InterruptConfig::new(pac::interrupt::OC15, true, true),
Some(&mut dp.sysconfig), Some(&mut dp.sysconfig),
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
); );
@ -56,7 +56,7 @@ fn main() -> ! {
} }
set_up_ms_tick( set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
50.MHz(), 50.MHz(),

View File

@ -22,9 +22,9 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
spi::{Spi, SpiBase, SpiConfig}, 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::{ use vorago_reb1::max11619::{
max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup, max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup,
max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL, max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL,
@ -112,7 +112,7 @@ fn main() -> ! {
let mut dp = pac::Peripherals::take().unwrap(); let mut dp = pac::Peripherals::take().unwrap();
let tim0 = set_up_ms_tick( let tim0 = set_up_ms_tick(
IrqCfg::new(pac::Interrupt::OC0, true, true), InterruptConfig::new(pac::Interrupt::OC0, true, true),
&mut dp.sysconfig, &mut dp.sysconfig,
Some(&mut dp.irqsel), Some(&mut dp.irqsel),
SYS_CLK, SYS_CLK,
@ -135,10 +135,10 @@ fn main() -> ! {
); );
if MUX_MODE == MuxMode::PortB19to17 { if MUX_MODE == MuxMode::PortB19to17 {
port_mux(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok(); port_function_select(&mut dp.ioconfig, PortSel::PortB, 19, FunSel::Sel1).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok(); port_function_select(&mut dp.ioconfig, PortSel::PortB, 18, FunSel::Sel2).ok();
port_mux(&mut dp.ioconfig, PortSel::PortB, 17, FunSel::Sel1).ok(); port_function_select(&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, 16, FunSel::Sel1).ok();
} }
// Set the accelerometer chip select low in case the board slot is populated // Set the accelerometer chip select low in case the board slot is populated
let mut accel_cs = pinsa.pa16.into_push_pull_output(); let mut accel_cs = pinsa.pa16.into_push_pull_output();

View File

@ -7,7 +7,7 @@
use embedded_hal::digital::InputPin; use embedded_hal::digital::InputPin;
use va108xx_hal::{ use va108xx_hal::{
gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11},
pac, IrqCfg, pac, InterruptConfig,
}; };
pub struct Button { pub struct Button {
@ -30,37 +30,34 @@ impl Button {
} }
/// Configures an IRQ on edge. /// Configures an IRQ on edge.
pub fn edge_irq( pub fn configure_edge_interrupt(
mut self, &mut self,
edge_type: InterruptEdge, edge_type: InterruptEdge,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Self { ) {
self.button = self self.button
.button .configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel);
.interrupt_edge(edge_type, irq_cfg, syscfg, irqsel);
self
} }
/// Configures an IRQ on level. /// Configures an IRQ on level.
pub fn level_irq( pub fn configure_level_interrupt(
mut self, mut self,
level: InterruptLevel, level: InterruptLevel,
irq_cfg: IrqCfg, irq_cfg: InterruptConfig,
syscfg: Option<&mut pac::Sysconfig>, syscfg: Option<&mut pac::Sysconfig>,
irqsel: Option<&mut pac::Irqsel>, irqsel: Option<&mut pac::Irqsel>,
) -> Self { ) {
self.button = self.button.interrupt_level(level, irq_cfg, syscfg, irqsel); self.button
self .configure_level_interrupt(level, irq_cfg, syscfg, irqsel);
} }
/// Configures a filter on the button. This can be useful for debouncing the switch. /// 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 /// 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. /// [`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 { pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.button = self.button.filter_type(filter, clksel); self.button.configure_filter_type(filter, clksel);
self
} }
} }

View File

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

View File

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