update-gpio-impl #48

Closed
muellerr wants to merge 2 commits from update-gpio-impl into main
9 changed files with 329 additions and 235 deletions

View File

@ -26,6 +26,7 @@ defmt = { version = "0.3", optional = true }
fugit = "0.3" fugit = "0.3"
delegate = "0.12" delegate = "0.12"
void = { version = "1", default-features = false } void = { version = "1", default-features = false }
thiserror = { version = "2", default-features = false }
[dependencies.va416xx] [dependencies.va416xx]
default-features = false default-features = false

View File

@ -30,12 +30,6 @@ rustup target add thumbv7em-none-eabihf
After that, you can use `cargo build` to build the development version of the crate. After that, you can use `cargo build` to build the development version of the crate.
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
## Setting up your own binary crate ## Setting up your own binary crate
If you have a custom board, you might be interested in setting up a new binary crate for your If you have a custom board, you might be interested in setting up a new binary crate for your
@ -71,3 +65,11 @@ is contained within the
7. Flashing the board might work differently for different boards and there is usually 7. Flashing the board might work differently for different boards and there is usually
more than one way. You can find example instructions in primary README. more than one way. You can find example instructions in primary README.
## Embedded Rust
If you have not done this yet, it is recommended to read some of the excellent resources
available to learn Rust:
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)

3
va416xx-hal/docs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open

View File

