From 0bcf611e46cce05c33118a78ed75e394f031da30 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 15 Feb 2025 12:45:31 +0100 Subject: [PATCH] Async GPIO implementation --- examples/embassy/Cargo.toml | 31 +- examples/embassy/src/bin/async-gpio.rs | 371 +++++++++ .../embassy/src/bin/uart-echo-with-irq.rs | 3 +- examples/embassy/src/main.rs | 3 +- examples/rtic/src/main.rs | 3 +- examples/simple/examples/blinky.rs | 3 +- va416xx-embassy/src/lib.rs | 6 +- va416xx-hal/Cargo.toml | 9 +- va416xx-hal/src/dma.rs | 6 +- va416xx-hal/src/edac.rs | 6 +- va416xx-hal/src/gpio/asynch.rs | 448 +++++++++++ va416xx-hal/src/gpio/dynpin.rs | 720 +++++++++++++++--- va416xx-hal/src/gpio/mod.rs | 47 +- va416xx-hal/src/gpio/pin.rs | 235 +++--- va416xx-hal/src/gpio/reg.rs | 388 ---------- va416xx-hal/src/lib.rs | 43 +- va416xx-hal/src/timer.rs | 9 +- va416xx-hal/src/uart.rs | 8 +- va416xx-hal/src/wdt.rs | 6 +- vorago-peb1/Cargo.toml | 1 + vorago-peb1/src/lib.rs | 4 +- 21 files changed, 1666 insertions(+), 684 deletions(-) create mode 100644 examples/embassy/src/bin/async-gpio.rs create mode 100644 va416xx-hal/src/gpio/asynch.rs delete mode 100644 va416xx-hal/src/gpio/reg.rs diff --git a/examples/embassy/Cargo.toml b/examples/embassy/Cargo.toml index a384ee2..8c661f0 100644 --- a/examples/embassy/Cargo.toml +++ b/examples/embassy/Cargo.toml @@ -9,32 +9,25 @@ cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" embedded-hal = "1" embedded-io = "0.6" +embedded-hal-async = "1" +embedded-io-async = "0.6" rtt-target = { version = "0.5" } panic-rtt-target = { version = "0.1" } critical-section = "1" +once_cell = { version = "1", default-features = false, features = ["critical-section"] } +ringbuf = { version = "0.4", default-features = false } + +embassy-sync = "0.6" +embassy-time = "0.4" +embassy-executor = { version = "0.7", features = [ + "arch-cortex-m", + "executor-thread", + "executor-interrupt" +]} -embassy-sync = { version = "0.6" } -embassy-time = { version = "0.4" } va416xx-embassy = { path = "../../va416xx-embassy", default-features = false } -[dependencies.ringbuf] -version = "0.4" -default-features = false - -[dependencies.once_cell] -version = "1" -default-features = false -features = ["critical-section"] - -[dependencies.embassy-executor] -version = "0.7" -features = [ - "arch-cortex-m", - "executor-thread", - "executor-interrupt", -] - [dependencies.va416xx-hal] path = "../../va416xx-hal" features = ["va41630"] diff --git a/examples/embassy/src/bin/async-gpio.rs b/examples/embassy/src/bin/async-gpio.rs new file mode 100644 index 0000000..a739e36 --- /dev/null +++ b/examples/embassy/src/bin/async-gpio.rs @@ -0,0 +1,371 @@ +//! This example demonstrates the usage of async GPIO operations on VA416xx. +//! +//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally also tie +//! more pin combinations together and test other ports by setting the appropriate +//! [CHECK_XXX_TO_XXX] constants to true. +#![no_std] +#![no_main] + +use embassy_example::EXTCLK_FREQ; +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_async::digital::Wait; +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use va416xx_hal::clock::ClkgenExt; +use va416xx_hal::gpio::{ + on_interrupt_for_async_gpio_for_port, InputDynPinAsync, InputPinAsync, PinsB, PinsC, PinsD, + PinsE, PinsF, PinsG, Port, +}; +use va416xx_hal::time::Hertz; +use va416xx_hal::{ + gpio::{DynPin, PinsA}, + pac::{self, interrupt}, +}; + +const CHECK_PA0_TO_PA1: bool = true; +const CHECK_PB0_TO_PB1: bool = true; +const CHECK_PC14_TO_PC15: bool = true; +const CHECK_PD2_TO_PD3: bool = true; +const CHECK_PE0_TO_PE1: bool = true; +const CHECK_PF0_TO_PF1: bool = true; + +#[derive(Clone, Copy)] +pub struct GpioCmd { + cmd_type: GpioCmdType, + after_delay: u32, +} + +impl GpioCmd { + pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self { + Self { + cmd_type, + after_delay, + } + } +} + +#[derive(Clone, Copy)] +pub enum GpioCmdType { + SetHigh, + SetLow, + RisingEdge, + FallingEdge, + CloseTask, +} + +// Declare a bounded channel of 3 u32s. +static CHANNEL_PA0_TO_PA1: Channel = Channel::new(); +static CHANNEL_PB0_TO_PB1: Channel = Channel::new(); +static CHANNEL_PC14_TO_PC15: Channel = Channel::new(); +static CHANNEL_PD2_TO_PD3: Channel = Channel::new(); +static CHANNEL_PE0_TO_PE1: Channel = Channel::new(); +static CHANNEL_PF0_TO_PF1: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + rtt_init_print!(); + rprintln!("-- VA416xx Async GPIO Demo --"); + + let mut dp = pac::Peripherals::take().unwrap(); + + // Initialize the systick interrupt & obtain the token to prove that we did + // Use the external clock connected to XTAL_N. + let clocks = dp + .clkgen + .constrain() + .xtal_n_clk_with_src_freq(Hertz::from_raw(EXTCLK_FREQ)) + .freeze(&mut dp.sysconfig) + .unwrap(); + // Safety: Only called once here. + unsafe { + va416xx_embassy::init( + &mut dp.sysconfig, + &dp.irq_router, + dp.tim15, + dp.tim14, + &clocks, + ) + }; + + let porta = PinsA::new(&mut dp.sysconfig, dp.porta); + let portb = PinsB::new(&mut dp.sysconfig, dp.portb); + let portc = PinsC::new(&mut dp.sysconfig, dp.portc); + let portd = PinsD::new(&mut dp.sysconfig, dp.portd); + let porte = PinsE::new(&mut dp.sysconfig, dp.porte); + let portf = PinsF::new(&mut dp.sysconfig, dp.portf); + + let portg = PinsG::new(&mut dp.sysconfig, dp.portg); + let mut led = portg.pg5.into_readable_push_pull_output(); + + if CHECK_PA0_TO_PA1 { + let out_pin = porta.pa0.into_readable_push_pull_output(); + let in_pin = porta.pa1.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputPinAsync::new(in_pin).unwrap(); + + spawner + .spawn(output_task( + "PA0 to PA1", + out_pin, + CHANNEL_PA0_TO_PA1.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_TO_PA1.sender(), in_pin).await; + rprintln!("Example PA0 to PA1 done"); + } + + if CHECK_PB0_TO_PB1 { + let out_pin = portb.pb0.into_readable_push_pull_output(); + let in_pin = portb.pb1.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + + spawner + .spawn(output_task( + "PB0 to PB1", + out_pin, + CHANNEL_PB0_TO_PB1.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PB0 to PB1", CHANNEL_PB0_TO_PB1.sender(), in_pin).await; + rprintln!("Example PB0 to PB1 done"); + } + + if CHECK_PC14_TO_PC15 { + let out_pin = portc.pc14.into_readable_push_pull_output(); + let in_pin = portc.pc15.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + spawner + .spawn(output_task( + "PC14 to PC15", + out_pin, + CHANNEL_PC14_TO_PC15.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PC14 to PC15", CHANNEL_PC14_TO_PC15.sender(), in_pin).await; + rprintln!("Example PC14 to PC15 done"); + } + + if CHECK_PD2_TO_PD3 { + let out_pin = portd.pd2.into_readable_push_pull_output(); + let in_pin = portd.pd3.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + spawner + .spawn(output_task( + "PD2 to PD3", + out_pin, + CHANNEL_PD2_TO_PD3.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PD2 to PD3", CHANNEL_PD2_TO_PD3.sender(), in_pin).await; + rprintln!("Example PD2 to PD3 done"); + } + + if CHECK_PE0_TO_PE1 { + let out_pin = porte.pe0.into_readable_push_pull_output(); + let in_pin = porte.pe1.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + spawner + .spawn(output_task( + "PE0 to PE1", + out_pin, + CHANNEL_PE0_TO_PE1.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PE0 to PE1", CHANNEL_PE0_TO_PE1.sender(), in_pin).await; + rprintln!("Example PE0 to PE1 done"); + } + + if CHECK_PF0_TO_PF1 { + let out_pin = portf.pf0.into_readable_push_pull_output(); + let in_pin = portf.pf1.into_floating_input(); + let out_pin = out_pin.downgrade(); + let in_pin = InputDynPinAsync::new(in_pin.downgrade()).unwrap(); + spawner + .spawn(output_task( + "PF0 to PF1", + out_pin, + CHANNEL_PF0_TO_PF1.receiver(), + )) + .unwrap(); + check_pin_to_pin_async_ops("PF0 to PF1", CHANNEL_PF0_TO_PF1.sender(), in_pin).await; + rprintln!("Example PF0 to PF1 done"); + } + + rprintln!("Example done, toggling LED0"); + loop { + led.toggle(); + Timer::after(Duration::from_millis(500)).await; + } +} + +async fn check_pin_to_pin_async_ops( + ctx: &'static str, + sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>, + mut async_input: impl Wait, +) { + rprintln!( + "{}: sending SetHigh command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await; + async_input.wait_for_high().await.unwrap(); + rprintln!( + "{}: Input pin is high now ({} ms)", + ctx, + Instant::now().as_millis() + ); + + rprintln!( + "{}: sending SetLow command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await; + async_input.wait_for_low().await.unwrap(); + rprintln!( + "{}: Input pin is low now ({} ms)", + ctx, + Instant::now().as_millis() + ); + + rprintln!( + "{}: sending RisingEdge command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await; + async_input.wait_for_rising_edge().await.unwrap(); + rprintln!( + "{}: input pin had rising edge ({} ms)", + ctx, + Instant::now().as_millis() + ); + + rprintln!( + "{}: sending Falling command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender + .send(GpioCmd::new(GpioCmdType::FallingEdge, 20)) + .await; + async_input.wait_for_falling_edge().await.unwrap(); + rprintln!( + "{}: input pin had a falling edge ({} ms)", + ctx, + Instant::now().as_millis() + ); + + rprintln!( + "{}: sending Falling command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender + .send(GpioCmd::new(GpioCmdType::FallingEdge, 20)) + .await; + async_input.wait_for_any_edge().await.unwrap(); + rprintln!( + "{}: input pin had a falling (any) edge ({} ms)", + ctx, + Instant::now().as_millis() + ); + + rprintln!( + "{}: sending Falling command ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await; + async_input.wait_for_any_edge().await.unwrap(); + rprintln!( + "{}: input pin had a rising (any) edge ({} ms)", + ctx, + Instant::now().as_millis() + ); + sender.send(GpioCmd::new(GpioCmdType::CloseTask, 0)).await; +} + +#[embassy_executor::task(pool_size = 8)] +async fn output_task( + ctx: &'static str, + mut out: DynPin, + receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>, +) { + loop { + let next_cmd = receiver.receive().await; + Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await; + match next_cmd.cmd_type { + GpioCmdType::SetHigh => { + rprintln!("{}: Set output high", ctx); + out.set_high().unwrap(); + } + GpioCmdType::SetLow => { + rprintln!("{}: Set output low", ctx); + out.set_low().unwrap(); + } + GpioCmdType::RisingEdge => { + rprintln!("{}: Rising edge", ctx); + if !out.is_low().unwrap() { + out.set_low().unwrap(); + } + out.set_high().unwrap(); + } + GpioCmdType::FallingEdge => { + rprintln!("{}: Falling edge", ctx); + if !out.is_high().unwrap() { + out.set_high().unwrap(); + } + out.set_low().unwrap(); + } + GpioCmdType::CloseTask => { + rprintln!("{}: Closing task", ctx); + break; + } + } + } +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTA1() { + on_interrupt_for_async_gpio_for_port(Port::A).unwrap(); +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTB1() { + on_interrupt_for_async_gpio_for_port(Port::B).unwrap(); +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTC15() { + on_interrupt_for_async_gpio_for_port(Port::C).unwrap(); +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTD3() { + on_interrupt_for_async_gpio_for_port(Port::D).unwrap(); +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTE1() { + on_interrupt_for_async_gpio_for_port(Port::E).unwrap(); +} + +#[interrupt] +#[allow(non_snake_case)] +fn PORTF1() { + on_interrupt_for_async_gpio_for_port(Port::F).unwrap(); +} diff --git a/examples/embassy/src/bin/uart-echo-with-irq.rs b/examples/embassy/src/bin/uart-echo-with-irq.rs index 79018e7..c8e7446 100644 --- a/examples/embassy/src/bin/uart-echo-with-irq.rs +++ b/examples/embassy/src/bin/uart-echo-with-irq.rs @@ -16,7 +16,6 @@ use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::{Duration, Ticker}; -use embedded_hal::digital::StatefulOutputPin; use embedded_io::Write; use panic_rtt_target as _; use ringbuf::{ @@ -119,7 +118,7 @@ async fn main(spawner: Spawner) { async fn blinky(mut led: Pin) { let mut ticker = Ticker::every(Duration::from_millis(500)); loop { - led.toggle().ok(); + led.toggle(); ticker.next().await; } } diff --git a/examples/embassy/src/main.rs b/examples/embassy/src/main.rs index 6455684..c580a68 100644 --- a/examples/embassy/src/main.rs +++ b/examples/embassy/src/main.rs @@ -3,7 +3,6 @@ use embassy_example::EXTCLK_FREQ; 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 va416xx_hal::{gpio::PinsG, pac, prelude::*, time::Hertz}; @@ -59,6 +58,6 @@ async fn main(_spawner: Spawner) { loop { ticker.next().await; rprintln!("Current time: {}", Instant::now().as_secs()); - led.toggle().ok(); + led.toggle(); } } diff --git a/examples/rtic/src/main.rs b/examples/rtic/src/main.rs index eb72cb9..3cb713f 100644 --- a/examples/rtic/src/main.rs +++ b/examples/rtic/src/main.rs @@ -10,7 +10,6 @@ const EXTCLK_FREQ: Hertz = Hertz::from_raw(40_000_000); mod app { use super::*; use cortex_m::asm; - use embedded_hal::digital::StatefulOutputPin; use panic_rtt_target as _; use rtic_monotonics::systick::prelude::*; use rtic_monotonics::Monotonic; @@ -64,7 +63,7 @@ mod app { )] async fn blinky(cx: blinky::Context) { loop { - cx.local.led.toggle().ok(); + cx.local.led.toggle(); Mono::delay(200.millis()).await; } } diff --git a/examples/simple/examples/blinky.rs b/examples/simple/examples/blinky.rs index 253bc6f..a890a24 100644 --- a/examples/simple/examples/blinky.rs +++ b/examples/simple/examples/blinky.rs @@ -3,7 +3,6 @@ #![no_std] use cortex_m_rt::entry; -use embedded_hal::digital::StatefulOutputPin; use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use va416xx_hal::{gpio::PinsG, pac}; @@ -18,6 +17,6 @@ fn main() -> ! { let mut led = portg.pg5.into_readable_push_pull_output(); loop { cortex_m::asm::delay(2_000_000); - led.toggle().ok(); + led.toggle(); } } diff --git a/va416xx-embassy/src/lib.rs b/va416xx-embassy/src/lib.rs index eade0d8..1d3928d 100644 --- a/va416xx-embassy/src/lib.rs +++ b/va416xx-embassy/src/lib.rs @@ -50,7 +50,7 @@ use embassy_time_queue_utils::Queue; use once_cell::sync::OnceCell; use va416xx_hal::{ clock::Clocks, - enable_interrupt, + enable_nvic_interrupt, irq_router::enable_and_init_irq_router, pac::{self, interrupt}, pwm::ValidTim, @@ -207,7 +207,7 @@ impl TimerDriver { .write(|w| unsafe { w.bits(u32::MAX) }); // Switch on. Timekeeping should always be done. unsafe { - enable_interrupt(TimekeeperTim::IRQ); + enable_nvic_interrupt(TimekeeperTim::IRQ); } timekeeper_tim_regs .ctrl() @@ -224,7 +224,7 @@ impl TimerDriver { }); // Enable general interrupts. The IRQ enable of the peripheral remains cleared. unsafe { - enable_interrupt(AlarmTim::IRQ); + enable_nvic_interrupt(AlarmTim::IRQ); } } diff --git a/va416xx-hal/Cargo.toml b/va416xx-hal/Cargo.toml index df81791..2a3d3b3 100644 --- a/va416xx-hal/Cargo.toml +++ b/va416xx-hal/Cargo.toml @@ -16,21 +16,26 @@ critical-section = "1" nb = "1" paste = "1" embedded-hal-nb = "1" +embedded-hal-async = "1" embedded-hal = "1" embedded-io = "0.6" num_enum = { version = "0.7", default-features = false } typenum = "1" bitflags = "2" bitfield = "0.17" -defmt = { version = "0.3", optional = true } fugit = "0.3" delegate = "0.12" void = { version = "1", default-features = false } thiserror = { version = "2", default-features = false } +portable-atomic = "1" +embassy-sync = "0.6" + +defmt = { version = "0.3", optional = true } [dependencies.va416xx] default-features = false -version = "0.3" +path = "../va416xx" +version = "0.4" features = ["critical-section"] [features] diff --git a/va416xx-hal/src/dma.rs b/va416xx-hal/src/dma.rs index ab4b904..9590a1a 100644 --- a/va416xx-hal/src/dma.rs +++ b/va416xx-hal/src/dma.rs @@ -5,7 +5,7 @@ //! - [Simple DMA example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/dma.rs) use crate::{ clock::{PeripheralClock, PeripheralSelect}, - enable_interrupt, pac, + enable_nvic_interrupt, pac, prelude::*, }; @@ -266,7 +266,7 @@ impl DmaChannel { /// /// This function is `unsafe` because it can break mask-based critical sections. pub unsafe fn enable_done_interrupt(&mut self) { - enable_interrupt(self.done_interrupt); + enable_nvic_interrupt(self.done_interrupt); } /// Enables the DMA_ACTIVE interrupt for the DMA channel. @@ -275,7 +275,7 @@ impl DmaChannel { /// /// This function is `unsafe` because it can break mask-based critical sections. pub unsafe fn enable_active_interrupt(&mut self) { - enable_interrupt(self.active_interrupt); + enable_nvic_interrupt(self.active_interrupt); } /// Prepares a 8-bit DMA transfer from memory to memory. diff --git a/va416xx-hal/src/edac.rs b/va416xx-hal/src/edac.rs index 24520b5..56eaf9b 100644 --- a/va416xx-hal/src/edac.rs +++ b/va416xx-hal/src/edac.rs @@ -1,4 +1,4 @@ -use crate::{enable_interrupt, pac}; +use crate::{enable_nvic_interrupt, pac}; #[inline(always)] pub fn enable_rom_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) { @@ -26,7 +26,7 @@ pub fn enable_ram1_scrub(syscfg: &mut pac::Sysconfig, counter_reset: u16) { #[inline(always)] pub fn enable_sbe_irq() { unsafe { - enable_interrupt(pac::Interrupt::EDAC_SBE); + enable_nvic_interrupt(pac::Interrupt::EDAC_SBE); } } @@ -35,7 +35,7 @@ pub fn enable_sbe_irq() { #[inline(always)] pub fn enable_mbe_irq() { unsafe { - enable_interrupt(pac::Interrupt::EDAC_MBE); + enable_nvic_interrupt(pac::Interrupt::EDAC_MBE); } } diff --git a/va416xx-hal/src/gpio/asynch.rs b/va416xx-hal/src/gpio/asynch.rs new file mode 100644 index 0000000..7d18c82 --- /dev/null +++ b/va416xx-hal/src/gpio/asynch.rs @@ -0,0 +1,448 @@ +//! # Async GPIO functionality for the VA416xx family. +//! +//! This module provides the [InputPinAsync] and [InputDynPinAsync] which both implement +//! the [embedded_hal_async::digital::Wait] trait. These types allow for asynchronous waiting +//! on GPIO pins. Please note that this module does not specify/declare the interrupt handlers +//! which must be provided for async support to work. However, it provides the +//! [on_interrupt_for_async_gpio_for_port] generic interrupt handler. This should be called in all +//! IRQ functions which handle any GPIO interrupts with the corresponding [Port] argument. +//! +//! # Example +//! +//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs) +use core::future::Future; + +use embassy_sync::waitqueue::AtomicWaker; +use embedded_hal_async::digital::Wait; +use portable_atomic::AtomicBool; +use va416xx::{self as pac}; + +use crate::enable_nvic_interrupt; + +use super::{ + pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port, + NUM_PINS_PORT_A_TO_F, +}; + +static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; +static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; +static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; +static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; +static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; +static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PINS_PORT_A_TO_F] = + [const { AtomicWaker::new() }; NUM_PINS_PORT_A_TO_F]; + +static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; +static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; +static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; +static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; +static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; +static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PINS_PORT_A_TO_F] = + [const { AtomicBool::new(false) }; NUM_PINS_PORT_A_TO_F]; + +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("port G does not support async functionality")] +pub struct PortGDoesNotSupportAsyncError; + +#[derive(Debug, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AsyncDynPinError { + #[error("invalid pin type: {0}")] + InvalidPinType(#[from] InvalidPinTypeError), + #[error("port g does not support async functionality: {0}")] + PortGDoesNotSupportAsync(#[from] PortGDoesNotSupportAsyncError), +} + +/// Generic interrupt handler for GPIO interrupts on a specific port to support async functionalities +/// +/// This function should be called in all interrupt handlers which handle any GPIO interrupts +/// matching the [Port] argument. +/// The handler will wake the corresponding wakers for the pins that triggered an interrupts +/// as well as update the static edge detection structures. This allows the pin future tocomplete +/// complete async operations. +pub fn on_interrupt_for_async_gpio_for_port( + port: Port, +) -> Result<(), PortGDoesNotSupportAsyncError> { + let periphs = unsafe { pac::Peripherals::steal() }; + + let (irq_enb, edge_status, wakers, edge_detection) = match port { + Port::A => ( + periphs.porta.irq_enb().read().bits(), + periphs.porta.edge_status().read().bits(), + &WAKERS_FOR_PORT_A, + &EDGE_DETECTION_PORT_A, + ), + Port::B => ( + periphs.portb.irq_enb().read().bits(), + periphs.portb.edge_status().read().bits(), + &WAKERS_FOR_PORT_B, + &EDGE_DETECTION_PORT_B, + ), + Port::C => ( + periphs.portc.irq_enb().read().bits(), + periphs.portc.edge_status().read().bits(), + &WAKERS_FOR_PORT_C, + &EDGE_DETECTION_PORT_C, + ), + Port::D => ( + periphs.portd.irq_enb().read().bits(), + periphs.portd.edge_status().read().bits(), + &WAKERS_FOR_PORT_D, + &EDGE_DETECTION_PORT_D, + ), + Port::E => ( + periphs.porte.irq_enb().read().bits(), + periphs.porte.edge_status().read().bits(), + &WAKERS_FOR_PORT_E, + &EDGE_DETECTION_PORT_E, + ), + Port::F => ( + periphs.portf.irq_enb().read().bits(), + periphs.portf.edge_status().read().bits(), + &WAKERS_FOR_PORT_F, + &EDGE_DETECTION_PORT_F, + ), + Port::G => return Err(PortGDoesNotSupportAsyncError), + }; + + on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection); + Ok(()) +} + +#[inline] +fn on_interrupt_for_port( + mut irq_enb: u32, + edge_status: u32, + wakers: &'static [AtomicWaker], + edge_detection: &'static [AtomicBool], +) { + while irq_enb != 0 { + let bit_pos = irq_enb.trailing_zeros() as usize; + let bit_mask = 1 << bit_pos; + + wakers[bit_pos].wake(); + + if edge_status & bit_mask != 0 { + edge_detection[bit_pos].store(true, core::sync::atomic::Ordering::Relaxed); + + // Clear the processed bit + irq_enb &= !bit_mask; + } + } +} + +/// Input pin future which implements the [Future] trait. +/// +/// Generally, you want to use the [InputPinAsync] or [InputDynPinAsync] types instead of this +/// which also implements the [embedded_hal_async::digital::Wait] trait. However, access to this +/// struture is granted to allow writing custom async structures. +pub struct InputPinFuture { + pin_id: DynPinId, + waker_group: &'static [AtomicWaker], + edge_detection_group: &'static [AtomicBool], +} + +impl InputPinFuture { + pub fn new_with_dyn_pin( + pin: &mut DynPin, + edge: InterruptEdge, + ) -> Result { + if !pin.is_input_pin() { + return Err(InvalidPinTypeError(pin.mode()).into()); + } + if pin.id().port() == Port::G { + return Err(PortGDoesNotSupportAsyncError.into()); + } + + let (waker_group, edge_detection_group) = + 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); + // Unwraps okay, checked for PORT G previously + pin.configure_edge_interrupt(edge).unwrap(); + unsafe { enable_nvic_interrupt(pin.irq_id().unwrap()) }; + pin.enable_interrupt(); + Ok(Self { + pin_id: pin.id(), + waker_group, + edge_detection_group, + }) + } + + pub fn new_with_pin( + pin: &mut Pin>, + edge: InterruptEdge, + ) -> Result { + if pin.id().port() == Port::G { + return Err(PortGDoesNotSupportAsyncError); + } + let (waker_group, edge_detection_group) = + 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); + // Unwraps okay, checked for PORT G previously + pin.configure_edge_interrupt(edge); + unsafe { enable_nvic_interrupt(I::IRQ.unwrap()) }; + pin.enable_interrupt(); + Ok(Self { + pin_id: pin.id(), + waker_group, + edge_detection_group, + }) + } + + #[inline] + pub fn pin_group_to_waker_and_edge_detection_group( + group: Port, + ) -> (&'static [AtomicWaker], &'static [AtomicBool]) { + match group { + Port::A => (WAKERS_FOR_PORT_A.as_ref(), EDGE_DETECTION_PORT_A.as_ref()), + Port::B => (WAKERS_FOR_PORT_B.as_ref(), EDGE_DETECTION_PORT_B.as_ref()), + Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()), + Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()), + Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()), + Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()), + _ => panic!("unexpected pin group G"), + } + } +} + +impl Drop for InputPinFuture { + fn drop(&mut self) { + // The API ensures that we actually own the pin, so stealing it here is okay. + unsafe { DynPin::steal(self.pin_id) }.disable_interrupt(); + } +} + +impl Future for InputPinFuture { + type Output = (); + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + 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(()); + } + core::task::Poll::Pending + } +} + +pub struct InputDynPinAsync { + pin: DynPin, +} + +impl InputDynPinAsync { + /// Create a new asynchronous input pin from a [DynPin]. The interrupt ID to be used must be + /// passed as well and is used to route and enable the interrupt. + /// + /// Please note that the interrupt handler itself must be provided by the user and the + /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function + /// for the asynchronous functionality to work. + pub fn new(pin: DynPin) -> Result { + if !pin.is_input_pin() { + return Err(InvalidPinTypeError(pin.mode()).into()); + } + if pin.id().port() == Port::G { + return Err(PortGDoesNotSupportAsyncError.into()); + } + Ok(Self { pin }) + } + + /// Asynchronously wait until the pin is high. + /// + /// This returns immediately if the pin is already high. + pub async fn wait_for_high(&mut self) { + // Unwrap okay, checked pin in constructor. + let fut = + InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap(); + if self.pin.is_high().unwrap() { + return; + } + fut.await; + } + + /// Asynchronously wait until the pin is low. + /// + /// This returns immediately if the pin is already high. + pub async fn wait_for_low(&mut self) { + // Unwrap okay, checked pin in constructor. + let fut = + InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap(); + if self.pin.is_low().unwrap() { + return; + } + fut.await; + } + + /// Asynchronously wait until the pin sees a falling edge. + pub async fn wait_for_falling_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::HighToLow) + .unwrap() + .await; + } + + /// Asynchronously wait until the pin sees a rising edge. + pub async fn wait_for_rising_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::LowToHigh) + .unwrap() + .await; + } + + /// Asynchronously wait until the pin sees any edge (either rising or falling). + pub async fn wait_for_any_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_dyn_pin(&mut self.pin, InterruptEdge::BothEdges) + .unwrap() + .await; + } + + pub fn release(self) -> DynPin { + self.pin + } +} + +impl embedded_hal::digital::ErrorType for InputDynPinAsync { + type Error = core::convert::Infallible; +} + +impl Wait for InputDynPinAsync { + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } +} + +pub struct InputPinAsync { + pin: Pin>, +} + +impl InputPinAsync { + /// Create a new asynchronous input pin from a typed [Pin]. The interrupt ID to be used must be + /// passed as well and is used to route and enable the interrupt. + /// + /// Please note that the interrupt handler itself must be provided by the user and the + /// generic [on_interrupt_for_async_gpio_for_port] function must be called inside that function + /// for the asynchronous functionality to work. + pub fn new(pin: Pin>) -> Result { + if pin.id().port() == Port::G { + return Err(PortGDoesNotSupportAsyncError); + } + Ok(Self { pin }) + } + + /// Asynchronously wait until the pin is high. + /// + /// This returns immediately if the pin is already high. + pub async fn wait_for_high(&mut self) { + // Unwrap okay, checked pin in constructor. + let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh).unwrap(); + if self.pin.is_high() { + return; + } + fut.await; + } + + /// Asynchronously wait until the pin is low. + /// + /// This returns immediately if the pin is already high. + pub async fn wait_for_low(&mut self) { + let fut = InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap(); + if self.pin.is_low() { + return; + } + fut.await; + } + + /// Asynchronously wait until the pin sees falling edge. + pub async fn wait_for_falling_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::HighToLow) + .unwrap() + .await; + } + + /// Asynchronously wait until the pin sees rising edge. + pub async fn wait_for_rising_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::LowToHigh) + .unwrap() + .await; + } + + /// Asynchronously wait until the pin sees any edge (either rising or falling). + pub async fn wait_for_any_edge(&mut self) { + // Unwrap okay, checked pin in constructor. + InputPinFuture::new_with_pin(&mut self.pin, InterruptEdge::BothEdges) + .unwrap() + .await; + } + + pub fn release(self) -> Pin> { + self.pin + } +} +impl embedded_hal::digital::ErrorType for InputPinAsync { + type Error = core::convert::Infallible; +} + +impl Wait for InputPinAsync { + async fn wait_for_high(&mut self) -> Result<(), Self::Error> { + self.wait_for_high().await; + Ok(()) + } + + async fn wait_for_low(&mut self) -> Result<(), Self::Error> { + self.wait_for_low().await; + Ok(()) + } + + async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_rising_edge().await; + Ok(()) + } + + async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_falling_edge().await; + Ok(()) + } + + async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { + self.wait_for_any_edge().await; + Ok(()) + } +} diff --git a/va416xx-hal/src/gpio/dynpin.rs b/va416xx-hal/src/gpio/dynpin.rs index 335493e..a944ca6 100644 --- a/va416xx-hal/src/gpio/dynpin.rs +++ b/va416xx-hal/src/gpio/dynpin.rs @@ -55,11 +55,14 @@ //! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the //! operation, the trait functions will return //! [InvalidPinTypeError]. -use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; + +use crate::FunSel; + +use va416xx as pac; use super::{ - reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId, - PinMode, PinState, + AsyncDynPinError, FilterClkSel, FilterType, InputDynPinAsync, InterruptEdge, InterruptLevel, + IsMaskedError, Pin, PinId, PinMode, PinState, Port, }; //================================================================================================== @@ -155,61 +158,213 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3) // DynGroup & DynPinId //================================================================================================== -/// Value-level `enum` for pin groups -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum DynGroup { - A, - B, - C, - D, - E, - F, - G, +pub type DynGroup = Port; + +#[inline] +pub const fn irq_id(port: Port, num: u8) -> Option { + match port { + Port::A => match num { + 0 => Some(va416xx::Interrupt::PORTA0), + 1 => Some(va416xx::Interrupt::PORTA1), + 2 => Some(va416xx::Interrupt::PORTA2), + 3 => Some(va416xx::Interrupt::PORTA3), + 4 => Some(va416xx::Interrupt::PORTA4), + 5 => Some(va416xx::Interrupt::PORTA5), + 6 => Some(va416xx::Interrupt::PORTA6), + 7 => Some(va416xx::Interrupt::PORTA7), + 8 => Some(va416xx::Interrupt::PORTA8), + 9 => Some(va416xx::Interrupt::PORTA9), + 10 => Some(va416xx::Interrupt::PORTA10), + 11 => Some(va416xx::Interrupt::PORTA11), + 12 => Some(va416xx::Interrupt::PORTA12), + 13 => Some(va416xx::Interrupt::PORTA13), + 14 => Some(va416xx::Interrupt::PORTA14), + 15 => Some(va416xx::Interrupt::PORTA15), + _ => None, + }, + Port::B => match num { + 0 => Some(va416xx::Interrupt::PORTB0), + 1 => Some(va416xx::Interrupt::PORTB1), + 2 => Some(va416xx::Interrupt::PORTB2), + 3 => Some(va416xx::Interrupt::PORTB3), + 4 => Some(va416xx::Interrupt::PORTB4), + 5 => Some(va416xx::Interrupt::PORTB5), + 6 => Some(va416xx::Interrupt::PORTB6), + 7 => Some(va416xx::Interrupt::PORTB7), + 8 => Some(va416xx::Interrupt::PORTB8), + 9 => Some(va416xx::Interrupt::PORTB9), + 10 => Some(va416xx::Interrupt::PORTB10), + 11 => Some(va416xx::Interrupt::PORTB11), + 12 => Some(va416xx::Interrupt::PORTB12), + 13 => Some(va416xx::Interrupt::PORTB13), + 14 => Some(va416xx::Interrupt::PORTB14), + 15 => Some(va416xx::Interrupt::PORTB15), + _ => None, + }, + Port::C => match num { + 0 => Some(va416xx::Interrupt::PORTC0), + 1 => Some(va416xx::Interrupt::PORTC1), + 2 => Some(va416xx::Interrupt::PORTC2), + 3 => Some(va416xx::Interrupt::PORTC3), + 4 => Some(va416xx::Interrupt::PORTC4), + 5 => Some(va416xx::Interrupt::PORTC5), + 6 => Some(va416xx::Interrupt::PORTC6), + 7 => Some(va416xx::Interrupt::PORTC7), + 8 => Some(va416xx::Interrupt::PORTC8), + 9 => Some(va416xx::Interrupt::PORTC9), + 10 => Some(va416xx::Interrupt::PORTC10), + 11 => Some(va416xx::Interrupt::PORTC11), + 12 => Some(va416xx::Interrupt::PORTC12), + 13 => Some(va416xx::Interrupt::PORTC13), + 14 => Some(va416xx::Interrupt::PORTC14), + 15 => Some(va416xx::Interrupt::PORTC15), + _ => None, + }, + Port::D => match num { + 0 => Some(va416xx::Interrupt::PORTD0), + 1 => Some(va416xx::Interrupt::PORTD1), + 2 => Some(va416xx::Interrupt::PORTD2), + 3 => Some(va416xx::Interrupt::PORTD3), + 4 => Some(va416xx::Interrupt::PORTD4), + 5 => Some(va416xx::Interrupt::PORTD5), + 6 => Some(va416xx::Interrupt::PORTD6), + 7 => Some(va416xx::Interrupt::PORTD7), + 8 => Some(va416xx::Interrupt::PORTD8), + 9 => Some(va416xx::Interrupt::PORTD9), + 10 => Some(va416xx::Interrupt::PORTD10), + 11 => Some(va416xx::Interrupt::PORTD11), + 12 => Some(va416xx::Interrupt::PORTD12), + 13 => Some(va416xx::Interrupt::PORTD13), + 14 => Some(va416xx::Interrupt::PORTD14), + 15 => Some(va416xx::Interrupt::PORTD15), + _ => None, + }, + Port::E => match num { + 0 => Some(va416xx::Interrupt::PORTE0), + 1 => Some(va416xx::Interrupt::PORTE1), + 2 => Some(va416xx::Interrupt::PORTE2), + 3 => Some(va416xx::Interrupt::PORTE3), + 4 => Some(va416xx::Interrupt::PORTE4), + 5 => Some(va416xx::Interrupt::PORTE5), + 6 => Some(va416xx::Interrupt::PORTE6), + 7 => Some(va416xx::Interrupt::PORTE7), + 8 => Some(va416xx::Interrupt::PORTE8), + 9 => Some(va416xx::Interrupt::PORTE9), + 10 => Some(va416xx::Interrupt::PORTE10), + 11 => Some(va416xx::Interrupt::PORTE11), + 12 => Some(va416xx::Interrupt::PORTE12), + 13 => Some(va416xx::Interrupt::PORTE13), + 14 => Some(va416xx::Interrupt::PORTE14), + 15 => Some(va416xx::Interrupt::PORTE15), + _ => None, + }, + Port::F => match num { + 0 => Some(va416xx::Interrupt::PORTF0), + 1 => Some(va416xx::Interrupt::PORTF1), + 2 => Some(va416xx::Interrupt::PORTF2), + 3 => Some(va416xx::Interrupt::PORTF3), + 4 => Some(va416xx::Interrupt::PORTF4), + 5 => Some(va416xx::Interrupt::PORTF5), + 6 => Some(va416xx::Interrupt::PORTF6), + 7 => Some(va416xx::Interrupt::PORTF7), + 8 => Some(va416xx::Interrupt::PORTF8), + 9 => Some(va416xx::Interrupt::PORTF9), + 10 => Some(va416xx::Interrupt::PORTF10), + 11 => Some(va416xx::Interrupt::PORTF11), + 12 => Some(va416xx::Interrupt::PORTF12), + 13 => Some(va416xx::Interrupt::PORTF13), + 14 => Some(va416xx::Interrupt::PORTF14), + 15 => Some(va416xx::Interrupt::PORTF15), + _ => None, + }, + Port::G => None, + } } -/// Value-level `struct` representing pin IDs #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct DynPinId { - pub group: DynGroup, - 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 //================================================================================================== @@ -221,58 +376,89 @@ impl DynRegisters { #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] 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 + } + + #[inline] + pub const fn irq_id(&self) -> Option { + irq_id(self.id.port(), self.id.num()) } /// 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] + pub fn is_input_pin(&self) -> bool { + matches!(self.mode, DynPinMode::Input(_)) + } + + #[inline] + pub fn is_output_pin(&self) -> bool { + matches!(self.mode, DynPinMode::Output(_)) + } + + /// Configure the pin for function select 1. See Programmer Guide p.286 for the function table #[inline] pub fn into_funsel_1(&mut self) { self.into_mode(DYN_ALT_FUNC_1); } + /// Configure the pin for function select 2. See Programmer Guide p.286 for the function table #[inline] pub fn into_funsel_2(&mut self) { self.into_mode(DYN_ALT_FUNC_2); } + /// Configure the pin for function select 3. See Programmer Guide p.286 for the function table #[inline] pub fn into_funsel_3(&mut self) { self.into_mode(DYN_ALT_FUNC_3); @@ -320,47 +506,118 @@ impl DynPin { self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); } + #[inline(always)] + pub fn is_low(&self) -> Result { + self.read_internal().map(|v| !v) + } + + #[inline(always)] + pub fn is_high(&self) -> Result { + self.read_internal() + } + + #[inline(always)] + pub fn set_low(&mut self) -> Result<(), InvalidPinTypeError> { + self.write_internal(false) + } + + #[inline(always)] + pub fn set_high(&mut self) -> Result<(), InvalidPinTypeError> { + self.write_internal(true) + } + + /// 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)); + } + // 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(()) + } + + #[inline(always)] + pub fn enable_interrupt(&self) { + self.port_reg() + .irq_enb() + .modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) }); + } + + #[inline(always)] + pub fn disable_interrupt(&self) { + 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) -> Result { + InputDynPinAsync::new(self) + } + + // Get DATAMASK bit for this particular pin + #[inline(always)] pub fn datamask(&self) -> bool { - self.regs.datamask() + (self.port_reg().datamask().read().bits() >> self.id().num) == 1 } - #[inline] - pub fn clear_datamask(&mut self) { - self.regs.clear_datamask(); + /// 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()) }); } - #[inline] - pub fn set_datamask(&mut self) { - self.regs.set_datamask(); + /// 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.regs.read_pin_masked() + self.read_pin_masked() } #[inline] pub fn is_low_masked(&self) -> Result { - self.regs.read_pin_masked().map(|v| !v) + self.read_pin_masked().map(|v| !v) } #[inline] pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(true) + self.write_pin_masked(true) } #[inline] pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(false) + self.write_pin_masked(false) } - #[inline] - pub fn irq_enb(&mut self) { - self.regs.enable_irq(); - } - - /// See p.53 of the programmers guide for more information. + /// See p.293 of the programmers guide for more information. /// Possible delays in clock cycles: /// - Delay 1: 1 /// - Delay 2: 2 @@ -373,14 +630,14 @@ 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. + /// See p.293 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( @@ -390,14 +647,14 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Output(_) => { - self.regs.configure_pulse_mode(enable, default_state); + self.configure_pulse_mode_internal(enable, default_state); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } - /// See p.37 and p.38 of the programmers guide for more information. + /// See p.284 of the programmers guide for more information. #[inline] pub fn configure_filter_type( &mut self, @@ -406,7 +663,7 @@ 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)), @@ -419,8 +676,7 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.regs.configure_edge_interrupt(edge_type); - self.irq_enb(); + self.configure_edge_interrupt_internal(edge_type); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), @@ -433,64 +689,273 @@ impl DynPin { ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { - self.regs.configure_level_interrupt(level_type); - self.irq_enb(); + self.configure_level_interrupt_internal(level_type); Ok(()) } _ => Err(InvalidPinTypeError(self.mode)), } } - #[inline] - fn _read(&self) -> Result { + #[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)), } } + /// Change the pin mode #[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) + 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)); + } + } } - /// 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() }); + #[inline(always)] + const fn port_reg(&self) -> &PortRegisterBlock { + match self.id().port() { + Port::A => unsafe { &(*pac::Porta::ptr()) }, + Port::B => unsafe { &(*pac::Portb::ptr()) }, + Port::C => unsafe { &(*pac::Portc::ptr()) }, + Port::D => unsafe { &(*pac::Portd::ptr()) }, + Port::E => unsafe { &(*pac::Porte::ptr()) }, + Port::F => unsafe { &(*pac::Portf::ptr()) }, + Port::G => unsafe { &(*pac::Portg::ptr()) }, } - Err(InvalidPinTypeError(self.mode)) + } + + #[inline(always)] + const fn iocfg_port(&self) -> &PortReg { + let ioconfig = unsafe { pac::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), + Port::C => ioconfig.portc0(self.id().num() as usize), + Port::D => ioconfig.portd0(self.id().num() as usize), + Port::E => ioconfig.porte0(self.id().num() as usize), + Port::F => ioconfig.portf0(self.id().num() as usize), + Port::G => ioconfig.portg0(self.id().num() as usize), + } + } + + #[inline(always)] + const fn mask_32(&self) -> u32 { + 1 << self.id().num() + } + + #[inline] + /// 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 + } + + /// 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)] + 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(()) + } + } + } + + /// 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) + } + }); + } + + /// Only useful for output pins + /// See p.293 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] + 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() } } @@ -533,39 +998,44 @@ where // Embedded HAL v1 traits //============================================================================== -impl ErrorType for DynPin { +impl embedded_hal::digital::ErrorType for DynPin { type Error = InvalidPinTypeError; } -impl OutputPin 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 InputPin for DynPin { +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 StatefulOutputPin for DynPin { +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/va416xx-hal/src/gpio/mod.rs b/va416xx-hal/src/gpio/mod.rs index 59de6a5..a01f8c7 100644 --- a/va416xx-hal/src/gpio/mod.rs +++ b/va416xx-hal/src/gpio/mod.rs @@ -21,15 +21,60 @@ //! ## Examples //! //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs) + +//================================================================================================== +// Errors, Definitions and Constants +//================================================================================================== + +pub const NUM_PINS_PORT_A_TO_F: usize = 16; +pub const NUM_PINS_PORT_G: usize = 8; +pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A_TO_F * 6 + NUM_PINS_PORT_G; +pub const NUM_GPIO_PINS_WITH_IRQ: usize = NUM_GPIO_PINS - NUM_PINS_PORT_G; + #[derive(Debug, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("pin is masked")] pub struct IsMaskedError; +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Port { + A, + B, + C, + D, + E, + F, + G, +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InterruptEdge { + HighToLow, + LowToHigh, + BothEdges, +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum InterruptLevel { + Low = 0, + High = 1, +} + +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PinState { + Low = 0, + High = 1, +} + pub mod pin; pub use pin::*; pub mod dynpin; pub use dynpin::*; -mod reg; +pub mod asynch; +pub use asynch::*; diff --git a/va416xx-hal/src/gpio/pin.rs b/va416xx-hal/src/gpio/pin.rs index 95a4d02..07927df 100644 --- a/va416xx-hal/src/gpio/pin.rs +++ b/va416xx-hal/src/gpio/pin.rs @@ -68,46 +68,19 @@ //! # 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 core::{convert::Infallible, marker::PhantomData, mem::transmute}; pub use crate::clock::FilterClkSel; use crate::typelevel::Sealed; -use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin}; -use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg}; +use va416xx::{self as pac, Porta, Portb, Portc, Portd, Porte, Portf, Portg}; use super::{ - reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPin, DynPinId, - DynPinMode, + DynAlternate, DynInput, DynOutput, DynPin, DynPinId, DynPinMode, InputPinAsync, InterruptEdge, + InterruptLevel, PinState, Port, PortGDoesNotSupportAsyncError, }; -//================================================================================================== -// Errors and Definitions -//================================================================================================== - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptEdge { - HighToLow, - LowToHigh, - BothEdges, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum InterruptLevel { - Low = 0, - High = 1, -} - -#[derive(Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum PinState { - Low = 0, - High = 1, -} - //================================================================================================== // Input configuration //================================================================================================== @@ -296,10 +269,11 @@ impl PinMode for Alternate { pub trait PinId: Sealed { /// Corresponding [DynPinId] const DYN: DynPinId; + const IRQ: Option; } macro_rules! pin_id { - ($Group:ident, $Id:ident, $NUM:literal $(, $meta: meta)?) => { + ($Port:ident, $Id:ident, $NUM:literal, $Irq:expr, $(, $meta: meta)?) => { // Need paste macro to use ident in doc attribute paste::paste! { $(#[$meta])? @@ -311,10 +285,8 @@ macro_rules! pin_id { $(#[$meta])? impl PinId for $Id { - const DYN: DynPinId = DynPinId { - group: DynGroup::$Group, - num: $NUM, - }; + const DYN: DynPinId = DynPinId::new(Port::$Port, $NUM); + const IRQ: Option = $Irq; } } }; @@ -340,7 +312,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), mode: PhantomData, @@ -348,35 +320,40 @@ impl Pin { } #[inline] - pub fn id(&self) -> DynPinId { + pub const fn id(&self) -> DynPinId { self.inner.id() } + #[inline(always)] + pub const fn irq_id(&self) -> Option { + I::IRQ + } + /// Convert the pin to the requested [`PinMode`] #[inline] pub fn into_mode(mut self) -> 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() } } - /// Configure the pin for function select 1. See Programmer Guide p.40 for the function table + /// Configure the pin for function select 1. See Programmer Guide p. 286 for the function table #[inline] pub fn into_funsel_1(self) -> Pin { self.into_mode() } - /// Configure the pin for function select 2. See Programmer Guide p.40 for the function table + /// Configure the pin for function select 2. See Programmer Guide p. 286 for the function table #[inline] pub fn into_funsel_2(self) -> Pin { self.into_mode() } - /// Configure the pin for function select 3. See Programmer Guide p.40 for the function table + /// Configure the pin for function select 3. See Programmer Guide p. 286 for the function table #[inline] pub fn into_funsel_3(self) -> Pin { self.into_mode() @@ -418,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() @@ -443,43 +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) { - self.inner.irq_enb(); + // 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) { + self.inner.enable_interrupt(); } #[inline] - pub(crate) fn _is_low(&self) -> bool { - !self.inner.regs.read_pin() + pub fn disable_interrupt(&mut self) { + self.inner.disable_interrupt(); } - #[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(); } } @@ -571,56 +556,61 @@ impl AsMut

for SpecificPin

{ //================================================================================================== impl Pin> { - pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { - self.inner.regs.configure_edge_interrupt(edge_type); - self.irq_enb(); - } - - pub fn configure_interrupt_level(&mut self, level_type: InterruptLevel) { - self.inner.regs.configure_level_interrupt(level_type); - self.irq_enb(); + /// Convert the pin into an async pin. The pin can be converted back by calling + /// [InputPinAsync::release] + pub fn into_async_input(self) -> Result, PortGDoesNotSupportAsyncError> { + InputPinAsync::new(self) } } impl Pin> { - /// See p.53 of the programmers guide for more information. + #[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() + } + /// Possible delays in clock cycles: /// - Delay 1: 1 /// - Delay 2: 2 /// - 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); + self.inner.configure_delay(delay_1, delay_2).unwrap(); } - #[inline] - pub fn toggle_with_toggle_reg(&mut self) { - self.inner.regs.toggle() - } - - /// 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.configure_pulse_mode(enable, default_state); - } - - pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) { - self.inner.regs.configure_edge_interrupt(edge_type); - self.irq_enb(); - } - - pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) { - self.inner.regs.configure_level_interrupt(level_type); - self.irq_enb(); + self.inner + .configure_pulse_mode(enable, default_state) + .unwrap(); } } 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(); } } @@ -628,7 +618,7 @@ impl Pin> { // Embedded HAL traits //================================================================================================== -impl ErrorType for Pin +impl embedded_hal::digital::ErrorType for Pin where I: PinId, M: PinMode, @@ -636,63 +626,69 @@ 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()) + Ok(self.is_low()) + } + + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) } } -impl InputPin for Pin> +impl embedded_hal::digital::InputPin for Pin> where I: PinId, C: OutputConfig + ReadableOutput, { #[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()) } } @@ -754,18 +750,31 @@ macro_rules! pins { } } +macro_rules! declare_pins_with_irq { + ( + $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+] + ) => { + pins!($Port, $PinsName, $($Id $(, $meta)?)+,); + $( + paste::paste! { + pin_id!($Group, $Id, $NUM, Some(pac::Interrupt::[<$Port:upper $NUM>]), $(, $meta)?); + } + )+ + } +} + macro_rules! declare_pins { ( $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+] ) => { pins!($Port, $PinsName, $($Id $(, $meta)?)+,); $( - pin_id!($Group, $Id, $NUM $(, $meta)?); + pin_id!($Group, $Id, $NUM, None, $(, $meta)?); )+ } } -declare_pins!( +declare_pins_with_irq!( A, PinsA, Porta, @@ -789,7 +798,7 @@ declare_pins!( ] ); -declare_pins!( +declare_pins_with_irq!( B, PinsB, Portb, @@ -813,7 +822,7 @@ declare_pins!( ] ); -declare_pins!( +declare_pins_with_irq!( C, PinsC, Portc, @@ -837,7 +846,7 @@ declare_pins!( ] ); -declare_pins!( +declare_pins_with_irq!( D, PinsD, Portd, @@ -861,7 +870,7 @@ declare_pins!( ] ); -declare_pins!( +declare_pins_with_irq!( E, PinsE, Porte, @@ -885,7 +894,7 @@ declare_pins!( ] ); -declare_pins!( +declare_pins_with_irq!( F, PinsF, Portf, diff --git a/va416xx-hal/src/gpio/reg.rs b/va416xx-hal/src/gpio/reg.rs deleted file mode 100644 index 427f6d1..0000000 --- a/va416xx-hal/src/gpio/reg.rs +++ /dev/null @@ -1,388 +0,0 @@ -use crate::FunSel; - -use super::{ - dynpin::{self, DynGroup, DynPinId}, - DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState, -}; -use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg}; - -/// 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; - fields.funsel = FunSel::Sel0 as u8; - 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; - fields.funsel = FunSel::Sel0 as u8; - 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 - } -} - -//============================================================================== -// RegisterInterface -//============================================================================== - -pub type PortReg = ioconfig::Porta; - -/// Provide a safe register interface for pin objects -/// -/// [`PORT`], 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; - - /// 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(always)] - fn port_reg(&self) -> &PortRegisterBlock { - match self.id().group { - DynGroup::A => unsafe { &(*Porta::ptr()) }, - DynGroup::B => unsafe { &(*Portb::ptr()) }, - DynGroup::C => unsafe { &(*Portc::ptr()) }, - DynGroup::D => unsafe { &(*Portd::ptr()) }, - DynGroup::E => unsafe { &(*Porte::ptr()) }, - DynGroup::F => unsafe { &(*Portf::ptr()) }, - DynGroup::G => unsafe { &(*Portg::ptr()) }, - } - } - - #[inline(always)] - fn iocfg_port(&self) -> &PortReg { - let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() }; - match self.id().group { - DynGroup::A => ioconfig.porta(self.id().num as usize), - DynGroup::B => ioconfig.portb0(self.id().num as usize), - DynGroup::C => ioconfig.portc0(self.id().num as usize), - DynGroup::D => ioconfig.portd0(self.id().num as usize), - DynGroup::E => ioconfig.porte0(self.id().num as usize), - DynGroup::F => ioconfig.portf0(self.id().num as usize), - DynGroup::G => ioconfig.portg0(self.id().num as usize), - } - } - - #[inline(always)] - fn mask_32(&self) -> u32 { - 1 << self.id().num - } - - #[inline(always)] - 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 configure_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/va416xx-hal/src/lib.rs b/va416xx-hal/src/lib.rs index ab22eee..b068054 100644 --- a/va416xx-hal/src/lib.rs +++ b/va416xx-hal/src/lib.rs @@ -35,6 +35,7 @@ compile_error!( " ); +use gpio::Port; pub use va416xx as device; pub use va416xx as pac; @@ -71,20 +72,52 @@ pub enum FunSel { Sel3 = 0b11, } +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[error("invalid pin with number {0}")] +pub struct InvalidPinError(u8); + +/// Can be used to manually manipulate the function select of port pins. +/// +/// The function selection table can be found on p.286 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. +#[inline] +pub fn port_function_select( + ioconfig: &mut pac::Ioconfig, + port: Port, + pin: u8, + funsel: FunSel, +) -> Result<(), InvalidPinError> { + if (port == Port::G && pin >= 8) || pin >= 16 { + return Err(InvalidPinError(pin)); + } + let reg_block = match port { + Port::A => ioconfig.porta(pin as usize), + Port::B => ioconfig.portb0(pin as usize), + Port::C => ioconfig.portc0(pin as usize), + Port::D => ioconfig.portd0(pin as usize), + Port::E => ioconfig.porte0(pin as usize), + Port::F => ioconfig.portf0(pin as usize), + Port::G => ioconfig.portg0(pin as usize), + }; + + reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) }); + Ok(()) +} + /// Enable a specific interrupt using the NVIC peripheral. /// /// # Safety /// /// This function is `unsafe` because it can break mask-based critical sections. #[inline] -pub unsafe fn enable_interrupt(irq: pac::Interrupt) { - unsafe { - cortex_m::peripheral::NVIC::unmask(irq); - } +pub unsafe fn enable_nvic_interrupt(irq: pac::Interrupt) { + cortex_m::peripheral::NVIC::unmask(irq); } /// Disable a specific interrupt using the NVIC peripheral. #[inline] -pub fn disable_interrupt(irq: pac::Interrupt) { +pub fn disable_nvic_interrupt(irq: pac::Interrupt) { cortex_m::peripheral::NVIC::mask(irq); } diff --git a/va416xx-hal/src/timer.rs b/va416xx-hal/src/timer.rs index 2e7d10e..07de4fe 100644 --- a/va416xx-hal/src/timer.rs +++ b/va416xx-hal/src/timer.rs @@ -24,8 +24,7 @@ use crate::gpio::{ use crate::time::Hertz; use crate::typelevel::Sealed; -use crate::{disable_interrupt, prelude::*}; -use crate::{enable_interrupt, pac}; +use crate::{disable_nvic_interrupt, enable_nvic_interrupt, pac, prelude::*}; pub static MS_COUNTER: Mutex> = Mutex::new(Cell::new(0)); @@ -406,7 +405,7 @@ pub type TimRegBlock = pac::tim0::RegisterBlock; /// /// # Safety /// -/// Users should only implement the [`tim_id`] function. No default function +/// Users should only implement the [Self::tim_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. @@ -590,7 +589,7 @@ impl CountdownTimer { pub fn listen(&mut self) { self.listening = true; self.enable_interrupt(); - unsafe { enable_interrupt(Tim::IRQ) } + unsafe { enable_nvic_interrupt(Tim::IRQ) } } /// Return `Ok` if the timer has wrapped. Peripheral will automatically clear the @@ -618,7 +617,7 @@ impl CountdownTimer { pub fn unlisten(&mut self) { self.listening = true; self.disable_interrupt(); - disable_interrupt(Tim::IRQ); + disable_nvic_interrupt(Tim::IRQ); } #[inline(always)] diff --git a/va416xx-hal/src/uart.rs b/va416xx-hal/src/uart.rs index 4f1ea2a..30cae8e 100644 --- a/va416xx-hal/src/uart.rs +++ b/va416xx-hal/src/uart.rs @@ -1,7 +1,7 @@ //! # API for the UART peripheral //! //! The core of this API are the [Uart], [UartBase], [Rx] and [Tx] structures. -//! The RX structure also has a dedicated [RxWithIrq] variant which allows reading the receiver +//! The RX structure also has a dedicated [RxWithInterrupt] variant which allows reading the receiver //! using interrupts. //! //! ## Examples @@ -18,7 +18,7 @@ use fugit::RateExtU32; use crate::clock::{Clocks, PeripheralSelect, SyscfgExt}; use crate::gpio::PF13; use crate::time::Hertz; -use crate::{disable_interrupt, enable_interrupt}; +use crate::{disable_nvic_interrupt, enable_nvic_interrupt}; use crate::{ gpio::{ AltFunc1, AltFunc2, AltFunc3, Pin, PA2, PA3, PB14, PB15, PC14, PC4, PC5, PD11, PD12, PE2, @@ -580,7 +580,7 @@ impl UartBase { w.rxenable().clear_bit(); w.txenable().clear_bit() }); - disable_interrupt(Uart::IRQ_RX); + disable_nvic_interrupt(Uart::IRQ_RX); self.uart } @@ -975,7 +975,7 @@ impl RxWithInterrupt { pub fn start(&mut self) { self.0.enable(); self.enable_rx_irq_sources(true); - unsafe { enable_interrupt(Uart::IRQ_RX) }; + unsafe { enable_nvic_interrupt(Uart::IRQ_RX) }; } #[inline(always)] diff --git a/va416xx-hal/src/wdt.rs b/va416xx-hal/src/wdt.rs index bb817d6..24b9cf4 100644 --- a/va416xx-hal/src/wdt.rs +++ b/va416xx-hal/src/wdt.rs @@ -9,7 +9,7 @@ use crate::{ pac, prelude::SyscfgExt, }; -use crate::{disable_interrupt, enable_interrupt}; +use crate::{disable_nvic_interrupt, enable_nvic_interrupt}; pub const WDT_UNLOCK_VALUE: u32 = 0x1ACC_E551; @@ -30,12 +30,12 @@ pub type WdtController = Wdt; /// This function is `unsafe` because it can break mask-based critical sections. #[inline] pub unsafe fn enable_wdt_interrupts() { - enable_interrupt(pac::Interrupt::WATCHDOG) + enable_nvic_interrupt(pac::Interrupt::WATCHDOG) } #[inline] pub fn disable_wdt_interrupts() { - disable_interrupt(pac::Interrupt::WATCHDOG) + disable_nvic_interrupt(pac::Interrupt::WATCHDOG) } impl Wdt { diff --git a/vorago-peb1/Cargo.toml b/vorago-peb1/Cargo.toml index 928f499..042a4bb 100644 --- a/vorago-peb1/Cargo.toml +++ b/vorago-peb1/Cargo.toml @@ -16,6 +16,7 @@ cortex-m-rt = "0.7" embedded-hal = "1" [dependencies.va416xx-hal] +path = "../va416xx-hal" features = ["va41630"] version = ">=0.3, <0.4" diff --git a/vorago-peb1/src/lib.rs b/vorago-peb1/src/lib.rs index 06243f2..5453cec 100644 --- a/vorago-peb1/src/lib.rs +++ b/vorago-peb1/src/lib.rs @@ -14,7 +14,7 @@ pub mod accelerometer { use lis2dh12::{self, detect_i2c_addr, AddrDetectionError, Lis2dh12}; use va416xx_hal::{ clock::Clocks, - i2c::{self, ClockTooSlowForFastI2c, I2cMaster, I2cSpeed, MasterConfig}, + i2c::{self, ClockTooSlowForFastI2cError, I2cMaster, I2cSpeed, MasterConfig}, pac, }; @@ -23,7 +23,7 @@ pub mod accelerometer { #[derive(Debug)] pub enum ConstructorError { - ClkError(ClockTooSlowForFastI2c), + ClkError(ClockTooSlowForFastI2cError), AddrDetectionError(AddrDetectionError), AccelerometerError(lis2dh12::Error), } -- 2.43.0