Rework library structure

Changed:

- Move most library components to new [`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs)
  which is mostly re-exported in this crate.
- All HAL API constructors now have a more consistent argument order: PAC structures and resource
  management structures first, then clock configuration, then any other configuration.
- Overhaul and simplification of several HAL APIs. The system configuration and IRQ router
  peripheral instance generally does not need to be passed to HAL API anymore.
- All HAL drivers are now type erased. The constructors will still expect and consume the PAC
  singleton component for resource management purposes, but are not cached anymore.
- Refactoring of GPIO library to be more inline with embassy GPIO API.

Added:

- I2C clock timeout feature support.
This commit is contained in:
2025-04-12 17:01:53 +02:00
parent 5b2ded51f9
commit 58934e293f
67 changed files with 790 additions and 9283 deletions

View File

@ -1,366 +0,0 @@
//! # Async GPIO functionality for the VA108xx 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 va108xx::{self as pac};
use crate::InterruptConfig;
use super::{
pin, DynPin, DynPinId, InputConfig, InterruptEdge, InvalidPinTypeError, Pin, PinId, Port,
NUM_PINS_PORT_A, NUM_PINS_PORT_B,
};
static WAKERS_FOR_PORT_A: [AtomicWaker; NUM_PINS_PORT_A] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_A];
static WAKERS_FOR_PORT_B: [AtomicWaker; NUM_PINS_PORT_B] =
[const { AtomicWaker::new() }; NUM_PINS_PORT_B];
static EDGE_DETECTION_PORT_A: [AtomicBool; NUM_PINS_PORT_A] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_A];
static EDGE_DETECTION_PORT_B: [AtomicBool; NUM_PINS_PORT_B] =
[const { AtomicBool::new(false) }; NUM_PINS_PORT_B];
/// 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) {
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.as_ref(),
EDGE_DETECTION_PORT_A.as_ref(),
),
Port::B => (
periphs.portb.irq_enb().read().bits(),
periphs.portb.edge_status().read().bits(),
WAKERS_FOR_PORT_B.as_ref(),
EDGE_DETECTION_PORT_B.as_ref(),
),
};
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] 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 {
#[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()),
}
}
pub fn new_with_dyn_pin(
pin: &mut DynPin,
irq: pac::Interrupt,
edge: InterruptEdge,
) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()));
}
let (waker_group, edge_detection_group) =
Self::pin_group_to_waker_and_edge_detection_group(pin.id().port());
edge_detection_group[pin.id().num() as usize]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.configure_edge_interrupt(edge).unwrap();
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Ok(Self {
pin_id: pin.id(),
waker_group,
edge_detection_group,
})
}
pub fn new_with_pin<I: PinId, C: InputConfig>(
pin: &mut Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
edge: InterruptEdge,
) -> Self {
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);
pin.configure_edge_interrupt(edge);
pin.enable_interrupt(InterruptConfig::new(irq, true, true));
Self {
pin_id: pin.id(),
edge_detection_group,
waker_group,
}
}
}
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);
}
}
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.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,
irq: pac::Interrupt,
}
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, irq: pac::Interrupt) -> Result<Self, InvalidPinTypeError> {
if !pin.is_input_pin() {
return Err(InvalidPinTypeError(pin.mode()));
}
Ok(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) {
// Unwrap okay, checked pin in constructor.
let fut =
InputPinFuture::new_with_dyn_pin(&mut self.pin, self.irq, InterruptEdge::LowToHigh)
.unwrap();
if self.pin.is_high().unwrap() {
return;
}
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, self.irq, 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, self.irq, 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, self.irq, 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, self.irq, 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<I: PinId, C: InputConfig> {
pin: Pin<I, pin::Input<C>>,
irq: pac::Interrupt,
}
impl<I: PinId, 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: PinId, C: InputConfig> embedded_hal::digital::ErrorType for InputPinAsync<I, C> {
type Error = core::convert::Infallible;
}
impl<I: PinId, 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,947 +0,0 @@
//! # Type-erased, value-level module for GPIO pins
//!
//! Although the type-level API is generally preferred, it is not suitable in
//! all cases. Because each pin is represented by a distinct type, it is not
//! possible to store multiple pins in a homogeneous data structure. The
//! value-level API solves this problem by erasing the type information and
//! tracking the pin at run-time.
//!
//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
//! respectively. The implementation of these types closely mirrors the
//! type-level API.
//!
//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
//! created from their type-level equivalents using [`From`]/[`Into`].
//!
//! ```
//! // Move a pin out of the Pins struct and convert to a DynPin
//! let pa0: DynPin = pins.pa0.into();
//! ```
//!
//! Conversions between pin modes use a value-level version of the type-level
//! API.
//!
//! ```
//! // Use one of the literal function names
//! pa0.into_floating_input();
//! // Use a method and a DynPinMode variant
//! pa0.into_mode(DYN_FLOATING_INPUT);
//! ```
//!
//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
//! operations become fallible. Run-time checks are inserted to ensure that
//! users don't try to, for example, set the output level of an input pin.
//!
//! Users may try to convert value-level pins back to their type-level
//! equivalents. However, this option is fallible, because the compiler cannot
//! guarantee the pin has the correct ID or is in the correct mode at
//! compile-time. Use [TryFrom]/[TryInto] for this conversion.
//!
//! ```
//! // Convert to a `DynPin`
//! let pa0: DynPin = pins.pa0.into();
//! // Change pin mode
//! pa0.into_floating_input();
//! // Convert back to a `Pin`
//! let pa0: Pin<PA0, FloatingInput> = pa0.try_into().unwrap();
//! ```
//!
//! # Embedded HAL traits
//!
//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
//! However, whereas the type-level API uses
//! `Error = core::convert::Infallible`, the value-level API can return a real
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
//! operation, the trait functions will return
//! [InvalidPinTypeError].
use super::{
pin::{FilterType, Pin, PinId, PinMode},
InputDynPinAsync, InterruptEdge, InterruptLevel, IsMaskedError, PinState, Port,
};
use crate::{clock::FilterClkSel, enable_nvic_interrupt, pac, FunSel};
//==================================================================================================
// DynPinMode configurations
//==================================================================================================
/// Value-level `enum` for disabled configurations
#[derive(PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynDisabled {
Floating,
PullDown,
PullUp,
}
/// Value-level `enum` for input configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynInput {
Floating,
PullDown,
PullUp,
}
/// Value-level `enum` for output configurations
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynOutput {
PushPull,
OpenDrain,
ReadablePushPull,
ReadableOpenDrain,
}
pub type DynAlternate = FunSel;
//==============================================================================
// Error
//==============================================================================
/// GPIO error type
///
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
/// operations are fallible. This `enum` represents the corresponding errors.
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("Invalid pin type for operation: {0:?}")]
pub struct InvalidPinTypeError(pub DynPinMode);
impl embedded_hal::digital::Error for InvalidPinTypeError {
fn kind(&self) -> embedded_hal::digital::ErrorKind {
embedded_hal::digital::ErrorKind::Other
}
}
//==================================================================================================
// DynPinMode
//==================================================================================================
/// Value-level `enum` representing pin modes
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DynPinMode {
Input(DynInput),
Output(DynOutput),
Alternate(DynAlternate),
}
/// Value-level variant of [`DynPinMode`] for floating input mode
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
/// Value-level variant of [`DynPinMode`] for pull-down input mode
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
/// Value-level variant of [`DynPinMode`] for pull-up input mode
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
/// Value-level variant of [`DynPinMode`] for push-pull output mode
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
/// Value-level variant of [`DynPinMode`] for open-drain output mode
pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain);
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull);
/// Value-level variant of [`DynPinMode`] for readable opendrain output mode
pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain);
/// Value-level variant of [`DynPinMode`] for function select 1
pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1);
/// Value-level variant of [`DynPinMode`] for function select 2
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2);
/// Value-level variant of [`DynPinMode`] for function select 3
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3);
//==================================================================================================
// DynGroup & DynPinId
//==================================================================================================
pub type DynGroup = Port;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DynPinId {
port: Port,
num: u8,
}
impl DynPinId {
pub const fn new(port: Port, num: u8) -> Self {
DynPinId { port, num }
}
pub const fn port(&self) -> Port {
self.port
}
pub const fn num(&self) -> u8 {
self.num
}
}
//==================================================================================================
// 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<DynPinMode> for ModeFields {
#[inline]
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
//==================================================================================================
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
///
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
/// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)]
pub struct DynPin {
id: DynPinId,
mode: DynPinMode,
}
impl DynPin {
/// Create a new [DynPin]
///
/// # Safety
///
/// 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) 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 {
id,
mode: DYN_FLOATING_INPUT,
}
}
/// Return a copy of the pin ID
#[inline]
pub const fn id(&self) -> DynPinId {
self.id
}
/// Return a copy of the pin mode
#[inline]
pub const fn mode(&self) -> DynPinMode {
self.mode
}
/// Convert the pin to the requested [`DynPinMode`]
#[inline]
pub fn into_mode(&mut self, mode: DynPinMode) {
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(_))
}
#[inline]
pub fn into_funsel_1(&mut self) {
self.into_mode(DYN_ALT_FUNC_1);
}
#[inline]
pub fn into_funsel_2(&mut self) {
self.into_mode(DYN_ALT_FUNC_2);
}
#[inline]
pub fn into_funsel_3(&mut self) {
self.into_mode(DYN_ALT_FUNC_3);
}
/// Configure the pin to operate as a floating input
#[inline]
pub fn into_floating_input(&mut self) {
self.into_mode(DYN_FLOATING_INPUT);
}
/// Configure the pin to operate as a pulled down input
#[inline]
pub fn into_pull_down_input(&mut self) {
self.into_mode(DYN_PULL_DOWN_INPUT);
}
/// Configure the pin to operate as a pulled up input
#[inline]
pub fn into_pull_up_input(&mut self) {
self.into_mode(DYN_PULL_UP_INPUT);
}
/// Configure the pin to operate as a push-pull output
#[inline]
pub fn into_push_pull_output(&mut self) {
self.into_mode(DYN_PUSH_PULL_OUTPUT);
}
/// Configure the pin to operate as a push-pull output
#[inline]
pub fn into_open_drain_output(&mut self) {
self.into_mode(DYN_OPEN_DRAIN_OUTPUT);
}
/// Configure the pin to operate as a push-pull output
#[inline]
pub fn into_readable_push_pull_output(&mut self) {
self.into_mode(DYN_RD_PUSH_PULL_OUTPUT);
}
/// Configure the pin to operate as a push-pull output
#[inline]
pub fn into_readable_open_drain_output(&mut self) {
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
}
#[inline(always)]
pub fn is_low(&self) -> Result<bool, InvalidPinTypeError> {
self.read_internal().map(|v| !v)
}
#[inline(always)]
pub fn is_high(&self) -> Result<bool, InvalidPinTypeError> {
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(())
}
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
if irq_cfg.route {
self.configure_irqsel(irq_cfg.id);
}
if irq_cfg.enable_in_nvic {
unsafe { enable_nvic_interrupt(irq_cfg.id) };
}
// We only manipulate our own bit.
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
if reset_irqsel {
self.reset_irqsel();
}
// We only manipulate our own bit.
self.port_reg()
.irq_enb()
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
}
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
///
/// There is no way for the compiler to know if the conversion will be
/// successful at compile-time. We must verify the conversion at run-time
/// or refuse to perform it.
#[inline]
pub fn upgrade<I: PinId, M: PinMode>(self) -> Result<Pin<I, M>, InvalidPinTypeError> {
if self.id == I::DYN && self.mode == M::DYN {
// The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin`
return Ok(unsafe { Pin::new() });
}
Err(InvalidPinTypeError(self.mode))
}
/// Convert the pin into an async pin. The pin can be converted back by calling
/// [InputDynPinAsync::release]
pub fn into_async_input(
self,
irq: crate::pac::Interrupt,
) -> Result<InputDynPinAsync, InvalidPinTypeError> {
InputDynPinAsync::new(self, irq)
}
/// Configure the IRQSEL peripheral for this particular pin with the given interrupt ID.
pub fn configure_irqsel(&mut self, id: pac::Interrupt) {
let mut syscfg = unsafe { pac::Sysconfig::steal() };
let irqsel = unsafe { pac::Irqsel::steal() };
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().num() as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
super::Port::B => {
irqsel
.portb0(self.id().num as usize)
.write(|w| unsafe { w.bits(id as u32) });
}
}
}
/// Reset the IRQSEL peripheral value for this particular pin.
pub fn reset_irqsel(&mut self) {
let mut syscfg = unsafe { pac::Sysconfig::steal() };
let irqsel = unsafe { pac::Irqsel::steal() };
crate::clock::enable_peripheral_clock(&mut syscfg, crate::clock::PeripheralClocks::Irqsel);
match self.id().port() {
// Set the correct interrupt number in the IRQSEL register
super::Port::A => {
irqsel
.porta0(self.id().num() as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
super::Port::B => {
irqsel
.portb0(self.id().num as usize)
.write(|w| unsafe { w.bits(u32::MAX) });
}
}
}
// Get DATAMASK bit for this particular pin
#[inline(always)]
pub fn datamask(&self) -> bool {
(self.port_reg().datamask().read().bits() >> self.id().num) == 1
}
/// Clear DATAMASK bit for this particular pin. This prevents access
/// of the corresponding bit for output and input operations
#[inline(always)]
pub fn clear_datamask(&self) {
self.port_reg()
.datamask()
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
}
/// Set DATAMASK bit for this particular pin. 1 is the default
/// state of the bit and allows access of the corresponding bit
#[inline(always)]
pub fn set_datamask(&self) {
self.port_reg()
.datamask()
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.write_pin_masked(false)
}
/// 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,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.configure_delay_internal(delay_1, delay_2);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// 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]
pub fn configure_pulse_mode(
&mut self,
enable: bool,
default_state: PinState,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
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.
#[inline]
pub fn configure_filter_type(
&mut self,
filter: FilterType,
clksel: FilterClkSel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) => {
self.configure_filter_type_internal(filter, clksel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.configure_edge_interrupt_internal(edge_type);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.configure_level_interrupt_internal(level_type);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
/// Change the pin mode
#[inline]
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));
}
}
}
#[inline]
const fn port_reg(&self) -> &PortRegisterBlock {
match self.id().port() {
Port::A => unsafe { &(*pac::Porta::ptr()) },
Port::B => unsafe { &(*pac::Portb::ptr()) },
}
}
#[inline]
const fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { va108xx::Ioconfig::ptr().as_ref().unwrap() };
match self.id().port() {
Port::A => ioconfig.porta(self.id().num() as usize),
Port::B => ioconfig.portb0(self.id().num() as usize),
}
}
#[inline(always)]
fn read_internal(&self) -> Result<bool, InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
Ok(self.read_pin())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[inline(always)]
fn write_internal(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.write_pin(bit);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
}
#[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<bool, IsMaskedError> {
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(())
}
}
}
/// Toggle the logic level of an output pin
#[inline(always)]
pub fn toggle_with_togout_reg(&mut self) {
// Safety: TOGOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
}
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn configure_edge_interrupt_internal(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
match edge_type {
InterruptEdge::HighToLow => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
InterruptEdge::LowToHigh => {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
InterruptEdge::BothEdges => {
self.port_reg()
.irq_edge()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
}
/// Configure which edge or level type triggers an interrupt
#[inline]
fn configure_level_interrupt_internal(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
if level == InterruptLevel::Low {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
self.port_reg()
.irq_evt()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for input pins
#[inline]
fn configure_filter_type_internal(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
w.flttype().bits(filter as u8);
w.fltclk().bits(clksel as u8)
}
});
}
#[inline]
fn configure_pulse_mode_internal(&mut self, enable: bool, default_state: PinState) {
let portreg = self.port_reg();
unsafe {
if enable {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.pulse()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if default_state == PinState::Low {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
} else {
portreg
.pulsebase()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
}
}
}
/// Only useful for output pins
#[inline]
fn configure_delay_internal(&mut self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay1()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
if delay_2 {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
} else {
portreg
.delay2()
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
}
}
}
// Only serves disambiguation purposes for the Embedded HAL impl
#[inline(always)]
fn is_low_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
self.is_low()
}
// Only serves disambiguation purposes for the Embedded HAL impl
#[inline(always)]
fn is_high_mut(&mut self) -> Result<bool, InvalidPinTypeError> {
self.is_high()
}
#[inline(always)]
const fn mask_32(&self) -> u32 {
1 << self.id().num()
}
}
//==================================================================================================
// Convert between Pin and DynPin
//==================================================================================================
impl<I: PinId, M: PinMode> From<Pin<I, M>> for DynPin {
/// Erase the type-level information in a [`Pin`] and return a value-level
/// [`DynPin`]
#[inline]
fn from(pin: Pin<I, M>) -> Self {
pin.downgrade()
}
}
impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
type Error = InvalidPinTypeError;
/// 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]
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
pin.upgrade()
}
}
//==================================================================================================
// Embedded HAL traits
//==================================================================================================
impl embedded_hal::digital::ErrorType for DynPin {
type Error = InvalidPinTypeError;
}
impl embedded_hal::digital::OutputPin for DynPin {
#[inline]
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high()
}
#[inline]
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low()
}
}
impl embedded_hal::digital::InputPin for DynPin {
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
self.is_high_mut()
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
self.is_low_mut()
}
}
impl embedded_hal::digital::StatefulOutputPin for DynPin {
#[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
self.is_high_mut()
}
#[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
self.is_low_mut()
}
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle()
}
}

