-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(gpio): improve internal structure for clarity
Signed-off-by: Zhouqi Jiang <[email protected]>
- Loading branch information
Showing
8 changed files
with
568 additions
and
506 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
use super::{ | ||
eint::EintPad, | ||
function::Function, | ||
input::Input, | ||
mode::{set_mode, HasMode}, | ||
output::Output, | ||
register::RegisterBlock, | ||
}; | ||
|
||
/// Disabled GPIO pad. | ||
pub struct Disabled<'a, const P: char, const N: u8> { | ||
gpio: &'a RegisterBlock, | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> Disabled<'a, P, N> { | ||
/// Configures the pad to operate as an input pad. | ||
#[inline] | ||
pub fn into_input(self) -> Input<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an output pad. | ||
#[inline] | ||
pub fn into_output(self) -> Output<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an alternate function pad. | ||
#[inline] | ||
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an external interrupt pad. | ||
#[inline] | ||
pub fn into_eint(self) -> EintPad<'a, P, N> { | ||
set_mode(self) | ||
} | ||
|
||
/// Internal constructor for ROM runtime. Do not use. | ||
#[doc(hidden)] | ||
#[inline(always)] | ||
pub const unsafe fn __new(gpio: &'a RegisterBlock) -> Self { | ||
Self { gpio } | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> HasMode<'a> for Disabled<'a, P, N> { | ||
const P: char = P; | ||
const N: u8 = N; | ||
const VALUE: u8 = 15; | ||
#[inline] | ||
fn gpio(&self) -> &'a RegisterBlock { | ||
self.gpio | ||
} | ||
#[inline] | ||
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self { | ||
Self { gpio } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
use super::{ | ||
disabled::Disabled, | ||
function::Function, | ||
input::Input, | ||
mode::{set_mode, HasMode}, | ||
output::Output, | ||
port_cfg_index, port_index, | ||
register::RegisterBlock, | ||
}; | ||
|
||
/// External interrupt mode pad. | ||
pub struct EintPad<'a, const P: char, const N: u8> { | ||
gpio: &'a RegisterBlock, | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> EintPad<'a, P, N> { | ||
/// Configures the pad to operate as an input pad. | ||
#[inline] | ||
pub fn into_input(self) -> Input<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an output pad. | ||
#[inline] | ||
pub fn into_output(self) -> Output<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an alternate function pad. | ||
#[inline] | ||
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as a disabled pad. | ||
#[inline] | ||
pub fn into_disabled(self) -> Disabled<'a, P, N> { | ||
set_mode(self) | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> EintPad<'a, P, N> { | ||
#[inline] | ||
pub fn listen(&mut self, event: Event) { | ||
let event_id = match event { | ||
Event::PositiveEdge => 0, | ||
Event::NegativeEdge => 1, | ||
Event::HighLevel => 2, | ||
Event::LowLevel => 3, | ||
Event::BothEdges => 4, | ||
}; | ||
let (port_idx, cfg_reg_idx, mask, cfg_field_idx) = const { | ||
let (port_idx, cfg_reg_idx, cfg_field_idx) = port_cfg_index(P, N); | ||
let mask = !(0xF << cfg_field_idx); | ||
(port_idx, cfg_reg_idx, mask, cfg_field_idx) | ||
}; | ||
let value = event_id << cfg_field_idx; | ||
let cfg_reg = &self.gpio.eint[port_idx].cfg[cfg_reg_idx]; | ||
unsafe { cfg_reg.modify(|cfg| (cfg & mask) | value) }; | ||
} | ||
#[inline] | ||
pub fn enable_interrupt(&mut self) { | ||
let idx = const { port_index(P) }; | ||
unsafe { self.gpio.eint[idx].ctl.modify(|value| value | (1 << N)) } | ||
} | ||
#[inline] | ||
pub fn disable_interrupt(&mut self) { | ||
let idx = const { port_index(P) }; | ||
unsafe { self.gpio.eint[idx].ctl.modify(|value| value & !(1 << N)) } | ||
} | ||
#[inline] | ||
pub fn clear_interrupt_pending_bit(&mut self) { | ||
unsafe { self.gpio.eint[const { port_index(P) }].status.write(1 << N) } | ||
} | ||
#[inline] | ||
pub fn check_interrupt(&mut self) -> bool { | ||
self.gpio.eint[const { port_index(P) }].status.read() & (1 << N) != 0 | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> HasMode<'a> for EintPad<'a, P, N> { | ||
const P: char = P; | ||
const N: u8 = N; | ||
const VALUE: u8 = 14; | ||
#[inline] | ||
fn gpio(&self) -> &'a RegisterBlock { | ||
self.gpio | ||
} | ||
#[inline] | ||
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self { | ||
Self { gpio } | ||
} | ||
} | ||
|
||
/// External interrupt event. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
pub enum Event { | ||
PositiveEdge, | ||
NegativeEdge, | ||
HighLevel, | ||
LowLevel, | ||
BothEdges, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use super::{ | ||
disabled::Disabled, | ||
eint::EintPad, | ||
input::Input, | ||
mode::{borrow_with_mode, set_mode, HasMode}, | ||
output::Output, | ||
register::RegisterBlock, | ||
}; | ||
|
||
/// Alternate function pad. | ||
/// | ||
/// F should be in 2..=8. | ||
pub struct Function<'a, const P: char, const N: u8, const F: u8> { | ||
gpio: &'a RegisterBlock, | ||
} | ||
|
||
impl<'a, const P: char, const N: u8, const F: u8> Function<'a, P, N, F> { | ||
/// Configures the pad to operate as an input pad. | ||
#[inline] | ||
pub fn into_input(self) -> Input<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an output pad. | ||
#[inline] | ||
pub fn into_output(self) -> Output<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an alternate function pad. | ||
#[inline] | ||
pub fn into_function<const F2: u8>(self) -> Function<'a, P, N, F2> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an external interrupt pad. | ||
#[inline] | ||
pub fn into_eint(self) -> EintPad<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as a disabled pad. | ||
#[inline] | ||
pub fn into_disabled(self) -> Disabled<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Borrows the pad to temporarily use it as an input pad. | ||
#[inline] | ||
pub fn with_input<G, T>(&mut self, f: G) -> T | ||
where | ||
G: FnOnce(&mut Input<'a, P, N>) -> T, | ||
{ | ||
borrow_with_mode(self, f) | ||
} | ||
/// Borrows the pad to temporarily use it as an output pad. | ||
#[inline] | ||
pub fn with_output<G, T>(&mut self, f: G) -> T | ||
where | ||
G: FnOnce(&mut Output<'a, P, N>) -> T, | ||
{ | ||
borrow_with_mode(self, f) | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8, const F: u8> HasMode<'a> for Function<'a, P, N, F> { | ||
const P: char = P; | ||
const N: u8 = N; | ||
const VALUE: u8 = F; | ||
#[inline] | ||
fn gpio(&self) -> &'a RegisterBlock { | ||
self.gpio | ||
} | ||
#[inline] | ||
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self { | ||
Self { gpio } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use super::{ | ||
disabled::Disabled, | ||
eint::EintPad, | ||
function::Function, | ||
mode::{borrow_with_mode, set_mode, HasMode}, | ||
output::Output, | ||
port_index, | ||
register::RegisterBlock, | ||
}; | ||
|
||
/// Input mode pad. | ||
pub struct Input<'a, const P: char, const N: u8> { | ||
gpio: &'a RegisterBlock, | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> Input<'a, P, N> { | ||
/// Configures the pad to operate as an output pad. | ||
#[inline] | ||
pub fn into_output(self) -> Output<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an alternate function pad. | ||
#[inline] | ||
pub fn into_function<const F: u8>(self) -> Function<'a, P, N, F> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as an external interrupt pad. | ||
#[inline] | ||
pub fn into_eint(self) -> EintPad<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Configures the pad to operate as a disabled pad. | ||
#[inline] | ||
pub fn into_disabled(self) -> Disabled<'a, P, N> { | ||
set_mode(self) | ||
} | ||
/// Borrows the pad to temporarily use it as an output pad. | ||
#[inline] | ||
pub fn with_output<F, T>(&mut self, f: F) -> T | ||
where | ||
F: FnOnce(&mut Output<'a, P, N>) -> T, | ||
{ | ||
borrow_with_mode(self, f) | ||
} | ||
/// Borrows the pad to temporarily use it an alternate function pad. | ||
#[inline] | ||
pub fn with_function<const G: u8, F, T>(&mut self, f: F) -> T | ||
where | ||
F: FnOnce(&mut Function<'a, P, N, G>) -> T, | ||
{ | ||
borrow_with_mode(self, f) | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> embedded_hal::digital::ErrorType for Input<'a, P, N> { | ||
type Error = core::convert::Infallible; | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> embedded_hal::digital::InputPin for Input<'a, P, N> { | ||
#[inline] | ||
fn is_high(&mut self) -> Result<bool, Self::Error> { | ||
Ok(self.gpio.port[const { port_index(P) }].dat.read() & (1 << N) != 0) | ||
} | ||
#[inline] | ||
fn is_low(&mut self) -> Result<bool, Self::Error> { | ||
Ok(self.gpio.port[const { port_index(P) }].dat.read() & (1 << N) == 0) | ||
} | ||
} | ||
|
||
impl<'a, const P: char, const N: u8> HasMode<'a> for Input<'a, P, N> { | ||
const P: char = P; | ||
const N: u8 = N; | ||
const VALUE: u8 = 0; | ||
#[inline] | ||
fn gpio(&self) -> &'a RegisterBlock { | ||
self.gpio | ||
} | ||
#[inline] | ||
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self { | ||
Self { gpio } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
use super::{port_cfg_index, register::RegisterBlock}; | ||
|
||
/// Internal function to set GPIO pad mode. | ||
#[inline] | ||
pub fn set_mode<'a, T, U>(value: T) -> U | ||
where | ||
T: HasMode<'a>, | ||
U: HasMode<'a>, | ||
{ | ||
// take ownership of pad | ||
let gpio = value.gpio(); | ||
unsafe { write_mode::<T, U>(gpio) }; | ||
// return ownership of pad | ||
unsafe { U::from_gpio(gpio) } | ||
} | ||
|
||
#[inline] | ||
pub fn borrow_with_mode<'a, T, U, F, R>(value: &mut T, f: F) -> R | ||
where | ||
T: HasMode<'a>, | ||
U: HasMode<'a>, | ||
F: FnOnce(&mut U) -> R, | ||
{ | ||
// take ownership of pad | ||
let gpio = value.gpio(); | ||
// set pad to new mode | ||
unsafe { write_mode::<T, U>(gpio) }; | ||
let mut pad = unsafe { U::from_gpio(gpio) }; | ||
let val = f(&mut pad); | ||
// restore pad to original mode | ||
unsafe { write_mode::<T, T>(gpio) }; | ||
val | ||
} | ||
|
||
#[inline] | ||
unsafe fn write_mode<'a, T: HasMode<'a>, U: HasMode<'a>>(gpio: &RegisterBlock) { | ||
// calculate mask, value and register address | ||
let (mask, value, port_idx, cfg_reg_idx) = const { | ||
let (port_idx, cfg_reg_idx, cfg_field_idx) = port_cfg_index(T::P, T::N); | ||
let mask = !(0xF << cfg_field_idx); | ||
let value = (U::VALUE as u32) << cfg_field_idx; | ||
(mask, value, port_idx, cfg_reg_idx) | ||
}; | ||
// apply configuration | ||
let cfg_reg = &gpio.port[port_idx].cfg[cfg_reg_idx]; | ||
unsafe { cfg_reg.modify(|cfg| (cfg & mask) | value) }; | ||
} | ||
|
||
pub trait HasMode<'a> { | ||
const P: char; | ||
const N: u8; | ||
const VALUE: u8; | ||
fn gpio(&self) -> &'a RegisterBlock; | ||
unsafe fn from_gpio(gpio: &'a RegisterBlock) -> Self; | ||
} |
Oops, something went wrong.