TheZoq2 4 лет назад
Родитель
Сommit
f7204fcd47
3 измененных файлов с 343 добавлено и 146 удалено
  1. 49 0
      examples/dynamic_gpio.rs
  2. 1 1
      examples/multi_mode_gpio.rs
  3. 293 145
      src/gpio.rs

+ 49 - 0
examples/dynamic_gpio.rs

@@ -0,0 +1,49 @@
+#![deny(unsafe_code)]
+#![no_std]
+#![no_main]
+
+use panic_halt as _;
+
+use nb::block;
+
+use cortex_m_rt::entry;
+use cortex_m_semihosting::hprintln;
+use embedded_hal::digital::v2::{InputPin, OutputPin};
+use stm32f1xx_hal::{gpio::State, pac, prelude::*, timer::Timer};
+
+#[entry]
+fn main() -> ! {
+    // Get access to the core peripherals from the cortex-m crate
+    let cp = cortex_m::Peripherals::take().unwrap();
+    // Get access to the device specific peripherals from the peripheral access crate
+    let dp = pac::Peripherals::take().unwrap();
+
+    // Take ownership over the raw flash and rcc devices and convert them into the corresponding
+    // HAL structs
+    let mut flash = dp.FLASH.constrain();
+    let mut rcc = dp.RCC.constrain();
+
+    // Freeze the configuration of all the clocks in the system and store the frozen frequencies in
+    // `clocks`
+    let clocks = rcc.cfgr.freeze(&mut flash.acr);
+
+    // Acquire the GPIOC peripheral
+    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
+
+    let mut pin = gpioc.pc13.into_dynamic(&mut gpioc.crh);
+    // Configure the syst timer to trigger an update every second
+    let mut timer = Timer::syst(cp.SYST, &clocks).start_count_down(1.hz());
+
+    // Wait for the timer to trigger an update and change the state of the LED
+    loop {
+        pin.make_floating_input(&mut gpioc.crh);
+        block!(timer.wait()).unwrap();
+        hprintln!("{}", pin.is_high().unwrap()).unwrap();
+
+        pin.make_push_pull_output(&mut gpioc.crh);
+        pin.set_high().unwrap();
+        block!(timer.wait()).unwrap();
+        pin.set_low().unwrap();
+        block!(timer.wait()).unwrap();
+    }
+}

+ 1 - 1
examples/multi_mode_gpio.rs

@@ -9,7 +9,7 @@ use nb::block;
 use cortex_m_rt::entry;
 use cortex_m_semihosting::hprintln;
 use embedded_hal::digital::v2::{InputPin, OutputPin};
