From fca038323576cae5a8bbebbea466dc0a8bb439e5 Mon Sep 17 00:00:00 2001 From: David Wood Date: Sun, 19 Jul 2020 19:12:57 +0100 Subject: [PATCH] Add fully abstracted GPIO --- tm4c-hal/src/lib.rs | 181 ++++++++++++++++++++++++++++++++++++++- tm4c123x-hal/src/gpio.rs | 39 +++++++-- tm4c129x-hal/src/gpio.rs | 75 ++++++++++++---- 3 files changed, 271 insertions(+), 24 deletions(-) diff --git a/tm4c-hal/src/lib.rs b/tm4c-hal/src/lib.rs index 790e1a5..59c0030 100644 --- a/tm4c-hal/src/lib.rs +++ b/tm4c-hal/src/lib.rs @@ -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 { + i: u8, + j: GpioPort, + _mode: PhantomData, + } + + impl StatefulOutputPin for Pxx> + 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 OutputPin for Pxx> + 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 InputPin for Pxx> + 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 Pxx> + 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, [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ ]) => { /// GPIO @@ -64,6 +229,20 @@ macro_rules! gpio_macro { _mode: PhantomData, } + impl $PXx { + /// 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 { + Pxx { + i: self.i, + j: GpioPort::$iopd, + _mode: self._mode, + } + } + } + impl StatefulOutputPin for $PXx> where MODE: OutputMode { fn is_set_high(&self) -> bool { let p = unsafe { &*$GPIOX::ptr() }; diff --git a/tm4c123x-hal/src/gpio.rs b/tm4c123x-hal/src/gpio.rs index 60ccddb..c0083e7 100644 --- a/tm4c123x-hal/src/gpio.rs +++ b/tm4c123x-hal/src/gpio.rs @@ -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 { @@ -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), @@ -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), @@ -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 @@ -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), @@ -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), @@ -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), @@ -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) => { + 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!(); diff --git a/tm4c129x-hal/src/gpio.rs b/tm4c129x-hal/src/gpio.rs index 19bee11..b290f14 100644 --- a/tm4c129x-hal/src/gpio.rs +++ b/tm4c129x-hal/src/gpio.rs @@ -42,7 +42,7 @@ use crate::{ sysctl, }; use core::marker::PhantomData; -use tm4c_hal::gpio_macro; +use tm4c_hal::{gpio_abstracted_macro, gpio_macro}; /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { @@ -53,7 +53,26 @@ pub trait GpioExt { fn split(self, power_control: &sysctl::PowerControl) -> Self::Parts; } -gpio_macro!(tm4c129x, GPIO_PORTA_AHB, gpioa, GpioA, PAx, [ +#[derive(PartialEq, Eq)] +enum GpioPort { + GpioA, + GpioB, + GpioC, + GpioD, + GpioE, + GpioF, + GpioG, + GpioH, + GpioJ, + GpioK, + GpioL, + GpioM, + GpioN, + GpioP, + GpioQ, +} + +gpio_macro!(tm4c129x, GPIO_PORTA_AHB, gpioa, GpioA, PAx, 0, [ PA0: (pa0, 0, Tristate), PA1: (pa1, 1, Tristate), PA2: (pa2, 2, Tristate), @@ -64,7 +83,7 @@ gpio_macro!(tm4c129x, GPIO_PORTA_AHB, gpioa, GpioA, PAx, [ PA7: (pa7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTB_AHB, gpiob, GpioB, PBx, [ +gpio_macro!(tm4c129x, GPIO_PORTB_AHB, gpiob, GpioB, PBx, 1, [ PB0: (pb0, 0, Tristate), PB1: (pb1, 1, Tristate), PB2: (pb2, 2, Tristate), @@ -74,7 +93,7 @@ gpio_macro!(tm4c129x, GPIO_PORTB_AHB, gpiob, GpioB, PBx, [ // PB6 and PB7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTC_AHB, gpioc, GpioC, PCx, [ +gpio_macro!(tm4c129x, GPIO_PORTC_AHB, 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 @@ -85,7 +104,7 @@ gpio_macro!(tm4c129x, GPIO_PORTC_AHB, gpioc, GpioC, PCx, [ PC7: (pc7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTD_AHB, gpiod, GpioD, PDx, [ +gpio_macro!(tm4c129x, GPIO_PORTD_AHB, gpiod, GpioD, PDx, 3, [ PD0: (pd0, 0, Tristate), PD1: (pd1, 1, Tristate), PD2: (pd2, 2, Tristate), @@ -96,7 +115,7 @@ gpio_macro!(tm4c129x, GPIO_PORTD_AHB, gpiod, GpioD, PDx, [ PD7: (pd7, 7, Locked), // GPIO pin ]); -gpio_macro!(tm4c129x, GPIO_PORTE_AHB, gpioe, GpioE, PEx, [ +gpio_macro!(tm4c129x, GPIO_PORTE_AHB, gpioe, GpioE, PEx, 4, [ PE0: (pe0, 0, Tristate), PE1: (pe1, 1, Tristate), PE2: (pe2, 2, Tristate), @@ -106,7 +125,7 @@ gpio_macro!(tm4c129x, GPIO_PORTE_AHB, gpioe, GpioE, PEx, [ // PE6 and PE7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTF_AHB, gpiof, GpioF, PFx, [ +gpio_macro!(tm4c129x, GPIO_PORTF_AHB, gpiof, GpioF, PFx, 5, [ PF0: (pf0, 0, Tristate), PF1: (pf1, 1, Tristate), PF2: (pf2, 2, Tristate), @@ -115,13 +134,13 @@ gpio_macro!(tm4c129x, GPIO_PORTF_AHB, gpiof, GpioF, PFx, [ // PF5, PF6 and PF7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTG_AHB, gpiog, GpioG, PGx, [ +gpio_macro!(tm4c129x, GPIO_PORTG_AHB, gpiog, GpioG, PGx, 6, [ PG0: (pg0, 0, Tristate), PG1: (pg1, 1, Tristate), // PG2 through PG7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTH_AHB, gpioh, GpioH, PHx, [ +gpio_macro!(tm4c129x, GPIO_PORTH_AHB, gpioh, GpioH, PHx, 7, [ PH0: (ph0, 0, Tristate), PH1: (ph1, 1, Tristate), PH2: (ph2, 2, Tristate), @@ -129,13 +148,13 @@ gpio_macro!(tm4c129x, GPIO_PORTH_AHB, gpioh, GpioH, PHx, [ // PH4 through PG7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTJ_AHB, gpioj, GpioJ, PJx, [ +gpio_macro!(tm4c129x, GPIO_PORTJ_AHB, gpioj, GpioJ, PJx, 8, [ PJ0: (pj0, 0, Tristate), PJ1: (pj1, 1, Tristate), // PJ2 through PJ7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTK, gpiok, GpioK, PKx, [ +gpio_macro!(tm4c129x, GPIO_PORTK, gpiok, GpioK, PKx, 9, [ PK0: (pk0, 0, Tristate), PK1: (pk1, 1, Tristate), PK2: (pk2, 2, Tristate), @@ -146,7 +165,7 @@ gpio_macro!(tm4c129x, GPIO_PORTK, gpiok, GpioK, PKx, [ PK7: (pk7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTL, gpiol, GpioL, PNL, [ +gpio_macro!(tm4c129x, GPIO_PORTL, gpiol, GpioL, PNx, 10, [ PL0: (pl0, 0, Tristate), PL1: (pl1, 1, Tristate), PL2: (pl2, 2, Tristate), @@ -157,7 +176,7 @@ gpio_macro!(tm4c129x, GPIO_PORTL, gpiol, GpioL, PNL, [ PL7: (pl7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTM, gpiom, GpioM, PMx, [ +gpio_macro!(tm4c129x, GPIO_PORTM, gpiom, GpioM, PMx, 11, [ PM0: (pm0, 0, Tristate), PM1: (pm1, 1, Tristate), PM2: (pm2, 2, Tristate), @@ -168,7 +187,7 @@ gpio_macro!(tm4c129x, GPIO_PORTM, gpiom, GpioM, PMx, [ PM7: (pm7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTN, gpion, GpioN, PNx, [ +gpio_macro!(tm4c129x, GPIO_PORTN, gpion, GpioN, PNx, 12, [ PN0: (pn0, 0, Tristate), PN1: (pn1, 1, Tristate), PN2: (pn2, 2, Tristate), @@ -179,7 +198,7 @@ gpio_macro!(tm4c129x, GPIO_PORTN, gpion, GpioN, PNx, [ PN7: (pn7, 7, Tristate), ]); -gpio_macro!(tm4c129x, GPIO_PORTP, gpiop, GpioP, PPx, [ +gpio_macro!(tm4c129x, GPIO_PORTP, gpiop, GpioP, PPx, 13, [ PP0: (pp0, 0, Tristate), PP1: (pp1, 1, Tristate), PP2: (pp2, 2, Tristate), @@ -189,7 +208,7 @@ gpio_macro!(tm4c129x, GPIO_PORTP, gpiop, GpioP, PPx, [ // PP6 and PP7 don't exist ]); -gpio_macro!(tm4c129x, GPIO_PORTQ, gpioq, GpioQ, PQx, [ +gpio_macro!(tm4c129x, GPIO_PORTQ, gpioq, GpioQ, PQx, 14, [ PQ0: (pq0, 0, Tristate), PQ1: (pq1, 1, Tristate), PQ2: (pq2, 2, Tristate), @@ -197,3 +216,27 @@ gpio_macro!(tm4c129x, GPIO_PORTQ, gpioq, GpioQ, PQx, [ PQ4: (pq4, 4, Tristate), // PQ5, PQ6 and PQ7 don't exist ]); + +macro_rules! get_field { + ($j:expr, $field:ident) => { + match $j { + GpioPort::GpioA => &(*tm4c129x::GPIO_PORTA_AHB::ptr()).data, + GpioPort::GpioB => &(*tm4c129x::GPIO_PORTB_AHB::ptr()).data, + GpioPort::GpioC => &(*tm4c129x::GPIO_PORTC_AHB::ptr()).data, + GpioPort::GpioD => &(*tm4c129x::GPIO_PORTD_AHB::ptr()).data, + GpioPort::GpioE => &(*tm4c129x::GPIO_PORTE_AHB::ptr()).data, + GpioPort::GpioF => &(*tm4c129x::GPIO_PORTF_AHB::ptr()).data, + GpioPort::GpioG => &(*tm4c129x::GPIO_PORTG_AHB::ptr()).data, + GpioPort::GpioH => &(*tm4c129x::GPIO_PORTH_AHB::ptr()).data, + GpioPort::GpioJ => &(*tm4c129x::GPIO_PORTJ_AHB::ptr()).data, + GpioPort::GpioK => &(*tm4c129x::GPIO_PORTK::ptr()).data, + GpioPort::GpioL => &(*tm4c129x::GPIO_PORTL::ptr()).data, + GpioPort::GpioM => &(*tm4c129x::GPIO_PORTM::ptr()).data, + GpioPort::GpioN => &(*tm4c129x::GPIO_PORTN::ptr()).data, + GpioPort::GpioP => &(*tm4c129x::GPIO_PORTP::ptr()).data, + GpioPort::GpioQ => &(*tm4c129x::GPIO_PORTQ::ptr()).data, + } + }; +} + +gpio_abstracted_macro!();