@ -1,4 +1,61 @@
use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin}; //! # 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 embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use super::{ use super::{
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId, reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
@ -10,7 +67,8 @@ use super::{
//================================================================================================== //==================================================================================================
/// Value-level `enum` for disabled configurations /// Value-level `enum` for disabled configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub enum DynDisabled { pub enum DynDisabled {
Floating, Floating,
PullDown, PullDown,
@ -18,7 +76,8 @@ pub enum DynDisabled {
} }
/// Value-level `enum` for input configurations /// Value-level `enum` for input configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub enum DynInput { pub enum DynInput {
Floating, Floating,
PullDown, PullDown,
@ -26,7 +85,8 @@ pub enum DynInput {
} }
/// Value-level `enum` for output configurations /// Value-level `enum` for output configurations
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub enum DynOutput { pub enum DynOutput {
PushPull, PushPull,
OpenDrain, OpenDrain,
@ -36,12 +96,32 @@ pub enum DynOutput {
pub type DynAlternate = crate::FunSel; pub type DynAlternate = crate::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 // DynPinMode
//================================================================================================== //==================================================================================================
/// Value-level `enum` representing pin modes /// Value-level `enum` representing pin modes
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub enum DynPinMode { pub enum DynPinMode {
Input(DynInput), Input(DynInput),
Output(DynOutput), Output(DynOutput),
@ -76,7 +156,8 @@ pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3)
//================================================================================================== //==================================================================================================
/// Value-level `enum` for pin groups /// Value-level `enum` for pin groups
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub enum DynGroup { pub enum DynGroup {
A, A,
B, B,
@ -88,7 +169,8 @@ pub enum DynGroup {
} }
/// Value-level `struct` representing pin IDs /// Value-level `struct` representing pin IDs
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub struct DynPinId { pub struct DynPinId {
pub group: DynGroup, pub group: DynGroup,
pub num: u8, pub num: u8,
@ -102,16 +184,16 @@ pub struct DynPinId {
/// ///
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to /// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters. /// access the corresponding regsiters.
struct DynRegisters { #[derive(Debug)]
id: DynPinId, #[cfg_attr(feature = "defmt", derive(defmt::format))]
} pub(crate) struct DynRegisters(DynPinId);
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] // [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
// guarantees that each pin is a singleton, so this implementation is safe. // guarantees that each pin is a singleton, so this implementation is safe.
unsafe impl RegisterInterface for DynRegisters { unsafe impl RegisterInterface for DynRegisters {
#[inline] #[inline]
fn id(&self) -> DynPinId { fn id(&self) -> DynPinId {
self.id self.0
} }
} }
@ -124,25 +206,7 @@ impl DynRegisters {
/// the same [`DynPinId`] /// the same [`DynPinId`]
#[inline] #[inline]
unsafe fn new(id: DynPinId) -> Self { unsafe fn new(id: DynPinId) -> Self {
DynRegisters { id } DynRegisters(id)
}
}
//==============================================================================
// 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)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidPinTypeError(pub(crate) ());
impl embedded_hal::digital::Error for InvalidPinTypeError {
fn kind(&self) -> embedded_hal::digital::ErrorKind {
ErrorKind::Other
} }
} }
@ -154,8 +218,10 @@ impl embedded_hal::digital::Error for InvalidPinTypeError {
/// ///
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented /// 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. /// by the same type, and pins are tracked and distinguished at run-time.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::format))]
pub struct DynPin { pub struct DynPin {
regs: DynRegisters, pub(crate) regs: DynRegisters,
mode: DynPinMode, mode: DynPinMode,
} }
@ -168,7 +234,7 @@ impl DynPin {
/// must be at most one corresponding [`DynPin`] in existence at any given /// must be at most one corresponding [`DynPin`] in existence at any given
/// time. Violating this requirement is `unsafe`. /// time. Violating this requirement is `unsafe`.
#[inline] #[inline]
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
DynPin { DynPin {
regs: DynRegisters::new(id), regs: DynRegisters::new(id),
mode, mode,
@ -178,7 +244,7 @@ impl DynPin {
/// Return a copy of the pin ID /// Return a copy of the pin ID
#[inline] #[inline]
pub fn id(&self) -> DynPinId { pub fn id(&self) -> DynPinId {
self.regs.id self.regs.0
} }
/// Return a copy of the pin mode /// Return a copy of the pin mode
@ -254,7 +320,45 @@ impl DynPin {
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
} }
common_reg_if_functions!(); #[inline]
pub fn datamask(&self) -> bool {
self.regs.datamask()
}
#[inline]
pub fn clear_datamask(&mut self) {
self.regs.clear_datamask();
}
#[inline]
pub fn set_datamask(&mut self) {
self.regs.set_datamask();
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(false)
}
#[inline]
pub fn irq_enb(&mut self) {
self.regs.enable_irq();
}
/// See p.53 of the programmers guide for more information. /// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles: /// Possible delays in clock cycles:
@ -262,71 +366,78 @@ impl DynPin {
/// - Delay 2: 2 /// - Delay 2: 2
/// - Delay 1 + Delay 2: 3 /// - Delay 1 + Delay 2: 3
#[inline] #[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> { pub fn configure_delay(
&mut self,
delay_1: bool,
delay_2: bool,
) -> Result<(), InvalidPinTypeError> {
match self.mode { match self.mode {
DynPinMode::Output(_) => { DynPinMode::Output(_) => {
self.regs.delay(delay_1, delay_2); self.regs.configure_delay(delay_1, delay_2);
Ok(self) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
/// See p.52 of the programmers guide for more information. /// 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 /// 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 /// one clock cycle before returning to the configured default state
pub fn pulse_mode( pub fn configure_pulse_mode(
self, &mut self,
enable: bool, enable: bool,
default_state: PinState, default_state: PinState,
) -> Result<Self, InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
match self.mode { match self.mode {
DynPinMode::Output(_) => { DynPinMode::Output(_) => {
self.regs.pulse_mode(enable, default_state); self.regs.configure_pulse_mode(enable, default_state);
Ok(self) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
/// See p.37 and p.38 of the programmers guide for more information. /// See p.37 and p.38 of the programmers guide for more information.
#[inline] #[inline]
pub fn filter_type( pub fn configure_filter_type(
self, &mut self,
filter: FilterType, filter: FilterType,
clksel: FilterClkSel, clksel: FilterClkSel,
) -> Result<Self, InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
match self.mode { match self.mode {
DynPinMode::Input(_) => { DynPinMode::Input(_) => {
self.regs.filter_type(filter, clksel); self.regs.configure_filter_type(filter, clksel);
Ok(self) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Result<Self, InvalidPinTypeError> { pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
) -> Result<(), InvalidPinTypeError> {
match self.mode { match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => { DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_edge(edge_type); self.regs.configure_edge_interrupt(edge_type);
self.irq_enb(); self.irq_enb();
Ok(self) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
pub fn interrupt_level( pub fn configure_level_interrupt(
mut self, &mut self,
level_type: InterruptLevel, level_type: InterruptLevel,
) -> Result<Self, InvalidPinTypeError> { ) -> Result<(), InvalidPinTypeError> {
match self.mode { match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => { DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_level(level_type); self.regs.configure_level_interrupt(level_type);
self.irq_enb(); self.irq_enb();
Ok(self) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -336,7 +447,7 @@ impl DynPin {
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => { DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
Ok(self.regs.read_pin()) Ok(self.regs.read_pin())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
#[inline] #[inline]
@ -346,7 +457,7 @@ impl DynPin {
self.regs.write_pin(bit); self.regs.write_pin(bit);
Ok(()) Ok(())
} }
_ => Err(InvalidPinTypeError(())), _ => Err(InvalidPinTypeError(self.mode)),
} }
} }
@ -366,6 +477,21 @@ impl DynPin {
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> { fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
self._write(true) self._write(true)
} }
/// 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.regs.0 == I::DYN && self.mode == M::DYN {
// The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin`
return Ok(unsafe { Pin::new() });
}
Err(InvalidPinTypeError(self.mode))
}
} }
//============================================================================== //==============================================================================
@ -380,10 +506,8 @@ where
/// Erase the type-level information in a [`Pin`] and return a value-level /// Erase the type-level information in a [`Pin`] and return a value-level
/// [`DynPin`] /// [`DynPin`]
#[inline] #[inline]
fn from(_pin: Pin<I, M>) -> Self { fn from(pin: Pin<I, M>) -> Self {
// The `Pin` is consumed, so it is safe to replace it with the pin.downgrade()
// corresponding `DynPin`
unsafe { DynPin::new(I::DYN, M::DYN) }
} }
} }
@ -401,13 +525,7 @@ where
/// or refuse to perform it. /// or refuse to perform it.
#[inline] #[inline]
fn try_from(pin: DynPin) -> Result<Self, Self::Error> { fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
if pin.regs.id == I::DYN && pin.mode == M::DYN { pin.upgrade()
// The `DynPin` is consumed, so it is safe to replace it with the
// corresponding `Pin`
Ok(unsafe { Self::new() })
} else {
Err(InvalidPinTypeError(()))
}
} }
} }

View File

@ -21,57 +21,11 @@
//! ## Examples //! ## Examples
//! //!
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs) //! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va416xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("pin is masked")]
pub struct IsMaskedError; pub struct IsMaskedError;
macro_rules! common_reg_if_functions {
() => {
paste::paste!(
#[inline]
pub fn datamask(&self) -> bool {
self.regs.datamask()
}
#[inline]
pub fn clear_datamask(self) -> Self {
self.regs.clear_datamask();
self
}
#[inline]
pub fn set_datamask(self) -> Self {
self.regs.set_datamask();
self
}
#[inline]
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.regs.write_pin_masked(false)
}
fn irq_enb(&mut self) {
self.regs.enable_irq();
}
);
};
}
pub mod pin; pub mod pin;
pub use pin::*; pub use pin::*;

View File

@ -78,7 +78,8 @@ use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg}; use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
use super::{ use super::{
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode, reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPin, DynPinId,
DynPinMode,
}; };
//================================================================================================== //==================================================================================================
@ -86,6 +87,7 @@ use super::{
//================================================================================================== //==================================================================================================
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptEdge { pub enum InterruptEdge {
HighToLow, HighToLow,
LowToHigh, LowToHigh,
@ -93,12 +95,14 @@ pub enum InterruptEdge {
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptLevel { pub enum InterruptLevel {
Low = 0, Low = 0,
High = 1, High = 1,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PinState { pub enum PinState {
Low = 0, Low = 0,
High = 1, High = 1,
@ -321,9 +325,10 @@ macro_rules! pin_id {
//================================================================================================== //==================================================================================================
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types /// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
#[derive(Debug)]
pub struct Pin<I: PinId, M: PinMode> { pub struct Pin<I: PinId, M: PinMode> {
pub(in crate::gpio) regs: Registers<I>, inner: DynPin,
mode: PhantomData<M>, mode: PhantomData<(I, M)>,
} }
impl<I: PinId, M: PinMode> Pin<I, M> { impl<I: PinId, M: PinMode> Pin<I, M> {
@ -337,18 +342,23 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
#[inline] #[inline]
pub(crate) unsafe fn new() -> Pin<I, M> { pub(crate) unsafe fn new() -> Pin<I, M> {
Pin { Pin {
regs: Registers::new(), inner: DynPin::new(I::DYN, M::DYN),
mode: PhantomData, mode: PhantomData,
} }
} }
#[inline]
pub fn id(&self) -> DynPinId {
self.inner.id()
}
/// Convert the pin to the requested [`PinMode`] /// Convert the pin to the requested [`PinMode`]
#[inline] #[inline]
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> { pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
// Only modify registers if we are actually changing pin mode // Only modify registers if we are actually changing pin mode
// This check should compile away // This check should compile away
if N::DYN != M::DYN { if N::DYN != M::DYN {
self.regs.change_mode::<N>(); self.inner.regs.change_mode(N::DYN);
} }
// Safe because we drop the existing Pin // Safe because we drop the existing Pin
unsafe { Pin::new() } unsafe { Pin::new() }
@ -408,26 +418,68 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.into_mode() self.into_mode()
} }
common_reg_if_functions!(); #[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 set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_high_masked()
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
self.inner.set_low_masked()
}
#[inline]
pub fn downgrade(self) -> DynPin {
self.inner
}
fn irq_enb(&mut self) {
self.inner.irq_enb();
}
#[inline] #[inline]
pub(crate) fn _set_high(&mut self) { pub(crate) fn _set_high(&mut self) {
self.regs.write_pin(true) self.inner.regs.write_pin(true)
} }
#[inline] #[inline]
pub(crate) fn _set_low(&mut self) { pub(crate) fn _set_low(&mut self) {
self.regs.write_pin(false) self.inner.regs.write_pin(false)
} }
#[inline] #[inline]
pub(crate) fn _is_low(&self) -> bool { pub(crate) fn _is_low(&self) -> bool {
!self.regs.read_pin() !self.inner.regs.read_pin()
} }
#[inline] #[inline]
pub(crate) fn _is_high(&self) -> bool { pub(crate) fn _is_high(&self) -> bool {
self.regs.read_pin() self.inner.regs.read_pin()
} }
} }
@ -519,16 +571,14 @@ impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
//================================================================================================== //==================================================================================================
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self { pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
self.regs.interrupt_edge(edge_type); self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(); self.irq_enb();
self
} }
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self { pub fn configure_interrupt_level(&mut self, level_type: InterruptLevel) {
self.regs.interrupt_level(level_type); self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(); self.irq_enb();
self
} }
} }
@ -539,38 +589,38 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
/// - Delay 2: 2 /// - Delay 2: 2
/// - Delay 1 + Delay 2: 3 /// - Delay 1 + Delay 2: 3
#[inline] #[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self { pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.regs.delay(delay_1, delay_2); self.inner.regs.configure_delay(delay_1, delay_2);
self }
#[inline]
pub fn toggle_with_toggle_reg(&mut self) {
self.inner.regs.toggle()
} }
/// See p.52 of the programmers guide for more information. /// 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 /// 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 /// one clock cycle before returning to the configured default state
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self { pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.regs.pulse_mode(enable, default_state); self.inner.regs.configure_pulse_mode(enable, default_state);
self
} }
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self { pub fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
self.regs.interrupt_edge(edge_type); self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(); self.irq_enb();
self
} }
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self { pub fn configure_level_interrupt(&mut self, level_type: InterruptLevel) {
self.regs.interrupt_level(level_type); self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(); self.irq_enb();
self
} }
} }
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information. /// See p.37 and p.38 of the programmers guide for more information.
#[inline] #[inline]
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self { pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.regs.filter_type(filter, clksel); self.inner.regs.configure_filter_type(filter, clksel);
self
} }
} }
@ -646,47 +696,6 @@ where
} }
} }
//==================================================================================================
// Registers
//==================================================================================================
/// Provide a safe register interface for [`Pin`]s
///
/// This `struct` takes ownership of a [`PinId`] and provides an API to
/// access the corresponding registers.
pub(in crate::gpio) struct Registers<I: PinId> {
id: PhantomData<I>,
}
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
// each pin is a singleton, so this implementation is safe.
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
#[inline]
fn id(&self) -> DynPinId {
I::DYN
}
}
impl<I: PinId> Registers<I> {
/// Create a new instance of [`Registers`]
///
/// # Safety
///
/// Users must never create two simultaneous instances of this `struct` with
/// the same [`PinId`]
#[inline]
unsafe fn new() -> Self {
Registers { id: PhantomData }
}
/// Provide a type-level equivalent for the
/// [`RegisterInterface::change_mode`] method.
#[inline]
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
RegisterInterface::change_mode(self, M::DYN);
}
}
//================================================================================================== //==================================================================================================
// Pin definitions // Pin definitions
//================================================================================================== //==================================================================================================
@ -745,8 +754,6 @@ macro_rules! pins {
} }
} }
//$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]
//$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal, $meta: meta),)+]
macro_rules! declare_pins { macro_rules! declare_pins {
( (
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+] $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal $(, $meta:meta)?)),+]

View File

@ -144,7 +144,7 @@ pub(super) unsafe trait RegisterInterface {
} }
} }
#[inline] #[inline(always)]
fn port_reg(&self) -> &PortRegisterBlock { fn port_reg(&self) -> &PortRegisterBlock {
match self.id().group { match self.id().group {
DynGroup::A => unsafe { &(*Porta::ptr()) }, DynGroup::A => unsafe { &(*Porta::ptr()) },
@ -157,6 +157,7 @@ pub(super) unsafe trait RegisterInterface {
} }
} }
#[inline(always)]
fn iocfg_port(&self) -> &PortReg { fn iocfg_port(&self) -> &PortReg {
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() }; let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
match self.id().group { match self.id().group {
@ -170,12 +171,12 @@ pub(super) unsafe trait RegisterInterface {
} }
} }
#[inline] #[inline(always)]
fn mask_32(&self) -> u32 { fn mask_32(&self) -> u32 {
1 << self.id().num 1 << self.id().num
} }
#[inline] #[inline(always)]
fn enable_irq(&self) { fn enable_irq(&self) {
self.port_reg() self.port_reg()
.irq_enb() .irq_enb()
@ -241,10 +242,18 @@ pub(super) unsafe trait RegisterInterface {
} }
} }
/// Toggle the logic level of an output pin
#[inline(always)]
fn toggle(&mut self) {
// Safety: TOGOUT is a "mask" register, and we only write the bit for
// this pin ID
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
}
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure /// 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 /// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline] #[inline]
fn interrupt_edge(&mut self, edge_type: InterruptEdge) { fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
unsafe { unsafe {
self.port_reg() self.port_reg()
.irq_sen() .irq_sen()
@ -271,7 +280,7 @@ pub(super) unsafe trait RegisterInterface {
/// Configure which edge or level type triggers an interrupt /// Configure which edge or level type triggers an interrupt
#[inline] #[inline]
fn interrupt_level(&mut self, level: InterruptLevel) { fn configure_level_interrupt(&mut self, level: InterruptLevel) {
unsafe { unsafe {
self.port_reg() self.port_reg()
.irq_sen() .irq_sen()
@ -290,7 +299,7 @@ pub(super) unsafe trait RegisterInterface {
/// Only useful for input pins /// Only useful for input pins
#[inline] #[inline]
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) { fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| { self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID // Safety: Only write to register for this Pin ID
unsafe { unsafe {
@ -328,7 +337,7 @@ pub(super) unsafe trait RegisterInterface {
/// See p.52 of the programmers guide for more information. /// 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 /// 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 /// one clock cycle before returning to the configured default state
fn pulse_mode(&self, enable: bool, default_state: PinState) { fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
let portreg = self.port_reg(); let portreg = self.port_reg();
unsafe { unsafe {
if enable { if enable {
@ -353,7 +362,7 @@ pub(super) unsafe trait RegisterInterface {
} }
/// Only useful for output pins /// Only useful for output pins
fn delay(&self, delay_1: bool, delay_2: bool) { fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg(); let portreg = self.port_reg();
unsafe { unsafe {
if delay_1 { if delay_1 {

View File

@ -30,7 +30,7 @@ pub enum FifoEmptyMode {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockTooSlowForFastI2c; pub struct ClockTooSlowForFastI2cError;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -52,11 +52,11 @@ pub enum InitError {
/// Wrong address used in constructor /// Wrong address used in constructor
WrongAddrMode, WrongAddrMode,
/// APB1 clock is too slow for fast I2C mode. /// APB1 clock is too slow for fast I2C mode.
ClkTooSlow(ClockTooSlowForFastI2c), ClkTooSlow(ClockTooSlowForFastI2cError),
} }
impl From<ClockTooSlowForFastI2c> for InitError { impl From<ClockTooSlowForFastI2cError> for InitError {
fn from(value: ClockTooSlowForFastI2c) -> Self { fn from(value: ClockTooSlowForFastI2cError) -> Self {
Self::ClkTooSlow(value) Self::ClkTooSlow(value)
} }
} }
@ -318,7 +318,7 @@ impl<I2c: Instance> I2cBase<I2c> {
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
ms_cfg: Option<&MasterConfig>, ms_cfg: Option<&MasterConfig>,
sl_cfg: Option<&SlaveConfig>, sl_cfg: Option<&SlaveConfig>,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
syscfg.enable_peripheral_clock(I2c::PERIPH_SEL); syscfg.enable_peripheral_clock(I2c::PERIPH_SEL);
let mut i2c_base = I2cBase { let mut i2c_base = I2cBase {
@ -421,19 +421,19 @@ impl<I2c: Instance> I2cBase<I2c> {
}); });
} }
fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2c> { fn calc_clk_div(&self, speed_mode: I2cSpeed) -> Result<u8, ClockTooSlowForFastI2cError> {
if speed_mode == I2cSpeed::Regular100khz { if speed_mode == I2cSpeed::Regular100khz {
Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8) Ok(((self.clock.raw() / CLK_100K.raw() / 20) - 1) as u8)
} else { } else {
if self.clock.raw() < MIN_CLK_400K.raw() { if self.clock.raw() < MIN_CLK_400K.raw() {
return Err(ClockTooSlowForFastI2c); return Err(ClockTooSlowForFastI2cError);
} }
Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8) Ok(((self.clock.raw() / CLK_400K.raw() / 25) - 1) as u8)
} }
} }
/// Configures the clock scale for a given speed mode setting /// Configures the clock scale for a given speed mode setting
pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2c> { pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) -> Result<(), ClockTooSlowForFastI2cError> {
let clk_div = self.calc_clk_div(speed_mode)?; let clk_div = self.calc_clk_div(speed_mode)?;
self.i2c self.i2c
.clkscale() .clkscale()
@ -472,7 +472,7 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
cfg: MasterConfig, cfg: MasterConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cMaster { Ok(I2cMaster {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?, i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, Some(&cfg), None)?,
addr: PhantomData, addr: PhantomData,
@ -733,7 +733,7 @@ impl<I2c: Instance, Addr> I2cSlave<I2c, Addr> {
cfg: SlaveConfig, cfg: SlaveConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Ok(I2cSlave { Ok(I2cSlave {
i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?, i2c_base: I2cBase::new(i2c, sys_cfg, clocks, speed_mode, None, Some(&cfg))?,
addr: PhantomData, addr: PhantomData,
@ -895,7 +895,7 @@ impl<I2c: Instance> I2cSlave<I2c, TenBitAddress> {
cfg: SlaveConfig, cfg: SlaveConfig,
clocks: &Clocks, clocks: &Clocks,
speed_mode: I2cSpeed, speed_mode: I2cSpeed,
) -> Result<Self, ClockTooSlowForFastI2c> { ) -> Result<Self, ClockTooSlowForFastI2cError> {
Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode) Self::new_generic(i2c, sys_cfg, cfg, clocks, speed_mode)
} }
} }

View File

@ -275,7 +275,7 @@ impl IrqContextTimeoutOrMaxSize {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct IrqResult { pub struct IrqResult {
pub bytes_read: usize, pub bytes_read: usize,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
} }
/// This struct is used to return the default IRQ handler result to the user /// This struct is used to return the default IRQ handler result to the user
@ -283,7 +283,7 @@ pub struct IrqResult {
pub struct IrqResultMaxSizeOrTimeout { pub struct IrqResultMaxSizeOrTimeout {
complete: bool, complete: bool,
timeout: bool, timeout: bool,
pub errors: Option<IrqUartError>, pub errors: Option<UartErrors>,
pub bytes_read: usize, pub bytes_read: usize,
} }
@ -336,14 +336,14 @@ enum IrqReceptionMode {
} }
#[derive(Default, Debug, Copy, Clone)] #[derive(Default, Debug, Copy, Clone)]
pub struct IrqUartError { pub struct UartErrors {
overflow: bool, overflow: bool,
framing: bool, framing: bool,
parity: bool, parity: bool,
other: bool, other: bool,
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn overflow(&self) -> bool { pub fn overflow(&self) -> bool {
self.overflow self.overflow
@ -365,7 +365,7 @@ impl IrqUartError {
} }
} }
impl IrqUartError { impl UartErrors {
#[inline(always)] #[inline(always)]
pub fn error(&self) -> bool { pub fn error(&self) -> bool {
self.overflow || self.framing || self.parity self.overflow || self.framing || self.parity
@ -405,10 +405,10 @@ impl Instance for Uart0 {
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART0_TX;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart0 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart0::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -419,10 +419,10 @@ impl Instance for Uart1 {
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART1_TX;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart1 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart1::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -433,10 +433,10 @@ impl Instance for Uart2 {
const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX; const IRQ_TX: pac::Interrupt = pac::Interrupt::UART2_TX;
unsafe fn steal() -> Self { unsafe fn steal() -> Self {
pac::Peripherals::steal().uart2 Self::steal()
} }
fn ptr() -> *const uart_base::RegisterBlock { fn ptr() -> *const uart_base::RegisterBlock {
Uart2::ptr() as *const _ Self::ptr() as *const _
} }
} }
@ -1164,7 +1164,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
fn read_handler( fn read_handler(
&self, &self,
errors: &mut Option<IrqUartError>, errors: &mut Option<UartErrors>,
read_res: &nb::Result<u8, RxError>, read_res: &nb::Result<u8, RxError>,
) -> Option<u8> { ) -> Option<u8> {
match read_res { match read_res {
@ -1172,7 +1172,7 @@ impl<Uart: Instance> RxWithIrq<Uart> {
Err(nb::Error::WouldBlock) => None, Err(nb::Error::WouldBlock) => None,
Err(nb::Error::Other(e)) => { Err(nb::Error::Other(e)) => {
// Ensure `errors` is Some(IrqUartError), initializing if it's None // Ensure `errors` is Some(IrqUartError), initializing if it's None
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
// Now we can safely modify fields inside `err` // Now we can safely modify fields inside `err`
match e { match e {
@ -1185,14 +1185,14 @@ impl<Uart: Instance> RxWithIrq<Uart> {
} }
} }
fn check_for_errors(&self, errors: &mut Option<IrqUartError>) { fn check_for_errors(&self, errors: &mut Option<UartErrors>) {
let rx_status = self.uart().rxstatus().read(); let rx_status = self.uart().rxstatus().read();
if rx_status.rxovr().bit_is_set() if rx_status.rxovr().bit_is_set()
|| rx_status.rxfrm().bit_is_set() || rx_status.rxfrm().bit_is_set()
|| rx_status.rxpar().bit_is_set() || rx_status.rxpar().bit_is_set()
{ {
let err = errors.get_or_insert(IrqUartError::default()); let err = errors.get_or_insert(UartErrors::default());
if rx_status.rxovr().bit_is_set() { if rx_status.rxovr().bit_is_set() {
err.overflow = true; err.overflow = true;