This commit is contained in:
2025-04-15 11:38:42 +02:00
parent ecf2f4bf11
commit dfe34e965f
15 changed files with 238 additions and 622 deletions

View File

@ -19,7 +19,10 @@ use portable_atomic::AtomicBool;
use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
pub use super::ll::InterruptEdge;
use super::{Input, Pin, PinIdProvider, Port, ll::PinId};
use super::{
Input, Port,
ll::{LowLevelGpio, PinId},
};
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_A] = [const { AtomicWaker::new() }; NUM_PORT_A];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_B] = [const { AtomicWaker::new() }; NUM_PORT_B];
@ -98,15 +101,14 @@ impl InputPinFuture {
edge: InterruptEdge,
) -> Self {
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.port());
edge_detection_group[pin.offset() as usize]
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().offset() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(edge).unwrap();
pin.configure_edge_interrupt(edge);
#[cfg(feature = "vor1x")]
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Self {
port: pin.port(),
offset: pin.offset(),
id: pin.id(),
waker_group,
edge_detection_group,
}
@ -115,8 +117,8 @@ impl InputPinFuture {
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(false);
let mut ll = LowLevelGpio::new(self.id);
ll.disable_interrupt(false);
}
}
@ -126,7 +128,7 @@ impl Future for InputPinFuture {
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
let idx = self.pin_id.num() as usize;
let idx = self.id.offset() 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(());
@ -159,9 +161,8 @@ impl InputPinAsync {
pub async fn wait_for_high(&mut self) {
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
.unwrap();
if self.pin.is_high().unwrap() {
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
if self.pin.is_high() {
return;
}
fut.await;
@ -173,9 +174,8 @@ impl InputPinAsync {
pub async fn wait_for_low(&mut self) {
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
.unwrap();
if self.pin.is_low().unwrap() {
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
if self.pin.is_low() {
return;
}
fut.await;
@ -184,25 +184,19 @@ impl InputPinAsync {
/// 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_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow)
.unwrap()
.await;
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).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_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
.unwrap()
.await;
InputPinFuture::new_with_input_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) {
// Unwrap okay, checked pin in constructor.
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges)
.unwrap()
.await;
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
}
pub fn release(self) -> Input {
@ -240,93 +234,3 @@ impl Wait for InputPinAsync {
Ok(())
}
}
pub struct InputPinAsync<I: PinIdProvider, C: InputConfig> {
pin: Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
}
impl<I: PinIdProvider, C: InputConfig> InputPinAsync<I, C> {
/// 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<I, pin::Input<C>>, irq: pac::Interrupt) -> Self {
Self { pin, irq }
}
/// Asynchronously wait until the pin is high.
///
/// This returns immediately if the pin is already high.
pub async fn wait_for_high(&mut self) {
let fut = InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
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, self.irq, InterruptEdge::HighToLow);
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, self.irq, InterruptEdge::HighToLow).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, 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) {
InputPinFuture::new_with_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
}
pub fn release(self) -> Pin<I, pin::Input<C>> {
self.pin
}
}
impl<I: PinIdProvider, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
type Error = core::convert::Infallible;
}
impl<I: PinIdProvider, C: InputConfig> Wait for InputPinAsync<I, C> {
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(())
}
}

View File