-use stm32f1xx_hal::{pac, prelude::*, timer::Timer, gpio::State};
+use stm32f1xx_hal::{gpio::State, pac, prelude::*, timer::Timer};
 
 #[entry]
 fn main() -> ! {

+ 293 - 145
src/gpio.rs

@@ -96,13 +96,63 @@ pub trait ExtiPin {
     fn check_interrupt(&mut self) -> bool;
 }
 
+pub enum Dynamic {
+    InputFloating,
+    InputPullUp,
+    InputPullDown,
+    OutputPushPull,
+    OutputOpenDrain,
+}
+
+#[derive(Debug, PartialEq)]
+pub enum PinModeError {
+    IncorrectMode,
+}
+
+impl Dynamic {
+    fn is_input(&self) -> bool {
+        use Dynamic::*;
+        match self {
+            InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true,
+            OutputPushPull => false,
+        }
+    }
+    fn is_output(&self) -> bool {
+        use Dynamic::*;
+        match self {
+            InputFloating | InputPullUp | InputPullDown => false,
+            OutputPushPull | OutputOpenDrain => true,
+        }
+    }
+}
+
 /// NOTE: This trait should ideally be private but must be pub in order to avoid
 /// complaints from the compiler.
 pub trait PinMode<CR> {
     unsafe fn set_mode(cr: &mut CR) -> Self;
 }
 
-
+// These impls are needed because a macro can not brace initialise a ty token
+impl<MODE> Input<MODE> {
+    fn _new() -> Self {
+        Self { _mode: PhantomData }
+    }
+}
+impl<MODE> Output<MODE> {
+    fn _new() -> Self {
+        Self { _mode: PhantomData }
+    }
+}
+impl<MODE> Alternate<MODE> {
+    fn _new() -> Self {
+        Self { _mode: PhantomData }
+    }
+}
+impl Debugger {
+    fn _new() -> Self {
+        Self {}
+    }
+}
 
 macro_rules! gpio {
     ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $PXx:ident, $extigpionr:expr, [
@@ -135,6 +185,8 @@ macro_rules! gpio {
                 Edge,
                 ExtiPin,
                 PinMode,
+                Dynamic,
+                PinModeError,
             };
 
             /// GPIO parts
@@ -160,7 +212,7 @@ macro_rules! gpio {
                         crl: CRL { _0: () },
                         crh: CRH { _0: () },
                         $(
-                            $pxi: $PXi { _mode: PhantomData },
+                            $pxi: $PXi { mode: <$MODE>::_new() },
                         )+
                     }
                 }
@@ -329,7 +381,7 @@ macro_rules! gpio {
             $(
                 /// Pin
                 pub struct $PXi<MODE> {
-                    _mode: PhantomData<MODE>,
+                    mode: MODE,
                 }
 
                 impl<MODE> Mode<MODE> for $PXi<MODE> {}
@@ -340,7 +392,7 @@ macro_rules! gpio {
                     /// state in the hardware.
                     #[allow(dead_code)]
                     pub(crate) unsafe fn activate(self) -> $PXi<Input<Floating>> {
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Input::_new() }
                     }
                 }
 
@@ -365,7 +417,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Alternate::_new() }
                     }
 
                     /// Configures the pin to operate as an alternate function open-drain output
@@ -388,7 +440,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Alternate::_new() }
                     }
 
                     /// Configures the pin to operate as a floating input pin
@@ -472,21 +524,12 @@ macro_rules! gpio {
                         }
                     }
 
-                    /**
-                      Set the output of the pin regardless of its mode.
-                      Primarily used to set the output value of the pin
-                      before changing its mode to an output to avoid
-                      a short spike of an incorrect value
-                    */
-                    fn set_state(&mut self, state: State) {
-                        match state {
-                            State::High => unsafe {
-                                (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i))
-                            }
-                            State::Low => unsafe {
-                                (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i)))
-                            }
-                        }
+                    /// Configures the pin as a pin that can change between input
+                    /// and output without changing the type. It starts out
+                    /// as a floating input
+                    pub fn into_dynamic(self, cr: &mut $CR) -> $PXi<Dynamic> {
+                        self.into_floating_input(cr);
+                        $PXi::<Dynamic>{mode: Dynamic::InputFloating}
                     }
                 }
 
@@ -584,6 +627,229 @@ macro_rules! gpio {
                 }
 
 
