init commit
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
//! # Async GPIO functionality for the Vorago GPIO peripherals.
|
||||
//!
|
||||
//! This module provides the [InputPinAsync] which implements
|
||||
//! 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.
|
||||
use core::future::Future;
|
||||
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use portable_atomic::AtomicBool;
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
use crate::NUM_PORT_DEFAULT;
|
||||
#[cfg(feature = "vor1x")]
|
||||
use crate::{InterruptConfig, NUM_PORT_A, NUM_PORT_B};
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
use super::ll::PortDoesNotSupportInterrupts;
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
use va108xx as pac;
|
||||
|
||||
pub use super::ll::InterruptEdge;
|
||||
use super::{
|
||||
Input, Port,
|
||||
ll::{LowLevelGpio, PinId},
|
||||
};
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "vor1x")] {
|
||||
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];
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_A] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_A];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_B] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_B];
|
||||
} else {
|
||||
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
static WAKERS_FOR_PORT_C: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
static WAKERS_FOR_PORT_D: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
static WAKERS_FOR_PORT_E: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
static WAKERS_FOR_PORT_F: [AtomicWaker; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicWaker::new() }; NUM_PORT_DEFAULT];
|
||||
|
||||
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
static EDGE_DETECTION_PORT_C: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
static EDGE_DETECTION_PORT_D: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
static EDGE_DETECTION_PORT_E: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
static EDGE_DETECTION_PORT_F: [AtomicBool; NUM_PORT_DEFAULT] =
|
||||
[const { AtomicBool::new(false) }; NUM_PORT_DEFAULT];
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pin_group_to_waker_and_edge_detection_group(
|
||||
port: Port,
|
||||
) -> (&'static [AtomicWaker], &'static [AtomicBool]) {
|
||||
match port {
|
||||
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()),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::C => (WAKERS_FOR_PORT_C.as_ref(), EDGE_DETECTION_PORT_C.as_ref()),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::D => (WAKERS_FOR_PORT_D.as_ref(), EDGE_DETECTION_PORT_D.as_ref()),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::E => (WAKERS_FOR_PORT_E.as_ref(), EDGE_DETECTION_PORT_E.as_ref()),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::F => (WAKERS_FOR_PORT_F.as_ref(), EDGE_DETECTION_PORT_F.as_ref()),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::G => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 to complete
|
||||
/// complete async operations.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn on_interrupt_for_async_gpio_for_port(port: Port) {
|
||||
on_interrupt_for_async_gpio_for_port_generic(port);
|
||||
}
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn on_interrupt_for_async_gpio_for_port(
|
||||
port: Port,
|
||||
) -> Result<(), PortDoesNotSupportInterrupts> {
|
||||
if port == Port::G {
|
||||
return Err(PortDoesNotSupportInterrupts);
|
||||
}
|
||||
on_interrupt_for_async_gpio_for_port_generic(port);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_interrupt_for_async_gpio_for_port_generic(port: Port) {
|
||||
let gpio = unsafe { port.steal_gpio() };
|
||||
|
||||
let irq_enb = gpio.read_irq_enable();
|
||||
let edge_status = gpio.read_edge_status();
|
||||
let (wakers, edge_detection) = pin_group_to_waker_and_edge_detection_group(port);
|
||||
|
||||
on_interrupt_for_port(irq_enb, edge_status, wakers, edge_detection);
|
||||
}
|
||||
|
||||
#[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] 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 {
|
||||
id: PinId,
|
||||
waker_group: &'static [AtomicWaker],
|
||||
edge_detection_group: &'static [AtomicBool],
|
||||
}
|
||||
|
||||
impl InputPinFuture {
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn new_with_input_pin(pin: &mut Input, irq: pac::Interrupt, edge: InterruptEdge) -> Self {
|
||||
let (waker_group, edge_detection_group) =
|
||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
edge_detection_group[pin.id().offset()].store(false, core::sync::atomic::Ordering::Relaxed);
|
||||
pin.configure_edge_interrupt(edge);
|
||||
#[cfg(feature = "vor1x")]
|
||||
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
|
||||
Self {
|
||||
id: pin.id(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn new_with_input_pin(
|
||||
pin: &mut Input,
|
||||
edge: InterruptEdge,
|
||||
) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||
let (waker_group, edge_detection_group) =
|
||||
pin_group_to_waker_and_edge_detection_group(pin.id().port());
|
||||
pin.configure_edge_interrupt(edge);
|
||||
pin.enable_interrupt(true)?;
|
||||
Ok(Self {
|
||||
id: pin.id(),
|
||||
waker_group,
|
||||
edge_detection_group,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InputPinFuture {
|
||||
fn drop(&mut self) {
|
||||
let mut ll = LowLevelGpio::new(self.id);
|
||||
#[cfg(feature = "vor1x")]
|
||||
ll.disable_interrupt(false);
|
||||
#[cfg(feature = "vor4x")]
|
||||
ll.disable_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for InputPinFuture {
|
||||
type Output = ();
|
||||
fn poll(
|
||||
self: core::pin::Pin<&mut Self>,
|
||||
cx: &mut core::task::Context<'_>,
|
||||
) -> core::task::Poll<Self::Output> {
|
||||
let idx = self.id.offset();
|
||||
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 InputPinAsync {
|
||||
pin: Input,
|
||||
#[cfg(feature = "vor1x")]
|
||||
irq: va108xx::Interrupt,
|
||||
}
|
||||
|
||||
impl InputPinAsync {
|
||||
/// Create a new asynchronous input pin from an [Input] 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.
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn new(pin: Input, irq: va108xx::Interrupt) -> Self {
|
||||
Self { pin, irq }
|
||||
}
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn new(pin: Input) -> Result<Self, PortDoesNotSupportInterrupts> {
|
||||
if pin.id().port() == Port::G {
|
||||
return Err(PortDoesNotSupportInterrupts);
|
||||
}
|
||||
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.
|
||||
#[cfg(feature = "vor1x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh);
|
||||
#[cfg(feature = "vor4x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_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) {
|
||||
// Unwrap okay, checked pin in constructor.
|
||||
#[cfg(feature = "vor1x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow);
|
||||
#[cfg(feature = "vor4x")]
|
||||
let fut =
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::HighToLow).unwrap();
|
||||
if self.pin.is_low() {
|
||||
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.
|
||||
#[cfg(feature = "vor1x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::HighToLow).await;
|
||||
#[cfg(feature = "vor4x")]
|
||||
InputPinFuture::new_with_input_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.
|
||||
#[cfg(feature = "vor1x")]
|
||||
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.
|
||||
#[cfg(feature = "vor1x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, self.irq, InterruptEdge::BothEdges).await;
|
||||
#[cfg(feature = "vor4x")]
|
||||
InputPinFuture::new_with_input_pin(&mut self.pin, InterruptEdge::BothEdges)
|
||||
.unwrap()
|
||||
.await;
|
||||
}
|
||||
|
||||
pub fn release(self) -> Input {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
pub use embedded_hal::digital::PinState;
|
||||
|
||||
use crate::ioconfig::FilterClkSel;
|
||||
use crate::ioconfig::FilterType;
|
||||
#[cfg(feature = "vor1x")]
|
||||
use crate::{PeripheralSelect, sysconfig::enable_peripheral_clock};
|
||||
|
||||
pub use crate::InvalidOffsetError;
|
||||
pub use crate::Port;
|
||||
pub use crate::ioconfig::regs::Pull;
|
||||
use crate::ioconfig::regs::{FunSel, IoConfig, MmioIoConfig};
|
||||
|
||||
use super::Pin;
|
||||
use super::PinIdProvider;
|
||||
|
||||
#[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,
|
||||
}
|
||||
|
||||
/// Pin identifier for all physical pins exposed by Vorago MCUs.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PinId {
|
||||
port: Port,
|
||||
/// Offset within the port.
|
||||
offset: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[cfg(feature = "vor4x")]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[error("port G does not support interrupts")]
|
||||
pub struct PortDoesNotSupportInterrupts;
|
||||
|
||||
impl PinId {
|
||||
/// Unchecked constructor which panics on invalid offsets.
|
||||
pub const fn new_unchecked(port: Port, offset: usize) -> Self {
|
||||
if offset >= port.max_offset() {
|
||||
panic!("Pin ID construction: offset is out of range");
|
||||
}
|
||||
PinId {
|
||||
port,
|
||||
offset: offset as u8,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new(port: Port, offset: usize) -> Result<Self, InvalidOffsetError> {
|
||||
if offset >= port.max_offset() {
|
||||
return Err(InvalidOffsetError { offset, port });
|
||||
}
|
||||
Ok(PinId {
|
||||
port,
|
||||
offset: offset as u8,
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn port(&self) -> Port {
|
||||
self.port
|
||||
}
|
||||
|
||||
pub const fn offset(&self) -> usize {
|
||||
self.offset as usize
|
||||
}
|
||||
|
||||
/// This function panics if the port is [Port::G].
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn irq(&self) -> Result<va416xx::Interrupt, PortDoesNotSupportInterrupts> {
|
||||
if self.port() == Port::G {
|
||||
return Err(PortDoesNotSupportInterrupts);
|
||||
}
|
||||
Ok(self.irq_unchecked())
|
||||
}
|
||||
|
||||
/// This function panics if the port is [Port::G].
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub const fn irq_unchecked(&self) -> va416xx::Interrupt {
|
||||
match self.port() {
|
||||
Port::A => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTA0,
|
||||
1 => va416xx::Interrupt::PORTA1,
|
||||
2 => va416xx::Interrupt::PORTA2,
|
||||
3 => va416xx::Interrupt::PORTA3,
|
||||
4 => va416xx::Interrupt::PORTA4,
|
||||
5 => va416xx::Interrupt::PORTA5,
|
||||
6 => va416xx::Interrupt::PORTA6,
|
||||
7 => va416xx::Interrupt::PORTA7,
|
||||
8 => va416xx::Interrupt::PORTA8,
|
||||
9 => va416xx::Interrupt::PORTA9,
|
||||
10 => va416xx::Interrupt::PORTA10,
|
||||
11 => va416xx::Interrupt::PORTA11,
|
||||
12 => va416xx::Interrupt::PORTA12,
|
||||
13 => va416xx::Interrupt::PORTA13,
|
||||
14 => va416xx::Interrupt::PORTA14,
|
||||
15 => va416xx::Interrupt::PORTA15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::B => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTB0,
|
||||
1 => va416xx::Interrupt::PORTB1,
|
||||
2 => va416xx::Interrupt::PORTB2,
|
||||
3 => va416xx::Interrupt::PORTB3,
|
||||
4 => va416xx::Interrupt::PORTB4,
|
||||
5 => va416xx::Interrupt::PORTB5,
|
||||
6 => va416xx::Interrupt::PORTB6,
|
||||
7 => va416xx::Interrupt::PORTB7,
|
||||
8 => va416xx::Interrupt::PORTB8,
|
||||
9 => va416xx::Interrupt::PORTB9,
|
||||
10 => va416xx::Interrupt::PORTB10,
|
||||
11 => va416xx::Interrupt::PORTB11,
|
||||
12 => va416xx::Interrupt::PORTB12,
|
||||
13 => va416xx::Interrupt::PORTB13,
|
||||
14 => va416xx::Interrupt::PORTB14,
|
||||
15 => va416xx::Interrupt::PORTB15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::C => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTC0,
|
||||
1 => va416xx::Interrupt::PORTC1,
|
||||
2 => va416xx::Interrupt::PORTC2,
|
||||
3 => va416xx::Interrupt::PORTC3,
|
||||
4 => va416xx::Interrupt::PORTC4,
|
||||
5 => va416xx::Interrupt::PORTC5,
|
||||
6 => va416xx::Interrupt::PORTC6,
|
||||
7 => va416xx::Interrupt::PORTC7,
|
||||
8 => va416xx::Interrupt::PORTC8,
|
||||
9 => va416xx::Interrupt::PORTC9,
|
||||
10 => va416xx::Interrupt::PORTC10,
|
||||
11 => va416xx::Interrupt::PORTC11,
|
||||
12 => va416xx::Interrupt::PORTC12,
|
||||
13 => va416xx::Interrupt::PORTC13,
|
||||
14 => va416xx::Interrupt::PORTC14,
|
||||
15 => va416xx::Interrupt::PORTC15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::D => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTD0,
|
||||
1 => va416xx::Interrupt::PORTD1,
|
||||
2 => va416xx::Interrupt::PORTD2,
|
||||
3 => va416xx::Interrupt::PORTD3,
|
||||
4 => va416xx::Interrupt::PORTD4,
|
||||
5 => va416xx::Interrupt::PORTD5,
|
||||
6 => va416xx::Interrupt::PORTD6,
|
||||
7 => va416xx::Interrupt::PORTD7,
|
||||
8 => va416xx::Interrupt::PORTD8,
|
||||
9 => va416xx::Interrupt::PORTD9,
|
||||
10 => va416xx::Interrupt::PORTD10,
|
||||
11 => va416xx::Interrupt::PORTD11,
|
||||
12 => va416xx::Interrupt::PORTD12,
|
||||
13 => va416xx::Interrupt::PORTD13,
|
||||
14 => va416xx::Interrupt::PORTD14,
|
||||
15 => va416xx::Interrupt::PORTD15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::E => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTE0,
|
||||
1 => va416xx::Interrupt::PORTE1,
|
||||
2 => va416xx::Interrupt::PORTE2,
|
||||
3 => va416xx::Interrupt::PORTE3,
|
||||
4 => va416xx::Interrupt::PORTE4,
|
||||
5 => va416xx::Interrupt::PORTE5,
|
||||
6 => va416xx::Interrupt::PORTE6,
|
||||
7 => va416xx::Interrupt::PORTE7,
|
||||
8 => va416xx::Interrupt::PORTE8,
|
||||
9 => va416xx::Interrupt::PORTE9,
|
||||
10 => va416xx::Interrupt::PORTE10,
|
||||
11 => va416xx::Interrupt::PORTE11,
|
||||
12 => va416xx::Interrupt::PORTE12,
|
||||
13 => va416xx::Interrupt::PORTE13,
|
||||
14 => va416xx::Interrupt::PORTE14,
|
||||
15 => va416xx::Interrupt::PORTE15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::F => match self.offset() {
|
||||
0 => va416xx::Interrupt::PORTF0,
|
||||
1 => va416xx::Interrupt::PORTF1,
|
||||
2 => va416xx::Interrupt::PORTF2,
|
||||
3 => va416xx::Interrupt::PORTF3,
|
||||
4 => va416xx::Interrupt::PORTF4,
|
||||
5 => va416xx::Interrupt::PORTF5,
|
||||
6 => va416xx::Interrupt::PORTF6,
|
||||
7 => va416xx::Interrupt::PORTF7,
|
||||
8 => va416xx::Interrupt::PORTF8,
|
||||
9 => va416xx::Interrupt::PORTF9,
|
||||
10 => va416xx::Interrupt::PORTF10,
|
||||
11 => va416xx::Interrupt::PORTF11,
|
||||
12 => va416xx::Interrupt::PORTF12,
|
||||
13 => va416xx::Interrupt::PORTF13,
|
||||
14 => va416xx::Interrupt::PORTF14,
|
||||
15 => va416xx::Interrupt::PORTF15,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Port::G => panic!("port G does not have interrupts"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Low-level driver structure for GPIO pins.
|
||||
pub struct LowLevelGpio {
|
||||
gpio: super::regs::MmioGpio<'static>,
|
||||
ioconfig: MmioIoConfig<'static>,
|
||||
id: PinId,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for LowLevelGpio {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("LowLevelGpio")
|
||||
.field("gpio", &self.gpio.port())
|
||||
.field("id", &self.id)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl LowLevelGpio {
|
||||
/// Create a new low-level GPIO pin instance from a given [Pin].
|
||||
///
|
||||
/// Can be used for performing resource management of the [Pin]s.
|
||||
pub fn new_with_pin<I: PinIdProvider>(_pin: Pin<I>) -> Self {
|
||||
Self::new(I::ID)
|
||||
}
|
||||
|
||||
/// Create a new low-level GPIO pin instance using only the [PinId].
|
||||
pub fn new(id: PinId) -> Self {
|
||||
LowLevelGpio {
|
||||
gpio: super::regs::Gpio::new_mmio(id.port),
|
||||
ioconfig: IoConfig::new_mmio(),
|
||||
id,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> PinId {
|
||||
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) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_funsel(FunSel::Sel0);
|
||||
config.set_io_disable(false);
|
||||
config.set_invert_input(false);
|
||||
config.set_open_drain(false);
|
||||
config.set_pull_enable(false);
|
||||
config.set_pull_when_output_active(false);
|
||||
config.set_invert_output(false);
|
||||
config.set_input_enable_when_output(false);
|
||||
config
|
||||
});
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir &= !(1 << self.id.offset());
|
||||
dir
|
||||
});
|
||||
}
|
||||
|
||||
pub fn configure_as_input_with_pull(&mut self, pull: Pull) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_funsel(FunSel::Sel0);
|
||||
config.set_io_disable(false);
|
||||
config.set_invert_input(false);
|
||||
config.set_open_drain(false);
|
||||
config.set_pull_enable(true);
|
||||
config.set_pull_dir(pull);
|
||||
config.set_pull_when_output_active(false);
|
||||
config.set_invert_output(false);
|
||||
config.set_input_enable_when_output(false);
|
||||
config
|
||||
});
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir &= !(1 << self.id.offset());
|
||||
dir
|
||||
});
|
||||
}
|
||||
|
||||
pub fn configure_as_output_push_pull(&mut self, init_level: PinState) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_funsel(FunSel::Sel0);
|
||||
config.set_io_disable(false);
|
||||
config.set_invert_input(false);
|
||||
config.set_open_drain(false);
|
||||
config.set_pull_enable(false);
|
||||
config.set_pull_when_output_active(false);
|
||||
config.set_invert_output(false);
|
||||
config.set_input_enable_when_output(true);
|
||||
config
|
||||
});
|
||||
match init_level {
|
||||
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 |= 1 << self.id.offset();
|
||||
dir
|
||||
});
|
||||
}
|
||||
|
||||
pub fn configure_as_output_open_drain(&mut self, init_level: PinState) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_funsel(FunSel::Sel0);
|
||||
config.set_io_disable(false);
|
||||
config.set_invert_input(false);
|
||||
config.set_open_drain(true);
|
||||
config.set_pull_enable(true);
|
||||
config.set_pull_dir(Pull::Up);
|
||||
config.set_pull_when_output_active(false);
|
||||
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(mask32),
|
||||
PinState::High => self.gpio.write_set_out(mask32),
|
||||
}
|
||||
self.gpio.modify_dir(|mut dir| {
|
||||
dir |= mask32;
|
||||
dir
|
||||
});
|
||||
}
|
||||
|
||||
pub fn configure_as_peripheral_pin(&mut self, fun_sel: FunSel, pull: Option<Pull>) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_funsel(fun_sel);
|
||||
config.set_io_disable(false);
|
||||
config.set_invert_input(false);
|
||||
config.set_open_drain(false);
|
||||
config.set_pull_enable(pull.is_some());
|
||||
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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low(&self) -> bool {
|
||||
!self.is_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
self.gpio.write_set_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
self.gpio.write_clr_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set_high(&self) -> bool {
|
||||
(self.gpio.read_data_out() >> self.offset()) & 1 == 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set_low(&self) -> bool {
|
||||
!self.is_set_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn toggle(&mut self) {
|
||||
self.gpio.write_tog_out(self.mask_32());
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
|
||||
if irq_cfg.route {
|
||||
self.configure_irqsel(irq_cfg.id);
|
||||
}
|
||||
if irq_cfg.enable_in_nvic {
|
||||
unsafe { crate::enable_nvic_interrupt(irq_cfg.id) };
|
||||
}
|
||||
self.gpio.modify_irq_enable(|mut value| {
|
||||
value |= 1 << self.id.offset;
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn enable_interrupt(
|
||||
&mut self,
|
||||
enable_in_nvic: bool,
|
||||
) -> Result<(), PortDoesNotSupportInterrupts> {
|
||||
if enable_in_nvic {
|
||||
unsafe { crate::enable_nvic_interrupt(self.id().irq_unchecked()) };
|
||||
}
|
||||
self.gpio.modify_irq_enable(|mut value| {
|
||||
value |= 1 << self.id.offset;
|
||||
value
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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_enable(|mut value| {
|
||||
value &= !(1 << self.id.offset);
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
pub fn disable_interrupt(&mut self) {
|
||||
self.gpio.modify_irq_enable(|mut value| {
|
||||
value &= !(1 << self.id.offset);
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure which edge or level type triggers an interrupt
|
||||
#[inline]
|
||||
pub fn configure_level_interrupt(&mut self, level: InterruptLevel) {
|
||||
let mask32 = self.mask_32();
|
||||
self.gpio.modify_irq_sen(|mut value| {
|
||||
value |= mask32;
|
||||
value
|
||||
});
|
||||
if level == InterruptLevel::Low {
|
||||
self.gpio.modify_irq_evt(|mut value| {
|
||||
value &= !mask32;
|
||||
value
|
||||
});
|
||||
} else {
|
||||
self.gpio.modify_irq_evt(|mut value| {
|
||||
value |= mask32;
|
||||
value
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Only useful for input pins
|
||||
#[inline]
|
||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.ioconfig.modify_pin_config(self.id, |mut config| {
|
||||
config.set_filter_type(filter);
|
||||
config.set_filter_clk_sel(clksel);
|
||||
config
|
||||
});
|
||||
}
|
||||
|
||||
/// Only useful for output pins.
|
||||
#[inline]
|
||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.gpio.modify_pulse(|mut value| {
|
||||
if enable {
|
||||
value |= 1 << self.id.offset;
|
||||
} else {
|
||||
value &= !(1 << self.id.offset);
|
||||
}
|
||||
value
|
||||
});
|
||||
self.gpio.modify_pulsebase(|mut value| {
|
||||
if default_state == PinState::High {
|
||||
value |= 1 << self.id.offset;
|
||||
} else {
|
||||
value &= !(1 << self.id.offset);
|
||||
}
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
/// Only useful for output pins
|
||||
#[inline]
|
||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||
self.gpio.modify_delay1(|mut value| {
|
||||
if delay_1 {
|
||||
value |= 1 << self.id.offset;
|
||||
} else {
|
||||
value &= !(1 << self.id.offset);
|
||||
}
|
||||
value
|
||||
});
|
||||
self.gpio.modify_delay2(|mut value| {
|
||||
if delay_2 {
|
||||
value |= 1 << self.id.offset;
|
||||
} else {
|
||||
value &= !(1 << self.id.offset);
|
||||
}
|
||||
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())
|
||||
.write(|w| unsafe { w.bits(id as u32) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().offset())
|
||||
.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())
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
super::Port::B => {
|
||||
irqsel
|
||||
.portb0(self.id().offset())
|
||||
.write(|w| unsafe { w.bits(u32::MAX) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn mask_32(&self) -> u32 {
|
||||
1 << self.id.offset()
|
||||
}
|
||||
}
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
//! GPIO support module.
|
||||
use core::convert::Infallible;
|
||||
|
||||
pub use crate::ioconfig::{regs::FunSel, FilterClkSel, FilterType};
|
||||
pub use embedded_hal::digital::PinState;
|
||||
pub use ll::{InterruptEdge, InterruptLevel, PinId, Port, Pull};
|
||||
|
||||
pub mod asynch;
|
||||
pub mod ll;
|
||||
pub mod regs;
|
||||
|
||||
/// Trait implemented by data structures assocaited with pin identifiacation.
|
||||
pub trait PinIdProvider {
|
||||
const ID: ll::PinId;
|
||||
}
|
||||
|
||||
/// Primary Pin structure for the physical pins exposed by Vorago MCUs.
|
||||
///
|
||||
/// This pin structure is only used for resource management and does not do anything on its
|
||||
/// own.
|
||||
pub struct Pin<I: PinIdProvider> {
|
||||
phantom: core::marker::PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I: PinIdProvider> Pin<I> {
|
||||
#[allow(clippy::new_without_default)]
|
||||
#[doc(hidden)]
|
||||
pub const fn __new() -> Self {
|
||||
Self {
|
||||
phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new pin instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This circumvents ownership rules of the HAL and allows creating multiple instances
|
||||
/// of the same pin.
|
||||
pub const unsafe fn steal() -> Self {
|
||||
Self::__new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Push-Pull output pin.
|
||||
#[derive(Debug)]
|
||||
pub struct Output(ll::LowLevelGpio);
|
||||
|
||||
impl Output {
|
||||
pub fn new<I: PinIdProvider>(_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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn port(&self) -> Port {
|
||||
self.0.port()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(&self) -> usize {
|
||||
self.0.offset()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
self.0.set_high();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
self.0.set_low();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set_high(&self) -> bool {
|
||||
self.0.is_set_high()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_set_low(&self) -> bool {
|
||||
self.0.is_set_low()
|
||||
}
|
||||
|
||||
/// Toggle pin output with dedicated HW feature.
|
||||
#[inline]
|
||||
pub fn toggle(&mut self) {
|
||||
self.0.toggle();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
|
||||
self.0.configure_pulse_mode(enable, default_state);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||
self.0.configure_delay(delay_1, delay_2);
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for Output {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::OutputPin for Output {
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.0.set_low();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.0.set_high();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::StatefulOutputPin for Output {
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.0.is_set_high())
|
||||
}
|
||||
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.0.is_set_low())
|
||||
}
|
||||
|
||||
/// Toggle pin output with dedicated HW feature.
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.0.toggle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Input pin.
|
||||
///
|
||||
/// Can be created as a floating input pin or as an input pin with pull-up or pull-down.
|
||||
#[derive(Debug)]
|
||||
pub struct Input(ll::LowLevelGpio);
|
||||
|
||||
impl Input {
|
||||
pub fn new_floating<I: PinIdProvider>(_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 {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_input_with_pull(pull);
|
||||
Input(ll)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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);
|
||||
}
|
||||
|
||||
#[cfg(feature = "vor4x")]
|
||||
#[inline]
|
||||
pub fn enable_interrupt(
|
||||
&mut self,
|
||||
enable_in_nvic: bool,
|
||||
) -> Result<(), ll::PortDoesNotSupportInterrupts> {
|
||||
self.0.enable_interrupt(enable_in_nvic)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_edge_interrupt(&mut self, edge: InterruptEdge) {
|
||||
self.0.configure_edge_interrupt(edge);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_level_interrupt(&mut self, edge: InterruptLevel) {
|
||||
self.0.configure_level_interrupt(edge);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
|
||||
self.0.configure_delay(delay_1, delay_2);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
|
||||
self.0.configure_filter_type(filter, clksel);
|
||||
}
|
||||
|
||||
#[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 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::InputPin for Input {
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.0.is_low())
|
||||
}
|
||||
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.0.is_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PinMode {
|
||||
InputFloating,
|
||||
InputWithPull(Pull),
|
||||
OutputPushPull,
|
||||
OutputOpenDrain,
|
||||
}
|
||||
|
||||
impl PinMode {
|
||||
pub fn is_input(&self) -> bool {
|
||||
matches!(self, PinMode::InputFloating | PinMode::InputWithPull(_))
|
||||
}
|
||||
|
||||
pub fn is_output(&self) -> bool {
|
||||
!self.is_input()
|
||||
}
|
||||
}
|
||||
|
||||
/// Flex pin abstraction which can be dynamically re-configured.
|
||||
///
|
||||
/// The following functions can be configured at run-time:
|
||||
///
|
||||
/// - Input Floating
|
||||
/// - Input with Pull-Up
|
||||
/// - Output Push-Pull
|
||||
/// - Output Open-Drain.
|
||||
///
|
||||
/// Flex pins are always floating input pins after construction.
|
||||
#[derive(Debug)]
|
||||
pub struct Flex {
|
||||
ll: ll::LowLevelGpio,
|
||||
mode: PinMode,
|
||||
}
|
||||
|
||||
impl Flex {
|
||||
pub fn new<I: PinIdProvider>(_pin: Pin<I>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(I::ID);
|
||||
ll.configure_as_input_floating();
|
||||
Flex {
|
||||
ll,
|
||||
mode: PinMode::InputFloating,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn port(&self) -> Port {
|
||||
self.ll.port()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset(&self) -> usize {
|
||||
self.ll.offset()
|
||||
}
|
||||
|
||||
/// Reads the input state of the pin, regardless of configured mode.
|
||||
#[inline]
|
||||
pub fn is_low(&self) -> bool {
|
||||
self.ll.is_low()
|
||||
}
|
||||
|
||||
/// Reads the input state of the pin, regardless of configured mode.
|
||||
#[inline]
|
||||
pub fn is_high(&self) -> bool {
|
||||
self.ll.is_high()
|
||||
}
|
||||
|
||||
/// If the pin is configured as an input pin, this function does nothing.
|
||||
#[inline]
|
||||
pub fn set_low(&mut self) {
|
||||
if !self.mode.is_input() {
|
||||
return;
|
||||
}
|
||||
self.ll.set_low();
|
||||
}
|
||||
|
||||
/// If the pin is configured as an input pin, this function does nothing.
|
||||
#[inline]
|
||||
pub fn set_high(&mut self) {
|
||||
if !self.mode.is_input() {
|
||||
return;
|
||||
}
|
||||
self.ll.set_high();
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::ErrorType for Flex {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::InputPin for Flex {
|
||||
/// Reads the input state of the pin, regardless of configured mode.
|
||||
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.ll.is_low())
|
||||
}
|
||||
|
||||
/// Reads the input state of the pin, regardless of configured mode.
|
||||
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.ll.is_high())
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::OutputPin for Flex {
|
||||
/// If the pin is configured as an input pin, this function does nothing.
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_low();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the pin is configured as an input pin, this function does nothing.
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.set_high();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_hal::digital::StatefulOutputPin for Flex {
|
||||
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||
/// of this function is undefined.
|
||||
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.ll.is_set_high())
|
||||
}
|
||||
|
||||
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||
/// of this function is undefined.
|
||||
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||
Ok(self.ll.is_set_low())
|
||||
}
|
||||
|
||||
/// Toggle pin output.
|
||||
///
|
||||
/// If the pin is not configured as a stateful output pin like Output Push-Pull, the result
|
||||
/// of this function is undefined.
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
self.ll.toggle();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// IO peripheral pin structure.
|
||||
///
|
||||
/// Can be used to configure pins as IO peripheral pins.
|
||||
pub struct IoPeriphPin {
|
||||
ll: ll::LowLevelGpio,
|
||||
fun_sel: FunSel,
|
||||
}
|
||||
|
||||
impl IoPeriphPin {
|
||||
pub fn new_with_pin<I: PinIdProvider>(
|
||||
_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 }
|
||||
}
|
||||
|
||||
pub fn new(pin_id: PinId, fun_sel: FunSel, pull: Option<Pull>) -> Self {
|
||||
let mut ll = ll::LowLevelGpio::new(pin_id);
|
||||
ll.configure_as_peripheral_pin(fun_sel, pull);
|
||||
IoPeriphPin { ll, fun_sel }
|
||||
}
|
||||
|
||||
pub fn port(&self) -> Port {
|
||||
self.ll.port()
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize {
|
||||
self.ll.offset()
|
||||
}
|
||||
|
||||
pub fn fun_sel(&self) -> FunSel {
|
||||
self.fun_sel
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use crate::Port;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "vor1x")] {
|
||||
/// PORT A base address.
|
||||
pub const GPIO_0_BASE: usize = 0x5000_0000;
|
||||
/// PORT B base address.
|
||||
pub const GPIO_1_BASE: usize = 0x5000_1000;
|
||||
} else if #[cfg(feature = "vor4x")] {
|
||||
/// PORT A base address.
|
||||
pub const GPIO_0_BASE: usize = 0x4001_2000;
|
||||
/// PORT B base address.
|
||||
pub const GPIO_1_BASE: usize = 0x4001_2400;
|
||||
/// PORT C base address.
|
||||
pub const GPIO_2_BASE: usize = 0x4001_2800;
|
||||
/// PORT D base address.
|
||||
pub const GPIO_3_BASE: usize = 0x4001_2C00;
|
||||
/// PORT E base address.
|
||||
pub const GPIO_4_BASE: usize = 0x4001_3000;
|
||||
/// PORT F base address.
|
||||
pub const GPIO_5_BASE: usize = 0x4001_3400;
|
||||
/// PORT G base address.
|
||||
pub const GPIO_6_BASE: usize = 0x4001_3800;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(derive_mmio::Mmio)]
|
||||
#[mmio(no_ctors)]
|
||||
#[repr(C)]
|
||||
pub struct Gpio {
|
||||
#[mmio(PureRead)]
|
||||
data_in: u32,
|
||||
#[mmio(PureRead)]
|
||||
data_in_raw: u32,
|
||||
data_out: u32,
|
||||
data_out_raw: u32,
|
||||
#[mmio(Write)]
|
||||
set_out: u32,
|
||||
#[mmio(Write)]
|
||||
clr_out: u32,
|
||||
#[mmio(Write)]
|
||||
tog_out: u32,
|
||||
data_mask: u32,
|
||||
/// Direction bits. 1 for output, 0 for input.
|
||||
dir: u32,
|
||||
pulse: u32,
|
||||
pulsebase: u32,
|
||||
delay1: u32,
|
||||
delay2: u32,
|
||||
irq_sen: u32,
|
||||
irq_edge: u32,
|
||||
irq_evt: u32,
|
||||
irq_enable: u32,
|
||||
/// Raw interrupt status. This register is not latched and may not indicated edge sensitive
|
||||
/// events.
|
||||
#[mmio(PureRead)]
|
||||
irq_raw: u32,
|
||||
/// Read-only register which shows enabled and active interrupts. Called IRQ_end by Vorago.
|
||||
#[mmio(PureRead)]
|
||||
irq_status: u32,
|
||||
#[mmio(PureRead)]
|
||||
edge_status: u32,
|
||||
|
||||
#[cfg(feature = "vor1x")]
|
||||
_reserved: [u32; 0x3eb],
|
||||
#[cfg(feature = "vor4x")]
|
||||
_reserved: [u32; 0xeb],
|
||||
|
||||
/// Peripheral ID. Vorago 1x reset value: 0x0040_07e1. Vorago 4x reset value: 0x0210_07E9.
|
||||
perid: u32,
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "vor1x")] {
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x1000);
|
||||
} else if #[cfg(feature = "vor4x")] {
|
||||
static_assertions::const_assert_eq!(core::mem::size_of::<Gpio>(), 0x400);
|
||||
}
|
||||
}
|
||||
|
||||
impl Gpio {
|
||||
const fn new_mmio_at(base: usize) -> MmioGpio<'static> {
|
||||
MmioGpio {
|
||||
ptr: base as *mut _,
|
||||
phantom: core::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new_mmio(port: Port) -> MmioGpio<'static> {
|
||||
match port {
|
||||
Port::A => Self::new_mmio_at(GPIO_0_BASE),
|
||||
Port::B => Self::new_mmio_at(GPIO_1_BASE),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::C => Self::new_mmio_at(GPIO_2_BASE),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::D => Self::new_mmio_at(GPIO_3_BASE),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::E => Self::new_mmio_at(GPIO_4_BASE),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::F => Self::new_mmio_at(GPIO_5_BASE),
|
||||
#[cfg(feature = "vor4x")]
|
||||
Port::G => Self::new_mmio_at(GPIO_6_BASE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MmioGpio<'_> {
|
||||
pub fn port(&self) -> Port {
|
||||
match unsafe { self.ptr() } as usize {
|
||||
GPIO_0_BASE => Port::A,
|
||||
GPIO_1_BASE => Port::B,
|
||||
#[cfg(feature = "vor4x")]
|
||||
GPIO_2_BASE => Port::C,
|
||||
#[cfg(feature = "vor4x")]
|
||||
GPIO_3_BASE => Port::D,
|
||||
#[cfg(feature = "vor4x")]
|
||||
GPIO_4_BASE => Port::E,
|
||||
#[cfg(feature = "vor4x")]
|
||||
GPIO_5_BASE => Port::F,
|
||||
#[cfg(feature = "vor4x")]
|
||||
GPIO_6_BASE => Port::G,
|
||||
// Constructors were disabled, so this should really not happen.
|
||||
_ => panic!("unexpected base address of GPIO register block"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user