Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fully abstracted GPIO #32

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 180 additions & 1 deletion tm4c-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,175 @@ pub mod serial;
pub mod sysctl;
pub mod time;

///! An internal macro to implement the GPIO functionality for a generic GPIO
#[macro_export]
macro_rules! gpio_abstracted_macro {
() => {
/// Fully erased pin
pub struct Pxx<MODE> {
i: u8,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe s/i/pin/ and s/j/port/?

j: GpioPort,
_mode: PhantomData<MODE>,
}

impl<MODE> StatefulOutputPin for Pxx<Output<MODE>>
where
MODE: OutputMode,
{
fn is_set_high(&self) -> bool {
unsafe {
let data = get_field!(self.j, data);
bb::read_bit(data, self.i)
}
}

fn is_set_low(&self) -> bool {
!self.is_set_high()
}
}

impl<MODE> OutputPin for Pxx<Output<MODE>>
where
MODE: OutputMode,
{
fn set_high(&mut self) {
unsafe {
let data = get_field!(self.j, data);
bb::change_bit(data, self.i, true);
}
}

fn set_low(&mut self) {
unsafe {
let data = get_field!(self.j, data);
bb::change_bit(data, self.i, false);
}
}
}

impl<MODE> InputPin for Pxx<Input<MODE>>
where
MODE: InputMode,
{
fn is_high(&self) -> bool {
unsafe {
let data = get_field!(self.j, data);
bb::read_bit(data, self.i)
}
}

fn is_low(&self) -> bool {
!self.is_high()
}
}

impl<MODE> Pxx<Input<MODE>>
where
MODE: InputMode,
{
/// Enables or disables interrupts on this GPIO pin.
pub fn set_interrupt_mode(&mut self, mode: InterruptMode) {
unsafe {
let im = get_field!(self.j, im);
let is = get_field!(self.j, is);
let ibe = get_field!(self.j, iev);
let iev = get_field!(self.j, iev);

bb::change_bit(im, self.i, false);

match mode {
InterruptMode::LevelHigh => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
// IS |= self.i;
bb::change_bit(is, self.i, true);
// IBE &= ~self.i;
bb::change_bit(ibe, self.i, false);
// IEV |= self.i;
bb::change_bit(iev, self.i, true);
// IM |= self.i;
bb::change_bit(im, self.i, true);
}
InterruptMode::LevelLow => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
// IS |= self.i;
bb::change_bit(is, self.i, true);
// IBE &= ~self.i;
bb::change_bit(ibe, self.i, false);
// IEV &= ~self.i;
bb::change_bit(iev, self.i, false);
// IM |= self.i;
bb::change_bit(im, self.i, true);
}
InterruptMode::EdgeRising => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
// IS &= ~self.i;
bb::change_bit(is, self.i, false);
// IBE &= ~self.i;
bb::change_bit(ibe, self.i, false);
// IEV |= self.i;
bb::change_bit(iev, self.i, true);
// IM |= self.i;
bb::change_bit(im, self.i, true);
}
InterruptMode::EdgeFalling => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
// IS &= ~self.i;
bb::change_bit(is, self.i, false);
// IBE &= ~self.i;
bb::change_bit(ibe, self.i, false);
// IEV &= ~self.i;
bb::change_bit(iev, self.i, false);
// IM |= self.i;
bb::change_bit(im, self.i, true);
}
InterruptMode::EdgeBoth => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
// IS &= ~self.i;
bb::change_bit(is, self.i, false);
// IBE |= self.i;
bb::change_bit(ibe, self.i, true);
// IEV |= self.i;
bb::change_bit(iev, self.i, true);
// IM |= self.i;
bb::change_bit(im, self.i, true);
}
InterruptMode::Disabled => {
// IM &= ~self.i;
bb::change_bit(im, self.i, false);
}
}
}
}

/// Returns the current interrupt status for this pin.
pub fn get_interrupt_status(&self) -> bool {
unsafe {
let mis = get_field!(self.j, mis);
bb::read_bit(mis, self.i)
}
}

/// Marks the interrupt for this pin as handled. You should
/// call this (or perform its functionality) from the ISR.
pub fn clear_interrupt(&self) {
unsafe {
let icr = get_field!(self.j, icr);
bb::change_bit(icr, self.i, true);
}
}
}
};
}

