From 021efc5a5a27dedf0e51b7f6cdf2afb6080a643b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 8 Nov 2021 01:01:44 +0100 Subject: [PATCH] Update GPIO code --- src/gpio.rs | 156 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 135 insertions(+), 21 deletions(-) diff --git a/src/gpio.rs b/src/gpio.rs index 27e7b0b..f84834e 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -1,4 +1,5 @@ use crate::pac::SYSCONFIG; +use cortex_m::singleton; use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; @@ -10,18 +11,30 @@ pub trait GpioExt { type Parts; /// Splits the GPIO block into independent pins and registers. - fn split(&mut self, syscfg: &mut SYSCONFIG) -> Self::Parts; + fn split(&mut self, syscfg: &mut SYSCONFIG) -> Option; } #[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: char) -> Result; - fn input_enabled_for_output(&self, pos: usize, port_id: char) -> bool; + fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result; + 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); @@ -85,7 +98,7 @@ pub enum FilterClkSel { /// Fully erased pin pub struct Pin { i: u8, - port_id: char, + port_id: PortId, port: *const dyn GpioRegExt, _mode: PhantomData, } @@ -98,7 +111,7 @@ impl StatefulOutputPin for Pin> { } #[inline(always)] fn is_set_low(&self) -> Result { - unsafe { (*self.port).is_set_low(self.i.into(), self.port_id) } + unsafe { (*self.port).is_set_low(self.i.into(), &self.port_id) } } } impl OutputPin for Pin> { @@ -155,7 +168,7 @@ impl GpioRegExt for crate::pac::porta::RegisterBlock { self.datainraw().read().bits() & (1 << pos) == 0 } #[inline(always)] - fn is_set_low(&self, pos: usize, port_id: char) -> Result { + fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result { if self.input_enabled_for_output(pos, port_id) { Ok(self.datainraw().read().bits() & (1 << pos) == 0) } else { @@ -175,20 +188,19 @@ impl GpioRegExt for crate::pac::porta::RegisterBlock { unsafe { self.togout().write(|w| w.bits(1 << pos)) } } #[inline(always)] - fn input_enabled_for_output(&self, pos: usize, port_id: char) -> bool { + fn input_enabled_for_output(&self, pos: usize, port_id: &PortId) -> bool { unsafe { let iocfg = &(*IOCONFIG::ptr()); match port_id { - 'A' => iocfg.porta[pos].read().iewo().bit_is_set(), - 'B' => iocfg.portb[pos].read().iewo().bit_is_set(), - _ => iocfg.porta[pos].read().iewo().bit_is_set(), + 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:expr, [ + ($PORTX:ident, $portx:ident, $port_id:path, [ $($PXi:ident: ($pxi:ident, $i:expr),)+ ]) => { pub mod $portx { @@ -197,7 +209,7 @@ macro_rules! gpio { use super::{ FUNSEL1, FUNSEL2, FUNSEL3, Floating, Funsel, GpioExt, Input, OpenDrain, PullUp, Output, FilterType, FilterClkSel, Pin, GpioRegExt, PushPull, - PinModeError + PinModeError, PinState, PortId, singleton }; use crate::{pac::$PORTX, pac::SYSCONFIG, pac::IOCONFIG}; use embedded_hal::digital::v2::{ @@ -209,20 +221,28 @@ macro_rules! gpio { )+ } + pub fn get_perid(port: &$PORTX) -> u32 { + port.perid.read().bits() + } + impl GpioExt for $PORTX { type Parts = Parts; - fn split(&mut self, syscfg: &mut SYSCONFIG) -> 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 { + 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 }); - Parts { + Some(Parts { $( $pxi: $PXi { _mode : PhantomData }, )+ - } + }) } } @@ -252,8 +272,34 @@ macro_rules! gpio { _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 + self, iocfg: &mut IOCONFIG, port: &mut $PORTX ) -> $PXi> { unsafe { iocfg.$portx[$i].modify(|_, w| { @@ -261,8 +307,7 @@ macro_rules! gpio { w.pen().clear_bit(); w.opendrn().clear_bit() }); - let port_reg = &(*$PORTX::ptr()); - port_reg.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i))); + port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i))); } $PXi { _mode: PhantomData } } @@ -320,6 +365,7 @@ macro_rules! gpio { 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 } } @@ -346,6 +392,22 @@ macro_rules! gpio { } self } + + /// This function takes account for the data mask register + #[inline(always)] + pub fn is_high_masked(&self, port: &$PORTX) -> Result { + 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 { + if ((port.datamask().read().bits() >> $i) & 1) == 0 { + Err(PinModeError::IsMasked) + } else { + Ok(port.datain().read().bits() & (1 << $i) == 0) + } + } } impl $PXi> { @@ -422,6 +484,58 @@ macro_rules! gpio { _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 StatefulOutputPin for $PXi> { @@ -432,7 +546,7 @@ macro_rules! gpio { #[inline(always)] fn is_set_low(&self) -> Result { unsafe { - (*$PORTX::ptr()).is_set_low($i, $port_id) + (*$PORTX::ptr()).is_set_low($i, &$port_id) } } } @@ -484,7 +598,7 @@ macro_rules! gpio { } } -gpio!(PORTA, porta, 'A', [ +gpio!(PORTA, porta, PortId::A, [ PA0: (pa0, 0), PA1: (pa1, 1), PA2: (pa2, 2), @@ -519,7 +633,7 @@ gpio!(PORTA, porta, 'A', [ PA31: (pa31, 31), ]); -gpio!(PORTB, portb, 'B', [ +gpio!(PORTB, portb, PortId::B, [ PB0: (pb0, 0), PB1: (pb1, 1), PB2: (pb2, 2),