Update GPIO code
This commit is contained in:
parent
a463332d58
commit
021efc5a5a
156
src/gpio.rs
156
src/gpio.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::pac::SYSCONFIG;
|
use crate::pac::SYSCONFIG;
|
||||||
|
use cortex_m::singleton;
|
||||||
use core::convert::Infallible;
|
use core::convert::Infallible;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||||
|
@ -10,18 +11,30 @@ pub trait GpioExt {
|
||||||
type Parts;
|
type Parts;
|
||||||
|
|
||||||
/// Splits the GPIO block into independent pins and registers.
|
/// 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<Self::Parts>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum PinModeError {
|
pub enum PinModeError {
|
||||||
InputDisabledForOutput,
|
InputDisabledForOutput,
|
||||||
|
IsMasked,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PortId {
|
||||||
|
A,
|
||||||
|
B
|
||||||
}
|
}
|
||||||
|
|
||||||
trait GpioRegExt {
|
trait GpioRegExt {
|
||||||
fn is_low(&self, pos: u8) -> bool;
|
fn is_low(&self, pos: u8) -> bool;
|
||||||
fn is_set_low(&self, pos: usize, port_id: char) -> Result<bool, PinModeError>;
|
fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result<bool, PinModeError>;
|
||||||
fn input_enabled_for_output(&self, pos: usize, port_id: char) -> bool;
|
fn input_enabled_for_output(&self, pos: usize, port_id: &PortId) -> bool;
|
||||||
fn set_high(&self, pos: u8);
|
fn set_high(&self, pos: u8);
|
||||||
fn set_low(&self, pos: u8);
|
fn set_low(&self, pos: u8);
|
||||||
fn toggle(&self, pos: u8);
|
fn toggle(&self, pos: u8);
|
||||||
|
@ -85,7 +98,7 @@ pub enum FilterClkSel {
|
||||||
/// Fully erased pin
|
/// Fully erased pin
|
||||||
pub struct Pin<MODE> {
|
pub struct Pin<MODE> {
|
||||||
i: u8,
|
i: u8,
|
||||||
port_id: char,
|
port_id: PortId,
|
||||||
port: *const dyn GpioRegExt,
|
port: *const dyn GpioRegExt,
|
||||||
_mode: PhantomData<MODE>,
|
_mode: PhantomData<MODE>,
|
||||||
}
|
}
|
||||||
|
@ -98,7 +111,7 @@ impl<MODE> StatefulOutputPin for Pin<Output<MODE>> {
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
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<MODE> OutputPin for Pin<Output<MODE>> {
|
impl<MODE> OutputPin for Pin<Output<MODE>> {
|
||||||
|
@ -155,7 +168,7 @@ impl GpioRegExt for crate::pac::porta::RegisterBlock {
|
||||||
self.datainraw().read().bits() & (1 << pos) == 0
|
self.datainraw().read().bits() & (1 << pos) == 0
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_set_low(&self, pos: usize, port_id: char) -> Result<bool, PinModeError> {
|
fn is_set_low(&self, pos: usize, port_id: &PortId) -> Result<bool, PinModeError> {
|
||||||
if self.input_enabled_for_output(pos, port_id) {
|
if self.input_enabled_for_output(pos, port_id) {
|
||||||
Ok(self.datainraw().read().bits() & (1 << pos) == 0)
|
Ok(self.datainraw().read().bits() & (1 << pos) == 0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -175,20 +188,19 @@ impl GpioRegExt for crate::pac::porta::RegisterBlock {
|
||||||
unsafe { self.togout().write(|w| w.bits(1 << pos)) }
|
unsafe { self.togout().write(|w| w.bits(1 << pos)) }
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[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 {
|
unsafe {
|
||||||
let iocfg = &(*IOCONFIG::ptr());
|
let iocfg = &(*IOCONFIG::ptr());
|
||||||
match port_id {
|
match port_id {
|
||||||
'A' => iocfg.porta[pos].read().iewo().bit_is_set(),
|
PortId::A => iocfg.porta[pos].read().iewo().bit_is_set(),
|
||||||
'B' => iocfg.portb[pos].read().iewo().bit_is_set(),
|
PortId::B => iocfg.portb[pos].read().iewo().bit_is_set(),
|
||||||
_ => iocfg.porta[pos].read().iewo().bit_is_set(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! gpio {
|
macro_rules! gpio {
|
||||||
($PORTX:ident, $portx:ident, $port_id:expr, [
|
($PORTX:ident, $portx:ident, $port_id:path, [
|
||||||
$($PXi:ident: ($pxi:ident, $i:expr),)+
|
$($PXi:ident: ($pxi:ident, $i:expr),)+
|
||||||
]) => {
|
]) => {
|
||||||
pub mod $portx {
|
pub mod $portx {
|
||||||
|
@ -197,7 +209,7 @@ macro_rules! gpio {
|
||||||
use super::{
|
use super::{
|
||||||
FUNSEL1, FUNSEL2, FUNSEL3, Floating, Funsel, GpioExt, Input, OpenDrain,
|
FUNSEL1, FUNSEL2, FUNSEL3, Floating, Funsel, GpioExt, Input, OpenDrain,
|
||||||
PullUp, Output, FilterType, FilterClkSel, Pin, GpioRegExt, PushPull,
|
PullUp, Output, FilterType, FilterClkSel, Pin, GpioRegExt, PushPull,
|
||||||
PinModeError
|
PinModeError, PinState, PortId, singleton
|
||||||
};
|
};
|
||||||
use crate::{pac::$PORTX, pac::SYSCONFIG, pac::IOCONFIG};
|
use crate::{pac::$PORTX, pac::SYSCONFIG, pac::IOCONFIG};
|
||||||
use embedded_hal::digital::v2::{
|
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 {
|
impl GpioExt for $PORTX {
|
||||||
type Parts = Parts;
|
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<Parts> {
|
||||||
|
let _: &'static mut bool = singleton!(: bool = false)?;
|
||||||
syscfg.peripheral_clk_enable.modify(|_, w| {
|
syscfg.peripheral_clk_enable.modify(|_, w| {
|
||||||
w.$portx().set_bit();
|
w.$portx().set_bit();
|
||||||
w.gpio().set_bit();
|
w.gpio().set_bit();
|
||||||
w.ioconfig().set_bit();
|
w.ioconfig().set_bit();
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
Parts {
|
Some(Parts {
|
||||||
$(
|
$(
|
||||||
$pxi: $PXi { _mode : PhantomData },
|
$pxi: $PXi { _mode : PhantomData },
|
||||||
)+
|
)+
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,8 +272,34 @@ macro_rules! gpio {
|
||||||
_set_alternate_mode(iocfg, $i, 3);
|
_set_alternate_mode(iocfg, $i, 3);
|
||||||
$PXi { _mode: PhantomData }
|
$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(
|
pub fn into_floating_input(
|
||||||
self, iocfg: &mut IOCONFIG
|
self, iocfg: &mut IOCONFIG, port: &mut $PORTX
|
||||||
) -> $PXi<Input<Floating>> {
|
) -> $PXi<Input<Floating>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
iocfg.$portx[$i].modify(|_, w| {
|
iocfg.$portx[$i].modify(|_, w| {
|
||||||
|
@ -261,8 +307,7 @@ macro_rules! gpio {
|
||||||
w.pen().clear_bit();
|
w.pen().clear_bit();
|
||||||
w.opendrn().clear_bit()
|
w.opendrn().clear_bit()
|
||||||
});
|
});
|
||||||
let port_reg = &(*$PORTX::ptr());
|
port.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
||||||
port_reg.dir().modify(|r,w| w.bits(r.bits() & !(1 << $i)));
|
|
||||||
}
|
}
|
||||||
$PXi { _mode: PhantomData }
|
$PXi { _mode: PhantomData }
|
||||||
}
|
}
|
||||||
|
@ -320,6 +365,7 @@ macro_rules! gpio {
|
||||||
w.opendrn().clear_bit()
|
w.opendrn().clear_bit()
|
||||||
});
|
});
|
||||||
port_reg.dir().modify(|r,w| w.bits(r.bits() | (1 << $i)));
|
port_reg.dir().modify(|r,w| w.bits(r.bits() | (1 << $i)));
|
||||||
|
port_reg.clrout().write(|w| w.bits(1 << $i))
|
||||||
}
|
}
|
||||||
$PXi { _mode: PhantomData }
|
$PXi { _mode: PhantomData }
|
||||||
}
|
}
|
||||||
|
@ -346,6 +392,22 @@ macro_rules! gpio {
|
||||||
}
|
}
|
||||||
self
|
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>> {
|
impl<MODE> $PXi<Output<MODE>> {
|
||||||
|
@ -422,6 +484,58 @@ macro_rules! gpio {
|
||||||
_mode: self._mode,
|
_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>> {
|
impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
|
||||||
|
@ -432,7 +546,7 @@ macro_rules! gpio {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||||
unsafe {
|
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),
|
PA0: (pa0, 0),
|
||||||
PA1: (pa1, 1),
|
PA1: (pa1, 1),
|
||||||
PA2: (pa2, 2),
|
PA2: (pa2, 2),
|
||||||
|
@ -519,7 +633,7 @@ gpio!(PORTA, porta, 'A', [
|
||||||
PA31: (pa31, 31),
|
PA31: (pa31, 31),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
gpio!(PORTB, portb, 'B', [
|
gpio!(PORTB, portb, PortId::B, [
|
||||||
PB0: (pb0, 0),
|
PB0: (pb0, 0),
|
||||||
PB1: (pb1, 1),
|
PB1: (pb1, 1),
|
||||||
PB2: (pb2, 2),
|
PB2: (pb2, 2),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user