diff --git a/board-tests/src/main.rs b/board-tests/src/main.rs index 7f9d71c..41fdd19 100644 --- a/board-tests/src/main.rs +++ b/board-tests/src/main.rs @@ -6,10 +6,7 @@ #![no_std] use cortex_m_rt::entry; -use embedded_hal::{ - delay::DelayNs, - digital::{InputPin, OutputPin, StatefulOutputPin}, -}; +use embedded_hal::delay::DelayNs; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va108xx_hal::{ @@ -67,35 +64,35 @@ fn main() -> ! { TestCase::TestBasic => { // Tie PORTA[0] to PORTA[1] for these tests! let mut out = pinsa.pa0.into_readable_push_pull_output(); - let mut input = pinsa.pa1.into_floating_input(); - out.set_high().unwrap(); - assert!(input.is_high().unwrap()); - out.set_low().unwrap(); - assert!(input.is_low().unwrap()); + let input = pinsa.pa1.into_floating_input(); + out.set_high(); + assert!(input.is_high()); + out.set_low(); + assert!(input.is_low()); } TestCase::TestPullup => { // Tie PORTA[0] to PORTA[1] for these tests! - let mut input = pinsa.pa1.into_pull_up_input(); - assert!(input.is_high().unwrap()); + let input = pinsa.pa1.into_pull_up_input(); + assert!(input.is_high()); let mut out = pinsa.pa0.into_readable_push_pull_output(); - out.set_low().unwrap(); - assert!(input.is_low().unwrap()); - out.set_high().unwrap(); - assert!(input.is_high().unwrap()); + out.set_low(); + assert!(input.is_low()); + out.set_high(); + assert!(input.is_high()); out.into_floating_input(); - assert!(input.is_high().unwrap()); + assert!(input.is_high()); } TestCase::TestPulldown => { // Tie PORTA[0] to PORTA[1] for these tests! - let mut input = pinsa.pa1.into_pull_down_input(); - assert!(input.is_low().unwrap()); + let input = pinsa.pa1.into_pull_down_input(); + assert!(input.is_low()); let mut out = pinsa.pa0.into_push_pull_output(); - out.set_low().unwrap(); - assert!(input.is_low().unwrap()); - out.set_high().unwrap(); - assert!(input.is_high().unwrap()); + out.set_low(); + assert!(input.is_low()); + out.set_high(); + assert!(input.is_high()); out.into_floating_input(); - assert!(input.is_low().unwrap()); + assert!(input.is_low()); } TestCase::TestMask => { // Tie PORTA[0] to PORTA[1] for these tests! @@ -110,11 +107,11 @@ fn main() -> ! { TestCase::PortB => { // Tie PORTB[22] to PORTB[23] for these tests! let mut out = pinsb.pb22.into_readable_push_pull_output(); - let mut input = pinsb.pb23.into_floating_input(); - out.set_high().unwrap(); - assert!(input.is_high().unwrap()); - out.set_low().unwrap(); - assert!(input.is_low().unwrap()); + let input = pinsb.pb23.into_floating_input(); + out.set_high(); + assert!(input.is_high()); + out.set_low(); + assert!(input.is_low()); } TestCase::Perid => { assert_eq!(PinsA::get_perid(), 0x004007e1); @@ -124,15 +121,15 @@ fn main() -> ! { let mut output_pulsed = pinsa.pa0.into_push_pull_output(); output_pulsed.configure_pulse_mode(true, PinState::Low); rprintln!("Pulsing high 10 times.."); - output_pulsed.set_low().unwrap(); + output_pulsed.set_low(); for _ in 0..10 { - output_pulsed.set_high().unwrap(); + output_pulsed.set_high(); cortex_m::asm::delay(25_000_000); } output_pulsed.configure_pulse_mode(true, PinState::High); rprintln!("Pulsing low 10 times.."); for _ in 0..10 { - output_pulsed.set_low().unwrap(); + output_pulsed.set_low(); cortex_m::asm::delay(25_000_000); } } @@ -144,9 +141,9 @@ fn main() -> ! { let mut out_2 = pinsa.pa3.into_readable_push_pull_output(); out_2.configure_delay(true, true); for _ in 0..20 { - out_0.toggle().unwrap(); - out_1.toggle().unwrap(); - out_2.toggle().unwrap(); + out_0.toggle(); + out_1.toggle(); + out_2.toggle(); cortex_m::asm::delay(25_000_000); } } @@ -159,18 +156,18 @@ fn main() -> ! { dp.tim0, ); for _ in 0..5 { - led1.toggle().ok(); + led1.toggle(); ms_timer.delay_ms(500); - led1.toggle().ok(); + led1.toggle(); ms_timer.delay_ms(500); } let mut delay_timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim1); let mut pa0 = pinsa.pa0.into_readable_push_pull_output(); for _ in 0..5 { - led1.toggle().ok(); + led1.toggle(); delay_timer.delay_ms(500); - led1.toggle().ok(); + led1.toggle(); delay_timer.delay_ms(500); } let ahb_freq: Hertz = 50.MHz(); @@ -178,13 +175,13 @@ fn main() -> ! { // Test usecond delay using both TIM peripheral and SYST. Use the release image if you // want to verify the timings! loop { - pa0.toggle().ok(); + pa0.toggle(); delay_timer.delay_us(50); - pa0.toggle().ok(); + pa0.toggle(); delay_timer.delay_us(50); - pa0.toggle_with_toggle_reg(); + pa0.toggle(); syst_delay.delay_us(50); - pa0.toggle_with_toggle_reg(); + pa0.toggle(); syst_delay.delay_us(50); } } @@ -192,7 +189,7 @@ fn main() -> ! { rprintln!("Test success"); loop { - led1.toggle().ok(); + led1.toggle(); cortex_m::asm::delay(25_000_000); } } diff --git a/examples/embassy/src/bin/async-gpio.rs b/examples/embassy/src/bin/async-gpio.rs index 2a44cc4..a722b66 100644 --- a/examples/embassy/src/bin/async-gpio.rs +++ b/examples/embassy/src/bin/async-gpio.rs @@ -9,7 +9,6 @@ use embassy_executor::Spawner; use embassy_sync::channel::{Receiver, Sender}; use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel}; use embassy_time::{Duration, Instant, Timer}; -use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin}; use embedded_hal_async::digital::Wait; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; @@ -116,7 +115,7 @@ async fn main(spawner: Spawner) { rprintln!("Example done, toggling LED0"); loop { - led0.toggle().unwrap(); + led0.toggle(); Timer::after(Duration::from_millis(500)).await; } } diff --git a/examples/embassy/src/bin/async-uart-rx.rs b/examples/embassy/src/bin/async-uart-rx.rs index e37eaab..e0ee129 100644 --- a/examples/embassy/src/bin/async-uart-rx.rs +++ b/examples/embassy/src/bin/async-uart-rx.rs @@ -18,7 +18,6 @@ use core::cell::RefCell; use critical_section::Mutex; use embassy_executor::Spawner; use embassy_time::Instant; -use embedded_hal::digital::StatefulOutputPin; use embedded_io::Write; use embedded_io_async::Read; use heapless::spsc::{Consumer, Producer, Queue}; @@ -30,9 +29,9 @@ use va108xx_hal::{ pac::{self, interrupt}, prelude::*, uart::{ - self, on_interrupt_uart_b_overwriting, - rx_asynch::{on_interrupt_uart_a, RxAsync}, - RxAsyncSharedConsumer, Tx, + self, on_interrupt_rx_overwriting, + rx_asynch::{on_interrupt_rx, RxAsync}, + Bank, RxAsyncOverwriting, Tx, }, InterruptConfig, }; @@ -106,16 +105,16 @@ async fn main(spawner: Spawner) { *CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b); }); let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a); - let async_rx_uart_b = RxAsyncSharedConsumer::new(rx_uart_b, &CONSUMER_UART_B); + let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B); spawner .spawn(uart_b_task(async_rx_uart_b, tx_uart_b)) .unwrap(); let mut buf = [0u8; 256]; loop { rprintln!("Current time UART A: {}", Instant::now().as_secs()); - led0.toggle().ok(); - led1.toggle().ok(); - led2.toggle().ok(); + led0.toggle(); + led1.toggle(); + led2.toggle(); let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap(); let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap(); rprintln!( @@ -128,7 +127,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer, mut tx: Tx) { +async fn uart_b_task(mut async_rx: RxAsyncOverwriting, mut tx: Tx) { let mut buf = [0u8; 256]; loop { rprintln!("Current time UART B: {}", Instant::now().as_secs()); @@ -149,7 +148,7 @@ async fn uart_b_task(mut async_rx: RxAsyncSharedConsumer, mut t fn OC2() { let mut prod = critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_uart_a(&mut prod); + let errors = on_interrupt_rx(Bank::A, &mut prod); critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { @@ -162,7 +161,7 @@ fn OC2() { fn OC3() { let mut prod = critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap()); - let errors = on_interrupt_uart_b_overwriting(&mut prod, &CONSUMER_UART_B); + let errors = on_interrupt_rx_overwriting(Bank::B, &mut prod, &CONSUMER_UART_B); critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod)); // In a production app, we could use a channel to send the errors to the main task. if let Err(errors) = errors { diff --git a/examples/embassy/src/bin/async-uart-tx.rs b/examples/embassy/src/bin/async-uart-tx.rs index 0c8345b..9963ecf 100644 --- a/examples/embassy/src/bin/async-uart-tx.rs +++ b/examples/embassy/src/bin/async-uart-tx.rs @@ -12,7 +12,6 @@ #![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}; @@ -21,7 +20,7 @@ use va108xx_hal::{ gpio::PinsA, pac::{self, interrupt}, prelude::*, - uart::{self, on_interrupt_uart_a_tx, TxAsync}, + uart::{self, on_interrupt_tx, Bank, TxAsync}, InterruptConfig, }; @@ -75,9 +74,9 @@ async fn main(_spawner: Spawner) { let mut idx = 0; loop { rprintln!("Current time: {}", Instant::now().as_secs()); - led0.toggle().ok(); - led1.toggle().ok(); - led2.toggle().ok(); + led0.toggle(); + led1.toggle(); + led2.toggle(); let _written = async_tx .write(STR_LIST[idx].as_bytes()) .await @@ -93,5 +92,5 @@ async fn main(_spawner: Spawner) { #[interrupt] #[allow(non_snake_case)] fn OC2() { - on_interrupt_uart_a_tx(); + on_interrupt_tx(Bank::A); } diff --git a/examples/embassy/src/main.rs b/examples/embassy/src/main.rs index 690fe26..fc15fd7 100644 --- a/examples/embassy/src/main.rs +++ b/examples/embassy/src/main.rs @@ -2,7 +2,6 @@ #![no_main] use embassy_executor::Spawner; use embassy_time::{Duration, Instant, Ticker}; -use embedded_hal::digital::StatefulOutputPin; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va108xx_embassy::embassy; @@ -60,8 +59,8 @@ async fn main(_spawner: Spawner) { loop { ticker.next().await; rprintln!("Current time: {}", Instant::now().as_secs()); - led0.toggle().ok(); - led1.toggle().ok(); - led2.toggle().ok(); + led0.toggle(); + led1.toggle(); + led2.toggle(); } } diff --git a/examples/rtic/Cargo.toml b/examples/rtic/Cargo.toml index 9070a3b..f56f9ed 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.9" -vorago-reb1 = "0.7" +va108xx-hal = { version = "0.9", path = "../../va108xx-hal" } +vorago-reb1 = { version = "0.7", path = "../../vorago-reb1" } diff --git a/examples/rtic/src/bin/blinky-button-rtic.rs b/examples/rtic/src/bin/blinky-button-rtic.rs index 548a59e..bba2d4c 100644 --- a/examples/rtic/src/bin/blinky-button-rtic.rs +++ b/examples/rtic/src/bin/blinky-button-rtic.rs @@ -69,18 +69,16 @@ mod app { // Configure an edge interrupt on the button and route it to interrupt vector 15 let mut button = Button::new(pinsa.pa11.into_floating_input()); - button.configure_edge_interrupt( - edge_irq, - 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.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000); } + button.configure_and_enable_edge_interrupt( + edge_irq, + InterruptConfig::new(pac::interrupt::OC15, true, true), + ); let mut leds = Leds::new( pinsa.pa10.into_push_pull_output(), pinsa.pa7.into_push_pull_output(), diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index 41540fa..ad6ccd9 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -5,7 +5,6 @@ #[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])] mod app { use cortex_m::asm; - use embedded_hal::digital::StatefulOutputPin; use panic_rtt_target as _; use rtic_example::SYSCLK_FREQ; use rtic_monotonics::systick::prelude::*; @@ -58,9 +57,9 @@ mod app { async fn blinky(cx: blinky::Context) { loop { rprintln!("toggling LEDs"); - cx.local.led0.toggle().ok(); - cx.local.led1.toggle().ok(); - cx.local.led2.toggle().ok(); + cx.local.led0.toggle(); + cx.local.led1.toggle(); + cx.local.led2.toggle(); Mono::delay(1000.millis()).await; } } diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index 05fc339..158dd37 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -16,8 +16,10 @@ embedded-io = "0.6" cortex-m-semihosting = "0.5.0" [dependencies.va108xx-hal] +path = "../../va108xx-hal" version = "0.9" features = ["rt", "defmt"] [dependencies.vorago-reb1] +path = "../../vorago-reb1" version = "0.7" diff --git a/examples/simple/examples/blinky.rs b/examples/simple/examples/blinky.rs index ae7385f..bcdfe0c 100644 --- a/examples/simple/examples/blinky.rs +++ b/examples/simple/examples/blinky.rs @@ -7,10 +7,7 @@ #![no_std] use cortex_m_rt::entry; -use embedded_hal::{ - delay::DelayNs, - digital::{OutputPin, StatefulOutputPin}, -}; +use embedded_hal::delay::DelayNs; use panic_halt as _; use va108xx_hal::{ gpio::PinsA, @@ -38,21 +35,21 @@ fn main() -> ! { let mut led2 = porta.pa7.into_readable_push_pull_output(); let mut led3 = porta.pa6.into_readable_push_pull_output(); for _ in 0..10 { - led1.set_low().ok(); - led2.set_low().ok(); - led3.set_low().ok(); + led1.set_low(); + led2.set_low(); + led3.set_low(); delay_ms.delay_ms(200); - led1.set_high().ok(); - led2.set_high().ok(); - led3.set_high().ok(); + led1.set_high(); + led2.set_high(); + led3.set_high(); delay_tim1.delay_ms(200); } loop { - led1.toggle().ok(); + led1.toggle(); delay_ms.delay_ms(200); - led2.toggle().ok(); + led2.toggle(); delay_tim1.delay_ms(200); - led3.toggle().ok(); + led3.toggle(); delay_ms.delay_ms(200); } } diff --git a/va108xx-hal/CHANGELOG.md b/va108xx-hal/CHANGELOG.md index e102d7c..fd7fb6f 100644 --- a/va108xx-hal/CHANGELOG.md +++ b/va108xx-hal/CHANGELOG.md @@ -16,7 +16,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Changed -- Missing GPIO API replacements from `x` to `configure_x` +- Larger refactoring of GPIO library. The edge and level interrupt configurator functions do not + enable interrupts anymore. Instead, there are explicit `enbable_interrupt` and + `disable_interrupt` methods - Renamed GPIO `DynGroup` to `Port` - Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio` into `on_interrupt_for_async_gpio_for_port` which expects a Port argument diff --git a/va108xx-hal/src/gpio/asynch.rs b/va108xx-hal/src/gpio/asynch.rs index 7e23648..36c4ab8 100644 --- a/va108xx-hal/src/gpio/asynch.rs +++ b/va108xx-hal/src/gpio/asynch.rs @@ -13,10 +13,9 @@ use core::future::Future; use embassy_sync::waitqueue::AtomicWaker; -use embedded_hal::digital::InputPin; use embedded_hal_async::digital::Wait; use portable_atomic::AtomicBool; -use va108xx::{self as pac, Irqsel, Sysconfig}; +use va108xx::{self as pac}; use crate::InterruptConfig; @@ -96,20 +95,6 @@ pub struct InputPinFuture { } impl InputPinFuture { - /// # Safety - /// - /// This calls [Self::new_with_dyn_pin] but uses [pac::Peripherals::steal] to get the system configuration - /// and IRQ selection peripherals. Users must ensure that the registers and configuration - /// related to this input pin are not being used elsewhere concurrently. - pub unsafe fn new_unchecked_with_dyn_pin( - pin: &mut DynPin, - irq: pac::Interrupt, - edge: InterruptEdge, - ) -> Result { - let mut periphs = pac::Peripherals::steal(); - Self::new_with_dyn_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel) - } - #[inline] pub fn pin_group_to_waker_and_edge_detection_group( group: Port, @@ -124,24 +109,17 @@ impl InputPinFuture { pin: &mut DynPin, irq: pac::Interrupt, edge: InterruptEdge, - sys_cfg: &mut Sysconfig, - irq_sel: &mut Irqsel, ) -> Result { if !pin.is_input_pin() { return Err(InvalidPinTypeError(pin.mode())); } let (waker_group, edge_detection_group) = - Self::pin_group_to_waker_and_edge_detection_group(pin.id().group); - edge_detection_group[pin.id().num as usize] + Self::pin_group_to_waker_and_edge_detection_group(pin.id().port()); + edge_detection_group[pin.id().num() as usize] .store(false, core::sync::atomic::Ordering::Relaxed); - pin.configure_edge_interrupt( - edge, - InterruptConfig::new(irq, true, true), - Some(sys_cfg), - Some(irq_sel), - ) - .unwrap(); + pin.configure_edge_interrupt(edge).unwrap(); + pin.enable_interrupt(InterruptConfig::new(irq, true, true)); Ok(Self { pin_id: pin.id(), waker_group, @@ -149,37 +127,17 @@ impl InputPinFuture { }) } - /// # Safety - /// - /// This calls [Self::new_with_pin] but uses [pac::Peripherals::steal] to get the system configuration - /// and IRQ selection peripherals. Users must ensure that the registers and configuration - /// related to this input pin are not being used elsewhere concurrently. - pub unsafe fn new_unchecked_with_pin( - pin: &mut Pin>, - irq: pac::Interrupt, - edge: InterruptEdge, - ) -> Self { - let mut periphs = pac::Peripherals::steal(); - Self::new_with_pin(pin, irq, edge, &mut periphs.sysconfig, &mut periphs.irqsel) - } - pub fn new_with_pin( pin: &mut Pin>, irq: pac::Interrupt, edge: InterruptEdge, - sys_cfg: &mut Sysconfig, - irq_sel: &mut Irqsel, ) -> Self { let (waker_group, edge_detection_group) = - Self::pin_group_to_waker_and_edge_detection_group(pin.id().group); - edge_detection_group[pin.id().num as usize] + Self::pin_group_to_waker_and_edge_detection_group(pin.id().port()); + edge_detection_group[pin.id().num() as usize] .store(false, core::sync::atomic::Ordering::Relaxed); - pin.configure_edge_interrupt( - edge, - InterruptConfig::new(irq, true, true), - Some(sys_cfg), - Some(irq_sel), - ); + pin.configure_edge_interrupt(edge); + pin.enable_interrupt(InterruptConfig::new(irq, true, true)); Self { pin_id: pin.id(), edge_detection_group, @@ -190,18 +148,8 @@ impl InputPinFuture { impl Drop for InputPinFuture { fn drop(&mut self) { - let periphs = unsafe { pac::Peripherals::steal() }; - if self.pin_id.group == Port::A { - periphs - .porta - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) }); - } else { - periphs - .porta - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id.num)) }); - } + // The API ensures that we actually own the pin, so stealing it here is okay. + unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(false); } } @@ -211,7 +159,7 @@ impl Future for InputPinFuture { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - let idx = self.pin_id.num as usize; + let idx = self.pin_id.num() as usize; self.waker_group[idx].register(cx.waker()); if self.edge_detection_group[idx].swap(false, core::sync::atomic::Ordering::Relaxed) { return core::task::Poll::Ready(()); @@ -243,15 +191,10 @@ impl InputDynPinAsync { /// /// This returns immediately if the pin is already high. pub async fn wait_for_high(&mut self) { - let fut = unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_dyn_pin( - &mut self.pin, - self.irq, - InterruptEdge::LowToHigh, - ) - .unwrap() - }; + // Unwrap okay, checked pin in constructor. + let fut = + InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh) + .unwrap(); if self.pin.is_high().unwrap() { return; } @@ -262,15 +205,10 @@ impl InputDynPinAsync { /// /// This returns immediately if the pin is already high. pub async fn wait_for_low(&mut self) { - let fut = unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_dyn_pin( - &mut self.pin, - self.irq, - InterruptEdge::HighToLow, - ) - .unwrap() - }; + // Unwrap okay, checked pin in constructor. + let fut = + InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow) + .unwrap(); if self.pin.is_low().unwrap() { return; } @@ -279,44 +217,26 @@ impl InputDynPinAsync { /// Asynchronously wait until the pin sees a falling edge. pub async fn wait_for_falling_edge(&mut self) { - unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_dyn_pin( - &mut self.pin, - self.irq, - InterruptEdge::HighToLow, - ) + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow) .unwrap() - } - .await; + .await; } /// Asynchronously wait until the pin sees a rising edge. pub async fn wait_for_rising_edge(&mut self) { - unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_dyn_pin( - &mut self.pin, - self.irq, - InterruptEdge::LowToHigh, - ) + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh) .unwrap() - } - .await; + .await; } /// Asynchronously wait until the pin sees any edge (either rising or falling). pub async fn wait_for_any_edge(&mut self) { - unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_dyn_pin( - &mut self.pin, - self.irq, - InterruptEdge::BothEdges, - ) + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges) .unwrap() - } - .await; + .await; } pub fn release(self) -> DynPin { @@ -375,14 +295,8 @@ impl InputPinAsync { /// /// This returns immediately if the pin is already high. pub async fn wait_for_high(&mut self) { - let fut = unsafe { - InputPinFuture::new_unchecked_with_pin( - &mut self.pin, - self.irq, - InterruptEdge::LowToHigh, - ) - }; - if self.pin.is_high().unwrap() { + let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh); + if self.pin.is_high() { return; } fut.await; @@ -392,14 +306,8 @@ impl InputPinAsync { /// /// This returns immediately if the pin is already high. pub async fn wait_for_low(&mut self) { - let fut = unsafe { - InputPinFuture::new_unchecked_with_pin( - &mut self.pin, - self.irq, - InterruptEdge::HighToLow, - ) - }; - if self.pin.is_low().unwrap() { + let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow); + if self.pin.is_low() { return; } fut.await; @@ -407,40 +315,19 @@ impl InputPinAsync { /// Asynchronously wait until the pin sees falling edge. pub async fn wait_for_falling_edge(&mut self) { - unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_pin( - &mut self.pin, - self.irq, - InterruptEdge::HighToLow, - ) - } - .await; + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await; } /// Asynchronously wait until the pin sees rising edge. pub async fn wait_for_rising_edge(&mut self) { - unsafe { - // Unwrap okay, checked pin in constructor. - InputPinFuture::new_unchecked_with_pin( - &mut self.pin, - self.irq, - InterruptEdge::LowToHigh, - ) - } - .await; + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh).await; } /// Asynchronously wait until the pin sees any edge (either rising or falling). pub async fn wait_for_any_edge(&mut self) { - unsafe { - InputPinFuture::new_unchecked_with_pin( - &mut self.pin, - self.irq, - InterruptEdge::BothEdges, - ) - } - .await; + InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await; } pub fn release(self) -> Pin> { diff --git a/va108xx-hal/src/gpio/dynpin.rs b/va108xx-hal/src/gpio/dynpin.rs index 29725d6..993150c 100644 --- a/va108xx-hal/src/gpio/dynpin.rs +++ b/va108xx-hal/src/gpio/dynpin.rs @@ -58,10 +58,9 @@ use super::{ pin::{FilterType, Pin, PinId, PinMode}, - reg::RegisterInterface, - InputDynPinAsync, InterruptEdge, InterruptLevel, PinState, + InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port, }; -use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel, InterruptConfig}; +use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel}; //================================================================================================== // DynPinMode configurations @@ -156,50 +155,92 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3) // DynGroup & DynPinId //================================================================================================== -pub type DynGroup = super::Port; +pub type DynGroup = Port; -/// Value-level `struct` representing pin IDs #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DynPinId { - pub group: super::Port, - pub num: u8, + port: Port, + num: u8, } -//================================================================================================== -// DynRegisters -//================================================================================================== +impl DynPinId { + pub const fn new(port: Port, num: u8) -> Self { + DynPinId { port, num } + } -/// Provide a safe register interface for [`DynPin`]s -/// -/// This `struct` takes ownership of a [`DynPinId`] and provides an API to -/// access the corresponding regsiters. -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct DynRegisters(DynPinId); - -// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] -// guarantees that each pin is a singleton, so this implementation is safe. -unsafe impl RegisterInterface for DynRegisters { - #[inline] - fn id(&self) -> DynPinId { - self.0 + pub const fn port(&self) -> Port { + self.port + } + pub const fn num(&self) -> u8 { + self.num } } -impl DynRegisters { - /// Create a new instance of [`DynRegisters`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`DynPinId`] +//================================================================================================== +// ModeFields +//================================================================================================== + +/// Collect all fields needed to set the [`PinMode`](super::PinMode) +#[derive(Default)] +struct ModeFields { + dir: bool, + opendrn: bool, + pull_en: bool, + /// true for pullup, false for pulldown + pull_dir: bool, + funsel: u8, + enb_input: bool, +} + +impl From for ModeFields { #[inline] - unsafe fn new(id: DynPinId) -> Self { - DynRegisters(id) + fn from(mode: DynPinMode) -> Self { + let mut fields = Self::default(); + match mode { + DynPinMode::Input(config) => { + fields.dir = false; + fields.funsel = FunSel::Sel0 as u8; + match config { + DynInput::Floating => (), + DynInput::PullUp => { + fields.pull_en = true; + fields.pull_dir = true; + } + DynInput::PullDown => { + fields.pull_en = true; + } + } + } + DynPinMode::Output(config) => { + fields.dir = true; + fields.funsel = FunSel::Sel0 as u8; + match config { + DynOutput::PushPull => (), + DynOutput::OpenDrain => { + fields.opendrn = true; + } + DynOutput::ReadableOpenDrain => { + fields.enb_input = true; + fields.opendrn = true; + } + DynOutput::ReadablePushPull => { + fields.enb_input = true; + } + } + } + DynPinMode::Alternate(config) => { + fields.funsel = config as u8; + } + } + fields } } +/// Type definition to avoid confusion: These register blocks are identical +type PortRegisterBlock = pac::porta::RegisterBlock; +pub type PortReg = pac::ioconfig::Porta; + //================================================================================================== // DynPin //================================================================================================== @@ -210,46 +251,59 @@ impl DynRegisters { /// by the same type, and pins are tracked and distinguished at run-time. #[derive(Debug)] pub struct DynPin { - pub(crate) regs: DynRegisters, + id: DynPinId, mode: DynPinMode, } impl DynPin { - /// Create a new [`DynPin`] + /// Create a new [DynPin] /// /// # Safety /// - /// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there + /// Each [DynPin] must be a singleton. For a given [DynPinId], there /// must be at most one corresponding [`DynPin`] in existence at any given /// time. Violating this requirement is `unsafe`. #[inline] - pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { + pub(crate) const unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { + DynPin { id, mode } + } + + /// Steals a new [DynPin]. + /// + /// This function will simply set the internal mode to [DYN_FLOATING_INPUT] pin without + /// modifying any registers related to the behaviour of the pin. The user should call + /// [Self::into_mode] to ensure the correct mode of the pin. + /// + /// # Safety + /// + /// Circumvents the HAL's safety guarantees. The caller must ensure that the pin is not + /// used cocurrently somewhere else. The caller might also want to call [Self::into_mode] + /// to ensure the correct desired state of the pin. It is recommended to create the pin using + /// [Pin::downgrade] instead. + pub const unsafe fn steal(id: DynPinId) -> Self { DynPin { - regs: DynRegisters::new(id), - mode, + id, + mode: DYN_FLOATING_INPUT, } } /// Return a copy of the pin ID #[inline] - pub fn id(&self) -> DynPinId { - self.regs.0 + pub const fn id(&self) -> DynPinId { + self.id } /// Return a copy of the pin mode #[inline] - pub fn mode(&self) -> DynPinMode { + pub const fn mode(&self) -> DynPinMode { self.mode } /// Convert the pin to the requested [`DynPinMode`] #[inline] pub fn into_mode(&mut self, mode: DynPinMode) { - // Only modify registers if we are actually changing pin mode - if mode != self.mode { - self.regs.change_mode(mode); - self.mode = mode; - } + self.change_mode(mode); + self.mode = mode; } #[inline] @@ -257,6 +311,11 @@ impl DynPin { matches!(self.mode, DynPinMode::Input(_)) } + #[inline] + pub fn is_output_pin(&self) -> bool { + matches!(self.mode, DynPinMode::Output(_)) + } + #[inline] pub fn into_funsel_1(&mut self) { self.into_mode(DYN_ALT_FUNC_1); @@ -314,74 +373,170 @@ impl DynPin { self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); } - #[inline] - pub fn datamask(&self) -> bool { - self.regs.datamask() + #[inline(always)] + pub fn is_low(&self) -> Result { + self.read_internal().map(|v| !v) } - #[inline] - pub fn clear_datamask(&mut self) { - self.regs.clear_datamask(); + #[inline(always)] + pub fn is_high(&self) -> Result { + self.read_internal() } - #[inline] - pub fn set_datamask(&mut self) { - self.regs.set_datamask(); + #[inline(always)] + pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> { + self.write_internal(false) } - #[inline] - pub fn is_high_masked(&self) -> Result { - self.regs.read_pin_masked() + #[inline(always)] + pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> { + self.write_internal(true) } - #[inline] - pub fn is_low_masked(&self) -> Result { - self.regs.read_pin_masked().map(|v| !v) - } - - #[inline] - pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(true) - } - - #[inline] - pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(false) - } - - pub(crate) fn irq_enb( - &mut self, - irq_cfg: crate::InterruptConfig, - syscfg: Option<&mut va108xx::Sysconfig>, - irqsel: Option<&mut va108xx::Irqsel>, - ) { - if let Some(syscfg) = syscfg { - crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel); + /// Toggle the logic level of an output pin + #[inline(always)] + pub fn toggle(&mut self) -> Result<(), InvalidPinTypeError> { + if !self.is_output_pin() { + return Err(InvalidPinTypeError(self.mode)); } - self.regs.enable_irq(); - if let Some(irqsel) = irqsel { - if irq_cfg.route { - match self.regs.id().group { - // Set the correct interrupt number in the IRQSEL register - super::Port::A => { - irqsel - .porta0(self.regs.id().num as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - super::Port::B => { - irqsel - .portb0(self.regs.id().num as usize) - .write(|w| unsafe { w.bits(irq_cfg.id as u32) }); - } - } - } + // Safety: TOGOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; + Ok(()) + } + + pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { + if irq_cfg.route { + self.configure_irqsel(irq_cfg.id); } if irq_cfg.enable_in_nvic { unsafe { enable_nvic_interrupt(irq_cfg.id) }; } + + // We only manipulate our own bit. + self.port_reg() + .irq_enb() + .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); + } + + pub fn disable_interrupt(&mut self, reset_irqsel: bool) { + if reset_irqsel { + self.reset_irqsel(); + } + // We only manipulate our own bit. + self.port_reg() + .irq_enb() + .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); + } + + /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] + /// + /// There is no way for the compiler to know if the conversion will be + /// successful at compile-time. We must verify the conversion at run-time + /// or refuse to perform it. + #[inline] + pub fn upgrade(self) -> Result, InvalidPinTypeError> { + if self.id == I::DYN && self.mode == M::DYN { + // The `DynPin` is consumed, so it is safe to replace it with the + // corresponding `Pin` + return Ok(unsafe { Pin::new() }); + } + Err(InvalidPinTypeError(self.mode)) + } + + /// Convert the pin into an async pin. The pin can be converted back by calling + /// [InputDynPinAsync::release] + pub fn into_async_input( + self, + irq: crate::pac::Interrupt, + ) -> Result { + InputDynPinAsync::new(self, irq) + } + + /// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID. + pub fn configure_irqsel(&mut self, id: pac::Interrupt) { + let mut syscfg = unsafe { pac::Sysconfig::steal() }; + let irqsel = unsafe { pac::Irqsel::steal() }; + crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel); + match self.id().port() { + // Set the correct interrupt number in the IRQSEL register + super::Port::A => { + irqsel + .porta0(self.id().num() as usize) + .write(|w| unsafe { w.bits(id as u32) }); + } + super::Port::B => { + irqsel + .portb0(self.id().num as usize) + .write(|w| unsafe { w.bits(id as u32) }); + } + } + } + + /// Reset the IRQSEL peripheral value for this particular pin. + pub fn reset_irqsel(&mut self) { + let mut syscfg = unsafe { pac::Sysconfig::steal() }; + let irqsel = unsafe { pac::Irqsel::steal() }; + crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel); + match self.id().port() { + // Set the correct interrupt number in the IRQSEL register + super::Port::A => { + irqsel + .porta0(self.id().num() as usize) + .write(|w| unsafe { w.bits(u32::MAX) }); + } + super::Port::B => { + irqsel + .portb0(self.id().num as usize) + .write(|w| unsafe { w.bits(u32::MAX) }); + } + } + } + + // Get DATAMASK bit for this particular pin + #[inline(always)] + pub fn datamask(&self) -> bool { + (self.port_reg().datamask().read().bits() >> self.id().num) == 1 + } + + /// Clear DATAMASK bit for this particular pin. This prevents access + /// of the corresponding bit for output and input operations + #[inline(always)] + pub fn clear_datamask(&self) { + self.port_reg() + .datamask() + .modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) }); + } + + /// Set DATAMASK bit for this particular pin. 1 is the default + /// state of the bit and allows access of the corresponding bit + #[inline(always)] + pub fn set_datamask(&self) { + self.port_reg() + .datamask() + .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); + } + + #[inline] + pub fn is_high_masked(&self) -> Result { + self.read_pin_masked() + } + + #[inline] + pub fn is_low_masked(&self) -> Result { + self.read_pin_masked().map(|v| !v) + } + + #[inline] + pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.write_pin_masked(true) + } + + #[inline] + pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.write_pin_masked(false) } - /// See p.53 of the programmers guide for more information. /// Possible delays in clock cycles: /// - Delay 1: 1 /// - Delay 2: 2 @@ -394,14 +549,13 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Output(_) => { - self.regs.configure_delay(delay_1, delay_2); + self.configure_delay_internal(delay_1, delay_2); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } - /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state #[inline] @@ -412,7 +566,7 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Output(_) => { - self.regs.pulse_mode(enable, default_state); + self.configure_pulse_mode_internal(enable, default_state); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), @@ -428,74 +582,102 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) => { - self.regs.configure_filter_type(filter, clksel); + self.configure_filter_type_internal(filter, clksel); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } - #[inline] pub fn configure_edge_interrupt( &mut self, edge_type: InterruptEdge, - irq_cfg: InterruptConfig, - syscfg: Option<&mut pac::Sysconfig>, - irqsel: Option<&mut pac::Irqsel>, ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.regs.configure_edge_interrupt(edge_type); - self.irq_enb(irq_cfg, syscfg, irqsel); + self.configure_edge_interrupt_internal(edge_type); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } - #[inline] pub fn configure_level_interrupt( &mut self, level_type: InterruptLevel, - irq_cfg: InterruptConfig, - syscfg: Option<&mut pac::Sysconfig>, - irqsel: Option<&mut pac::Irqsel>, ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.regs.configure_level_interrupt(level_type); - self.irq_enb(irq_cfg, syscfg, irqsel); + self.configure_level_interrupt_internal(level_type); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } + /// Change the pin mode #[inline] - pub fn toggle_with_toggle_reg(&mut self) -> Result<(), InvalidPinTypeError> { - match self.mode { - DynPinMode::Output(_) => { - self.regs.toggle(); - Ok(()) + pub(crate) fn change_mode(&mut self, mode: DynPinMode) { + let ModeFields { + dir, + funsel, + opendrn, + pull_dir, + pull_en, + enb_input, + } = mode.into(); + let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); + iocfg.write(|w| { + w.opendrn().bit(opendrn); + w.pen().bit(pull_en); + w.plevel().bit(pull_dir); + w.iewo().bit(enb_input); + unsafe { w.funsel().bits(funsel) } + }); + let mask = self.mask_32(); + unsafe { + if dir { + portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); + // Clear output + portreg.clrout().write(|w| w.bits(mask)); + } else { + portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); } - _ => Err(InvalidPinTypeError(self.mode)), } } #[inline] - fn _read(&self) -> Result { + const fn port_reg(&self) -> &PortRegisterBlock { + match self.id().port() { + Port::A => unsafe { &(*pac::Porta::ptr()) }, + Port::B => unsafe { &(*pac::Portb::ptr()) }, + } + } + + #[inline] + const fn iocfg_port(&self) -> &PortReg { + let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() }; + match self.id().port() { + Port::A => ioconfig.porta(self.id().num() as usize), + Port::B => ioconfig.portb0(self.id().num() as usize), + } + } + + #[inline(always)] + fn read_internal(&self) -> Result { match self.mode { DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { - Ok(self.regs.read_pin()) + Ok(self.read_pin()) } _ => Err(InvalidPinTypeError(self.mode)), } } - #[inline] - fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> { + + #[inline(always)] + fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Output(_) => { - self.regs.write_pin(bit); + self.write_pin(bit); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), @@ -503,44 +685,190 @@ impl DynPin { } #[inline] - fn _is_low(&self) -> Result { - self._read().map(|v| !v) - } - #[inline] - fn _is_high(&self) -> Result { - self._read() - } - #[inline] - fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> { - self._write(false) - } - #[inline] - fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> { - self._write(true) + /// Read the logic level of an output pin + pub(crate) fn read_pin(&self) -> bool { + let portreg = self.port_reg(); + ((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1 } - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - pub fn upgrade(self) -> Result, InvalidPinTypeError> { - if self.regs.0 == I::DYN && self.mode == M::DYN { - // The `DynPin` is consumed, so it is safe to replace it with the - // corresponding `Pin` - return Ok(unsafe { Pin::new() }); + /// Read a pin but use the masked version but check whether the datamask for the pin is + /// cleared as well + #[inline(always)] + fn read_pin_masked(&self) -> Result { + if !self.datamask() { + Err(IsMaskedError) + } else { + Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) } - Err(InvalidPinTypeError(self.mode)) } - /// Convert the pin into an async pin. The pin can be converted back by calling - /// [InputDynPinAsync::release] - pub fn into_async_input( - self, - irq: crate::pac::Interrupt, - ) -> Result { - InputDynPinAsync::new(self, irq) + /// Write the logic level of an output pin + #[inline(always)] + pub(crate) fn write_pin(&mut self, bit: bool) { + // Safety: SETOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { + if bit { + self.port_reg().setout().write(|w| w.bits(self.mask_32())); + } else { + self.port_reg().clrout().write(|w| w.bits(self.mask_32())); + } + } + } + + /// Write the logic level of an output pin but check whether the datamask for the pin is + /// cleared as well + #[inline] + fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { + if !self.datamask() { + Err(IsMaskedError) + } else { + // Safety: SETOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { + if bit { + self.port_reg().setout().write(|w| w.bits(self.mask_32())); + } else { + self.port_reg().clrout().write(|w| w.bits(self.mask_32())); + } + Ok(()) + } + } + } + + /// Toggle the logic level of an output pin + #[inline(always)] + pub fn toggle_with_togout_reg(&mut self) { + // Safety: TOGOUT is a "mask" register, and we only write the bit for + // this pin ID + unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; + } + + /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure + /// When using edge mode, it is possible to generate interrupts on both edges as well + #[inline] + fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) { + unsafe { + self.port_reg() + .irq_sen() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + match edge_type { + InterruptEdge::HighToLow => { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + InterruptEdge::LowToHigh => { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + InterruptEdge::BothEdges => { + self.port_reg() + .irq_edge() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + } + + /// Configure which edge or level type triggers an interrupt + #[inline] + fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) { + unsafe { + self.port_reg() + .irq_sen() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + if level == InterruptLevel::Low { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } else { + self.port_reg() + .irq_evt() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + + /// Only useful for input pins + #[inline] + fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) { + self.iocfg_port().modify(|_, w| { + // Safety: Only write to register for this Pin ID + unsafe { + w.flttype().bits(filter as u8); + w.fltclk().bits(clksel as u8) + } + }); + } + + #[inline] + fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) { + let portreg = self.port_reg(); + unsafe { + if enable { + portreg + .pulse() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .pulse() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + if default_state == PinState::Low { + portreg + .pulsebase() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } else { + portreg + .pulsebase() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } + } + } + + /// Only useful for output pins + #[inline] + fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) { + let portreg = self.port_reg(); + unsafe { + if delay_1 { + portreg + .delay1() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .delay1() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + if delay_2 { + portreg + .delay2() + .modify(|r, w| w.bits(r.bits() | self.mask_32())); + } else { + portreg + .delay2() + .modify(|r, w| w.bits(r.bits() & !self.mask_32())); + } + } + } + + // Only serves disambiguation purposes for the Embedded HAL impl + #[inline(always)] + fn is_low_mut(&mut self) -> Result { + self.is_low() + } + + // Only serves disambiguation purposes for the Embedded HAL impl + #[inline(always)] + fn is_high_mut(&mut self) -> Result { + self.is_high() + } + + #[inline(always)] + const fn mask_32(&self) -> u32 { + 1 << self.id().num() } } @@ -582,33 +910,38 @@ impl embedded_hal::digital::ErrorType for DynPin { impl embedded_hal::digital::OutputPin for DynPin { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high() + self.set_high() } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low() + self.set_low() } } impl embedded_hal::digital::InputPin for DynPin { #[inline] fn is_high(&mut self) -> Result { - self._is_high() + self.is_high_mut() } #[inline] fn is_low(&mut self) -> Result { - self._is_low() + self.is_low_mut() } } impl embedded_hal::digital::StatefulOutputPin for DynPin { #[inline] fn is_set_high(&mut self) -> Result { - self._is_high() + self.is_high_mut() } #[inline] fn is_set_low(&mut self) -> Result { - self._is_low() + self.is_low_mut() + } + + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle() } } diff --git a/va108xx-hal/src/gpio/mod.rs b/va108xx-hal/src/gpio/mod.rs index 8a521fa..6c95ae0 100644 --- a/va108xx-hal/src/gpio/mod.rs +++ b/va108xx-hal/src/gpio/mod.rs @@ -72,5 +72,3 @@ pub use pin::*; pub mod asynch; pub use asynch::*; - -mod reg; diff --git a/va108xx-hal/src/gpio/pin.rs b/va108xx-hal/src/gpio/pin.rs index 1e11fde..8575f2e 100644 --- a/va108xx-hal/src/gpio/pin.rs +++ b/va108xx-hal/src/gpio/pin.rs @@ -68,20 +68,17 @@ //! # Embedded HAL traits //! //! This module implements all of the embedded HAL GPIO traits for each [`Pin`] -//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], -//! and [`StatefulOutputPin`]. +//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin], +//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin]. use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode}; -use super::reg::RegisterInterface; use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port}; use crate::{ - pac::{Irqsel, Porta, Portb, Sysconfig}, + pac::{Porta, Portb}, typelevel::Sealed, - InterruptConfig, }; use core::convert::Infallible; use core::marker::PhantomData; use core::mem::transmute; -use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin}; use paste::paste; //================================================================================================== @@ -294,10 +291,7 @@ macro_rules! pin_id { pub enum $Id {} impl Sealed for $Id {} impl PinId for $Id { - const DYN: DynPinId = DynPinId { - group: Port::$Group, - num: $NUM, - }; + const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM); } } }; @@ -323,7 +317,7 @@ impl Pin { /// at most one corresponding [Pin] in existence at any given time. /// Violating this requirement is `unsafe`. #[inline] - pub(crate) unsafe fn new() -> Pin { + pub(crate) const unsafe fn new() -> Pin { Pin { inner: DynPin::new(I::DYN, M::DYN), phantom: PhantomData, @@ -331,7 +325,7 @@ impl Pin { } #[inline] - pub fn id(&self) -> DynPinId { + pub const fn id(&self) -> DynPinId { self.inner.id() } @@ -341,7 +335,7 @@ impl Pin { // Only modify registers if we are actually changing pin mode // This check should compile away if N::DYN != M::DYN { - self.inner.regs.change_mode(N::DYN); + self.inner.change_mode(N::DYN); } // Safe because we drop the existing Pin unsafe { Pin::new() } @@ -401,6 +395,16 @@ impl Pin { self.into_mode() } + #[inline] + pub fn is_low(&self) -> bool { + !self.inner.read_pin() + } + + #[inline] + pub fn is_high(&self) -> bool { + self.inner.read_pin() + } + #[inline] pub fn datamask(&self) -> bool { self.inner.datamask() @@ -426,48 +430,41 @@ impl Pin { self.inner.is_low_masked() } - #[inline] - pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.inner.set_high_masked() - } - - #[inline] - pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.inner.set_low_masked() - } - #[inline] pub fn downgrade(self) -> DynPin { self.inner } - fn irq_enb( - &mut self, - irq_cfg: crate::InterruptConfig, - syscfg: Option<&mut va108xx::Sysconfig>, - irqsel: Option<&mut va108xx::Irqsel>, - ) { - self.inner.irq_enb(irq_cfg, syscfg, irqsel); + // Those only serve for the embedded HAL implementations which have different mutability. + + #[inline] + fn is_low_mut(&mut self) -> bool { + self.is_low() } #[inline] - pub(crate) fn _set_high(&mut self) { - self.inner.regs.write_pin(true) + fn is_high_mut(&mut self) -> bool { + self.is_high() } #[inline] - pub(crate) fn _set_low(&mut self) { - self.inner.regs.write_pin(false) + pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) { + self.inner.enable_interrupt(irq_cfg); } #[inline] - pub(crate) fn _is_low(&self) -> bool { - !self.inner.regs.read_pin() + pub fn disable_interrupt(&mut self, reset_irqsel: bool) { + self.inner.disable_interrupt(reset_irqsel); } - #[inline] - pub(crate) fn _is_high(&self) -> bool { - self.inner.regs.read_pin() + /// Configure the pin for an edge interrupt but does not enable the interrupt. + pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { + self.inner.configure_edge_interrupt(edge_type).unwrap(); + } + + /// Configure the pin for a level interrupt but does not enable the interrupt. + pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) { + self.inner.configure_level_interrupt(level_type).unwrap(); } } @@ -564,31 +561,34 @@ impl Pin> { pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync { InputPinAsync::new(self, irq) } - - pub fn configure_edge_interrupt( - &mut self, - edge_type: InterruptEdge, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.inner.regs.configure_edge_interrupt(edge_type); - self.irq_enb(irq_cfg, syscfg, irqsel); - } - - pub fn configure_level_interrupt( - &mut self, - level_type: InterruptLevel, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.inner.regs.configure_level_interrupt(level_type); - self.irq_enb(irq_cfg, syscfg, irqsel); - } } impl Pin> { + #[inline] + pub fn set_high(&mut self) { + self.inner.write_pin(true) + } + + #[inline] + pub fn set_low(&mut self) { + self.inner.write_pin(false) + } + + #[inline] + pub fn toggle(&mut self) { + self.inner.toggle().unwrap() + } + + #[inline] + pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.inner.set_high_masked() + } + + #[inline] + pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.inner.set_low_masked() + } + /// See p.53 of the programmers guide for more information. /// Possible delays in clock cycles: /// - Delay 1: 1 @@ -596,78 +596,17 @@ impl Pin> { /// - Delay 1 + Delay 2: 3 #[inline] pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) { - self.inner.regs.configure_delay(delay_1, delay_2); - } - - #[inline] - pub fn toggle_with_toggle_reg(&mut self) { - self.inner.regs.toggle() - } - - #[deprecated( - since = "0.9.0", - note = "Please use the `configure_pulse_mode` method instead" - )] - pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) { - self.configure_pulse_mode(enable, default_state); + self.inner.configure_delay(delay_1, delay_2).unwrap(); } /// See p.52 of the programmers guide for more information. + /// /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) { - self.inner.regs.pulse_mode(enable, default_state); - } - - #[deprecated( - since = "0.9.0", - note = "Please use the `configure_edge_interrupt` method instead" - )] - pub fn interrupt_edge( - &mut self, - edge_type: InterruptEdge, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.inner.regs.configure_edge_interrupt(edge_type); - self.irq_enb(irq_cfg, syscfg, irqsel); - } - - pub fn configure_edge_interrupt( - &mut self, - edge_type: InterruptEdge, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.inner.regs.configure_edge_interrupt(edge_type); - self.irq_enb(irq_cfg, syscfg, irqsel); - } - - #[deprecated( - since = "0.9.0", - note = "Please use the `configure_level_interrupt` method instead" - )] - pub fn level_interrupt( - &mut self, - level_type: InterruptLevel, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.configure_level_interrupt(level_type, irq_cfg, syscfg, irqsel); - } - - pub fn configure_level_interrupt( - &mut self, - level_type: InterruptLevel, - irq_cfg: InterruptConfig, - syscfg: Option<&mut Sysconfig>, - irqsel: Option<&mut Irqsel>, - ) { - self.inner.regs.configure_level_interrupt(level_type); - self.irq_enb(irq_cfg, syscfg, irqsel); + self.inner + .configure_pulse_mode(enable, default_state) + .unwrap(); } } @@ -675,7 +614,7 @@ impl Pin> { /// See p.37 and p.38 of the programmers guide for more information. #[inline] pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.inner.regs.configure_filter_type(filter, clksel); + self.inner.configure_filter_type(filter, clksel).unwrap(); } } @@ -691,63 +630,53 @@ where type Error = Infallible; } -impl OutputPin for Pin> { +impl embedded_hal::digital::OutputPin for Pin> { #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high(); + self.set_high(); Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low(); + self.set_low(); Ok(()) } } -impl InputPin for Pin> +impl embedded_hal::digital::InputPin for Pin> where I: PinId, C: InputConfig, { #[inline] fn is_high(&mut self) -> Result { - Ok(self._is_high()) + Ok(self.is_high_mut()) } #[inline] fn is_low(&mut self) -> Result { - Ok(self._is_low()) + Ok(self.is_low_mut()) } } -impl StatefulOutputPin for Pin> +impl embedded_hal::digital::StatefulOutputPin for Pin> where I: PinId, C: OutputConfig + ReadableOutput, { #[inline] fn is_set_high(&mut self) -> Result { - Ok(self._is_high()) + Ok(self.is_high()) } #[inline] fn is_set_low(&mut self) -> Result { - Ok(self._is_low()) - } -} - -impl InputPin for Pin> -where - I: PinId, - C: OutputConfig + ReadableOutput, -{ - #[inline] - fn is_high(&mut self) -> Result { - Ok(self._is_high()) + Ok(self.is_low()) } #[inline] - fn is_low(&mut self) -> Result { - Ok(self._is_low()) + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) } } diff --git a/va108xx-hal/src/gpio/reg.rs b/va108xx-hal/src/gpio/reg.rs deleted file mode 100644 index 55f4b11..0000000 --- a/va108xx-hal/src/gpio/reg.rs +++ /dev/null @@ -1,375 +0,0 @@ -use super::dynpin::{self, DynPinId, DynPinMode}; -use super::pin::FilterType; -use super::{InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port}; -use crate::clock::FilterClkSel; -use va108xx::{ioconfig, porta}; - -/// Type definition to avoid confusion: These register blocks are identical -type PortRegisterBlock = porta::RegisterBlock; - -//================================================================================================== -// ModeFields -//================================================================================================== - -/// Collect all fields needed to set the [`PinMode`](super::PinMode) -#[derive(Default)] -struct ModeFields { - dir: bool, - opendrn: bool, - pull_en: bool, - /// true for pullup, false for pulldown - pull_dir: bool, - funsel: u8, - enb_input: bool, -} - -impl From for ModeFields { - #[inline] - fn from(mode: DynPinMode) -> Self { - let mut fields = Self::default(); - use DynPinMode::*; - match mode { - Input(config) => { - use dynpin::DynInput::*; - fields.dir = false; - match config { - Floating => (), - PullUp => { - fields.pull_en = true; - fields.pull_dir = true; - } - PullDown => { - fields.pull_en = true; - } - } - } - Output(config) => { - use dynpin::DynOutput::*; - fields.dir = true; - match config { - PushPull => (), - OpenDrain => { - fields.opendrn = true; - } - ReadableOpenDrain => { - fields.enb_input = true; - fields.opendrn = true; - } - ReadablePushPull => { - fields.enb_input = true; - } - } - } - Alternate(config) => { - fields.funsel = config as u8; - } - } - fields - } -} - -//================================================================================================== -// Register Interface -//================================================================================================== - -pub type PortReg = ioconfig::Porta; - -/// Provide a safe register interface for pin objects -/// -/// [`PORTA`] and [`PORTB`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it -/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an -/// interface is quite restrictive. Instead, it would be ideal if we could split -/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`]. -/// -/// [`PORT`] is a single, zero-sized marker `struct` that provides access to -/// every [`PORT`] register. Instead, we would like to create zero-sized marker -/// `struct`s for every pin, where each pin is only allowed to control its own -/// registers. Furthermore, each pin `struct` should be a singleton, so that -/// exclusive access to the `struct` also guarantees exclusive access to the -/// corresponding registers. Finally, the pin `struct`s should not have any -/// interior mutability. Together, these requirements would allow the pin -/// `struct`s to be both [`Send`] and [`Sync`]. -/// -/// This trait creates a safe API for accomplishing these goals. Implementers -/// supply a pin ID through the [`id`] function. The remaining functions provide -/// a safe API for accessing the registers associated with that pin ID. Any -/// modification of the registers requires `&mut self`, which destroys interior -/// mutability. -/// -/// # Safety -/// -/// Users should only implement the [`id`] function. No default function -/// implementations should be overridden. The implementing type must also have -/// "control" over the corresponding pin ID, i.e. it must guarantee that a each -/// pin ID is a singleton. -/// -/// [`id`]: Self::id -pub(super) unsafe trait RegisterInterface { - /// Provide a [`DynPinId`] identifying the set of registers controlled by - /// this type. - fn id(&self) -> DynPinId; - - const PORTA: *const PortRegisterBlock = va108xx::Porta::ptr(); - const PORTB: *const PortRegisterBlock = va108xx::Portb::ptr(); - - /// Change the pin mode - #[inline] - fn change_mode(&mut self, mode: DynPinMode) { - let ModeFields { - dir, - funsel, - opendrn, - pull_dir, - pull_en, - enb_input, - } = mode.into(); - let (portreg, iocfg) = (self.port_reg(), self.iocfg_port()); - iocfg.write(|w| { - w.opendrn().bit(opendrn); - w.pen().bit(pull_en); - w.plevel().bit(pull_dir); - w.iewo().bit(enb_input); - unsafe { w.funsel().bits(funsel) } - }); - let mask = self.mask_32(); - unsafe { - if dir { - portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); - // Clear output - portreg.clrout().write(|w| w.bits(mask)); - } else { - portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); - } - } - } - - #[inline] - fn port_reg(&self) -> &PortRegisterBlock { - match self.id().group { - Port::A => unsafe { &(*Self::PORTA) }, - Port::B => unsafe { &(*Self::PORTB) }, - } - } - fn iocfg_port(&self) -> &PortReg { - let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() }; - match self.id().group { - Port::A => ioconfig.porta(self.id().num as usize), - Port::B => ioconfig.portb0(self.id().num as usize), - } - } - - #[inline] - fn mask_32(&self) -> u32 { - 1 << self.id().num - } - - #[inline] - fn enable_irq(&self) { - self.port_reg() - .irq_enb() - .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); - } - - #[inline] - /// Read the logic level of an output pin - fn read_pin(&self) -> bool { - let portreg = self.port_reg(); - ((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1 - } - - // Get DATAMASK bit for this particular pin - #[inline(always)] - fn datamask(&self) -> bool { - let portreg = self.port_reg(); - (portreg.datamask().read().bits() >> self.id().num) == 1 - } - - /// Read a pin but use the masked version but check whether the datamask for the pin is - /// cleared as well - #[inline(always)] - fn read_pin_masked(&self) -> Result { - if !self.datamask() { - Err(IsMaskedError) - } else { - Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1) - } - } - - /// Write the logic level of an output pin - #[inline(always)] - fn write_pin(&mut self, bit: bool) { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - } - } - - /// Write the logic level of an output pin but check whether the datamask for the pin is - /// cleared as well - #[inline] - fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> { - if !self.datamask() { - Err(IsMaskedError) - } else { - // Safety: SETOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { - if bit { - self.port_reg().setout().write(|w| w.bits(self.mask_32())); - } else { - self.port_reg().clrout().write(|w| w.bits(self.mask_32())); - } - Ok(()) - } - } - } - - /// Toggle the logic level of an output pin - #[inline(always)] - fn toggle(&mut self) { - // Safety: TOGOUT is a "mask" register, and we only write the bit for - // this pin ID - unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; - } - - /// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure - /// When using edge mode, it is possible to generate interrupts on both edges as well - #[inline] - fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - match edge_type { - InterruptEdge::HighToLow => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - InterruptEdge::LowToHigh => { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - InterruptEdge::BothEdges => { - self.port_reg() - .irq_edge() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - } - - /// Configure which edge or level type triggers an interrupt - #[inline] - fn configure_level_interrupt(&mut self, level: InterruptLevel) { - unsafe { - self.port_reg() - .irq_sen() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - if level == InterruptLevel::Low { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - self.port_reg() - .irq_evt() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for input pins - #[inline] - fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { - self.iocfg_port().modify(|_, w| { - // Safety: Only write to register for this Pin ID - unsafe { - w.flttype().bits(filter as u8); - w.fltclk().bits(clksel as u8) - } - }); - } - - /// Set DATAMASK bit for this particular pin. 1 is the default - /// state of the bit and allows access of the corresponding bit - #[inline(always)] - fn set_datamask(&self) { - let portreg = self.port_reg(); - unsafe { - portreg - .datamask() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - - /// Clear DATAMASK bit for this particular pin. This prevents access - /// of the corresponding bit for output and input operations - #[inline(always)] - fn clear_datamask(&self) { - let portreg = self.port_reg(); - unsafe { - portreg - .datamask() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - } - - /// Only useful for output pins - /// See p.52 of the programmers guide for more information. - /// When configured for pulse mode, a given pin will set the non-default state for exactly - /// one clock cycle before returning to the configured default state - fn pulse_mode(&mut self, enable: bool, default_state: PinState) { - let portreg = self.port_reg(); - unsafe { - if enable { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .pulse() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if default_state == PinState::Low { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } else { - portreg - .pulsebase() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } - } - } - - /// Only useful for output pins - fn configure_delay(&mut self, delay_1: bool, delay_2: bool) { - let portreg = self.port_reg(); - unsafe { - if delay_1 { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay1() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - if delay_2 { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() | self.mask_32())); - } else { - portreg - .delay2() - .modify(|r, w| w.bits(r.bits() & !self.mask_32())); - } - } - } -} diff --git a/va108xx-hal/src/lib.rs b/va108xx-hal/src/lib.rs index b146c77..1b28470 100644 --- a/va108xx-hal/src/lib.rs +++ b/va108xx-hal/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +use gpio::Port; pub use va108xx; pub use va108xx as pac; @@ -19,18 +20,12 @@ pub mod uart; #[derive(Debug, Eq, Copy, Clone, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum FunSel { + Sel0 = 0b00, Sel1 = 0b01, Sel2 = 0b10, Sel3 = 0b11, } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PortSel { - PortA, - PortB, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum PeripheralSelect { @@ -77,37 +72,33 @@ impl InterruptConfig { pub type IrqCfg = InterruptConfig; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InvalidPin(pub(crate) ()); +#[error("invalid pin with number {0}")] +pub struct InvalidPinError(u8); -/// Can be used to manually manipulate the function select of port pins +/// Can be used to manually manipulate the function select of port pins. +/// +/// The function selection table can be found on p.36 of the programmers guide. Please note +/// that most of the structures and APIs in this library will automatically correctly configure +/// the pin or statically expect the correct pin type. pub fn port_function_select( ioconfig: &mut pac::Ioconfig, - port: PortSel, + port: Port, pin: u8, funsel: FunSel, -) -> Result<(), InvalidPin> { - match port { - PortSel::PortA => { - if pin > 31 { - return Err(InvalidPin(())); - } - ioconfig - .porta(pin as usize) - .modify(|_, w| unsafe { w.funsel().bits(funsel as u8) }); - Ok(()) - } - PortSel::PortB => { - if pin > 23 { - return Err(InvalidPin(())); - } - ioconfig - .portb0(pin as usize) - .modify(|_, w| unsafe { w.funsel().bits(funsel as u8) }); - Ok(()) - } +) -> Result<(), InvalidPinError> { + if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) { + return Err(InvalidPinError(pin)); } + + let reg_block = match port { + Port::A => ioconfig.porta(pin as usize), + Port::B => ioconfig.portb0(pin as usize), + }; + + reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) }); + Ok(()) } /// Enable a specific interrupt using the NVIC peripheral. diff --git a/va108xx-hal/src/uart/mod.rs b/va108xx-hal/src/uart/mod.rs index 6337d2b..115feb9 100644 --- a/va108xx-hal/src/uart/mod.rs +++ b/va108xx-hal/src/uart/mod.rs @@ -22,7 +22,7 @@ use crate::{ }; use embedded_hal_nb::serial::Read; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Bank { A = 0, @@ -381,6 +381,7 @@ pub struct BufferTooShortError { pub trait Instance: Deref { const IDX: u8; const PERIPH_SEL: PeripheralSelect; + const PTR: *const uart_base::RegisterBlock; /// Retrieve the peripheral structure. /// @@ -388,7 +389,11 @@ pub trait Instance: Deref { /// /// This circumvents the safety guarantees of the HAL. unsafe fn steal() -> Self; - fn ptr() -> *const uart_base::RegisterBlock; + + #[inline(always)] + fn ptr() -> *const uart_base::RegisterBlock { + Self::PTR + } /// Retrieve the type erased peripheral register block. /// @@ -405,14 +410,11 @@ impl Instance for pac::Uarta { const IDX: u8 = 0; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart0; + const PTR: *const uart_base::RegisterBlock = Self::PTR; #[inline(always)] unsafe fn steal() -> Self { - pac::Peripherals::steal().uarta - } - #[inline(always)] - fn ptr() -> *const uart_base::RegisterBlock { - Self::ptr() as *const _ + Self::steal() } } @@ -420,14 +422,25 @@ impl Instance for pac::Uartb { const IDX: u8 = 1; const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Uart1; + const PTR: *const uart_base::RegisterBlock = Self::PTR; #[inline(always)] unsafe fn steal() -> Self { - pac::Peripherals::steal().uartb + Self::steal() } - #[inline(always)] - fn ptr() -> *const uart_base::RegisterBlock { - Self::ptr() as *const _ +} + +impl Bank { + /// Retrieve the peripheral register block. + /// + /// # Safety + /// + /// Circumvents the HAL safety guarantees. + pub unsafe fn reg_block(&self) -> &'static uart_base::RegisterBlock { + match self { + Bank::A => unsafe { pac::Uarta::reg_block() }, + Bank::B => unsafe { pac::Uartb::reg_block() }, + } } } @@ -794,14 +807,12 @@ pub fn disable_rx_interrupts(uart: &uart_base::RegisterBlock) { /// Serial receiver. /// /// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Rx { - uart: Uart, -} +pub struct Rx(Uart); impl Rx { #[inline(always)] - fn new(uart: Uart) -> Self { - Self { uart } + const fn new(uart: Uart) -> Self { + Self(uart) } /// Direct access to the peripheral structure. @@ -810,13 +821,13 @@ impl Rx { /// /// You must ensure that only registers related to the operation of the RX side are used. #[inline(always)] - pub unsafe fn uart(&self) -> &Uart { - &self.uart + pub const unsafe fn uart(&self) -> &Uart { + &self.0 } #[inline] pub fn clear_fifo(&self) { - self.uart.fifo_clr().write(|w| w.rxfifo().set_bit()); + self.0.fifo_clr().write(|w| w.rxfifo().set_bit()); } #[inline] @@ -846,7 +857,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.uart.rxstatus().read().rdavl().bit_is_clear() { + if self.0.rxstatus().read().rdavl().bit_is_clear() { return Err(nb::Error::WouldBlock); } Ok(self.read_fifo_unchecked()) @@ -862,7 +873,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_unchecked(&self) -> u32 { - self.uart.data().read().bits() + self.0.data().read().bits() } pub fn into_rx_with_irq(self) -> RxWithInterrupt { @@ -871,7 +882,7 @@ impl Rx { #[inline(always)] pub fn release(self) -> Uart { - self.uart + self.0 } } @@ -959,9 +970,7 @@ pub fn disable_tx_interrupts(uart: &uart_base::RegisterBlock) { /// Serial transmitter /// /// Can be created by using the [Uart::split] or [UartBase::split] API. -pub struct Tx { - uart: Uart, -} +pub struct Tx(Uart); impl Tx { /// Retrieve a TX pin without expecting an explicit UART structure @@ -971,14 +980,12 @@ impl Tx { /// Circumvents the HAL safety guarantees. #[inline(always)] pub unsafe fn steal() -> Self { - Self { - uart: Uart::steal(), - } + Self(Uart::steal()) } #[inline(always)] fn new(uart: Uart) -> Self { - Self { uart } + Self(uart) } /// Direct access to the peripheral structure. @@ -987,25 +994,23 @@ impl Tx { /// /// You must ensure that only registers related to the operation of the TX side are used. #[inline(always)] - pub unsafe fn uart(&self) -> &Uart { - &self.uart + pub const unsafe fn uart(&self) -> &Uart { + &self.0 } #[inline] pub fn clear_fifo(&self) { - self.uart.fifo_clr().write(|w| w.txfifo().set_bit()); + self.0.fifo_clr().write(|w| w.txfifo().set_bit()); } #[inline] pub fn enable(&mut self) { - // Safety: We own the UART structure - enable_tx(unsafe { Uart::reg_block() }); + self.0.enable().modify(|_, w| w.txenable().set_bit()); } #[inline] pub fn disable(&mut self) { - // Safety: We own the UART structure - disable_tx(unsafe { Uart::reg_block() }); + self.0.enable().modify(|_, w| w.txenable().clear_bit()); } /// Enables the IRQ_TX, IRQ_TX_STATUS and IRQ_TX_EMPTY interrupts. @@ -1037,7 +1042,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.uart.txstatus().read().wrrdy().bit_is_clear() { + if self.0.txstatus().read().wrrdy().bit_is_clear() { return Err(nb::Error::WouldBlock); } self.write_fifo_unchecked(data); @@ -1052,7 +1057,7 @@ impl Tx { /// API. #[inline(always)] pub fn write_fifo_unchecked(&self, data: u32) { - self.uart.data().write(|w| unsafe { w.bits(data) }); + self.0.data().write(|w| unsafe { w.bits(data) }); } pub fn into_async(self) -> TxAsync { @@ -1135,7 +1140,7 @@ impl RxWithInterrupt { #[inline(always)] pub fn uart(&self) -> &Uart { - &self.0.uart + &self.0 .0 } /// This function is used together with the [Self::on_interrupt_max_size_or_timeout_based] diff --git a/va108xx-hal/src/uart/rx_asynch.rs b/va108xx-hal/src/uart/rx_asynch.rs index faa5c19..95e4b65 100644 --- a/va108xx-hal/src/uart/rx_asynch.rs +++ b/va108xx-hal/src/uart/rx_asynch.rs @@ -1,18 +1,16 @@ -//! # Async UART reception functionality for the VA108xx family. +//! # Async UART reception functionality for the VA416xx family. //! -//! This module provides the [RxAsync] and [RxAsyncSharedConsumer] struct which both implement the +//! This module provides the [RxAsync] and [RxAsyncOverwriting] struct which both implement the //! [embedded_io_async::Read] trait. //! This trait allows for asynchronous reception of data streams. Please note that this module does //! not specify/declare the interrupt handlers which must be provided for async support to work. -//! However, it provides four interrupt handlers: +//! However, it provides two interrupt handlers: //! -//! - [on_interrupt_uart_a] -//! - [on_interrupt_uart_b] -//! - [on_interrupt_uart_a_overwriting] -//! - [on_interrupt_uart_b_overwriting] +//! - [on_interrupt_rx] +//! - [on_interrupt_rx_overwriting] //! //! The first two are used for the [RxAsync] struct, while the latter two are used with the -//! [RxAsyncSharedConsumer] struct. The later two will overwrite old values in the used ring buffer. +//! [RxAsyncOverwriting] struct. The later two will overwrite old values in the used ring buffer. //! //! Error handling is performed in the user interrupt handler by checking the [AsyncUartErrors] //! structure returned by the interrupt handlers. @@ -25,11 +23,10 @@ use core::{cell::RefCell, convert::Infallible, future::Future, sync::atomic::Ord use critical_section::Mutex; use embassy_sync::waitqueue::AtomicWaker; use embedded_io::ErrorType; -use heapless::spsc::Consumer; use portable_atomic::AtomicBool; -use va108xx as pac; +use va108xx::uarta as uart_base; -use super::{Instance, Rx, RxError, UartErrors}; +use super::{Bank, Instance, Rx, RxError, UartErrors}; static UART_RX_WAKERS: [AtomicWaker; 2] = [const { AtomicWaker::new() }; 2]; static RX_READ_ACTIVE: [AtomicBool; 2] = [const { AtomicBool::new(false) }; 2]; @@ -72,7 +69,7 @@ pub struct AsyncUartErrors { pub uart_errors: UartErrors, } -fn on_interrupt_handle_rx_errors(uart: &Uart) -> Option { +fn on_interrupt_handle_rx_errors(uart: &'static uart_base::RegisterBlock) -> Option { let rx_status = uart.rxstatus().read(); if rx_status.rxovr().bit_is_set() || rx_status.rxfrm().bit_is_set() @@ -94,81 +91,65 @@ fn on_interrupt_handle_rx_errors(uart: &Uart) -> Option( - uart: &Uart, +fn on_interrupt_rx_common_post_processing( + bank: Bank, rx_enabled: bool, read_some_data: bool, irq_end: u32, ) -> Option { + let idx = bank as usize; if read_some_data { - RX_HAS_DATA[Uart::IDX as usize].store(true, Ordering::Relaxed); - if RX_READ_ACTIVE[Uart::IDX as usize].load(Ordering::Relaxed) { - UART_RX_WAKERS[Uart::IDX as usize].wake(); + RX_HAS_DATA[idx].store(true, Ordering::Relaxed); + if RX_READ_ACTIVE[idx].load(Ordering::Relaxed) { + UART_RX_WAKERS[idx].wake(); } } let mut errors = None; + let uart_regs = unsafe { bank.reg_block() }; // Check for RX errors if rx_enabled { - errors = on_interrupt_handle_rx_errors(uart); + errors = on_interrupt_handle_rx_errors(uart_regs); } // Clear the interrupt status bits - uart.irq_clr().write(|w| unsafe { w.bits(irq_end) }); + uart_regs.irq_clr().write(|w| unsafe { w.bits(irq_end) }); errors } -/// Interrupt handler for UART A. +/// Interrupt handler with overwriting behaviour when the ring buffer is full. /// /// Should be called in the user interrupt handler to enable /// asynchronous reception. This variant will overwrite old data in the ring buffer in case /// the ring buffer is full. -pub fn on_interrupt_uart_a_overwriting( +pub fn on_interrupt_rx_overwriting( + bank: Bank, prod: &mut heapless::spsc::Producer, shared_consumer: &Mutex>>>, ) -> Result<(), AsyncUartErrors> { - on_interrupt_rx_async_heapless_queue_overwriting( - unsafe { pac::Uarta::steal() }, - prod, - shared_consumer, - ) + on_interrupt_rx_async_heapless_queue_overwriting(bank, prod, shared_consumer) } -/// Interrupt handler for UART B. -/// -/// Should be called in the user interrupt handler to enable -/// asynchronous reception. This variant will overwrite old data in the ring buffer in case -/// the ring buffer is full. -pub fn on_interrupt_uart_b_overwriting( +pub fn on_interrupt_rx_async_heapless_queue_overwriting( + bank: Bank, prod: &mut heapless::spsc::Producer, shared_consumer: &Mutex>>>, ) -> Result<(), AsyncUartErrors> { - on_interrupt_rx_async_heapless_queue_overwriting( - unsafe { pac::Uartb::steal() }, - prod, - shared_consumer, - ) -} - -pub fn on_interrupt_rx_async_heapless_queue_overwriting( - uart: Uart, - prod: &mut heapless::spsc::Producer, - shared_consumer: &Mutex>>>, -) -> Result<(), AsyncUartErrors> { - let irq_end = uart.irq_end().read(); - let enb_status = uart.enable().read(); + let uart_regs = unsafe { bank.reg_block() }; + let irq_end = uart_regs.irq_end().read(); + let enb_status = uart_regs.enable().read(); let rx_enabled = enb_status.rxenable().bit_is_set(); let mut read_some_data = false; let mut queue_overflow = false; // Half-Full interrupt. We have a guaranteed amount of data we can read. if irq_end.irq_rx().bit_is_set() { - let available_bytes = uart.rxfifoirqtrg().read().bits() as usize; + let available_bytes = uart_regs.rxfifoirqtrg().read().bits() as usize; // If this interrupt bit is set, the trigger level is available at the very least. // Read everything as fast as possible for _ in 0..available_bytes { - let byte = uart.data().read().bits(); + let byte = uart_regs.data().read().bits(); if !prod.ready() { queue_overflow = true; critical_section::with(|cs| { @@ -183,9 +164,9 @@ pub fn on_interrupt_rx_async_heapless_queue_overwriting( +pub fn on_interrupt_rx( + bank: Bank, prod: &mut heapless::spsc::Producer<'_, u8, N>, ) -> Result<(), AsyncUartErrors> { - on_interrupt_rx_async_heapless_queue(unsafe { pac::Uarta::steal() }, prod) + on_interrupt_rx_async_heapless_queue(bank, prod) } -/// Interrupt handler for UART B. -/// -/// Should be called in the user interrupt handler to enable asynchronous reception. -pub fn on_interrupt_uart_b( +pub fn on_interrupt_rx_async_heapless_queue( + bank: Bank, prod: &mut heapless::spsc::Producer<'_, u8, N>, ) -> Result<(), AsyncUartErrors> { - on_interrupt_rx_async_heapless_queue(unsafe { pac::Uartb::steal() }, prod) -} - -pub fn on_interrupt_rx_async_heapless_queue( - uart: Uart, - prod: &mut heapless::spsc::Producer<'_, u8, N>, -) -> Result<(), AsyncUartErrors> { - //let uart = unsafe { Uart::steal() }; + let uart = unsafe { bank.reg_block() }; let irq_end = uart.irq_end().read(); let enb_status = uart.enable().read(); let rx_enabled = enb_status.rxenable().bit_is_set(); @@ -268,7 +241,7 @@ pub fn on_interrupt_rx_async_heapless_queue( } let uart_errors = - on_interrupt_rx_common_post_processing(&uart, rx_enabled, read_some_data, irq_end.bits()); + on_interrupt_rx_common_post_processing(bank, rx_enabled, read_some_data, irq_end.bits()); if uart_errors.is_some() || queue_overflow { return Err(AsyncUartErrors { queue_overflow, @@ -286,24 +259,32 @@ impl Drop for ActiveReadGuard { } } -/// Core data structure to allow asynchronous UART reception. -/// -/// If the ring buffer becomes full, data will be lost. -pub struct RxAsync { +struct RxAsyncInner { rx: Rx, pub queue: heapless::spsc::Consumer<'static, u8, N>, } +/// Core data structure to allow asynchronous UART reception. +/// +/// If the ring buffer becomes full, data will be lost. +pub struct RxAsync(Option>); + impl ErrorType for RxAsync { /// Error reporting is done using the result of the interrupt functions. type Error = Infallible; } +fn stop_async_rx(rx: &mut Rx) { + rx.disable_interrupts(); + rx.disable(); + rx.clear_fifo(); +} + impl RxAsync { /// Create a new asynchronous receiver. /// /// The passed [heapless::spsc::Consumer] will be used to asynchronously receive data which - /// is filled by the interrupt handler. + /// is filled by the interrupt handler [on_interrupt_rx]. pub fn new(mut rx: Rx, queue: heapless::spsc::Consumer<'static, u8, N>) -> Self { rx.disable_interrupts(); rx.disable(); @@ -313,7 +294,23 @@ impl RxAsync { rx.enable_interrupts(); rx.enable(); }); - Self { rx, queue } + Self(Some(RxAsyncInner { rx, queue })) + } + + pub fn stop(&mut self) { + stop_async_rx(&mut self.0.as_mut().unwrap().rx); + } + + pub fn release(mut self) -> (Rx, heapless::spsc::Consumer<'static, u8, N>) { + self.stop(); + let inner = self.0.take().unwrap(); + (inner.rx, inner.queue) + } +} + +impl Drop for RxAsync { + fn drop(&mut self) { + self.stop(); } } @@ -321,7 +318,7 @@ impl embedded_io_async::Read for RxAsync Result { // Need to wait for the IRQ to read data and set this flag. If the queue is not // empty, we can read data immediately. - if self.queue.len() == 0 { + if self.0.as_ref().unwrap().queue.len() == 0 { RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed); } let _guard = ActiveReadGuard(Uart::IDX as usize); @@ -333,33 +330,38 @@ impl embedded_io_async::Read for RxAsync 0 { return Ok(read_data); } // Await data. let _ = fut.await; - Ok(handle_data_in_queue(&mut self.queue)) + Ok(handle_data_in_queue(&mut mut_ref.queue)) } } +struct RxAsyncOverwritingInner { + rx: Rx, + pub shared_consumer: &'static Mutex>>>, +} + /// Core data structure to allow asynchronous UART reception. /// /// If the ring buffer becomes full, the oldest data will be overwritten when using the -/// [on_interrupt_uart_a_overwriting] and [on_interrupt_uart_b_overwriting] interrupt handlers. -pub struct RxAsyncSharedConsumer { - rx: Rx, - queue: &'static Mutex>>>, -} +/// [on_interrupt_rx_overwriting] interrupt handlers. +pub struct RxAsyncOverwriting( + Option>, +); -impl ErrorType for RxAsyncSharedConsumer { +impl ErrorType for RxAsyncOverwriting { /// Error reporting is done using the result of the interrupt functions. type Error = Infallible; } -impl RxAsyncSharedConsumer { +impl RxAsyncOverwriting { /// Create a new asynchronous receiver. /// /// The passed shared [heapless::spsc::Consumer] will be used to asynchronously receive data @@ -367,7 +369,7 @@ impl RxAsyncSharedConsumer { /// interrupt handler to overwrite old data. pub fn new( mut rx: Rx, - queue: &'static Mutex>>>, + shared_consumer: &'static Mutex>>>, ) -> Self { rx.disable_interrupts(); rx.disable(); @@ -377,25 +379,44 @@ impl RxAsyncSharedConsumer { rx.enable_interrupts(); rx.enable(); }); - Self { rx, queue } + Self(Some(RxAsyncOverwritingInner { + rx, + shared_consumer, + })) + } + + pub fn stop(&mut self) { + stop_async_rx(&mut self.0.as_mut().unwrap().rx); + } + + pub fn release(mut self) -> Rx { + self.stop(); + let inner = self.0.take().unwrap(); + inner.rx } } -impl embedded_io_async::Read for RxAsyncSharedConsumer { +impl Drop for RxAsyncOverwriting { + fn drop(&mut self) { + self.stop(); + } +} + +impl embedded_io_async::Read for RxAsyncOverwriting { async fn read(&mut self, buf: &mut [u8]) -> Result { // Need to wait for the IRQ to read data and set this flag. If the queue is not // empty, we can read data immediately. critical_section::with(|cs| { - let queue = self.queue.borrow(cs); + let queue = self.0.as_ref().unwrap().shared_consumer.borrow(cs); if queue.borrow().as_ref().unwrap().len() == 0 { RX_HAS_DATA[Uart::IDX as usize].store(false, Ordering::Relaxed); } }); let _guard = ActiveReadGuard(Uart::IDX as usize); - let mut handle_data_in_queue = || { + let mut handle_data_in_queue = |inner: &mut RxAsyncOverwritingInner| { critical_section::with(|cs| { - let mut consumer_ref = self.queue.borrow(cs).borrow_mut(); + let mut consumer_ref = inner.shared_consumer.borrow(cs).borrow_mut(); let consumer = consumer_ref.as_mut().unwrap(); let data_to_read = consumer.len().min(buf.len()); for byte in buf.iter_mut().take(data_to_read) { @@ -405,15 +426,15 @@ impl embedded_io_async::Read for RxAsyncSharedCo data_to_read }) }; - let fut = RxFuture::new(&mut self.rx); + let fut = RxFuture::new(&mut self.0.as_mut().unwrap().rx); // Data is available, so read that data immediately. - let read_data = handle_data_in_queue(); + let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); if read_data > 0 { return Ok(read_data); } // Await data. let _ = fut.await; - let read_data = handle_data_in_queue(); + let read_data = handle_data_in_queue(self.0.as_mut().unwrap()); Ok(read_data) } } diff --git a/va108xx-hal/src/uart/tx_asynch.rs b/va108xx-hal/src/uart/tx_asynch.rs index 6b97db3..3dc36f7 100644 --- a/va108xx-hal/src/uart/tx_asynch.rs +++ b/va108xx-hal/src/uart/tx_asynch.rs @@ -3,13 +3,10 @@ //! 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: +//! However, it the [on_interrupt_tx] interrupt handler. //! -//! - [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. +//! This handler should be called in ALL user interrupt handlers which handle UART TX interrupts +//! for a given UART bank. //! //! # Example //! @@ -30,21 +27,14 @@ static TX_CONTEXTS: [Mutex>; 2] = // 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) { +/// This is a generic interrupt handler to handle asynchronous UART TX operations for a given +/// UART bank. +/// +/// The user has to call this once in the interrupt handler responsible for the TX interrupts on +/// the given UART bank. +pub fn on_interrupt_tx(bank: Bank) { + let uart = unsafe { bank.reg_block() }; + let idx = bank as usize; 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() { @@ -54,7 +44,7 @@ fn on_interrupt_uart_tx(uart: Uart) { 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); + let context_ref = TX_CONTEXTS[idx].borrow(cs); *context_ref.borrow() }); context.tx_overrun = unexpected_overrun; @@ -67,12 +57,12 @@ fn on_interrupt_uart_tx(uart: Uart) { 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); + let context_ref = TX_CONTEXTS[idx].borrow(cs); *context_ref.borrow_mut() = context; }); // Transfer is done. - TX_DONE[Uart::IDX as usize].store(true, core::sync::atomic::Ordering::Relaxed); - UART_TX_WAKERS[Uart::IDX as usize].wake(); + TX_DONE[idx].store(true, core::sync::atomic::Ordering::Relaxed); + UART_TX_WAKERS[idx].wake(); return; } // Safety: We documented that the user provided slice must outlive the future, so we convert @@ -92,7 +82,7 @@ fn on_interrupt_uart_tx(uart: Uart) { // Write back updated context structure. critical_section::with(|cs| { - let context_ref = TX_CONTEXTS[Uart::IDX as usize].borrow(cs); + let context_ref = TX_CONTEXTS[idx].borrow(cs); *context_ref.borrow_mut() = context; }); } diff --git a/vorago-reb1/Cargo.toml b/vorago-reb1/Cargo.toml index 1101f0e..c705c46 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.9" features = ["rt"] diff --git a/vorago-reb1/examples/adxl343-accelerometer.rs b/vorago-reb1/examples/adxl343-accelerometer.rs index 8f99e4d..ce7be6c 100644 --- a/vorago-reb1/examples/adxl343-accelerometer.rs +++ b/vorago-reb1/examples/adxl343-accelerometer.rs @@ -5,8 +5,8 @@ #![no_main] #![no_std] use cortex_m_rt::entry; +use embedded_hal::delay::DelayNs; use embedded_hal::spi::{SpiBus, MODE_3}; -use embedded_hal::{delay::DelayNs, digital::OutputPin}; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va108xx_hal::spi::SpiClkConfig; @@ -41,9 +41,7 @@ fn main() -> ! { // Need to set the ADC chip select low let mut adc_cs = pinsa.pa17.into_push_pull_output(); - adc_cs - .set_high() - .expect("Setting ADC chip select high failed"); + adc_cs.set_high(); let spi_cfg = SpiConfig::default() .clk_cfg( diff --git a/vorago-reb1/examples/blinky-button-irq.rs b/vorago-reb1/examples/blinky-button-irq.rs index 986a10b..bc113fc 100644 --- a/vorago-reb1/examples/blinky-button-irq.rs +++ b/vorago-reb1/examples/blinky-button-irq.rs @@ -43,18 +43,16 @@ fn main() -> ! { // Configure an edge interrupt on the button and route it to interrupt vector 15 let mut button = Button::new(pinsa.pa11.into_floating_input()); - button.configure_edge_interrupt( - edge_irq, - 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.configure_filter_type(FilterType::FilterFourClockCycles, FilterClkSel::Clk1); set_clk_div_register(&mut dp.sysconfig, FilterClkSel::Clk1, 50_000); } + button.configure_and_enable_edge_interrupt( + edge_irq, + InterruptConfig::new(pac::interrupt::OC15, true, true), + ); set_up_ms_tick( InterruptConfig::new(pac::Interrupt::OC0, true, true), diff --git a/vorago-reb1/examples/blinky-leds.rs b/vorago-reb1/examples/blinky-leds.rs index 2650b09..de9fbd5 100644 --- a/vorago-reb1/examples/blinky-leds.rs +++ b/vorago-reb1/examples/blinky-leds.rs @@ -8,7 +8,6 @@ use cortex_m_rt::entry; use embedded_hal::delay::DelayNs; -use embedded_hal::digital::{OutputPin, StatefulOutputPin}; use panic_halt as _; use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::set_up_ms_delay_provider}; use vorago_reb1::leds::Leds; @@ -67,22 +66,19 @@ fn main() -> ! { let mut led3 = pins.pa6.into_readable_push_pull_output(); let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0); for _ in 0..10 { - led1.set_low().ok(); - led2.set_low().ok(); - led3.set_low().ok(); + led1.set_low(); + led2.set_low(); + led3.set_low(); delay.delay_ms(200); - led1.set_high().ok(); - led2.set_high().ok(); - led3.set_high().ok(); + led1.set_high(); + led2.set_high(); + led3.set_high(); delay.delay_ms(200); } loop { - led1.toggle().ok(); + led1.toggle(); delay.delay_ms(200); - led2.toggle().ok(); - delay.delay_ms(200); - // Alternatively use deidscted register. - led3.toggle_with_toggle_reg(); + led2.toggle(); delay.delay_ms(200); } } diff --git a/vorago-reb1/examples/max11619-adc.rs b/vorago-reb1/examples/max11619-adc.rs index e400096..14c03c5 100644 --- a/vorago-reb1/examples/max11619-adc.rs +++ b/vorago-reb1/examples/max11619-adc.rs @@ -8,13 +8,13 @@ use core::convert::Infallible; use cortex_m_rt::entry; -use embedded_hal::digital::OutputPin; use embedded_hal::spi::{SpiBus, SpiDevice, MODE_0}; use embedded_hal::{delay::DelayNs, spi}; use max116xx_10bit::VoltageRefMode; use max116xx_10bit::{AveragingConversions, AveragingResults}; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; +use va108xx_hal::gpio::Port; use va108xx_hal::spi::{OptionalHwCs, SpiClkConfig}; use va108xx_hal::timer::CountdownTimer; use va108xx_hal::{ @@ -24,7 +24,7 @@ use va108xx_hal::{ spi::{Spi, SpiBase, SpiConfig}, timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, InterruptConfig}, }; -use va108xx_hal::{port_function_select, FunSel, PortSel}; +use va108xx_hal::{port_function_select, FunSel}; use vorago_reb1::max11619::{ max11619_externally_clocked_no_wakeup, max11619_externally_clocked_with_wakeup, max11619_internally_clocked, EocPin, AN2_CHANNEL, POTENTIOMETER_CHANNEL, @@ -135,16 +135,14 @@ fn main() -> ! { ); if MUX_MODE == MuxMode::PortB19to17 { - 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(); + port_function_select(&mut dp.ioconfig, Port::B, 19, FunSel::Sel1).ok(); + port_function_select(&mut dp.ioconfig, Port::B, 18, FunSel::Sel2).ok(); + port_function_select(&mut dp.ioconfig, Port::B, 17, FunSel::Sel1).ok(); + port_function_select(&mut dp.ioconfig, Port::B, 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(); - accel_cs - .set_high() - .expect("Setting accelerometer chip select high failed"); + accel_cs.set_high(); let spi = Spi::new( &mut dp.sysconfig, diff --git a/vorago-reb1/src/button.rs b/vorago-reb1/src/button.rs index c7477fa..a16ff7f 100644 --- a/vorago-reb1/src/button.rs +++ b/vorago-reb1/src/button.rs @@ -4,10 +4,9 @@ //! //! - [Button Blinky with IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs) //! - [Button Blinky with IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs) -use embedded_hal::digital::InputPin; use va108xx_hal::{ gpio::{FilterClkSel, FilterType, InputFloating, InterruptEdge, InterruptLevel, Pin, PA11}, - pac, InterruptConfig, + InterruptConfig, }; #[derive(Debug)] @@ -20,36 +19,32 @@ impl Button { #[inline] pub fn pressed(&mut self) -> bool { - self.0.is_low().ok().unwrap() + self.0.is_low() } #[inline] pub fn released(&mut self) -> bool { - self.0.is_high().ok().unwrap() + self.0.is_high() } /// Configures an IRQ on edge. - pub fn configure_edge_interrupt( + pub fn configure_and_enable_edge_interrupt( &mut self, edge_type: InterruptEdge, irq_cfg: InterruptConfig, - syscfg: Option<&mut pac::Sysconfig>, - irqsel: Option<&mut pac::Irqsel>, ) { - self.0 - .configure_edge_interrupt(edge_type, irq_cfg, syscfg, irqsel); + self.0.configure_edge_interrupt(edge_type); + self.0.enable_interrupt(irq_cfg); } /// Configures an IRQ on level. - pub fn configure_level_interrupt( + pub fn configure_and_enable_level_interrupt( &mut self, level: InterruptLevel, irq_cfg: InterruptConfig, - syscfg: Option<&mut pac::Sysconfig>, - irqsel: Option<&mut pac::Irqsel>, ) { - self.0 - .configure_level_interrupt(level, irq_cfg, syscfg, irqsel); + self.0.configure_level_interrupt(level); + self.0.enable_interrupt(irq_cfg); } /// Configures a filter on the button. This can be useful for debouncing the switch. diff --git a/vorago-reb1/src/leds.rs b/vorago-reb1/src/leds.rs index 8939b24..dfa9ec1 100644 --- a/vorago-reb1/src/leds.rs +++ b/vorago-reb1/src/leds.rs @@ -5,7 +5,6 @@ //! - [LED example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-leds.rs) //! - [Button Blinky using IRQs](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-irq.rs) //! - [Button Blinky using IRQs and RTIC](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/blinky-button-rtic.rs) -use embedded_hal::digital::OutputPin; use va108xx_hal::{ gpio::dynpin::DynPin, gpio::pin::{Pin, PushPullOutput, PA10, PA6, PA7}, @@ -85,6 +84,6 @@ impl Led { /// Toggles the LED #[inline] pub fn toggle(&mut self) { - self.0.toggle_with_toggle_reg().ok(); + self.0.toggle().ok(); } }