///! An internal macro to implement the GPIO functionality for each port
#[macro_export]
macro_rules! gpio_macro {
($chip_crate:ident, $GPIOX:ident, $gpiox:ident, $iopd:ident, $PXx:ident, [
($chip_crate:ident, $GPIOX:ident, $gpiox:ident, $iopd:ident, $PXx:ident, $j:expr, [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the $j parameter for?

$($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+
]) => {
/// GPIO
Expand Down Expand Up @@ -64,6 +229,20 @@ macro_rules! gpio_macro {
_mode: PhantomData<MODE>,
}

impl<MODE> $PXx<MODE> {
/// Erases the port 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) -> Pxx<MODE> {
Pxx {
i: self.i,
j: GpioPort::$iopd,
_mode: self._mode,
}
}
}

impl<MODE> StatefulOutputPin for $PXx<Output<MODE>> where MODE: OutputMode {
fn is_set_high(&self) -> bool {
let p = unsafe { &*$GPIOX::ptr() };
Expand Down
39 changes: 32 additions & 7 deletions tm4c123x-hal/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,17 @@ use crate::{
sysctl,
};
use core::marker::PhantomData;
use tm4c_hal::gpio_macro;
use tm4c_hal::{gpio_abstracted_macro, gpio_macro};

#[derive(PartialEq, Eq)]
enum GpioPort {
GpioA,
GpioB,
GpioC,
GpioD,
GpioE,
GpioF,
}

/// Extension trait to split a GPIO peripheral in independent pins and registers
pub trait GpioExt {
Expand All @@ -51,7 +61,7 @@ pub trait GpioExt {
fn split(self, power_control: &sysctl::PowerControl) -> Self::Parts;
}

gpio_macro!(tm4c123x, GPIO_PORTA, gpioa, GpioA, PAx, [
gpio_macro!(tm4c123x, GPIO_PORTA, gpioa, GpioA, PAx, 0, [
PA0: (pa0, 0, Tristate),
PA1: (pa1, 1, Tristate),
PA2: (pa2, 2, Tristate),
Expand All @@ -62,7 +72,7 @@ gpio_macro!(tm4c123x, GPIO_PORTA, gpioa, GpioA, PAx, [
PA7: (pa7, 7, Tristate),
]);

gpio_macro!(tm4c123x, GPIO_PORTB, gpiob, GpioB, PBx, [
gpio_macro!(tm4c123x, GPIO_PORTB, gpiob, GpioB, PBx, 1, [
PB0: (pb0, 0, Tristate),
PB1: (pb1, 1, Tristate),
PB2: (pb2, 2, Tristate),
Expand All @@ -73,7 +83,7 @@ gpio_macro!(tm4c123x, GPIO_PORTB, gpiob, GpioB, PBx, [
PB7: (pb7, 7, Tristate),
]);

gpio_macro!(tm4c123x, GPIO_PORTC, gpioc, GpioC, PCx, [
gpio_macro!(tm4c123x, GPIO_PORTC, gpioc, GpioC, PCx, 2, [
PC0: (pc0, 0, Locked), // JTAG/SWD pin
PC1: (pc1, 1, Locked), // JTAG/SWD pin
PC2: (pc2, 2, Locked), // JTAG/SWD pin
Expand All @@ -84,7 +94,7 @@ gpio_macro!(tm4c123x, GPIO_PORTC, gpioc, GpioC, PCx, [
PC7: (pc7, 7, Tristate),
]);

gpio_macro!(tm4c123x, GPIO_PORTD, gpiod, GpioD, PDx, [
gpio_macro!(tm4c123x, GPIO_PORTD, gpiod, GpioD, PDx, 3, [
PD0: (pd0, 0, Tristate),
PD1: (pd1, 1, Tristate),
PD2: (pd2, 2, Tristate),
Expand All @@ -95,7 +105,7 @@ gpio_macro!(tm4c123x, GPIO_PORTD, gpiod, GpioD, PDx, [
PD7: (pd7, 7, Locked), // NMI pin
]);

gpio_macro!(tm4c123x, GPIO_PORTE, gpioe, GpioE, PEx, [
gpio_macro!(tm4c123x, GPIO_PORTE, gpioe, GpioE, PEx, 4, [
PE0: (pe0, 0, Tristate),
PE1: (pe1, 1, Tristate),
PE2: (pe2, 2, Tristate),
Expand All @@ -106,7 +116,7 @@ gpio_macro!(tm4c123x, GPIO_PORTE, gpioe, GpioE, PEx, [
PE7: (pe7, 7, Tristate),
]);

gpio_macro!(tm4c123x, GPIO_PORTF, gpiof, GpioF, PFx, [
gpio_macro!(tm4c123x, GPIO_PORTF, gpiof, GpioF, PFx, 5, [
PF0: (pf0, 0, Locked), // NMI pin
PF1: (pf1, 1, Tristate),
PF2: (pf2, 2, Tristate),
Expand All @@ -116,3 +126,18 @@ gpio_macro!(tm4c123x, GPIO_PORTF, gpiof, GpioF, PFx, [
PF6: (pf6, 6, Tristate),
PF7: (pf7, 7, Tristate),
]);

macro_rules! get_field {
($j:expr, $field:ident) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the field parameter for?

match $j {
GpioPort::GpioA => &(*tm4c123x::GPIO_PORTA_AHB::ptr()).data,
GpioPort::GpioB => &(*tm4c123x::GPIO_PORTB_AHB::ptr()).data,
GpioPort::GpioC => &(*tm4c123x::GPIO_PORTC_AHB::ptr()).data,
GpioPort::GpioD => &(*tm4c123x::GPIO_PORTD_AHB::ptr()).data,
GpioPort::GpioE => &(*tm4c123x::GPIO_PORTE_AHB::ptr()).data,
GpioPort::GpioF => &(*tm4c123x::GPIO_PORTF_AHB::ptr()).data,
}
};
}

gpio_abstracted_macro!();
Loading