rp/gpio: add optional pins

This commit is contained in:
Dario Nieuwenhuis 2021-06-25 18:17:59 +02:00
parent da014afb89
commit 749f4838d5
2 changed files with 87 additions and 21 deletions

View File

@ -223,9 +223,11 @@ pub(crate) mod sealed {
SIO.gpio_in(self.bank() as _)
}
}
pub trait OptionalPin {}
}
pub trait Pin: sealed::Pin {
pub trait Pin: Unborrow<Target = Self> + sealed::Pin {
/// Degrade to a generic pin struct
fn degrade(self) -> AnyPin {
AnyPin {
@ -245,6 +247,56 @@ impl sealed::Pin for AnyPin {
}
}
// ==========================
pub trait OptionalPin: Unborrow<Target = Self> + sealed::OptionalPin + Sized {
type Pin: Pin;
fn pin(&self) -> Option<&Self::Pin>;
fn pin_mut(&mut self) -> Option<&mut Self::Pin>;
/// Convert from concrete pin type PIN_XX to type erased `Option<AnyPin>`.
#[inline]
fn degrade_optional(mut self) -> Option<AnyPin> {
self.pin_mut()
.map(|pin| unsafe { core::ptr::read(pin) }.degrade())
}
}
impl<T: Pin> sealed::OptionalPin for T {}
impl<T: Pin> OptionalPin for T {
type Pin = T;
#[inline]
fn pin(&self) -> Option<&T> {
Some(self)
}
#[inline]
fn pin_mut(&mut self) -> Option<&mut T> {
Some(self)
}
}
#[derive(Clone, Copy, Debug)]
pub struct NoPin;
unsafe_impl_unborrow!(NoPin);
impl sealed::OptionalPin for NoPin {}
impl OptionalPin for NoPin {
type Pin = AnyPin;
#[inline]
fn pin(&self) -> Option<&AnyPin> {
None
}
#[inline]
fn pin_mut(&mut self) -> Option<&mut AnyPin> {
None
}
}
// ==========================
macro_rules! impl_pin {
($name:ident, $bank:expr, $pin_num:expr) => {
impl Pin for peripherals::$name {}

View File

@ -3,9 +3,10 @@ use core::marker::PhantomData;
use embassy::util::Unborrow;
use embassy_extras::unborrow;
use embedded_hal::blocking::spi as eh;
use gpio::Pin;
use crate::{gpio, pac, peripherals};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{NoPin, OptionalPin};
use crate::{pac, peripherals};
#[non_exhaustive]
pub struct Config {
@ -31,9 +32,10 @@ impl<'d, T: Instance> Spi<'d, T> {
clk: impl Unborrow<Target = impl ClkPin<T>>,
mosi: impl Unborrow<Target = impl MosiPin<T>>,
miso: impl Unborrow<Target = impl MisoPin<T>>,
cs: impl Unborrow<Target = impl CsPin<T>>,
config: Config,
) -> Self {
unborrow!(inner, clk, mosi, miso);
unborrow!(inner, clk, mosi, miso, cs);
unsafe {
let p = inner.regs();
@ -41,6 +43,8 @@ impl<'d, T: Instance> Spi<'d, T> {
let clk_peri = crate::clocks::clk_peri_freq();
assert!(config.frequency <= clk_peri);
// TODO replace these trial-and-error loops with decent calculations.
// Find smallest prescale value which puts output frequency in range of
// post-divide. Prescale is an even number from 2 to 254 inclusive.
let presc = (2u32..=254).step_by(2).find(|&presc| {
@ -52,7 +56,8 @@ impl<'d, T: Instance> Spi<'d, T> {
// Find largest post-divide which makes output <= baudrate. Post-divide is
// an integer in the range 1 to 256 inclusive.
let postdiv = (1u32..=256)
// TODO figure what's up with postdiv=1, it is dividing by 0. Iterate down to 2 for now.
let postdiv = (2u32..=256)
.rev()
.find(|&postdiv| clk_peri / (presc * (postdiv - 1)) > config.frequency);
let postdiv = unwrap!(postdiv);
@ -70,9 +75,18 @@ impl<'d, T: Instance> Spi<'d, T> {
info!("SPI freq: {=u32}", clk_peri / (presc * postdiv));
clk.io().ctrl().write(|w| w.set_funcsel(1));
mosi.io().ctrl().write(|w| w.set_funcsel(1));
miso.io().ctrl().write(|w| w.set_funcsel(1));
if let Some(pin) = clk.pin_mut() {
pin.io().ctrl().write(|w| w.set_funcsel(1));
}
if let Some(pin) = mosi.pin_mut() {
pin.io().ctrl().write(|w| w.set_funcsel(1));
}
if let Some(pin) = miso.pin_mut() {
pin.io().ctrl().write(|w| w.set_funcsel(1));
}
if let Some(pin) = cs.pin_mut() {
pin.io().ctrl().write(|w| w.set_funcsel(1));
}
}
Self {
inner,
@ -97,15 +111,6 @@ impl<'d, T: Instance> Spi<'d, T> {
while p.sr().read().bsy() {}
}
}
fn drain_rx(&mut self) {
unsafe {
let p = self.inner.regs();
while !p.sr().read().rne() {
p.dr().read();
}
}
}
}
impl<'d, T: Instance> eh::Write<u8> for Spi<'d, T> {
@ -145,10 +150,19 @@ macro_rules! impl_instance {
impl_instance!(SPI0, Spi0);
impl_instance!(SPI1, Spi1);
pub trait ClkPin<T: Instance>: sealed::ClkPin<T> + Pin {}
pub trait CsPin<T: Instance>: sealed::CsPin<T> + Pin {}
pub trait MosiPin<T: Instance>: sealed::MosiPin<T> + Pin {}
pub trait MisoPin<T: Instance>: sealed::MisoPin<T> + Pin {}
pub trait ClkPin<T: Instance>: sealed::ClkPin<T> + OptionalPin {}
pub trait CsPin<T: Instance>: sealed::CsPin<T> + OptionalPin {}
pub trait MosiPin<T: Instance>: sealed::MosiPin<T> + OptionalPin {}
pub trait MisoPin<T: Instance>: sealed::MisoPin<T> + OptionalPin {}
impl<T: Instance> sealed::ClkPin<T> for NoPin {}
impl<T: Instance> ClkPin<T> for NoPin {}
impl<T: Instance> sealed::CsPin<T> for NoPin {}
impl<T: Instance> CsPin<T> for NoPin {}
impl<T: Instance> sealed::MosiPin<T> for NoPin {}
impl<T: Instance> MosiPin<T> for NoPin {}
impl<T: Instance> sealed::MisoPin<T> for NoPin {}
impl<T: Instance> MisoPin<T> for NoPin {}
macro_rules! impl_pin {
($pin:ident, $instance:ident, $function:ident) => {