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]
|
## [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
|
### Added
|
||||||
|
|
||||||
- UART implementation
|
- UART implementation
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "va108xx-hal"
|
name = "va108xx-hal"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
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 = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
|
paste = "1.0"
|
||||||
embedded-hal = { features = ["unproven"], version = "0.2.6" }
|
embedded-hal = { features = ["unproven"], version = "0.2.6" }
|
||||||
void = { version = "1.0", default-features = false }
|
void = { version = "1.0", default-features = false }
|
||||||
once_cell = { version = "1.8.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"
|
embedded-hal = "0.2.6"
|
||||||
|
|
||||||
[dependencies.va108xx-hal]
|
[dependencies.va108xx-hal]
|
||||||
version = "0.1"
|
version = "0.2"
|
||||||
features = ["rt"]
|
features = ["rt"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -9,21 +9,15 @@
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::digital::v2::ToggleableOutputPin;
|
use embedded_hal::digital::v2::ToggleableOutputPin;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va108xx_hal::{pac, prelude::*};
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let porta = dp.PORTA.split(&mut dp.SYSCONFIG).unwrap();
|
let porta = PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA);
|
||||||
let mut led1 = porta
|
let mut led1 = porta.pa10.into_push_pull_output();
|
||||||
.pa10
|
let mut led2 = porta.pa7.into_push_pull_output();
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
let mut led3 = porta.pa6.into_push_pull_output();
|
||||||
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);
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
led1.set_low().ok();
|
led1.set_low().ok();
|
||||||
led2.set_low().ok();
|
led2.set_low().ok();
|
||||||
|
@ -6,11 +6,10 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
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 panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::gpio::{porta, portb, PinState};
|
use va108xx_hal::gpio::{PinState, PinsA, PinsB};
|
||||||
use va108xx_hal::prelude::*;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -20,6 +19,8 @@ enum TestCase {
|
|||||||
TestPullup,
|
TestPullup,
|
||||||
TestPulldown,
|
TestPulldown,
|
||||||
TestMask,
|
TestMask,
|
||||||
|
// Tie PORTB[22] to PORTB[23] for this test
|
||||||
|
PortB,
|
||||||
Perid,
|
Perid,
|
||||||
// Tie PA0 to an oscilloscope and configure pulse detection
|
// Tie PA0 to an oscilloscope and configure pulse detection
|
||||||
Pulse,
|
Pulse,
|
||||||
@ -32,11 +33,9 @@ fn main() -> ! {
|
|||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA108xx Test Application --");
|
rprintln!("-- VA108xx Test Application --");
|
||||||
let mut dp = va108xx::Peripherals::take().unwrap();
|
let mut dp = va108xx::Peripherals::take().unwrap();
|
||||||
let porta = dp.PORTA.split(&mut dp.SYSCONFIG).unwrap();
|
let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA);
|
||||||
let _portb = dp.PORTB.split(&mut dp.SYSCONFIG).unwrap();
|
let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
||||||
let mut led1 = porta
|
let mut led1 = pinsa.pa10.into_push_pull_output();
|
||||||
.pa10
|
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
|
||||||
let test_case = TestCase::Delay;
|
let test_case = TestCase::Delay;
|
||||||
|
|
||||||
match test_case {
|
match test_case {
|
||||||
@ -56,82 +55,70 @@ fn main() -> ! {
|
|||||||
match test_case {
|
match test_case {
|
||||||
TestCase::TestBasic => {
|
TestCase::TestBasic => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let mut out = porta
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
.pa0
|
let input = pinsa.pa1.into_floating_input();
|
||||||
.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);
|
|
||||||
out.set_high().unwrap();
|
out.set_high().unwrap();
|
||||||
assert!(out.is_set_high().unwrap());
|
|
||||||
assert!(input.is_high().unwrap());
|
assert!(input.is_high().unwrap());
|
||||||
out.set_low().unwrap();
|
out.set_low().unwrap();
|
||||||
assert!(out.is_set_low().unwrap());
|
|
||||||
assert!(input.is_low().unwrap());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPullup => {
|
TestCase::TestPullup => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = porta
|
let input = pinsa.pa1.into_pull_up_input();
|
||||||
.pa1
|
|
||||||
.into_pull_up_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
|
||||||
assert!(input.is_high().unwrap());
|
assert!(input.is_high().unwrap());
|
||||||
let mut out = porta
|
let mut out = pinsa.pa0.into_readable_push_pull_output();
|
||||||
.pa0
|
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
|
||||||
out.set_low().unwrap();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low().unwrap());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high().unwrap();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_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());
|
assert!(input.is_high().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestPulldown => {
|
TestCase::TestPulldown => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = porta
|
let input = pinsa.pa1.into_pull_down_input();
|
||||||
.pa1
|
|
||||||
.into_pull_down_input(&mut dp.IOCONFIG, &mut dp.PORTA);
|
|
||||||
assert!(input.is_low().unwrap());
|
assert!(input.is_low().unwrap());
|
||||||
let mut out = porta
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
.pa0
|
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA);
|
|
||||||
out.set_low().unwrap();
|
out.set_low().unwrap();
|
||||||
assert!(input.is_low().unwrap());
|
assert!(input.is_low().unwrap());
|
||||||
out.set_high().unwrap();
|
out.set_high().unwrap();
|
||||||
assert!(input.is_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());
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::TestMask => {
|
TestCase::TestMask => {
|
||||||
// Tie PORTA[0] to PORTA[1] for these tests!
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
let input = porta
|
let input = pinsa.pa1.into_pull_down_input().clear_datamask();
|
||||||
.pa1
|
assert!(!input.datamask());
|
||||||
.into_pull_down_input(&mut dp.IOCONFIG, &mut dp.PORTA)
|
let mut out = pinsa.pa0.into_push_pull_output().clear_datamask();
|
||||||
.clear_datamask(&mut dp.PORTA);
|
assert!(input.is_low_masked().is_err());
|
||||||
assert!(!input.datamask(&dp.PORTA));
|
assert!(out.set_high_masked().is_err());
|
||||||
let out = porta
|
}
|
||||||
.pa0
|
TestCase::PortB => {
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
.clear_datamask(&mut dp.PORTA);
|
let mut out = pinsb.pb22.into_readable_push_pull_output();
|
||||||
assert!(input.is_low_masked(&mut dp.PORTA).is_err());
|
let input = pinsb.pb23.into_floating_input();
|
||||||
assert!(out.set_high_masked(&mut dp.PORTA).is_err());
|
out.set_high().unwrap();
|
||||||
|
assert!(input.is_high().unwrap());
|
||||||
|
out.set_low().unwrap();
|
||||||
|
assert!(input.is_low().unwrap());
|
||||||
}
|
}
|
||||||
TestCase::Perid => {
|
TestCase::Perid => {
|
||||||
assert_eq!(porta::get_perid(&dp.PORTA), 0x004007e1);
|
assert_eq!(PinsA::get_perid(), 0x004007e1);
|
||||||
assert_eq!(portb::get_perid(&dp.PORTB), 0x004007e1);
|
assert_eq!(PinsB::get_perid(), 0x004007e1);
|
||||||
}
|
}
|
||||||
TestCase::Pulse => {
|
TestCase::Pulse => {
|
||||||
let mut output_pulsed = porta
|
let mut output_pulsed = pinsa
|
||||||
.pa0
|
.pa0
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
.into_push_pull_output()
|
||||||
.pulse_mode(&mut dp.PORTA, true, PinState::Low);
|
.pulse_mode(true, PinState::Low);
|
||||||
rprintln!("Pulsing high 10 times..");
|
rprintln!("Pulsing high 10 times..");
|
||||||
output_pulsed.set_low().unwrap();
|
output_pulsed.set_low().unwrap();
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_high().unwrap();
|
output_pulsed.set_high().unwrap();
|
||||||
cortex_m::asm::delay(25_000_000);
|
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..");
|
rprintln!("Pulsing low 10 times..");
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
output_pulsed.set_low().unwrap();
|
output_pulsed.set_low().unwrap();
|
||||||
@ -139,18 +126,9 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TestCase::Delay => {
|
TestCase::Delay => {
|
||||||
let mut out_0 = porta
|
let mut out_0 = pinsa.pa0.into_push_pull_output().delay(true, false);
|
||||||
.pa0
|
let mut out_1 = pinsa.pa1.into_push_pull_output().delay(false, true);
|
||||||
.into_push_pull_output(&mut dp.IOCONFIG, &mut dp.PORTA)
|
let mut out_2 = pinsa.pa3.into_push_pull_output().delay(true, true);
|
||||||
.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);
|
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
out_0.toggle().unwrap();
|
out_0.toggle().unwrap();
|
||||||
out_1.toggle().unwrap();
|
out_1.toggle().unwrap();
|
||||||
|
@ -7,18 +7,18 @@ use core::fmt::Write;
|
|||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{pac, prelude::*, uart};
|
use va108xx_hal::{gpio::PinsB, pac, prelude::*, uart};
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
rtt_init_print!();
|
rtt_init_print!();
|
||||||
rprintln!("-- VA108xx UART test application--");
|
rprintln!("-- VA108xx UART example application--");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let gpiob = dp.PORTB.split(&mut dp.SYSCONFIG).unwrap();
|
let gpiob = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB);
|
||||||
let tx = gpiob.pb21.into_funsel_1(&mut dp.IOCONFIG);
|
let tx = gpiob.pb21.into_funsel_1();
|
||||||
let rx = gpiob.pb20.into_funsel_1(&mut dp.IOCONFIG);
|
let rx = gpiob.pb20.into_funsel_1();
|
||||||
|
|
||||||
let uartb = uart::Uart::uartb(
|
let uartb = uart::Uart::uartb(
|
||||||
dp.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> {
|
||||||
|
self._read().map(|v| !v)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _is_high(&self) -> Result<bool, PinError> {
|
||||||
|
self._read()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_low(&mut self) -> Result<(), PinError> {
|
||||||
|
self._write(false)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_high(&mut self) -> Result<(), PinError> {
|
||||||
|
self._write(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// 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 {
|
||||||
|
// The `Pin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `DynPin`
|
||||||
|
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> TryFrom<DynPin> for Pin<I, M> {
|
||||||
|
type Error = PinError;
|
||||||
|
|
||||||
|
/// 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, PinError> {
|
||||||
|
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
Ok(unsafe { Self::new() })
|
||||||
|
} else {
|
||||||
|
Err(PinError::InvalidPinType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl OutputPin for DynPin {
|
||||||
|
type Error = PinError;
|
||||||
|
#[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 InputPin for DynPin {
|
||||||
|
type Error = PinError;
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToggleableOutputPin for DynPin {
|
||||||
|
type Error = PinError;
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._toggle()
|
||||||
|
}
|
||||||
|
}
|
30
src/gpio/mod.rs
Normal file
30
src/gpio/mod.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//! # GPIO module
|
||||||
|
//!
|
||||||
|
//! The implementation of this GPIO module is heavily based on the
|
||||||
|
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html).
|
||||||
|
//!
|
||||||
|
//! 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 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.
|
||||||
|
//!
|
||||||
|
//! 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.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/blinky.rs)
|
||||||
|
pub mod dynpins;
|
||||||
|
pub use dynpins::*;
|
||||||
|
|
||||||
|
pub mod pins;
|
||||||
|
pub use pins::*;
|
||||||
|
|
||||||
|
mod reg;
|
821
src/gpio/pins.rs
Normal file
821
src/gpio/pins.rs
Normal file
@ -0,0 +1,821 @@
|
|||||||
|
//! # Type-level module for GPIO pins
|
||||||
|
//!
|
||||||
|
//! 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, B) and pin number. Each
|
||||||
|
//! `PinId` instance is named according to its datasheet identifier, e.g.
|
||||||
|
//! [`PA02`].
|
||||||
|
//!
|
||||||
|
//! 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 [`Pins`]
|
||||||
|
//! struct.
|
||||||
|
//!
|
||||||
|
//! To create the [`PinsA`] or [`PinsB`] struct, users must supply the PAC
|
||||||
|
//! [`PORTA`](crate::pac::PORTA) or [`PORTB`](crate::pac::PORTB)peripheral.
|
||||||
|
//! The struct takes ownership of the port and provides the corresponding pins. Each [`Pin`]
|
||||||
|
//! within the [`PinsA`] or [`PinsB`] struct can be moved out and used individually.
|
||||||
|
//! The pins structure can also consume the [`IOCONFIG`] structure optionally by
|
||||||
|
//! passing it as an option
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
//! let pins = Pins::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA);
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Pins can be converted between modes using several different methods.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! // Use one of the literal function names
|
||||||
|
//! let pa0 = pins.pa0.into_floating_input();
|
||||||
|
//! // Use a generic method and one of the `PinMode` variant types
|
||||||
|
//! let pa0 = pins.pa0.into_mode::<FloatingInput>();
|
||||||
|
//! // Specify the target type and use `From`/`Into`
|
||||||
|
//! let pa0: Pin<PA0, FloatingInput> = pins.pa0.into();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Embedded HAL traits
|
||||||
|
//!
|
||||||
|
//! This module implements all of the embedded HAL GPIO traits for each [`Pin`]
|
||||||
|
//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`],
|
||||||
|
//! [`ToggleableOutputPin`].
|
||||||
|
//!
|
||||||
|
//! For example, you can control the logic level of an `OutputPin` like so
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use atsamd_hal::pac::Peripherals;
|
||||||
|
//! use atsamd_hal::gpio::v2::Pins;
|
||||||
|
//! use embedded_hal::digital::v2::OutputPin;
|
||||||
|
//!
|
||||||
|
//! let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
//! let mut pins = Pins::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA);
|
||||||
|
//! pins.pa0.set_high();
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Type-level features
|
||||||
|
//!
|
||||||
|
//! This module also provides additional, type-level tools to work with GPIO
|
||||||
|
//! pins.
|
||||||
|
//!
|
||||||
|
//! The [`OptionalPinId`] and [`OptionalPin`] traits use the [`OptionalKind`]
|
||||||
|
//! pattern to act as type-level versions of [`Option`] for `PinId` and `Pin`
|
||||||
|
//! respectively. And the [`AnyPin`] trait defines an [`AnyKind`] type class
|
||||||
|
//! for all `Pin` types.
|
||||||
|
//!
|
||||||
|
//! [type classes]: crate::typelevel#type-classes
|
||||||
|
//! [type-level enum]: crate::typelevel#type-level-enum
|
||||||
|
//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
|
||||||
|
//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
|
||||||
|
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
|
||||||
|
use super::reg::RegisterInterface;
|
||||||
|
use crate::pac::{IOCONFIG, PORTA, PORTB, SYSCONFIG};
|
||||||
|
use crate::typelevel::Is;
|
||||||
|
use crate::Sealed;
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
|
||||||
|
use paste::paste;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors and Definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
||||||
|
pub enum PinError {
|
||||||
|
/// The pin did not have the correct ID or mode for the requested operation
|
||||||
|
InvalidPinType,
|
||||||
|
InputDisabledForOutput,
|
||||||
|
IsMasked,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Input configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for input configurations
|
||||||
|
///
|
||||||
|
/// The valid options are [`Floating`], [`PullDown`] and [`PullUp`].
|
||||||
|
pub trait InputConfig: Sealed {
|
||||||
|
/// Corresponding [`DynInput`](super::DynInput)
|
||||||
|
const DYN: DynInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Floating {}
|
||||||
|
pub enum PullDown {}
|
||||||
|
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`]
|
||||||
|
pub struct Input<C: InputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> Sealed for Input<C> {}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Output configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait OutputConfig: Sealed {
|
||||||
|
const DYN: DynOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||||
|
pub enum PushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||||
|
pub enum OpenDrain {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||||
|
pub enum ReadablePushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||||
|
pub enum ReadableOpenDrain {}
|
||||||
|
|
||||||
|
impl Sealed for PushPull {}
|
||||||
|
impl Sealed for OpenDrain {}
|
||||||
|
impl Sealed for ReadableOpenDrain {}
|
||||||
|
impl Sealed 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 two output configurations: [`PushPull`] or [`Readable`]
|
||||||
|
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::Funsel1;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel2 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Funsel2;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel3 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Funsel3;
|
||||||
|
}
|
||||||
|
|
||||||
|
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`](super::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`](super::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]
|
||||||
|
pub enum $Id {}
|
||||||
|
impl Sealed for $Id {}
|
||||||
|
impl PinId for $Id {
|
||||||
|
const DYN: DynPinId = DynPinId {
|
||||||
|
group: DynGroup::$Group,
|
||||||
|
num: $NUM,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
|
||||||
|
|
||||||
|
pub struct Pin<I: PinId, M: PinMode> {
|
||||||
|
pub(in crate::gpio) regs: Registers<I>,
|
||||||
|
mode: PhantomData<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> Sealed for Pin<I, M> {}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {
|
||||||
|
type Id = I;
|
||||||
|
type Mode = 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) unsafe fn new() -> Pin<I, M> {
|
||||||
|
Pin {
|
||||||
|
regs: Registers::new(),
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.regs.change_mode::<N>();
|
||||||
|
}
|
||||||
|
// 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 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_high(&mut self) {
|
||||||
|
self.regs.write_pin(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_low(&mut self) {
|
||||||
|
self.regs.write_pin(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _toggle(&mut self) {
|
||||||
|
self.regs.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
|
!self.regs.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
|
self.regs.read_pin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// 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.
|
||||||
|
pub trait AnyPin: Is<Type = SpecificPin<Self>> {
|
||||||
|
type Id: PinId;
|
||||||
|
type Mode: PinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> AsRef<Self> for Pin<I, M> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> AsMut<Self> for Pin<I, M> {
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> &mut Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Additional functionality
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
|
/// 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) -> Self {
|
||||||
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> Self {
|
||||||
|
self.regs.pulse_mode(enable, default_state);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||||
|
self.regs.filter_type(filter, clksel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[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: PinId, C: OutputConfig> ToggleableOutputPin for Pin<I, Output<C>> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._toggle();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> InputPin for Pin<I, Input<C>> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> InputPin for Pin<I, OutputReadableOpenDrain> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> InputPin for Pin<I, OutputReadablePushPull> {
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Registers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`Pin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||||
|
/// access the corresponding regsiters.
|
||||||
|
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
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pins {
|
||||||
|
(
|
||||||
|
$Port:ident, $PinsName:ident, $($Id:ident,)+,
|
||||||
|
) => {
|
||||||
|
paste!(
|
||||||
|
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||||
|
pub struct $PinsName {
|
||||||
|
iocfg: Option<IOCONFIG>,
|
||||||
|
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 SYSCONFIG,
|
||||||
|
iocfg: Option<IOCONFIG>,
|
||||||
|
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) -> (Option<IOCONFIG>, $Port) {
|
||||||
|
(self.iocfg, 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),
|
||||||
|
]
|
||||||
|
);
|
369
src/gpio/reg.rs
Normal file
369
src/gpio/reg.rs
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode};
|
||||||
|
use super::pins::{FilterClkSel, FilterType, PinError, PinState};
|
||||||
|
use va108xx::{ioconfig, porta, IOCONFIG, PORTA, PORTB};
|
||||||
|
|
||||||
|
/// Type definition to avoid confusion: These register blocks are identical
|
||||||
|
type PortRegisterBlock = porta::RegisterBlock;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// 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();
|
||||||
|
use DynPinMode::*;
|
||||||
|
match mode {
|
||||||
|
Input(config) => {
|
||||||
|
use dynpins::DynInput::*;
|
||||||
|
fields.dir = false;
|
||||||
|
match config {
|
||||||
|
Floating => {
|
||||||
|
fields.pull_en = false;
|
||||||
|
}
|
||||||
|
PullUp => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = true;
|
||||||
|
}
|
||||||
|
PullDown => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Output(config) => {
|
||||||
|
use dynpins::DynOutput::*;
|
||||||
|
fields.dir = true;
|
||||||
|
match config {
|
||||||
|
PushPull => {
|
||||||
|
fields.opendrn = false;
|
||||||
|
}
|
||||||
|
OpenDrain => {
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadableOpenDrain => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadablePushPull => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Alternate(config) => {
|
||||||
|
use dynpins::DynAlternate::*;
|
||||||
|
match config {
|
||||||
|
Funsel1 => {
|
||||||
|
fields.funsel = 1;
|
||||||
|
}
|
||||||
|
Funsel2 => {
|
||||||
|
fields.funsel = 2;
|
||||||
|
}
|
||||||
|
Funsel3 => {
|
||||||
|
fields.funsel = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//==================================================================================================
|
||||||
|
// Register Interface
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub type IocfgPort = ioconfig::PORTA;
|
||||||
|
#[repr(C)]
|
||||||
|
pub(super) struct IOCFG_PORT_GROUP {
|
||||||
|
port: [IocfgPort; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a safe register interface for pin objects
|
||||||
|
///
|
||||||
|
/// [`PORTA`] and [`PORTB`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
|
||||||
|
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
|
||||||
|
/// interface is quite restrictive. Instead, it would be ideal if we could split
|
||||||
|
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
|
||||||
|
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
|
||||||
|
/// `struct`s for every pin, where each pin is only allowed to control its own
|
||||||
|
/// registers. Furthermore, each pin `struct` should be a singleton, so that
|
||||||
|
/// exclusive access to the `struct` also guarantees exclusive access to the
|
||||||
|
/// corresponding registers. Finally, the pin `struct`s should not have any
|
||||||
|
/// interior mutability. Together, these requirements would allow the pin
|
||||||
|
/// `struct`s to be both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// This trait creates a safe API for accomplishing these goals. Implementers
|
||||||
|
/// supply a pin ID through the [`id`] function. The remaining functions provide
|
||||||
|
/// a safe API for accessing the registers associated with that pin ID. Any
|
||||||
|
/// modification of the registers requires `&mut self`, which destroys interior
|
||||||
|
/// mutability.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
///
|
||||||
|
/// [`id`]: Self::id
|
||||||
|
pub(super) unsafe trait RegisterInterface {
|
||||||
|
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
||||||
|
/// this type.
|
||||||
|
fn id(&self) -> DynPinId;
|
||||||
|
|
||||||
|
const PORTA: *const PortRegisterBlock = PORTA::ptr();
|
||||||
|
const PORTB: *const PortRegisterBlock = PORTB::ptr();
|
||||||
|
const PORT_CFG: *const IOCFG_PORT_GROUP = IOCONFIG::ptr() as *const _;
|
||||||
|
|
||||||
|
/// Change the pin mode
|
||||||
|
#[inline]
|
||||||
|
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.port[self.id().num as usize].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));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => unsafe { &(*Self::PORTA) },
|
||||||
|
DynGroup::B => unsafe { &(*Self::PORTB) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn iocfg_port(&self) -> &IOCFG_PORT_GROUP {
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => unsafe { &*Self::PORT_CFG },
|
||||||
|
DynGroup::B => unsafe { &*Self::PORT_CFG.add(1) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_input_for_output(&self, enable: bool) {
|
||||||
|
let iocfg = self.iocfg_port();
|
||||||
|
iocfg.port[self.id().num as usize].modify(|_, w| w.iewo().bit(enable))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the direction of a pin
|
||||||
|
#[inline]
|
||||||
|
fn set_dir(&mut self, bit: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
let mask = self.mask_32();
|
||||||
|
// Safety: Only the bit for this Pin ID is modified
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_perid(&self) -> u32 {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
portreg.perid.read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the logic level of an output pin
|
||||||
|
fn read_pin(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DATAMASK bit for this particular pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn datamask(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
(portreg.datamask().read().bits() >> self.id().num) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
fn read_pin_masked(&self) -> Result<bool, PinError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(PinError::IsMasked)
|
||||||
|
} else {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
Ok(((portreg.datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin
|
||||||
|
#[inline]
|
||||||
|
fn write_pin(&mut self, bit: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
let mask = self.mask_32();
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
portreg.setout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<(), PinError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(PinError::IsMasked)
|
||||||
|
} else {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
let mask = self.mask_32();
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
portreg.setout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Toggle the logic level of an output pin
|
||||||
|
#[inline]
|
||||||
|
fn toggle(&mut self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
let mask = self.mask_32();
|
||||||
|
// Safety: TOGOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe { portreg.togout().write(|w| w.bits(mask)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for input pins
|
||||||
|
#[inline]
|
||||||
|
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
let iocfg = self.iocfg_port();
|
||||||
|
iocfg.port[self.id().num as usize].modify(|_, w| {
|
||||||
|
// Safety: Only write to register for this Pin ID
|
||||||
|
unsafe {
|
||||||
|
w.flttype().bits(filter as u8);
|
||||||
|
w.fltclk().bits(clksel as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||||
|
/// state of the bit and allows access of the corresponding bit
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||||
|
/// of the corresponding bit for output and input operations
|
||||||
|
#[inline(always)]
|
||||||
|
fn clear_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
/// 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
|
||||||
|
fn pulse_mode(&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
|
||||||
|
fn delay(&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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,15 @@ pub mod gpio;
|
|||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
pub mod typelevel;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
|
||||||
pub use va108xx as pac;
|
pub use va108xx as pac;
|
||||||
|
|
||||||
mod sealed {
|
mod private {
|
||||||
|
/// Super trait used to mark traits with an exhaustive set of
|
||||||
|
/// implementations
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) use private::Sealed;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! Prelude
|
//! Prelude
|
||||||
pub use embedded_hal::prelude::*;
|
pub use embedded_hal::prelude::*;
|
||||||
|
|
||||||
pub use crate::gpio::GpioExt as _va108xx_hal_gpio_GpioExt;
|
// embedded-hal doesn’t yet have v2 in its prelude, so we need to
|
||||||
|
// export it ourselves
|
||||||
pub use embedded_hal::digital::v2::InputPin as _embedded_hal_gpio_InputPin;
|
pub use embedded_hal::digital::v2::InputPin as _embedded_hal_gpio_InputPin;
|
||||||
pub use embedded_hal::digital::v2::OutputPin as _embedded_hal_gpio_OutputPin;
|
pub use embedded_hal::digital::v2::OutputPin as _embedded_hal_gpio_OutputPin;
|
||||||
pub use embedded_hal::digital::v2::StatefulOutputPin as _embedded_hal_gpio_StatefulOutputPin;
|
pub use embedded_hal::digital::v2::StatefulOutputPin as _embedded_hal_gpio_StatefulOutputPin;
|
||||||
|
60
src/typelevel.rs
Normal file
60
src/typelevel.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
///! # Module support type-level programming
|
||||||
|
///
|
||||||
|
/// This module is more or less taken from the
|
||||||
|
/// [ATSAMD HAL](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html).
|
||||||
|
/// You can find a thorough explanation of the patterns used there.
|
||||||
|
use crate::Sealed;
|
||||||
|
|
||||||
|
/// Type-level version of the [None] variant
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NoneT;
|
||||||
|
impl Sealed for NoneT {}
|
||||||
|
|
||||||
|
/// Marker trait for type identity
|
||||||
|
///
|
||||||
|
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
|
||||||
|
/// the concept of type identity, because all implementors have
|
||||||
|
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
|
||||||
|
/// type, it guarantees that the corresponding type parameter is exactly the
|
||||||
|
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||||
|
/// the following example.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// where T: Is<Type = Specific>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||||
|
/// type `T` can be converted into the `Specific` type.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn example<T>(mut any: T)
|
||||||
|
/// where
|
||||||
|
/// T: Is<Type = Specific>,
|
||||||
|
/// {
|
||||||
|
/// let specific_mut: &mut Specific = any.as_mut();
|
||||||
|
/// let specific_ref: &Specific = any.as_ref();
|
||||||
|
/// let specific: Specific = any.into();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: #anykind-trait-pattern
|
||||||
|
pub trait Is
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<IsType<Self>>,
|
||||||
|
Self: Into<IsType<Self>>,
|
||||||
|
Self: AsRef<IsType<Self>>,
|
||||||
|
Self: AsMut<IsType<Self>>,
|
||||||
|
{
|
||||||
|
type Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for [`Is::Type`]
|
||||||
|
pub type IsType<T> = <T as Is>::Type;
|
||||||
|
|
||||||
|
impl<T> Is for T
|
||||||
|
where
|
||||||
|
T: Sealed + AsRef<T> + AsMut<T>,
|
||||||
|
{
|
||||||
|
type Type = T;
|
||||||
|
}
|
29
src/uart.rs
29
src/uart.rs
@ -6,9 +6,10 @@ use libm::floorf;
|
|||||||
use crate::clock::enable_peripheral_clock;
|
use crate::clock::enable_peripheral_clock;
|
||||||
use crate::{
|
use crate::{
|
||||||
clock,
|
clock,
|
||||||
gpio::porta::{PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3, PA30, PA31, PA8, PA9},
|
gpio::pins::{
|
||||||
gpio::portb::{PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9},
|
Alternate, Funsel1, Funsel2, Funsel3, Pin, PA16, PA17, PA18, PA19, PA2, PA26, PA27, PA3,
|
||||||
gpio::{AltFunc, FUNSEL1, FUNSEL2, FUNSEL3},
|
PA30, PA31, PA8, PA9, PB18, PB19, PB20, PB21, PB22, PB23, PB6, PB7, PB8, PB9,
|
||||||
|
},
|
||||||
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
|
pac::{uarta as uart_base, SYSCONFIG, UARTA, UARTB},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
time::{Bps, Hertz},
|
time::{Bps, Hertz},
|
||||||
@ -18,20 +19,20 @@ use embedded_hal::{blocking, serial};
|
|||||||
|
|
||||||
pub trait Pins<UART> {}
|
pub trait Pins<UART> {}
|
||||||
|
|
||||||
impl Pins<UARTA> for (PA9<AltFunc<FUNSEL2>>, PA8<AltFunc<FUNSEL2>>) {}
|
impl Pins<UARTA> for (Pin<PA9, Alternate<Funsel2>>, Pin<PA8, Alternate<Funsel2>>) {}
|
||||||
impl Pins<UARTA> for (PA17<AltFunc<FUNSEL3>>, PA16<AltFunc<FUNSEL3>>) {}
|
impl Pins<UARTA> for (Pin<PA17, Alternate<Funsel3>>, Pin<PA16, Alternate<Funsel3>>) {}
|
||||||
impl Pins<UARTA> for (PA31<AltFunc<FUNSEL3>>, PA30<AltFunc<FUNSEL3>>) {}
|
impl Pins<UARTA> for (Pin<PA31, Alternate<Funsel3>>, Pin<PA30, Alternate<Funsel3>>) {}
|
||||||
|
|
||||||
impl Pins<UARTA> for (PB9<AltFunc<FUNSEL1>>, PB8<AltFunc<FUNSEL1>>) {}
|
impl Pins<UARTA> for (Pin<PB9, Alternate<Funsel1>>, Pin<PB8, Alternate<Funsel1>>) {}
|
||||||
impl Pins<UARTA> for (PB23<AltFunc<FUNSEL1>>, PB22<AltFunc<FUNSEL1>>) {}
|
impl Pins<UARTA> for (Pin<PB23, Alternate<Funsel1>>, Pin<PB22, Alternate<Funsel1>>) {}
|
||||||
|
|
||||||
impl Pins<UARTB> for (PA3<AltFunc<FUNSEL2>>, PA2<AltFunc<FUNSEL2>>) {}
|
impl Pins<UARTB> for (Pin<PA3, Alternate<Funsel2>>, Pin<PA2, Alternate<Funsel2>>) {}
|
||||||
impl Pins<UARTB> for (PA19<AltFunc<FUNSEL3>>, PA18<AltFunc<FUNSEL3>>) {}
|
impl Pins<UARTB> for (Pin<PA19, Alternate<Funsel3>>, Pin<PA18, Alternate<Funsel3>>) {}
|
||||||
impl Pins<UARTB> for (PA27<AltFunc<FUNSEL3>>, PA26<AltFunc<FUNSEL3>>) {}
|
impl Pins<UARTB> for (Pin<PA27, Alternate<Funsel3>>, Pin<PA26, Alternate<Funsel3>>) {}
|
||||||
|
|
||||||
impl Pins<UARTB> for (PB7<AltFunc<FUNSEL1>>, PB6<AltFunc<FUNSEL1>>) {}
|
impl Pins<UARTB> for (Pin<PB7, Alternate<Funsel1>>, Pin<PB6, Alternate<Funsel1>>) {}
|
||||||
impl Pins<UARTB> for (PB19<AltFunc<FUNSEL2>>, PB18<AltFunc<FUNSEL2>>) {}
|
impl Pins<UARTB> for (Pin<PB19, Alternate<Funsel2>>, Pin<PB18, Alternate<Funsel2>>) {}
|
||||||
impl Pins<UARTB> for (PB21<AltFunc<FUNSEL1>>, PB20<AltFunc<FUNSEL1>>) {}
|
impl Pins<UARTB> for (Pin<PB21, Alternate<Funsel1>>, Pin<PB20, Alternate<Funsel1>>) {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
Reference in New Issue
Block a user