diff --git a/board-tests/src/main.rs b/board-tests/src/main.rs index 17afbce..d59e3ac 100644 --- a/board-tests/src/main.rs +++ b/board-tests/src/main.rs @@ -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(), diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index 717adab..d00303a 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -9,6 +9,7 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" embedded-hal = "1" embedded-hal-async = "1" +embedded-io-async = "0.6" rtt-target = "0.6" panic-rtt-target = "0.2" diff --git a/examples/embassy/src/bin/async-gpio.rs b/examples/embassy/src/bin/async-gpio.rs index 5f8ce86..32e4e79 100644 --- a/examples/embassy/src/bin/async-gpio.rs +++ b/examples/embassy/src/bin/async-gpio.rs @@ -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(); } diff --git a/examples/embassy/src/bin/async-uart.rs b/examples/embassy/src/bin/async-uart.rs new file mode 100644 index 0000000..b84ba33 --- /dev/null +++ b/examples/embassy/src/bin/async-uart.rs @@ -0,0 +1,87 @@ +#![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 Embassy 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(); +} diff --git a/examples/rtic/Cargo.toml b/examples/rtic/Cargo.toml index 8df5541..f91e3ae 100644 --- a/examples/rtic/Cargo.toml +++ b/examples/rtic/Cargo.toml @@ -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" +va108xx-hal = { version = "0.8", path = "../../va108xx-hal" } vorago-reb1 = { path = "../../vorago-reb1" } diff --git a/examples/rtic/src/bin/blinky-button-rtic.rs b/examples/rtic/src/bin/blinky-button-rtic.rs index e844230..548a59e 100644 --- a/examples/rtic/src/bin/blinky-button-rtic.rs +++ b/examples/rtic/src/bin/blinky-button-rtic.rs @@ -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(), diff --git a/examples/rtic/src/bin/uart-echo-rtic.rs b/examples/rtic/src/bin/uart-echo-rtic.rs index 2a71a05..296e66e 100644 --- a/examples/rtic/src/bin/uart-echo-rtic.rs +++ b/examples/rtic/src/bin/uart-echo-rtic.rs @@ -23,12 +23,13 @@ mod app { gpio::PinsA, pac, prelude::*, - uart::{self, RxWithIrq, Tx}, + uart::{self, RxWithInterrupt, Tx}, + InterruptConfig, }; #[local] struct Local { - rx: RxWithIrq, + rx: RxWithInterrupt, tx: Tx, } @@ -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 { diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index 699dcd6..41540fa 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -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(); diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index 7f6969b..ed1b536 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -16,6 +16,7 @@ embedded-io = "0.6" cortex-m-semihosting = "0.5.0" [dependencies.va108xx-hal] +path = "../../va108xx-hal" version = "0.8" features = ["rt", "defmt"] diff --git a/examples/simple/examples/blinky.rs b/examples/simple/examples/blinky.rs index bee4a96..ae7385f 100644 --- a/examples/simple/examples/blinky.rs +++ b/examples/simple/examples/blinky.rs @@ -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(); diff --git a/examples/simple/examples/cascade.rs b/examples/simple/examples/cascade.rs index 6d96240..bac4745 100644 --- a/examples/simple/examples/cascade.rs +++ b/examples/simple/examples/cascade.rs @@ -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), ); diff --git a/examples/simple/examples/pwm.rs b/examples/simple/examples/pwm.rs index e51b5b0..57acac5 100644 --- a/examples/simple/examples/pwm.rs +++ b/examples/simple/examples/pwm.rs @@ -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(), diff --git a/examples/simple/examples/spi.rs b/examples/simple/examples/spi.rs index 11be074..7869b5a 100644 --- a/examples/simple/examples/spi.rs +++ b/examples/simple/examples/spi.rs @@ -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>> = RefCell::new(None); let spib_ref: RefCell>> = 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 { diff --git a/examples/simple/examples/timer-ticks.rs b/examples/simple/examples/timer-ticks.rs index 1c0cb84..3b4b275 100644 --- a/examples/simple/examples/timer-ticks.rs +++ b/examples/simple/examples/timer-ticks.rs @@ -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), ); diff --git a/examples/simple/examples/uart.rs b/examples/simple/examples/uart.rs index de94bed..72119bb 100644 --- a/examples/simple/examples/uart.rs +++ b/examples/simple/examples/uart.rs @@ -24,11 +24,17 @@ 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 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(); writeln!(tx, "Hello World\r").unwrap(); loop { diff --git a/flashloader/src/main.rs b/flashloader/src/main.rs index 0abd324..a87a669 100644 --- a/flashloader/src/main.rs +++ b/flashloader/src/main.rs @@ -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, + uart_rx: uart::RxWithInterrupt, uart_tx: uart::Tx, 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 { diff --git a/va108xx-embassy/src/lib.rs b/va108xx-embassy/src/lib.rs index 171fc49..26b9980 100644 --- a/va108xx-embassy/src/lib.rs +++ b/va108xx-embassy/src/lib.rs @@ -43,7 +43,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, @@ -221,7 +221,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 +239,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) diff --git a/va108xx-hal/CHANGELOG.md b/va108xx-hal/CHANGELOG.md index 13cc885..ff275ba 100644 --- a/va108xx-hal/CHANGELOG.md +++ b/va108xx-hal/CHANGELOG.md @@ -24,12 +24,29 @@ 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` ## Added - Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion methods. +- Asynchronous GPIO support. +- Asynchronous UART TX 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 diff --git a/va108xx-hal/Cargo.toml b/va108xx-hal/Cargo.toml index bba5650..fcb9ba9 100644 --- a/va108xx-hal/Cargo.toml +++ b/va108xx-hal/Cargo.toml @@ -19,6 +19,7 @@ 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" diff --git a/va108xx-hal/src/gpio/asynch.rs b/va108xx-hal/src/gpio/asynch.rs index cca921b..34eeb0b 100644 --- a/va108xx-hal/src/gpio/asynch.rs +++ b/va108xx-hal/src/gpio/asynch.rs @@ -5,11 +5,11 @@ //! 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. +//! 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( @@ -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), ) @@ -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), ); diff --git a/va108xx-hal/src/gpio/dynpin.rs b/va108xx-hal/src/gpio/dynpin.rs index 8dde283..57c7b6d 100644 --- a/va108xx-hal/src/gpio/dynpin.rs +++ b/va108xx-hal/src/gpio/dynpin.rs @@ -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 @@ -351,7 +351,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>, ) { @@ -366,18 +366,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) }; } } @@ -435,7 +435,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> { @@ -453,7 +453,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> { diff --git a/va108xx-hal/src/gpio/pin.rs b/va108xx-hal/src/gpio/pin.rs index 32f8653..2e11f86 100644 --- a/va108xx-hal/src/gpio/pin.rs +++ b/va108xx-hal/src/gpio/pin.rs @@ -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; @@ -454,7 +454,7 @@ impl Pin { 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 +581,10 @@ impl Pin> { 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 +592,10 @@ impl Pin> { 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 +631,7 @@ impl Pin> { 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 +642,7 @@ impl Pin> { 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 +654,7 @@ impl Pin> { impl Pin> { /// 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); } } diff --git a/va108xx-hal/src/lib.rs b/va108xx-hal/src/lib.rs index b61335d..b12f853 100644 --- a/va108xx-hal/src/lib.rs +++ b/va108xx-hal/src/lib.rs @@ -25,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, @@ -47,31 +49,38 @@ 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 #[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)] 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, @@ -105,7 +114,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); } @@ -113,6 +122,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); } diff --git a/va108xx-hal/src/pwm.rs b/va108xx-hal/src/pwm.rs index 7afdaf7..5fa487b 100644 --- a/va108xx-hal/src/pwm.rs +++ b/va108xx-hal/src/pwm.rs @@ -80,7 +80,7 @@ where pin } - pub fn reduce(self) -> ReducedPwmPin { + pub fn downgrade(self) -> ReducedPwmPin { self.inner } @@ -277,6 +277,24 @@ impl ReducedPwmPin { } } +impl From> for ReducedPwmPin +where + (Pin, Tim): ValidTimAndPin, +{ + fn from(value: PwmPin) -> Self { + value.downgrade() + } +} + +impl From> for ReducedPwmPin +where + (Pin, Tim): ValidTimAndPin, +{ + fn from(value: PwmPin) -> Self { + value.downgrade() + } +} + impl From> for ReducedPwmPin { fn from(other: ReducedPwmPin) -> Self { let mut pwmb = Self { diff --git a/va108xx-hal/src/timer.rs b/va108xx-hal/src/timer.rs index 117ffa6..8bf10f3 100644 --- a/va108xx-hal/src/timer.rs +++ b/va108xx-hal/src/timer.rs @@ -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, @@ -362,7 +362,7 @@ unsafe impl TimRegInterface for TimDynRegister { pub struct CountdownTimer { tim: Tim, curr_freq: Hertz, - irq_cfg: Option, + irq_cfg: Option, sys_clk: Hertz, rst_val: u32, last_cnt: u32, @@ -415,13 +415,13 @@ impl CountdownTimer { 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 +430,7 @@ impl CountdownTimer { 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 +520,8 @@ impl CountdownTimer { 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 +719,7 @@ impl embedded_hal::delay::DelayNs for CountdownTimer { // 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( - irq_cfg: IrqCfg, + irq_cfg: InterruptConfig, sys_cfg: &mut pac::Sysconfig, irq_sel: Option<&mut pac::Irqsel>, sys_clk: impl Into, diff --git a/va108xx-hal/src/uart/asynch.rs b/va108xx-hal/src/uart/asynch.rs new file mode 100644 index 0000000..97c24f7 --- /dev/null +++ b/va108xx-hal/src/uart/asynch.rs @@ -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>; 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: 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(tx: &mut Tx, 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; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + 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 { + tx: Tx, +} + +impl TxAsync { + pub fn new(tx: Tx) -> Self { + Self { tx } + } + + pub fn release(self) -> Tx { + 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 embedded_io::ErrorType for TxAsync { + type Error = TxOverrunError; +} + +impl Write for TxAsync { + /// 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 { + let fut = unsafe { TxFuture::new(&mut self.tx, buf) }; + fut.await + } +} diff --git a/va108xx-hal/src/uart.rs b/va108xx-hal/src/uart/mod.rs similarity index 85% rename from va108xx-hal/src/uart.rs rename to va108xx-hal/src/uart/mod.rs index fa6daa5..944b994 100644 --- a/va108xx-hal/src/uart.rs +++ b/va108xx-hal/src/uart/mod.rs @@ -9,10 +9,10 @@ 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, @@ -48,6 +48,11 @@ impl Pins for (Pin, Pin) {} // 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")] @@ -373,6 +378,16 @@ pub trait Instance: Deref { /// 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,9 +395,11 @@ 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 _ } @@ -393,9 +410,11 @@ 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 _ } @@ -592,15 +611,51 @@ where UartInstance: Instance, PinsInstance: Pins, { - 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, + uart: UartInstance, + pins: PinsInstance, + config: impl Into, + 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, uart: UartInstance, pins: PinsInstance, config: impl Into, + ) -> 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, + uart: UartInstance, + pins: PinsInstance, + config: impl Into, + opt_irq_cfg: Option, ) -> 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 +663,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 @@ -686,11 +755,13 @@ where /// Serial receiver. /// /// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Rx(Uart); +pub struct Rx { + uart: Uart, +} impl Rx { fn new(uart: Uart) -> Self { - Self(uart) + Self { uart } } /// Direct access to the peripheral structure. @@ -699,22 +770,22 @@ impl Rx { /// /// You must ensure that only registers related to the operation of the RX side are used. 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 enable(&mut self) { - self.0.enable().modify(|_, w| w.rxenable().set_bit()); + self.uart.enable().modify(|_, w| w.rxenable().set_bit()); } #[inline] 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. @@ -725,7 +796,7 @@ impl Rx { /// 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 { - 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 +812,15 @@ impl Rx { /// 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 { - RxWithIrq::new(self, sysconfig, irqsel, interrupt) + pub fn into_rx_with_irq(self) -> RxWithInterrupt { + RxWithInterrupt::new(self) } pub fn release(self) -> Uart { - self.0 + self.uart } } @@ -811,14 +877,51 @@ impl embedded_io::Read for Rx { } } +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 /// /// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Tx(Uart); +pub struct Tx { + uart: Uart, +} impl Tx { + /// 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 { - Self(uart) + Self { uart } } /// Direct access to the peripheral structure. @@ -827,22 +930,45 @@ impl Tx { /// /// You must ensure that only registers related to the operation of the TX side are used. 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 +979,7 @@ impl Tx { /// 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 +994,11 @@ impl Tx { /// 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 { + TxAsync::new(self) } } @@ -931,36 +1061,23 @@ impl embedded_io::Write for Tx { /// 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 /// start reading the next packet. -pub struct RxWithIrq { - pub rx: Rx, - pub interrupt: pac::Interrupt, -} +pub struct RxWithInterrupt(Rx); -impl RxWithIrq { - pub fn new( - rx: 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 } +impl RxWithInterrupt { + pub fn new(rx: Rx) -> 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. 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] @@ -1006,7 +1123,7 @@ impl RxWithIrq { 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 +1134,7 @@ impl RxWithIrq { /// 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 +1157,7 @@ impl RxWithIrq { 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 +1191,7 @@ impl RxWithIrq { /// 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 +1240,7 @@ impl RxWithIrq { 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; @@ -1197,7 +1314,7 @@ impl RxWithIrq { 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 +1327,9 @@ impl RxWithIrq { /// 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 asynch; +pub use asynch::*; diff --git a/vorago-reb1/Cargo.toml b/vorago-reb1/Cargo.toml index 31ee76e..6b64996 100644 --- a/vorago-reb1/Cargo.toml +++ b/vorago-reb1/Cargo.toml @@ -19,6 +19,7 @@ bitfield = ">=0.17, <=0.18" max116xx-10bit = "0.3" [dependencies.va108xx-hal] +path = "../va108xx-hal" version = ">=0.8, <0.9" features = ["rt"] diff --git a/vorago-reb1/examples/adxl343-accelerometer.rs b/vorago-reb1/examples/adxl343-accelerometer.rs index ce4c3f5..8f99e4d 100644 --- a/vorago-reb1/examples/adxl343-accelerometer.rs +++ b/vorago-reb1/examples/adxl343-accelerometer.rs @@ -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(), diff --git a/vorago-reb1/examples/blinky-button-irq.rs b/vorago-reb1/examples/blinky-button-irq.rs index 0f29f5f..986a10b 100644 --- a/vorago-reb1/examples/blinky-button-irq.rs +++ b/vorago-reb1/examples/blinky-button-irq.rs @@ -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(), diff --git a/vorago-reb1/examples/blinky-leds.rs b/vorago-reb1/examples/blinky-leds.rs index 0e0e0cf..2650b09 100644 --- a/vorago-reb1/examples/blinky-leds.rs +++ b/vorago-reb1/examples/blinky-leds.rs @@ -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); } } } diff --git a/vorago-reb1/examples/max11619-adc.rs b/vorago-reb1/examples/max11619-adc.rs index 5936e68..e400096 100644 --- a/vorago-reb1/examples/max11619-adc.rs +++ b/vorago-reb1/examples/max11619-adc.rs @@ -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(); diff --git a/vorago-reb1/src/button.rs b/vorago-reb1/src/button.rs index 4c7a03d..81e1b87 100644 --- a/vorago-reb1/src/button.rs +++ b/vorago-reb1/src/button.rs @@ -7,7 +7,7 @@ use embedded_hal::digital::InputPin; use va108xx_hal::{ gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, - pac, IrqCfg, + pac, InterruptConfig, }; pub struct Button { @@ -30,37 +30,34 @@ impl Button { } /// 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.button + .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.button + .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.button.configure_filter_type(filter, clksel); } } diff --git a/vscode/launch.json b/vscode/launch.json index 37982c6..20edd1a 100644 --- a/vscode/launch.json +++ b/vscode/launch.json @@ -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,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" + } + ] + } + }, ] } \ No newline at end of file diff --git a/vscode/tasks.json b/vscode/tasks.json index 796f555..671efe2 100644 --- a/vscode/tasks.json +++ b/vscode/tasks.json @@ -276,6 +276,16 @@ "async-gpio" ] }, + { + "label": "async-uart", + "type": "shell", + "command": "~/.cargo/bin/cargo", // note: full path to the cargo + "args": [ + "build", + "--bin", + "async-uart" + ] + }, { "label": "bootloader", "type": "shell",