+                impl<MODE> $PXi<MODE> where MODE: Active {
+                    /// Erases the pin number from the type
+                    fn into_generic(self) -> Generic<MODE> {
+                        Generic {
+                            i: $i,
+                            _mode: PhantomData,
+                        }
+                    }
+
+                    /// Erases the pin number and port 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> {
+                        self.into_generic().downgrade()
+                    }
+                }
+
+                // embedded_hal impls
+
+                impl<MODE> OutputPin for $PXi<Output<MODE>> {
+                    type Error = Infallible;
+                    fn set_high(&mut self) -> Result<(), Self::Error> {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        Ok(self.set_state(State::High))
+                    }
+
+                    fn set_low(&mut self) -> Result<(), Self::Error> {
+                        // NOTE(unsafe) atomic write to a stateless register
+                        Ok(self.set_state(State::Low))
+                    }
+                }
+
+                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
+                    fn is_set_high(&self) -> Result<bool, Self::Error> {
+                        self.is_set_low().map(|b| !b)
+                    }
+
+                    fn is_set_low(&self) -> Result<bool, Self::Error> {
+                        Ok(self._is_set_low())
+                    }
+                }
+
+                impl<MODE> toggleable::Default for $PXi<Output<MODE>> {}
+
+                impl<MODE> InputPin for $PXi<Input<MODE>> {
+                    type Error = Infallible;
+                    fn is_high(&self) -> Result<bool, Self::Error> {
+                        self.is_low().map(|b| !b)
+                    }
+
+                    fn is_low(&self) -> Result<bool, Self::Error> {
+                        // NOTE(unsafe) atomic read with no side effects
+                        Ok(self._is_low())
+                    }
+                }
+
+                impl InputPin for $PXi<Output<OpenDrain>> {
+                    type Error = Infallible;
+                    fn is_high(&self) -> Result<bool, Self::Error> {
+                        self.is_low().map(|b| !b)
+                    }
+
+                    fn is_low(&self) -> Result<bool, Self::Error> {
+                        Ok(self._is_low())
+                    }
+                }
+
+
+                // Dynamic pin
+
+                impl $PXi<Dynamic> {
+                    pub fn make_pull_up_input(&mut self, cr: &mut $CR) {
+                        // NOTE(unsafe), we have a mutable reference to the current pin
+                        unsafe { $PXi::<Input<PullUp>>::set_mode(cr) };
+                        self.mode = Dynamic::InputPullUp;
+                    }
+                    pub fn make_pull_down_input(&mut self, cr: &mut $CR) {
+                        // NOTE(unsafe), we have a mutable reference to the current pin
+                        unsafe { $PXi::<Input<PullDown>>::set_mode(cr) };
+                        self.mode = Dynamic::InputPullDown;
+                    }
+                    pub fn make_floating_input(&mut self, cr: &mut $CR) {
+                        // NOTE(unsafe), we have a mutable reference to the current pin
+                        unsafe { $PXi::<Input<Floating>>::set_mode(cr) };
+                        self.mode = Dynamic::InputFloating;
+                    }
+                    pub fn make_push_pull_output(&mut self, cr: &mut $CR) {
+                        // NOTE(unsafe), we have a mutable reference to the current pin
+                        unsafe { $PXi::<Output<PushPull>>::set_mode(cr) };
+                        self.mode = Dynamic::OutputPushPull;
+                    }
+                    pub fn make_open_drain_output(&mut self, cr: &mut $CR) {
+                        // NOTE(unsafe), we have a mutable reference to the current pin
+                        unsafe { $PXi::<Output<OpenDrain>>::set_mode(cr) };
+                        self.mode = Dynamic::OutputOpenDrain;
+                    }
+                }
+
+                impl OutputPin for $PXi<Dynamic> {
+                    type Error = PinModeError;
+                    fn set_high(&mut self) -> Result<(), Self::Error> {
+                        if self.mode.is_output() {
+                            self.set_state(State::High);
+                            Ok(())
+                        }
+                        else {
+                            Err(PinModeError::IncorrectMode)
+                        }
+                    }
+                    fn set_low(&mut self) -> Result<(), Self::Error> {
+                        if self.mode.is_output() {
+                            self.set_state(State::Low);
+                            Ok(())
+                        }
+                        else {
+                            Err(PinModeError::IncorrectMode)
+                        }
+                    }
+                }
+
+                impl InputPin for $PXi<Dynamic> {
+                    type Error = PinModeError;
+                    fn is_high(&self) -> Result<bool, Self::Error> {
+                        self.is_low().map(|b| !b)
+                    }
+                    fn is_low(&self) -> Result<bool, Self::Error> {
+                        if self.mode.is_input() {
+                            Ok(self._is_low())
+                        }
+                        else {
+                            Err(PinModeError::IncorrectMode)
+                        }
+                    }
+                }
+
+
+                // Exti pin impls
+
+                impl<MODE> ExtiPin for $PXi<Input<MODE>> {
+                    /// Configure EXTI Line $i to trigger from this pin.
+                    fn make_interrupt_source(&mut self, afio: &mut afio::Parts) {
+                        let offset = 4 * ($i % 4);
+                        afio.$exticri.$exticri().modify(|r, w| unsafe {
+                            let mut exticr = r.bits();
+                            exticr = (exticr & !(0xf << offset)) | ($extigpionr << offset);
+                            w.bits(exticr)
+                        });
+                    }
+
+                    /// Generate interrupt on rising edge, falling edge or both
+                    fn trigger_on_edge(&mut self, exti: &EXTI, edge: Edge) {
+                        match edge {
+                            Edge::RISING => {
+                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
+                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
+                            },
+                            Edge::FALLING => {
+                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
+                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
+                            },
+                            Edge::RISING_FALLING => {
+                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
+                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
+                            }
+                        }
+                    }
+
+                    /// Enable external interrupts from this pin.
+                    fn enable_interrupt(&mut self, exti: &EXTI) {
+                        exti.imr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
+                    }
+
+                    /// Disable external interrupts from this pin
+                    fn disable_interrupt(&mut self, exti: &EXTI) {
+                        exti.imr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
+                    }
+
+                    /// Clear the interrupt pending bit for this pin
+                    fn clear_interrupt_pending_bit(&mut self) {
+                        unsafe { (*EXTI::ptr()).pr.write(|w| w.bits(1 << $i) ) };
+                    }
+
+                    /// Reads the interrupt pending bit for this pin
+                    fn check_interrupt(&mut self) -> bool {
+                        unsafe { ((*EXTI::ptr()).pr.read().bits() & (1 << $i)) != 0 }
+                    }
+                }
+
+
+                // Internal helper functions
+
+                // NOTE: The functions in this impl block are "safe", but they
+                // are callable when the pin is in modes where they don't make
+                // sense.
+                impl<MODE> $PXi<MODE> {
+                    /**
+                      Set the output of the pin regardless of its mode.
+                      Primarily used to set the output value of the pin
+                      before changing its mode to an output to avoid
+                      a short spike of an incorrect value
+                    */
+                    fn set_state(&mut self, state: State) {
+                        match state {
+                            State::High => unsafe {
+                                (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i))
+                            }
+                            State::Low => unsafe {
+                                (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i)))
+                            }
+                        }
+                    }
+
+                    fn _is_set_low(&self) -> bool {
+                        unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 }
+                    }
+
+                    fn _is_low(&self) -> bool {
+                        // NOTE(unsafe) atomic read with no side effects
+                        unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }
+                    }
+                }
+
                 impl PinMode<$CR> for $PXi<Input<Floating>> {
                     unsafe fn set_mode(cr: &mut $CR) -> Self {
                         const OFFSET: u32 = (4 * $i) % 32;
@@ -600,7 +866,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Input::_new() }
                     }
                 }
 
