Refactored GPIO module
- The GPIO module uses type-level programming now - Implementation heavily based on the ATSAMD GPIO HAL: https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html - Changes to API, but no passing of peripheral references necessary anymore. All examples and tests updated accordingly
This commit is contained in:
parent
47e64c2606
commit
63be6ed5fe
@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### Changed
|
||||
|
||||
- New GPIO implementation which uses type-level programming. Implementation heavily based on the
|
||||
ATSAMD GPIO HAL: https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html
|
||||
- Changes to API, therefore minor version bump
|
||||
|
||||
### Added
|
||||
|
||||
- UART implementation
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "va108xx-hal"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||
@ -14,6 +14,7 @@ categories = ["embedded", "no-std", "hardware-support"]
|
||||
cortex-m = "0.7"
|
||||
cortex-m-rt = "0.7"
|
||||
nb = "1"
|
||||
paste = "1.0"
|
||||
embedded-hal = { features = ["unproven"], version = "0.2.6" }
|
||||
void = { version = "1.0", default-features = false }
|
||||
once_cell = { version = "1.8.0", default-features = false }
|
||||
|
@ -77,7 +77,7 @@ is contained within the
|
||||
embedded-hal = "0.2.6"
|
||||
|
||||
[dependencies.va108xx-hal]
|
||||
version = "0.1"
|
||||
version = "0.2"
|
||||
features = ["rt"]
|
||||
```
|
||||
|
||||
|
@ -9,21 +9,15 @@
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||
use panic_halt as _;
|
||||
use va108xx_hal::{pac, prelude::*};
|
||||
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
let porta = dp.PORTA.split(&mut dp.SYSCONFIG).unwrap();
|
||||
let mut led1 = porta
|
||||
.pa10
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let mut led2 = porta
|
||||
.pa7
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let mut led3 = porta
|
||||
.pa6
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let porta = PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA);
|
||||
let mut led1 = porta.pa10.into_push_pull_output();
|
||||
let mut led2 = porta.pa7.into_push_pull_output();
|
||||
let mut led3 = porta.pa6.into_push_pull_output();
|
||||
for _ in 0..10 {
|
||||
led1.set_low().ok();
|
||||
led2.set_low().ok();
|
||||
|
@ -6,11 +6,10 @@
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::gpio::{porta, portb, PinState};
|
||||
use va108xx_hal::prelude::*;
|
||||
use va108xx_hal::gpio::{PinState, PinsA, PinsB};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
@ -20,6 +19,8 @@ enum TestCase {
|
||||
TestPullup,
|
||||
TestPulldown,
|
||||
TestMask,
|
||||
// Tie PORTB[22] to PORTB[23] for this test
|
||||
PortB,
|
||||
Perid,
|
||||
// Tie PA0 to an oscilloscope and configure pulse detection
|
||||
Pulse,
|
||||
@ -32,11 +33,9 @@ fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx Test Application --");
|
||||
let mut dp = va108xx::Peripherals::take().unwrap();
|
||||
let porta = dp.PORTA.split(&mut dp.SYSCONFIG).unwrap();
|
||||
let _portb = dp.PORTB.split(&mut dp.SYSCONFIG).unwrap();
|
||||
let mut led1 = porta
|
||||
.pa10
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA);
|
||||
let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
||||
let mut led1 = pinsa.pa10.into_push_pull_output();
|
||||
let test_case = TestCase::Delay;
|
||||
|
||||
match test_case {
|
||||
@ -56,82 +55,70 @@ fn main() -> ! {
|
||||
match test_case {
|
||||
TestCase::TestBasic => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let mut out = porta
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.enable_input(&mut dp.IOCONFIG, true);
|
||||
let input = porta
|
||||
.pa1
|
||||
.into_floating_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||
let input = pinsa.pa1.into_floating_input();
|
||||
out.set_high().unwrap();
|
||||
assert!(out.is_set_high().unwrap());
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low().unwrap();
|
||||
assert!(out.is_set_low().unwrap());
|
||||
assert!(input.is_low().unwrap());
|
||||
}
|
||||
TestCase::TestPullup => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let input = porta
|
||||
.pa1
|
||||
.into_pull_up_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let input = pinsa.pa1.into_pull_up_input();
|
||||
assert!(input.is_high().unwrap());
|
||||
let mut out = porta
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.into_floating_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
out.into_floating_input();
|
||||
assert!(input.is_high().unwrap());
|
||||
}
|
||||
TestCase::TestPulldown => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let input = porta
|
||||
.pa1
|
||||
.into_pull_down_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let input = pinsa.pa1.into_pull_down_input();
|
||||
assert!(input.is_low().unwrap());
|
||||
let mut out = porta
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
let mut out = pinsa.pa0.into_push_pull_output();
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.into_floating_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
||||
out.into_floating_input();
|
||||
assert!(input.is_low().unwrap());
|
||||
}
|
||||
TestCase::TestMask => {
|
||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||
let input = porta
|
||||
.pa1
|
||||
.into_pull_down_input(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.clear_datamask(&mut dp.PORTA);
|
||||
assert!(!input.datamask(&dp.PORTA));
|
||||
let out = porta
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.clear_datamask(&mut dp.PORTA);
|
||||
assert!(input.is_low_masked(&mut dp.PORTA).is_err());
|
||||
assert!(out.set_high_masked(&mut dp.PORTA).is_err());
|
||||
let input = pinsa.pa1.into_pull_down_input().clear_datamask();
|
||||
assert!(!input.datamask());
|
||||
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
||||
assert!(input.is_low_masked().is_err());
|
||||
assert!(out.set_high_masked().is_err());
|
||||
}
|
||||
TestCase::PortB => {
|
||||
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||
let input = pinsb.pb23.into_floating_input();
|
||||
out.set_high().unwrap();
|
||||
assert!(input.is_high().unwrap());
|
||||
out.set_low().unwrap();
|
||||
assert!(input.is_low().unwrap());
|
||||
}
|
||||
TestCase::Perid => {
|
||||
assert_eq!(porta::get_perid(&dp.PORTA), 0x004007e1);
|
||||
assert_eq!(portb::get_perid(&dp.PORTB), 0x004007e1);
|
||||
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||
}
|
||||
TestCase::Pulse => {
|
||||
let mut output_pulsed = porta
|
||||
let mut output_pulsed = pinsa
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.pulse_mode(&mut dp.PORTA, true, PinState::Low);
|
||||
.into_push_pull_output()
|
||||
.pulse_mode(true, PinState::Low);
|
||||
rprintln!("Pulsing high 10 times..");
|
||||
output_pulsed.set_low().unwrap();
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_high().unwrap();
|
||||
cortex_m::asm::delay(25_000_000);
|
||||
}
|
||||
let mut output_pulsed = output_pulsed.pulse_mode(&mut dp.PORTA, true, PinState::High);
|
||||
let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High);
|
||||
rprintln!("Pulsing low 10 times..");
|
||||
for _ in 0..10 {
|
||||
output_pulsed.set_low().unwrap();
|
||||
@ -139,18 +126,9 @@ fn main() -> ! {
|
||||
}
|
||||
}
|
||||
TestCase::Delay => {
|
||||
let mut out_0 = porta
|
||||
.pa0
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.delay(&mut dp.PORTA, true, false);
|
||||
let mut out_1 = porta
|
||||
.pa1
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.delay(&mut dp.PORTA, false, true);
|
||||
let mut out_2 = porta
|
||||
.pa3
|
||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
||||
.delay(&mut dp.PORTA, true, true);
|
||||
let mut out_0 = pinsa.pa0.into_push_pull_output().delay(true, false);
|
||||
let mut out_1 = pinsa.pa1.into_push_pull_output().delay(false, true);
|
||||
let mut out_2 = pinsa.pa3.into_push_pull_output().delay(true, true);
|
||||
for _ in 0..20 {
|
||||
out_0.toggle().unwrap();
|
||||
out_1.toggle().unwrap();
|
||||
|
@ -7,18 +7,18 @@ use core::fmt::Write;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_rtt_target as _;
|
||||
use rtt_target::{rprintln, rtt_init_print};
|
||||
use va108xx_hal::{pac, prelude::*, uart};
|
||||
use va108xx_hal::{gpio::PinsB, pac, prelude::*, uart};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
rtt_init_print!();
|
||||
rprintln!("-- VA108xx UART test application--");
|
||||
rprintln!("-- VA108xx UART example application--");
|
||||
|
||||
let mut dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
let gpiob = dp.PORTB.split(&mut dp.SYSCONFIG).unwrap();
|
||||
let tx = gpiob.pb21.into_funsel_1(&mut dp.IOCONFIG);
|
||||
let rx = gpiob.pb20.into_funsel_1(&mut dp.IOCONFIG);
|
||||
let gpiob = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
||||
let tx = gpiob.pb21.into_funsel_1();
|
||||
let rx = gpiob.pb20.into_funsel_1();
|
||||
|
||||
let uartb = uart::Uart::uartb(
|
||||
dp.UARTB,
|
||||
|
663
src/gpio.rs
663
src/gpio.rs
@ -1,663 +0,0 @@
|
||||
//! API for the GPIO pins
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! - [Blinky example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/blinky.rs)
|
||||
use crate::pac::SYSCONFIG;
|
||||
use core::convert::Infallible;
|
||||
use core::marker::PhantomData;
|
||||
use cortex_m::singleton;
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
use va108xx::IOCONFIG;
|
||||
|
||||
/// Extension trait to split a GPIO peripheral in independent pins and registers
|
||||
pub trait GpioExt {
|
||||
/// The parts to split the GPIO into.
|
||||
type Parts;
|
||||
|
||||
/// Splits the GPIO block into independent pins and registers.
|
||||
fn split(&mut self, syscfg: &mut SYSCONFIG) -> Option<Self::Parts>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PinModeError {
|
||||
InputDisabledForOutput,
|
||||
IsMasked,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum PinState {
|
||||
Low = 0,
|
||||
High = 1,
|
||||
}
|
||||
|
||||
enum PortId {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
trait GpioRegExt {
|
||||
fn is_low(&self, pos: u8) -> bool;
|
||||
fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result<bool, PinModeError>;
|
||||
fn input_enabled_for_output(&self, pos: usize, port_id: &PortId) -> bool;
|
||||
fn set_high(&self, pos: u8);
|
||||
fn set_low(&self, pos: u8);
|
||||
fn toggle(&self, pos: u8);
|
||||
}
|
||||
|
||||
/// Input mode (type state)
|
||||
pub struct Input<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
/// Output mode (type state)
|
||||
pub struct Output<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
/// Floating input (type state)
|
||||
pub struct Floating;
|
||||
/// Pulled down input (type state)
|
||||
pub struct PullDown;
|
||||
/// Pulled up input (type state)
|
||||
pub struct PullUp;
|
||||
/// Open drain output (type state)
|
||||
pub struct OpenDrain;
|
||||
// Push-pull output (type state)
|
||||
pub struct PushPull;
|
||||
|
||||
pub struct OutputInverted;
|
||||
|
||||
pub struct InputInverted;
|
||||
|
||||
// FUNSEL0 is the regular GPIO port configuration
|
||||
pub struct FUNSEL1;
|
||||
pub struct FUNSEL2;
|
||||
pub struct FUNSEL3;
|
||||
|
||||
/// Function select (type state)
|
||||
pub struct AltFunc<FUN> {
|
||||
_mode: PhantomData<FUN>,
|
||||
}
|
||||
|
||||
pub enum FilterType {
|
||||
SystemClock = 0,
|
||||
DirectInputWithSynchronization = 1,
|
||||
FilterOneClockCycle = 2,
|
||||
FilterTwoClockCycles = 3,
|
||||
FilterThreeClockCycles = 4,
|
||||
FilterFourClockCycles = 5,
|
||||
}
|
||||
|
||||
pub enum FilterClkSel {
|
||||
SysClk = 0,
|
||||
Clk1 = 1,
|
||||
Clk2 = 2,
|
||||
Clk3 = 3,
|
||||
Clk4 = 4,
|
||||
Clk5 = 5,
|
||||
Clk6 = 6,
|
||||
Clk7 = 7,
|
||||
}
|
||||
|
||||
/// Fully erased pin
|
||||
pub struct Pin<MODE> {
|
||||
i: u8,
|
||||
port_id: PortId,
|
||||
port: *const dyn GpioRegExt,
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
// NOTE(unsafe) this only enables read access to the same pin from multiple threads
|
||||
unsafe impl<MODE> Send for Pin<MODE> {}
|
||||
impl<MODE> StatefulOutputPin for Pin<Output<MODE>> {
|
||||
#[inline(always)]
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
self.is_set_low().map(|v| !v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
unsafe { (*self.port).is_set_low(self.i.into(), &self.port_id) }
|
||||
}
|
||||
}
|
||||
impl<MODE> OutputPin for Pin<Output<MODE>> {
|
||||
type Error = PinModeError;
|
||||
#[inline(always)]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe { (*self.port).set_high(self.i) };
|
||||
Ok(())
|
||||
}
|
||||
#[inline(always)]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe { (*self.port).set_low(self.i) }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> ToggleableOutputPin for Pin<Output<MODE>> {
|
||||
type Error = Infallible;
|
||||
#[inline(always)]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
unsafe { (*self.port).toggle(self.i) }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl InputPin for Pin<Output<OpenDrain>> {
|
||||
type Error = Infallible;
|
||||
#[inline(always)]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.is_low().map(|v| !v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe { (*self.port).is_low(self.i) })
|
||||
}
|
||||
}
|
||||
impl<MODE> InputPin for Pin<Input<MODE>> {
|
||||
type Error = Infallible;
|
||||
#[inline(always)]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.is_low().map(|v| !v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe { (*self.port).is_low(self.i) })
|
||||
}
|
||||
}
|
||||
|
||||
// This is only needs to be implemented for PORTA because PORTB is derived
|
||||
// from PORTA
|
||||
impl GpioRegExt for crate::pac::porta::RegisterBlock {
|
||||
#[inline(always)]
|
||||
fn is_low(&self, pos: u8) -> bool {
|
||||
self.datainraw().read().bits() & (1 << pos) == 0
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result<bool, PinModeError> {
|
||||
if self.input_enabled_for_output(pos, port_id) {
|
||||
Ok(self.datainraw().read().bits() & (1 << pos) == 0)
|
||||
} else {
|
||||
Err(PinModeError::InputDisabledForOutput)
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn set_high(&self, pos: u8) {
|
||||
unsafe { self.setout().write(|w| w.bits(1 << pos)) }
|
||||
}
|
||||
#[inline(always)]
|
||||
fn set_low(&self, pos: u8) {
|
||||
unsafe { self.clrout().write(|w| w.bits(1 << pos)) }
|
||||
}
|
||||
#[inline(always)]
|
||||
fn toggle(&self, pos: u8) {
|
||||
unsafe { self.togout().write(|w| w.bits(1 << pos)) }
|
||||
}
|
||||
#[inline(always)]
|
||||
fn input_enabled_for_output(&self, pos: usize, port_id: &PortId) -> bool {
|
||||
unsafe {
|
||||
let iocfg = &(*IOCONFIG::ptr());
|
||||
match port_id {
|
||||
PortId::A => iocfg.porta[pos].read().iewo().bit_is_set(),
|
||||
PortId::B => iocfg.portb[pos].read().iewo().bit_is_set(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gpio {
|
||||
($PORTX:ident, $portx:ident, $port_id:path, [
|
||||
$($PXi:ident: ($pxi:ident, $i:expr),)+
|
||||
]) => {
|
||||
pub mod $portx {
|
||||
use core::marker::PhantomData;
|
||||
use core::convert::Infallible;
|
||||
use super::{
|
||||
FUNSEL1, FUNSEL2, FUNSEL3, Floating, AltFunc, GpioExt, Input, OpenDrain,
|
||||
PullUp, Output, FilterType, FilterClkSel, Pin, GpioRegExt, PushPull,
|
||||
PinModeError, PinState, PortId, singleton
|
||||
};
|
||||
use crate::{pac::$PORTX, pac::SYSCONFIG, pac::IOCONFIG};
|
||||
use embedded_hal::digital::v2::{
|
||||
InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin
|
||||
};
|
||||
pub struct Parts {
|
||||
$(
|
||||
pub $pxi: $PXi<Input<Floating>>,
|
||||
)+
|
||||
}
|
||||
|
||||
pub fn get_perid(port: &$PORTX) -> u32 {
|
||||
port.perid.read().bits()
|
||||
}
|
||||
|
||||
impl GpioExt for $PORTX {
|
||||
type Parts = Parts;
|
||||
|
||||
/// This function splits the PORT into the individual pins
|
||||
/// Should only be called once and returns None on subsequent calls
|
||||
fn split(&mut self, syscfg: &mut SYSCONFIG) -> Option<Parts> {
|
||||
let _: &'static mut bool = singleton!(: bool = false)?;
|
||||
syscfg.peripheral_clk_enable.modify(|_, w| {
|
||||
w.$portx().set_bit();
|
||||
w.gpio().set_bit();
|
||||
w.ioconfig().set_bit();
|
||||
w
|
||||
});
|
||||
Some(Parts {
|
||||
$(
|
||||
$pxi: $PXi { _mode : PhantomData },
|
||||
)+
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn _set_alternate_mode(iocfg: &mut IOCONFIG, index: usize, mode: u8) {
|
||||
iocfg.$portx[index].modify(|_, w| unsafe {
|
||||
w.funsel().bits(mode)
|
||||
});
|
||||
}
|
||||
|
||||
$(
|
||||
pub struct $PXi<MODE> {
|
||||
_mode: PhantomData<MODE>,
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<MODE> {
|
||||
pub fn into_funsel_1(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL1>> {
|
||||
_set_alternate_mode(iocfg, $i, 1);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
pub fn into_funsel_2(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL2>> {
|
||||
_set_alternate_mode(iocfg, $i, 2);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
pub fn into_funsel_3(self, iocfg: &mut IOCONFIG) -> $PXi<AltFunc<FUNSEL3>> {
|
||||
_set_alternate_mode(iocfg, $i, 3);
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
// Get DATAMASK bit for this particular pin
|
||||
#[inline(always)]
|
||||
pub fn datamask(&self, port: &$PORTX) -> bool {
|
||||
(port.datamask().read().bits() >> $i) == 1
|
||||
}
|
||||
/// 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, port: &mut $PORTX) -> Self {
|
||||
unsafe {
|
||||
port.datamask().modify(|r, w| w.bits(r.bits() | (1 << $i)))
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// 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, port: &mut $PORTX) -> Self {
|
||||
unsafe {
|
||||
port.datamask().modify(|r, w| w.bits(r.bits() & !(1 << $i)))
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn into_floating_input(
|
||||
self, iocfg: &mut IOCONFIG, port: &mut $PORTX
|
||||
) -> $PXi<Input<Floating>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
w.pen().clear_bit();
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_up_input(self, iocfg: &mut IOCONFIG, port: &mut $PORTX) -> $PXi<Input<PullUp>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
w.pen().set_bit();
|
||||
w.plevel().set_bit();
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_pull_down_input(
|
||||
self, iocfg: &mut IOCONFIG, port: &mut $PORTX
|
||||
) -> $PXi<Input<PullUp>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
w.pen().set_bit();
|
||||
w.plevel().clear_bit();
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_open_drain_output(
|
||||
self, iocfg: &mut IOCONFIG
|
||||
) -> $PXi<Output<OpenDrain>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
w.pen().clear_bit();
|
||||
w.opendrn().set_bit()
|
||||
});
|
||||
let port_reg = &(*$PORTX::ptr());
|
||||
port_reg.dir().modify(|r,w| w.bits(r.bits() | (1 << $i)));
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn into_push_pull_output(
|
||||
self, iocfg: &mut IOCONFIG, port_reg: &mut $PORTX
|
||||
) -> $PXi<Output<PushPull>> {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.funsel().bits(0);
|
||||
w.opendrn().clear_bit()
|
||||
});
|
||||
port_reg.dir().modify(|r,w| w.bits(r.bits() | (1 << $i)));
|
||||
port_reg.clrout().write(|w| w.bits(1 << $i))
|
||||
}
|
||||
$PXi { _mode: PhantomData }
|
||||
}
|
||||
|
||||
pub fn filter_type(
|
||||
self, iocfg: &mut IOCONFIG, filter: FilterType, clksel: FilterClkSel
|
||||
) -> Self {
|
||||
unsafe {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
w.flttype().bits(filter as u8);
|
||||
w.fltclk().bits(clksel as u8)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<Input<MODE>> {
|
||||
pub fn input_inversion(self, iocfg: &mut IOCONFIG, enable: bool) -> Self {
|
||||
if enable {
|
||||
iocfg.$portx[$i].modify(|_, w| w.invinp().set_bit());
|
||||
} else {
|
||||
iocfg.$portx[$i].modify(|_, w| w.invinp().clear_bit());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// This function takes account for the data mask register
|
||||
#[inline(always)]
|
||||
pub fn is_high_masked(&self, port: &$PORTX) -> Result<bool, PinModeError> {
|
||||
self.is_low_masked(port).map(|v| !v)
|
||||
}
|
||||
|
||||
/// This function takes account for the data mask register
|
||||
#[inline(always)]
|
||||
pub fn is_low_masked(&self, port: &$PORTX) -> Result<bool, PinModeError> {
|
||||
if ((port.datamask().read().bits() >> $i) & 1) == 0 {
|
||||
Err(PinModeError::IsMasked)
|
||||
} else {
|
||||
Ok(port.datain().read().bits() & (1 << $i) == 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<Output<MODE>> {
|
||||
pub fn output_inversion(self, iocfg: &mut IOCONFIG, enable: bool) -> Self {
|
||||
if enable {
|
||||
iocfg.$portx[$i].modify(|_, w| w.invout().set_bit());
|
||||
} else {
|
||||
iocfg.$portx[$i].modify(|_, w| w.invout().clear_bit());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Input even when in output mode. In
|
||||
/// this mode the input receiver is enabled even
|
||||
/// if the direction is configured as an output.
|
||||
/// This allows monitoring of output values
|
||||
pub fn enable_input(self, iocfg: &mut IOCONFIG, enable: bool) -> Self {
|
||||
if enable {
|
||||
iocfg.$portx[$i].modify(|_, w| w.iewo().set_bit());
|
||||
} else {
|
||||
iocfg.$portx[$i].modify(|_, w| w.iewo().clear_bit());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Pull up/down even when output is active. The Default is to
|
||||
/// disable pull up/down when output is actively driven. This bit enables the
|
||||
/// pull up/down all the time.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// `enable` - Enable the peripheral functionality
|
||||
/// `enable_pullup` - Enable the pullup itself
|
||||
pub fn enable_pull_up(
|
||||
self, iocfg: &mut IOCONFIG, enable: bool, enable_pullup: bool
|
||||
) -> Self {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
if enable { w.pwoa().set_bit(); } else { w.pwoa().clear_bit(); }
|
||||
if enable_pullup { w.pen().set_bit(); } else { w.pen().clear_bit(); }
|
||||
w.plevel().set_bit()
|
||||
});
|
||||
self
|
||||
}
|
||||
/// Enable Pull up/down even when output is active. The Default is to disable
|
||||
/// pull up/down when output is actively driven. This bit enables the
|
||||
/// pull up/down all the time.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// `enable` - Enable the peripheral functionality
|
||||
/// `enable_pullup` - Enable the pulldown itself
|
||||
pub fn enable_pull_down(
|
||||
self, iocfg: &mut IOCONFIG, enable: bool, enable_pulldown: bool
|
||||
) -> Self {
|
||||
iocfg.$portx[$i].modify(|_, w| {
|
||||
if enable { w.pwoa().set_bit(); } else { w.pwoa().clear_bit(); }
|
||||
if enable_pulldown { w.pen().set_bit(); } else { w.pen().clear_bit(); }
|
||||
w.plevel().clear_bit()
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> $PXi<Output<MODE>> {
|
||||
/// Erases the pin number from the type
|
||||
///
|
||||
/// This is useful when you want to collect the pins into an array where you
|
||||
/// need all the elements to have the same type
|
||||
pub fn downgrade(self) -> Pin<Output<MODE>> {
|
||||
Pin {
|
||||
i: $i,
|
||||
port_id: $port_id,
|
||||
port: $PORTX::ptr() as *const dyn GpioRegExt,
|
||||
_mode: self._mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// This function takes account for the data mask register
|
||||
#[inline(always)]
|
||||
pub fn set_low_masked(&self, port: &$PORTX) -> Result<(), PinModeError> {
|
||||
if ((port.datamask().read().bits() >> $i) & 1) == 0 {
|
||||
Err(PinModeError::IsMasked)
|
||||
} else {
|
||||
Ok(unsafe { (*$PORTX::ptr()).set_low($i) })
|
||||
}
|
||||
}
|
||||
/// This function takes account for the data mask register
|
||||
#[inline(always)]
|
||||
pub fn set_high_masked(&self, port: &$PORTX) -> Result<(), PinModeError> {
|
||||
if ((port.datamask().read().bits() >> $i) & 1) == 0 {
|
||||
Err(PinModeError::IsMasked)
|
||||
} else {
|
||||
Ok(unsafe { (*$PORTX::ptr()).set_high($i) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pulse_mode(self, port: &mut $PORTX, enable: bool, default_state: PinState) -> Self {
|
||||
unsafe {
|
||||
if enable {
|
||||
port.pulse().modify(|r, w| w.bits(r.bits() | (1 << $i)));
|
||||
} else {
|
||||
port.pulse().modify(|r, w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
if default_state == PinState::Low {
|
||||
port.pulsebase().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
else {
|
||||
port.pulsebase().modify(|r,w| w.bits(r.bits() | (1 << $i)));
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn delay(self, port: &mut $PORTX, delay_1: bool, delay_2: bool) -> Self {
|
||||
unsafe {
|
||||
if delay_1 {
|
||||
port.delay1().modify(|r, w| w.bits(r.bits() | (1 << $i)));
|
||||
} else {
|
||||
port.delay1().modify(|r, w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
if delay_2 {
|
||||
port.delay2().modify(|r, w| w.bits(r.bits() | (1 << $i)));
|
||||
} else {
|
||||
port.delay2().modify(|r, w| w.bits(r.bits() & !(1 << $i)));
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
|
||||
#[inline(always)]
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
self.is_set_low().map(|v| !v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
unsafe {
|
||||
(*$PORTX::ptr()).is_set_low($i, &$port_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<MODE> OutputPin for $PXi<Output<MODE>> {
|
||||
type Error = PinModeError;
|
||||
#[inline(always)]
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(unsafe { (*$PORTX::ptr()).set_high($i) })
|
||||
}
|
||||
#[inline(always)]
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(unsafe { (*$PORTX::ptr()).set_low($i) })
|
||||
}
|
||||
}
|
||||
impl<MODE> ToggleableOutputPin for $PXi<Output<MODE>> {
|
||||
type Error = Infallible;
|
||||
#[inline(always)]
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(unsafe { (*$PORTX::ptr()).toggle($i) })
|
||||
}
|
||||
}
|
||||
impl<MODE> $PXi<Input<MODE>> {
|
||||
/// Erases the pin number from the type
|
||||
///
|
||||
/// This is useful when you want to collect the pins into an array where you
|
||||
/// need all the elements to have the same type
|
||||
pub fn downgrade(self) -> Pin<Input<MODE>> {
|
||||
Pin {
|
||||
i: $i,
|
||||
port_id: $port_id,
|
||||
port: $PORTX::ptr() as *const dyn GpioRegExt,
|
||||
_mode: self._mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<MODE> InputPin for $PXi<Input<MODE>> {
|
||||
type Error = Infallible;
|
||||
#[inline(always)]
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.is_low().map(|v| !v)
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(unsafe { (*$PORTX::ptr()).is_low($i) })
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpio!(PORTA, porta, PortId::A, [
|
||||
PA0: (pa0, 0),
|
||||
PA1: (pa1, 1),
|
||||
PA2: (pa2, 2),
|
||||
PA3: (pa3, 3),
|
||||
PA4: (pa4, 4),
|
||||
PA5: (pa5, 5),
|
||||
PA6: (pa6, 6),
|
||||
PA7: (pa7, 7),
|
||||
PA8: (pa8, 8),
|
||||
PA9: (pa9, 9),
|
||||
PA10: (pa10, 10),
|
||||
PA11: (pa11, 11),
|
||||
PA12: (pa12, 12),
|
||||
PA13: (pa13, 13),
|
||||
PA14: (pa14, 14),
|
||||
PA15: (pa15, 15),
|
||||
PA16: (pa16, 16),
|
||||
PA17: (pa17, 17),
|
||||
PA18: (pa18, 18),
|
||||
PA19: (pa19, 19),
|
||||
PA20: (pa20, 20),
|
||||
PA21: (pa21, 21),
|
||||
PA22: (pa22, 22),
|
||||
PA23: (pa23, 23),
|
||||
PA24: (pa24, 24),
|
||||
PA25: (pa25, 25),
|
||||
PA26: (pa26, 26),
|
||||
PA27: (pa27, 27),
|
||||
PA28: (pa28, 28),
|
||||
PA29: (pa29, 29),
|
||||
PA30: (pa30, 30),
|
||||
PA31: (pa31, 31),
|
||||
]);
|
||||
|
||||
gpio!(PORTB, portb, PortId::B, [
|
||||
PB0: (pb0, 0),
|
||||
PB1: (pb1, 1),
|
||||
PB2: (pb2, 2),
|
||||
PB3: (pb3, 3),
|
||||
PB4: (pb4, 4),
|
||||
PB5: (pb5, 5),
|
||||
PB6: (pb6, 6),
|
||||
PB7: (pb7, 7),
|
||||
PB8: (pb8, 8),
|
||||
PB9: (pb9, 9),
|
||||
PB10: (pb10, 10),
|
||||
PB11: (pb11, 11),
|
||||
PB12: (pb12, 12),
|
||||
PB13: (pb13, 13),
|
||||
PB14: (pb14, 14),
|
||||
PB15: (pb15, 15),
|
||||
PB16: (pb16, 16),
|
||||
PB17: (pb17, 17),
|
||||
PB18: (pb18, 18),
|
||||
PB19: (pb19, 19),
|
||||
PB20: (pb20, 20),
|
||||
PB21: (pb21, 21),
|
||||
PB22: (pb22, 22),
|
||||
PB23: (pb23, 23),
|
||||
]);
|
491
src/gpio/dynpins.rs
Normal file
491
src/gpio/dynpins.rs
Normal file
@ -0,0 +1,491 @@
|
||||
//! # 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`](core::convert::TryFrom)/
|
||||
//! [`TryInto`](core::convert::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
|
||||
//! [`InvalidPinType`](Error::InvalidPinType).
|
||||
|
||||
use super::pins::{FilterClkSel, FilterType, Pin, PinError, PinId, PinMode, PinState};
|
||||
use super::reg::RegisterInterface;
|
||||
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode configurations
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` for disabled configurations
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynDisabled {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for input configurations
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynInput {
|
||||
Floating,
|
||||
PullDown,
|
||||
PullUp,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for output configurations
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynOutput {
|
||||
PushPull,
|
||||
OpenDrain,
|
||||
ReadablePushPull,
|
||||
ReadableOpenDrain,
|
||||
}
|
||||
|
||||
/// Value-level `enum` for alternate peripheral function configurations
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum DynAlternate {
|
||||
Funsel1,
|
||||
Funsel2,
|
||||
Funsel3,
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// DynPinMode
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` representing pin modes
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
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::Funsel1);
|
||||
/// Value-level variant of [`DynPinMode`] for function select 2
|
||||
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Funsel2);
|
||||
/// Value-level variant of [`DynPinMode`] for function select 3
|
||||
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Funsel3);
|
||||
|
||||
//==================================================================================================
|
||||
// DynGroup & DynPinId
|
||||
//==================================================================================================
|
||||
|
||||
/// Value-level `enum` for pin groups
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub enum DynGroup {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
/// Value-level `struct` representing pin IDs
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub struct DynPinId {
|
||||
pub group: DynGroup,
|
||||
pub num: u8,
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// DynRegisters
|
||||
//==================================================================================================
|
||||
|
||||
/// Provide a safe register interface for [`DynPin`]s
|
||||
///
|
||||
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||
/// access the corresponding regsiters.
|
||||
struct DynRegisters {
|
||||
id: DynPinId,
|
||||
}
|
||||
|
||||
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||
unsafe impl RegisterInterface for DynRegisters {
|
||||
#[inline]
|
||||
fn id(&self) -> DynPinId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl DynRegisters {
|
||||
/// Create a new instance of [`DynRegisters`]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Users must never create two simultaneous instances of this `struct` with
|
||||
/// the same [`DynPinId`]
|
||||
#[inline]
|
||||
unsafe fn new(id: DynPinId) -> Self {
|
||||
DynRegisters { id }
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// 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.
|
||||
pub struct DynPin {
|
||||
regs: DynRegisters,
|
||||
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]
|
||||
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||
DynPin {
|
||||
regs: DynRegisters::new(id),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a copy of the pin ID
|
||||
#[inline]
|
||||
pub fn id(&self) -> DynPinId {
|
||||
self.regs.id
|
||||
}
|
||||
|
||||
/// Return a copy of the pin mode
|
||||
#[inline]
|
||||
pub fn mode(&self) -> DynPinMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// Convert the pin to the requested [`DynPinMode`]
|
||||
#[inline]
|
||||
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||
// Only modify registers if we are actually changing pin mode
|
||||
if mode != self.mode {
|
||||
self.regs.change_mode(mode);
|
||||
self.mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
#[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]
|
||||
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, PinError> {
|
||||
self.regs.read_pin_masked()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_low_masked(&self) -> Result<bool, PinError> {
|
||||
self.regs.read_pin_masked().map(|v| !v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(true)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
|
||||
self.regs.write_pin_masked(false)
|
||||
}
|
||||
|
||||
/// 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 delay(self, delay_1: bool, delay_2: bool) -> Result<Self, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.delay(delay_1, delay_2);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 pulse_mode(self, enable: bool, default_state: PinState) -> Result<Self, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.pulse_mode(enable, default_state);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
/// See p.37 and p.38 of the programmers guide for more information.
|
||||
#[inline]
|
||||
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Result<Self, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) => {
|
||||
self.regs.filter_type(filter, clksel);
|
||||
Ok(self)
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _read(&self) -> Result<bool, PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||
Ok(self.regs.read_pin())
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _write(&mut self, bit: bool) -> Result<(), PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.write_pin(bit);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn _toggle(&mut self) -> Result<(), PinError> {
|
||||
match self.mode {
|
||||
DynPinMode::Output(_) => {
|
||||
self.regs.toggle();
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(PinError::InvalidPinType),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _is_low(&self) -> Result<bool, PinError |