@ -1,5 +1,8 @@
pub use embedded_hal::digital::PinState;
#[cfg(feature = "vor1x")]
use crate::{PeripheralSelect, sysconfig::enable_peripheral_clock};
pub use crate::InvalidOffsetError;
pub use crate::Port;
pub use crate::ioconfig::regs::Pull;
@ -20,7 +23,7 @@ pub enum InterruptLevel {
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PinId {
port: Port,
@ -40,7 +43,7 @@ impl PinId {
}
pub const fn offset(&self) -> usize {
self.port
self.offset
}
}
@ -64,10 +67,22 @@ impl LowLevelGpio {
self.id
}
#[inline]
pub fn port(&self) -> Port {
self.id.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.id.offset()
}
pub fn configure_as_input_floating(&mut self) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |mut config| {
self.ioconfig.modify_pin_config_unchecked(
self.id.port(),
self.id.offset(),
|mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
@ -77,18 +92,21 @@ impl LowLevelGpio {
config.set_invert_output(false);
config.set_input_enable_when_output(false);
config
});
},
);
}
self.gpio.modify_dir(|mut dir| {
dir &= !self.mask_32();
dir &= !(1 << self.id.offset());
dir
});
}
pub fn configure_as_input_with_pull(&mut self, pull: Pull) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |mut config| {
self.ioconfig.modify_pin_config_unchecked(
self.id.port(),
self.id.offset(),
|mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
@ -99,18 +117,21 @@ impl LowLevelGpio {
config.set_invert_output(false);
config.set_input_enable_when_output(false);
config
});
},
);
}
self.gpio.modify_dir(|mut dir| {
dir &= !self.mask_32();
dir &= !(1 << self.id.offset());
dir
});
}
pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |mut config| {
self.ioconfig.modify_pin_config_unchecked(
self.id.port(),
self.id.offset(),
|mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
@ -120,22 +141,25 @@ impl LowLevelGpio {
config.set_invert_output(false);
config.set_input_enable_when_output(true);
config
});
},
);
}
match init_level {
PinState::Low => self.gpio.write_clr_out(1 << self.offset),
PinState::High => self.gpio.write_set_out(1 << self.offset),
PinState::Low => self.gpio.write_clr_out(self.mask_32()),
PinState::High => self.gpio.write_set_out(self.mask_32()),
}
self.gpio.modify_dir(|mut dir| {
dir |= self.mask_32();
dir |= 1 << self.id.offset();
dir
});
}
pub fn configure_as_output_open_drain(&mut self, init_level: PinState) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |mut config| {
self.ioconfig.modify_pin_config_unchecked(
self.id.port(),
self.id.offset(),
|mut config| {
config.set_funsel(FunSel::Sel0);
config.set_io_disable(false);
config.set_invert_input(false);
@ -146,22 +170,26 @@ impl LowLevelGpio {
config.set_invert_output(false);
config.set_input_enable_when_output(true);
config
});
},
);
}
let mask32 = self.mask_32();
match init_level {
PinState::Low => self.gpio.write_clr_out(1 << self.offset),
PinState::High => self.gpio.write_set_out(1 << self.offset),
PinState::Low => self.gpio.write_clr_out(mask32),
PinState::High => self.gpio.write_set_out(mask32),
}
self.gpio.modify_dir(|mut dir| {
dir |= 1 << self.offset;
dir |= mask32;
dir
});
}
pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunSel, pull: Option<Pull>) {
unsafe {
self.ioconfig
.modify_pin_config_unchecked(self.port, self.offset, |mut config| {
self.ioconfig.modify_pin_config_unchecked(
self.id.port(),
self.id.offset(),
|mut config| {
config.set_funsel(fun_sel);
config.set_io_disable(false);
config.set_invert_input(false);
@ -170,13 +198,14 @@ impl LowLevelGpio {
config.set_pull_dir(pull.unwrap_or(Pull::Up));
config.set_invert_output(false);
config
});
},
);
}
}
#[inline]
pub fn is_high(&self) -> bool {
(self.gpio.read_data_in() >> self.offset) & 1 == 1
(self.gpio.read_data_in() >> self.offset()) & 1 == 1
}
#[inline]
@ -196,7 +225,7 @@ impl LowLevelGpio {
#[inline]
pub fn is_set_high(&self) -> bool {
(self.gpio.read_data_out() >> self.offset) & 1 == 1
(self.gpio.read_data_out() >> self.offset()) & 1 == 1
}
#[inline]
@ -218,7 +247,19 @@ impl LowLevelGpio {
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
}
self.gpio.modify_irq_enb(|mut value| {
value |= self.mask_32();
value |= 1 << self.id.offset;
value
});
}
#[cfg(feature = "vor1x")]
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
if reset_irqsel {
self.reset_irqsel();
}
// We only manipulate our own bit.
self.gpio.modify_irq_enb(|mut value| {
value &= !(1 << self.id.offset);
value
});
}
@ -227,36 +268,75 @@ impl LowLevelGpio {
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
unsafe {
self.gpio.modify_irq_sen(|mut value| {
value &= !self.mask_32();
value
});
match edge_type {
InterruptEdge::HighToLow => {
self.gpio.modify_irq_evt(|mut value| {
value &= !self.mask_32();
value
});
}
InterruptEdge::LowToHigh => {
self.gpio.modify_irq_evt(|mut value| {
value |= self.mask_32();
value
});
}
InterruptEdge::BothEdges => {
self.gpio.modify_irq_edge(|mut value| {
value |= self.mask_32();
value
});
}
let mask32 = self.mask_32();
self.gpio.modify_irq_sen(|mut value| {
value &= !mask32;
value
});
match edge_type {
InterruptEdge::HighToLow => {
self.gpio.modify_irq_evt(|mut value| {
value &= !mask32;
value
});
}
InterruptEdge::LowToHigh => {
self.gpio.modify_irq_evt(|mut value| {
value |= mask32;
value
});
}
InterruptEdge::BothEdges => {
self.gpio.modify_irq_edge(|mut value| {
value |= mask32;
value
});
}
}
}
#[cfg(feature = "vor1x")]
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
pub fn configure_irqsel(&mut self, id: va108xx::Interrupt) {
let irqsel = unsafe { va108xx::Irqsel::steal() };
enable_peripheral_clock(PeripheralSelect::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().offset() as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
super::Port::B => {
irqsel
.portb0(self.id().offset() as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
}
}
#[cfg(feature = "vor1x")]
/// Reset the IRQSEL peripheral value for this particular pin.
pub fn reset_irqsel(&mut self) {
let irqsel = unsafe { va108xx::Irqsel::steal() };
enable_peripheral_clock(PeripheralSelect::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().offset() as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
super::Port::B => {
irqsel
.portb0(self.id().offset() as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
}
}
#[inline(always)]
pub const fn mask_32(&self) -> u32 {
1 << self.offset
1 << self.id.offset()
}
}

View File

@ -1,6 +1,7 @@
use core::convert::Infallible;
pub use embedded_hal::digital::PinState;
pub use ll::PinId;
pub use ll::{Port, Pull};
use crate::ioconfig::regs::FunSel;
@ -9,15 +10,15 @@ pub mod asynch;
pub mod ll;
pub mod regs;
pub trait PinIdProvider {
pub trait PinMarker {
const ID: ll::PinId;
}
pub struct Pin<I: PinIdProvider> {
pub struct Pin<I: PinMarker> {
phantom: core::marker::PhantomData<I>,
}
impl<I: PinIdProvider> Pin<I> {
impl<I: PinMarker> Pin<I> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
@ -29,7 +30,7 @@ impl<I: PinIdProvider> Pin<I> {
pub struct Output(ll::LowLevelGpio);
impl Output {
pub fn new<I: PinIdProvider>(_pin: Pin<I>, init_level: PinState) -> Self {
pub fn new<I: PinMarker>(_pin: Pin<I>, init_level: PinState) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_output_push_pull(init_level);
Output(ll)
@ -101,35 +102,43 @@ impl embedded_hal::digital::StatefulOutputPin for Output {
pub struct Input(ll::LowLevelGpio);
impl Input {
pub fn new_floating<I: PinIdProvider>(_pin: Pin<I>) -> Self {
pub fn new_floating<I: PinMarker>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_floating();
Input(ll)
}
pub fn new_with_pull<I: PinIdProvider>(_pin: Pin<I>, pull: Pull) -> Self {
pub fn new_with_pull<I: PinMarker>(_pin: Pin<I>, pull: Pull) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_with_pull(pull);
Input(ll)
}
#[inline]
pub fn port(&self) -> Port {
self.0.port()
}
#[inline]
pub fn offset(&self) -> usize {
self.0.offset()
pub fn id(&self) -> PinId {
self.0.id()
}
#[cfg(feature = "vor1x")]
#[inline]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
self.0.enable_interrupt(irq_cfg);
}
#[inline]
pub fn configure_edge_interrupt(&mut self, edge: ll::InterruptEdge) {
self.0.configure_edge_interrupt(edge);
}
#[inline]
pub fn is_low(&self) -> bool {
self.0.is_low()
}
#[inline]
pub fn is_high(&self) -> bool {
self.0.is_high()
}
}
impl embedded_hal::digital::ErrorType for Input {
@ -169,7 +178,7 @@ pub struct Flex {
}
impl Flex {
pub fn new<I: PinIdProvider>(_pin: Pin<I>) -> Self {
pub fn new<I: PinMarker>(_pin: Pin<I>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_input_floating();
Flex {
@ -278,7 +287,7 @@ pub struct IoPeriphPin {
}
impl IoPeriphPin {
pub fn new<I: PinIdProvider>(_pin: Pin<I>, fun_sel: FunSel, pull: Option<Pull>) -> Self {
pub fn new<I: PinMarker>(_pin: Pin<I>, fun_sel: FunSel, pull: Option<Pull>) -> Self {
let mut ll = ll::LowLevelGpio::new(I::ID);
ll.configure_as_peripheral_pin(fun_sel, pull);
IoPeriphPin { ll, fun_sel }

View File

@ -167,7 +167,7 @@ impl MmioIoConfig<'_> {
}
}
pub fn modify_pin_config<F: FnMut(Config) -> Config>(
pub fn modify_pin_config<F: FnOnce(Config) -> Config>(
&mut self,
port: crate::Port,
offset: usize,
@ -185,7 +185,7 @@ impl MmioIoConfig<'_> {
/// # Safety
///
/// Calling this function with an invalid offset can lead to undefined behaviour.
pub unsafe fn modify_pin_config_unchecked<F: FnMut(Config) -> Config>(
pub unsafe fn modify_pin_config_unchecked<F: FnOnce(Config) -> Config>(
&mut self,
port: crate::Port,
offset: usize,

View File

@ -1,12 +1,32 @@
#![no_std]
pub mod gpio;
pub mod ioconfig;
pub mod sysconfig;
#[cfg(not(feature = "_family-selected"))]
compile_error!("no Vorago CPU family was select. Choices: vor1x or vor4x");
pub use ioconfig::regs::FunSel;
#[cfg(feature = "vor1x")]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
PortB = 1,
Spi0 = 4,
Spi1 = 5,
Spi2 = 6,
Uart0 = 8,
Uart1 = 9,
I2c0 = 16,
I2c1 = 17,
Irqsel = 21,
Ioconfig = 22,
Utility = 23,
Gpio = 24,
}
cfg_if::cfg_if! {
if #[cfg(feature = "vor1x")] {
/// Number of GPIO ports and IOCONFIG registers for PORT A
@ -43,7 +63,7 @@ pub enum Port {
}
impl Port {
pub fn max_offset(&self) -> usize {
pub const fn max_offset(&self) -> usize {
match self {
Port::A => NUM_PORT_A,
Port::B => NUM_PORT_B,
@ -60,7 +80,7 @@ impl Port {
///
/// Circumvents ownership and safety guarantees by the HAL.
pub unsafe fn steal_gpio(&self) -> gpio::regs::MmioGpio<'static> {
gpio::regs::Gpio::new_mmio(self)
gpio::regs::Gpio::new_mmio(*self)
}
}

View File

@ -0,0 +1,17 @@
#[cfg(feature = "vor1x")]
#[inline]
pub fn enable_peripheral_clock(clock: crate::PeripheralSelect) {
let syscfg = unsafe { va108xx::Sysconfig::steal() };
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << clock as u8)) });
}
#[cfg(feature = "vor1x")]
#[inline]
pub fn disable_peripheral_clock(clock: crate::PeripheralSelect) {
let syscfg = unsafe { va108xx::Sysconfig::steal() };
syscfg
.peripheral_clk_enable()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << clock as u8)) });
}