@@ -624,7 +890,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Input::_new() }
                     }
                 }
 
@@ -649,7 +915,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Input::_new() }
                     }
                 }
 
@@ -668,7 +934,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Output::_new() }
                     }
                 }
 
@@ -688,7 +954,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
+                        $PXi { mode: Output::_new() }
                     }
                 }
 
@@ -708,125 +974,7 @@ macro_rules! gpio {
                                 w.bits((r.bits() & !(0b1111 << OFFSET)) | (BITS << OFFSET))
                             });
 
-                        $PXi { _mode: PhantomData }
-                    }
-                }
-
-                impl<MODE> $PXi<MODE> where MODE: Active {
-                    /// Erases the pin number from the type
-                    fn into_generic(self) -> Generic<MODE> {
-                        Generic {
-                            i: $i,
-                            _mode: self._mode,
-                        }
-                    }
-
-                    /// Erases the pin number and port 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> {
-                        self.into_generic().downgrade()
-                    }
-                }
-
-                impl<MODE> OutputPin for $PXi<Output<MODE>> {
-                    type Error = Infallible;
-                    fn set_high(&mut self) -> Result<(), Self::Error> {
-                        // NOTE(unsafe) atomic write to a stateless register
-                        Ok(unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) })
-                    }
-
-                    fn set_low(&mut self) -> Result<(), Self::Error> {
-                        // NOTE(unsafe) atomic write to a stateless register
-                        Ok(unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) })
-                    }
-                }
-
-                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
-                    fn is_set_high(&self) -> Result<bool, Self::Error> {
-                        self.is_set_low().map(|b| !b)
-                    }
-
-                    fn is_set_low(&self) -> Result<bool, Self::Error> {
-                        // NOTE(unsafe) atomic read with no side effects
-                        Ok(unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 })
-                    }
-                }
-
-                impl<MODE> toggleable::Default for $PXi<Output<MODE>> {}
-
-                impl<MODE> InputPin for $PXi<Input<MODE>> {
-                    type Error = Infallible;
-                    fn is_high(&self) -> Result<bool, Self::Error> {
-                        self.is_low().map(|b| !b)
-                    }
-
-                    fn is_low(&self) -> Result<bool, Self::Error> {
-                        // NOTE(unsafe) atomic read with no side effects
-                        Ok(unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 })
-                    }
-                }
-
-                impl InputPin for $PXi<Output<OpenDrain>> {
-                    type Error = Infallible;
-                    fn is_high(&self) -> Result<bool, Self::Error> {
-                        self.is_low().map(|b| !b)
-                    }
-
-                    fn is_low(&self) -> Result<bool, Self::Error> {
-                        // NOTE(unsafe) atomic read with no side effects
-                        Ok(unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 })
-                    }
-                }
-
-                impl<MODE> ExtiPin for $PXi<Input<MODE>> {
-                    /// Configure EXTI Line $i to trigger from this pin.
-                    fn make_interrupt_source(&mut self, afio: &mut afio::Parts) {
-                        let offset = 4 * ($i % 4);
-                        afio.$exticri.$exticri().modify(|r, w| unsafe {
-                            let mut exticr = r.bits();
-                            exticr = (exticr & !(0xf << offset)) | ($extigpionr << offset);
-                            w.bits(exticr)
-                        });
-                    }
-
-                    /// Generate interrupt on rising edge, falling edge or both
-                    fn trigger_on_edge(&mut self, exti: &EXTI, edge: Edge) {
-                        match edge {
-                            Edge::RISING => {
-                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
-                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
-                            },
-                            Edge::FALLING => {
-                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
-                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
-                            },
-                            Edge::RISING_FALLING => {
-                                exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
-                                exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
-                            }
-                        }
-                    }
-
-                    /// Enable external interrupts from this pin.
-                    fn enable_interrupt(&mut self, exti: &EXTI) {
-                        exti.imr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) });
-                    }
-
-                    /// Disable external interrupts from this pin
-                    fn disable_interrupt(&mut self, exti: &EXTI) {
-                        exti.imr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) });
-                    }
-
-                    /// Clear the interrupt pending bit for this pin
-                    fn clear_interrupt_pending_bit(&mut self) {
-                        unsafe { (*EXTI::ptr()).pr.write(|w| w.bits(1 << $i) ) };
-                    }
-
-                    /// Reads the interrupt pending bit for this pin
-                    fn check_interrupt(&mut self) -> bool {
-                        unsafe { ((*EXTI::ptr()).pr.read().bits() & (1 << $i)) != 0 }
+                        $PXi { mode: Analog{} }
                     }
                 }
             )+