View File

@ -1,74 +1,20 @@
//! # API for the GPIO peripheral
//! GPIO support module.
//!
//! The implementation of this GPIO module is heavily based on the
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
//! Contains abstractions to use the pins provided by the [crate::pins] module as GPIO or
//! IO peripheral pins.
//!
//! This API provides two different submodules, [pin] and [dynpin],
//! representing two different ways to handle GPIO pins. The default, [pin],
//! is a type-level API that tracks the state of each pin at compile-time. The
//! alternative, [dynpin] is a type-erased, value-level API that tracks the
//! state of each pin at run-time.
//! The core data structures provided for this are the
//!
//! The type-level API is strongly preferred. By representing the state of each
//! pin within the type system, the compiler can detect logic errors at
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
//! cost.
//! - [Output] for push-pull output pins.
//! - [Input] for input pins.
//! - [Flex] for pins with flexible configuration requirements.
//! - [IoPeriphPin] for IO peripheral pins.
//!
//! If needed, [dynpin] can be used to erase the type-level differences
//! between pins. However, by doing so, pins must now be tracked at run-time,
//! and each pin has a non-zero memory footprint.
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
//! in a type-safe way.
//!
//! ## Examples
//!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
//==================================================================================================
// Errors, Definitions and Constants
//==================================================================================================
pub const NUM_PINS_PORT_A: usize = 32;
pub const NUM_PINS_PORT_B: usize = 24;
pub const NUM_GPIO_PINS: usize = NUM_PINS_PORT_A + NUM_PINS_PORT_B;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("The pin is masked")]
pub struct IsMaskedError;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Port {
A,
B,
}
#[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 dynpin;
pub use dynpin::*;
pub mod pin;
pub use pin::*;
pub mod asynch;
pub use asynch::*;
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
pub use vorago_shared_periphs::gpio::*;

View File

@ -1,823 +0,0 @@
//! # Type-level module for GPIO pins
//!
//! This documentation is strongly based on the
//! [atsamd documentation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/pin/index.html).
//!
//! This module provides a type-level API for GPIO pins. It uses the type system
//! to track the state of pins at compile-time. Representing GPIO pins in this
//! manner incurs no run-time overhead. Each [`Pin`] struct is zero-sized, so
//! there is no data to copy around. Instead, real code is generated as a side
//! effect of type transformations, and the resulting assembly is nearly
//! identical to the equivalent, hand-written C.
//!
//! To track the state of pins at compile-time, this module uses traits to
//! represent [type classes] and types as instances of those type classes. For
//! example, the trait [`InputConfig`] acts as a [type-level enum] of the
//! available input configurations, and the types [`Floating`], [`PullDown`] and
//! [`PullUp`] are its type-level variants.
//!
//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and
//! [`PinMode`].
//!
//! ```
//! pub struct Pin<I, M>
//! where
//! I: PinId,
//! M: PinMode,
//! {
//! // ...
//! }
//! ```
//!
//! A [PinId] identifies a pin by it's group (A or B) and pin number. Each
//! [PinId] instance is named according to its datasheet identifier, e.g.
//! [PA2].
//!
//! A [PinMode] represents the various pin modes. The available [PinMode]
//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own
//! corresponding configurations.
//!
//! It is not possible for users to create new instances of a [`Pin`]. Singleton
//! instances of each pin are made available to users through the PinsX
//! struct.
//!
//! Example for the pins of PORT A:
//!
//! To create the [PinsA] struct, users must supply the PAC
//! [Port](crate::pac::Porta) peripheral. The [PinsA] struct takes
//! ownership of the [Porta] and provides the corresponding pins. Each [`Pin`]
//! within the [PinsA] struct can be moved out and used individually.
//!
//!
//! ```
//! let mut peripherals = Peripherals::take().unwrap();
//! let pinsa = PinsA::new(peripherals.PORT);
//! ```
//!
//! Pins can be converted between modes using several different methods.
//!
//! ```no_run
//! // Use one of the literal function names
//! let pa0 = pinsa.pa0.into_floating_input();
//! // Use a generic method and one of the `PinMode` variant types
//! let pa0 = pinsa.pa0.into_mode::<FloatingInput>();
//! // Specify the target type and use `From`/`Into`
//! let pa0: Pin<PA0, FloatingInput> = pinsa.pa27.into();
//! ```
//!
//! # Embedded HAL traits
//!
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
//! in the corresponding [`PinMode`]s, namely: [embedded_hal::digital::InputPin],
//! [embedded_hal::digital::OutputPin] and [embedded_hal::digital::StatefulOutputPin].
use super::dynpin::{DynAlternate, DynInput, DynOutput, DynPinId, DynPinMode};
use super::{DynPin, InputPinAsync, InterruptEdge, InterruptLevel, PinState, Port};
use crate::{
pac::{Porta, Portb},
typelevel::Sealed,
};
use core::convert::Infallible;
use core::marker::PhantomData;
use core::mem::transmute;
use paste::paste;
//==================================================================================================
// Input configuration
//==================================================================================================
/// Type-level enum for input configurations
///
/// The valid options are [Floating], [PullDown] and [PullUp].
pub trait InputConfig: Sealed {
/// Corresponding [DynInput]
const DYN: DynInput;
}
#[derive(Debug)]
pub enum Floating {}
#[derive(Debug)]
pub enum PullDown {}
#[derive(Debug)]
pub enum PullUp {}
impl InputConfig for Floating {
const DYN: DynInput = DynInput::Floating;
}
impl InputConfig for PullDown {
const DYN: DynInput = DynInput::PullDown;
}
impl InputConfig for PullUp {
const DYN: DynInput = DynInput::PullUp;
}
impl Sealed for Floating {}
impl Sealed for PullDown {}
impl Sealed for PullUp {}
/// Type-level variant of [`PinMode`] for floating input mode
pub type InputFloating = Input<Floating>;
/// Type-level variant of [`PinMode`] for pull-down input mode
pub type InputPullDown = Input<PullDown>;
/// Type-level variant of [`PinMode`] for pull-up input mode
pub type InputPullUp = Input<PullUp>;
/// Type-level variant of [`PinMode`] for input modes
///
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
/// [`PullUp`]
#[derive(Debug)]
pub struct Input<C: InputConfig> {
cfg: PhantomData<C>,
}
impl<C: InputConfig> Sealed for Input<C> {}
#[derive(Debug, PartialEq, Eq)]
pub enum FilterType {
SystemClock = 0,
DirectInputWithSynchronization = 1,
FilterOneClockCycle = 2,
FilterTwoClockCycles = 3,
FilterThreeClockCycles = 4,
FilterFourClockCycles = 5,
}
pub use crate::clock::FilterClkSel;
//==================================================================================================
// Output configuration
//==================================================================================================
pub trait OutputConfig: Sealed {
const DYN: DynOutput;
}
pub trait ReadableOutput: Sealed {}
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
#[derive(Debug)]
pub enum PushPull {}
/// Type-level variant of [`OutputConfig`] for an open drain configuration
#[derive(Debug)]
pub enum OpenDrain {}
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
#[derive(Debug)]
pub enum ReadablePushPull {}
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
#[derive(Debug)]
pub enum ReadableOpenDrain {}
impl Sealed for PushPull {}
impl Sealed for OpenDrain {}
impl Sealed for ReadableOpenDrain {}
impl Sealed for ReadablePushPull {}
impl ReadableOutput for ReadableOpenDrain {}
impl ReadableOutput for ReadablePushPull {}
impl OutputConfig for PushPull {
const DYN: DynOutput = DynOutput::PushPull;
}
impl OutputConfig for OpenDrain {
const DYN: DynOutput = DynOutput::OpenDrain;
}
impl OutputConfig for ReadablePushPull {
const DYN: DynOutput = DynOutput::ReadablePushPull;
}
impl OutputConfig for ReadableOpenDrain {
const DYN: DynOutput = DynOutput::ReadableOpenDrain;
}
/// Type-level variant of [`PinMode`] for output modes
///
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
/// their respective readable versions
#[derive(Debug)]
pub struct Output<C: OutputConfig> {
cfg: PhantomData<C>,
}
impl<C: OutputConfig> Sealed for Output<C> {}
/// Type-level variant of [`PinMode`] for push-pull output mode
pub type PushPullOutput = Output<PushPull>;
/// Type-level variant of [`PinMode`] for open drain output mode
pub type OutputOpenDrain = Output<OpenDrain>;
pub type OutputReadablePushPull = Output<ReadablePushPull>;
pub type OutputReadableOpenDrain = Output<ReadableOpenDrain>;
//==================================================================================================
// Alternate configurations
//==================================================================================================
/// Type-level enum for alternate peripheral function configurations
pub trait AlternateConfig: Sealed {
const DYN: DynAlternate;
}
pub enum Funsel1 {}
pub enum Funsel2 {}
pub enum Funsel3 {}
impl AlternateConfig for Funsel1 {
const DYN: DynAlternate = DynAlternate::Sel1;
}
impl AlternateConfig for Funsel2 {
const DYN: DynAlternate = DynAlternate::Sel2;
}
impl AlternateConfig for Funsel3 {
const DYN: DynAlternate = DynAlternate::Sel3;
}
impl Sealed for Funsel1 {}
impl Sealed for Funsel2 {}
impl Sealed for Funsel3 {}
/// Type-level variant of [`PinMode`] for alternate peripheral functions
///
/// Type `C` is an [`AlternateConfig`]
pub struct Alternate<C: AlternateConfig> {
cfg: PhantomData<C>,
}
impl<C: AlternateConfig> Sealed for Alternate<C> {}
pub type AltFunc1 = Alternate<Funsel1>;
pub type AltFunc2 = Alternate<Funsel2>;
pub type AltFunc3 = Alternate<Funsel3>;
/// Type alias for the [`PinMode`] at reset
pub type Reset = InputFloating;
//==================================================================================================
// Pin modes
//==================================================================================================
/// Type-level enum representing pin modes
///
/// The valid options are [Input], [Output] and [Alternate].
pub trait PinMode: Sealed {
/// Corresponding [DynPinMode]
const DYN: DynPinMode;
}
impl<C: InputConfig> PinMode for Input<C> {
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
}
impl<C: OutputConfig> PinMode for Output<C> {
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
}
impl<C: AlternateConfig> PinMode for Alternate<C> {
const DYN: DynPinMode = DynPinMode::Alternate(C::DYN);
}
//==================================================================================================
// Pin IDs
//==================================================================================================
/// Type-level enum for pin IDs
pub trait PinId: Sealed {
/// Corresponding [DynPinId]
const DYN: DynPinId;
}
macro_rules! pin_id {
($Group:ident, $Id:ident, $NUM:literal) => {
// Need paste macro to use ident in doc attribute
paste! {
#[doc = "Pin ID representing pin " $Id]
#[derive(Debug)]
pub enum $Id {}
impl Sealed for $Id {}
impl PinId for $Id {
const DYN: DynPinId = DynPinId::new(Port::$Group, $NUM);
}
}
};
}
//==================================================================================================
// Pin
//==================================================================================================
/// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> {
inner: DynPin,
phantom: PhantomData<(I, M)>,
}
impl<I: PinId, M: PinMode> Pin<I, M> {
/// Create a new [Pin]
///
/// # Safety
///
/// Each [Pin] must be a singleton. For a given [PinId], there must be
/// at most one corresponding [Pin] in existence at any given time.
/// Violating this requirement is `unsafe`.
#[inline]
pub(crate) const unsafe fn new() -> Pin<I, M> {
Pin {
inner: DynPin::new(I::DYN, M::DYN),
phantom: PhantomData,
}
}
#[inline]
pub const fn id(&self) -> DynPinId {
self.inner.id()
}
/// Convert the pin to the requested [`PinMode`]
#[inline]
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
// Only modify registers if we are actually changing pin mode
// This check should compile away
if N::DYN != M::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
#[inline]
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
self.into_mode()
}
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
#[inline]
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
self.into_mode()
}
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
#[inline]
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
self.into_mode()
}
/// Configure the pin to operate as a floating input
#[inline]
pub fn into_floating_input(self) -> Pin<I, InputFloating> {
self.into_mode()
}
/// Configure the pin to operate as a pulled down input
#[inline]
pub fn into_pull_down_input(self) -> Pin<I, InputPullDown> {
self.into_mode()
}
/// Configure the pin to operate as a pulled up input
#[inline]
pub fn into_pull_up_input(self) -> Pin<I, InputPullUp> {
self.into_mode()
}
/// Configure the pin to operate as a push-pull output
#[inline]
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
self.into_mode()
}
/// Configure the pin to operate as a readable push-pull output
#[inline]
pub fn into_readable_push_pull_output(self) -> Pin<I, OutputReadablePushPull> {
self.into_mode()
}
/// Configure the pin to operate as a readable open-drain output
#[inline]
pub fn into_readable_open_drain_output(self) -> Pin<I, OutputReadableOpenDrain> {
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()
}
#[inline]
pub fn clear_datamask(&mut self) {
self.inner.clear_datamask()
}
#[inline]
pub fn set_datamask(&mut self) {
self.inner.set_datamask()
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.inner.is_high_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.inner.is_low_masked()
}
#[inline]
pub fn downgrade(self) -> DynPin {
self.inner
}
// Those only serve for the embedded HAL implementations which have different mutability.
#[inline]
fn is_low_mut(&mut self) -> bool {
self.is_low()
}
#[inline]
fn is_high_mut(&mut self) -> bool {
self.is_high()
}
#[inline]
pub fn enable_interrupt(&mut self, irq_cfg: crate::InterruptConfig) {
self.inner.enable_interrupt(irq_cfg);
}
#[inline]
pub fn disable_interrupt(&mut self, reset_irqsel: bool) {
self.inner.disable_interrupt(reset_irqsel);
}
/// 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();
}
}
//==============================================================================
// AnyPin
//==============================================================================
/// Type class for [`Pin`] types
///
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
/// pattern.
///
/// ## `v1` Compatibility
///
/// Normally, this trait would use `Is<Type = SpecificPin<Self>>` as a super
/// trait. But doing so would restrict implementations to only the `v2` `Pin`
/// type in this module. To aid in backwards compatibility, we want to implement
/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few
/// reasons. First, both structs are zero-sized, so there is no meaningful
/// memory layout to begin with. And even if there were, the `v1` `Pin` type is
/// a newtype wrapper around a `v2` `Pin`, and single-field structs are
/// guaranteed to have the same layout as the field, even for `repr(Rust)`.
///
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
/// [type class]: crate::typelevel#type-classes
pub trait AnyPin
where
Self: Sealed,
Self: From<SpecificPin<Self>>,
Self: Into<SpecificPin<Self>>,
Self: AsRef<SpecificPin<Self>>,
Self: AsMut<SpecificPin<Self>>,
{
/// [`PinId`] of the corresponding [`Pin`]
type Id: PinId;
/// [`PinMode`] of the corresponding [`Pin`]
type Mode: PinMode;
}
impl<I, M> Sealed for Pin<I, M>
where
I: PinId,
M: PinMode,
{
}
impl<I, M> AnyPin for Pin<I, M>
where
I: PinId,
M: PinMode,
{
type Id = I;
type Mode = M;
}
/// Type alias to recover the specific [`Pin`] type from an implementation of
/// [`AnyPin`]
///
/// See the [`AnyKind`] documentation for more details on the pattern.
///
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
#[inline]
fn as_ref(&self) -> &P {
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
// both are zero-sized, and single-field, newtype structs are guaranteed
// to have the same layout as the field anyway, even for repr(Rust).
unsafe { transmute(self) }
}
}
impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
#[inline]
fn as_mut(&mut self) -> &mut P {
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
// both are zero-sized, and single-field, newtype structs are guaranteed
// to have the same layout as the field anyway, even for repr(Rust).
unsafe { transmute(self) }
}
}
//==================================================================================================
// Additional functionality
//==================================================================================================
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// Convert the pin into an async pin. The pin can be converted back by calling
/// [InputPinAsync::release]
pub fn into_async_input(self, irq: crate::pac::Interrupt) -> InputPinAsync<I, C> {
InputPinAsync::new(self, irq)
}
}
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
#[inline]
pub fn set_high(&mut self) {
self.inner.write_pin(true)
}
#[inline]
pub fn set_low(&mut self) {
self.inner.write_pin(false)
}
#[inline]
pub fn toggle(&mut self) {
self.inner.toggle().unwrap()
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_high_masked()
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_low_masked()
}
/// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles:
/// - Delay 1: 1
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.inner.configure_delay(delay_1, delay_2).unwrap();
}
/// See p.52 of the programmers guide for more information.
///
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.inner
.configure_pulse_mode(enable, default_state)
.unwrap();
}
}
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// 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.configure_filter_type(filter, clksel).unwrap();
}
}
//==================================================================================================
// Embedded HAL traits
//==================================================================================================
impl<I, M> embedded_hal::digital::ErrorType for Pin<I, M>
where
I: PinId,
M: PinMode,
{
type Error = Infallible;
}
impl<I: PinId, C: OutputConfig> embedded_hal::digital::OutputPin for Pin<I, Output<C>> {
#[inline]
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_high();
Ok(())
}
#[inline]
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_low();
Ok(())
}
}
impl<I, C> embedded_hal::digital::InputPin for Pin<I, Input<C>>
where
I: PinId,
C: InputConfig,
{
#[inline]
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_high_mut())
}
#[inline]
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_low_mut())
}
}
impl<I, C> embedded_hal::digital::StatefulOutputPin for Pin<I, Output<C>>
where
I: PinId,
C: OutputConfig + ReadableOutput,
{
#[inline]
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
#[inline]
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(self.is_low())
}
#[inline]
fn toggle(&mut self) -> Result<(), Self::Error> {
self.toggle();
Ok(())
}
}
//==================================================================================================
// Pin definitions
//==================================================================================================
macro_rules! pins {
(
$Port:ident, $PinsName:ident, $($Id:ident,)+,
) => {
paste!(
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
#[derive(Debug)]
pub struct $PinsName {
port: $Port,
$(
#[doc = "Pin " $Id]
pub [<$Id:lower>]: Pin<$Id, Reset>,
)+
}
impl $PinsName {
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
/// is optional because it might be required to create pin definitions for both
/// ports.
#[inline]
pub fn new(
syscfg: &mut va108xx::Sysconfig,
port: $Port
) -> $PinsName {
syscfg.peripheral_clk_enable().modify(|_, w| {
w.[<$Port:lower>]().set_bit();
w.gpio().set_bit();
w.ioconfig().set_bit()
});
$PinsName {
//iocfg,
port,
// Safe because we only create one `Pin` per `PinId`
$(
[<$Id:lower>]: unsafe { Pin::new() },
)+
}
}
/// Get the peripheral ID
/// Safety: Read-only register
pub fn get_perid() -> u32 {
let port = unsafe { &(*$Port::ptr()) };
port.perid().read().bits()
}
/// Consumes the Pins struct and returns the port definitions
pub fn release(self) -> $Port {
self.port
}
}
);
}
}
macro_rules! declare_pins {
(
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+]
) => {
pins!($Port, $PinsName, $($Id,)+,);
$(
pin_id!($Group, $Id, $NUM);
)+
}
}
declare_pins!(
A,
PinsA,
Porta,
[
(PA0, 0),
(PA1, 1),
(PA2, 2),
(PA3, 3),
(PA4, 4),
(PA5, 5),
(PA6, 6),
(PA7, 7),
(PA8, 8),
(PA9, 9),
(PA10, 10),
(PA11, 11),
(PA12, 12),
(PA13, 13),
(PA14, 14),
(PA15, 15),
(PA16, 16),
(PA17, 17),
(PA18, 18),
(PA19, 19),
(PA20, 20),
(PA21, 21),
(PA22, 22),
(PA23, 23),
(PA24, 24),
(PA25, 25),
(PA26, 26),
(PA27, 27),
(PA28, 28),
(PA29, 29),
(PA30, 30),
(PA31, 31),
]
);
declare_pins!(
B,
PinsB,
Portb,
[
(PB0, 0),
(PB1, 1),
(PB2, 2),
(PB3, 3),
(PB4, 4),
(PB5, 5),
(PB6, 6),
(PB7, 7),
(PB8, 8),
(PB9, 9),
(PB10, 10),
(PB11, 11),
(PB12, 12),
(PB13, 13),
(PB14, 14),
(PB15, 15),
(PB16, 16),
(PB17, 17),
(PB18, 18),
(PB19, 19),
(PB20, 20),
(PB21, 21),
(PB22, 22),
(PB23, 23),
]
);