diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5904517..7511f86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable - name: Install nextest uses: taiki-e/install-action@nextest - - run: cargo nextest run --all-features -p va108xx-hal + - run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass # I think we can skip those on an embedded crate.. # - run: cargo test --doc -p va108xx-hal diff --git a/board-tests/src/main.rs b/board-tests/src/main.rs index 8c47f9c..75a2078 100644 --- a/board-tests/src/main.rs +++ b/board-tests/src/main.rs @@ -99,9 +99,11 @@ fn main() -> ! { } TestCase::TestMask => { // Tie PORTA[0] to PORTA[1] for these tests! - let input = pinsa.pa1.into_pull_down_input().clear_datamask(); + let mut input = pinsa.pa1.into_pull_down_input(); + input.clear_datamask(); assert!(!input.datamask()); - let mut out = pinsa.pa0.into_push_pull_output().clear_datamask(); + let mut out = pinsa.pa0.into_push_pull_output(); + out.clear_datamask(); assert!(input.is_low_masked().is_err()); assert!(out.set_high_masked().is_err()); } @@ -119,17 +121,15 @@ fn main() -> ! { assert_eq!(PinsB::get_perid(), 0x004007e1); } TestCase::Pulse => { - let mut output_pulsed = pinsa - .pa0 - .into_push_pull_output() - .pulse_mode(true, PinState::Low); + let mut output_pulsed = pinsa.pa0.into_push_pull_output(); + output_pulsed.pulse_mode(true, PinState::Low); rprintln!("Pulsing high 10 times.."); output_pulsed.set_low().unwrap(); for _ in 0..10 { output_pulsed.set_high().unwrap(); cortex_m::asm::delay(25_000_000); } - let mut output_pulsed = output_pulsed.pulse_mode(true, PinState::High); + output_pulsed.pulse_mode(true, PinState::High); rprintln!("Pulsing low 10 times.."); for _ in 0..10 { output_pulsed.set_low().unwrap(); diff --git a/examples/rtic/src/bin/uart-echo-rtic.rs b/examples/rtic/src/bin/uart-echo-rtic.rs index 1a22b64..2a71a05 100644 --- a/examples/rtic/src/bin/uart-echo-rtic.rs +++ b/examples/rtic/src/bin/uart-echo-rtic.rs @@ -68,10 +68,7 @@ mod app { Shared { rb: StaticRb::default(), }, - Local { - rx, - tx, - }, + Local { rx, tx }, ) } diff --git a/flashloader/src/main.rs b/flashloader/src/main.rs index cc3478a..4143c3c 100644 --- a/flashloader/src/main.rs +++ b/flashloader/src/main.rs @@ -251,10 +251,10 @@ mod app { } let packet_len = packet_len.unwrap(); log::info!(target: "TC Handler", "received packet with length {}", packet_len); - let popped_packet_len = cx.shared.tc_rb.lock(|rb| { - rb.buf - .pop_slice(&mut cx.local.tc_buf[0..packet_len]) - }); + let popped_packet_len = cx + .shared + .tc_rb + .lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len])); assert_eq!(popped_packet_len, packet_len); // Read a telecommand, now handle it. handle_valid_pus_tc(&mut cx); @@ -272,8 +272,7 @@ mod app { let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap(); cx.shared.tm_rb.lock(|prod| { prod.sizes.try_push(tm.len_written()).unwrap(); - prod.buf - .push_slice(&cx.local.verif_buf[0..written_size]); + prod.buf.push_slice(&cx.local.verif_buf[0..written_size]); }); }; let token = cx.local.verif_reporter.add_tc(&pus_tc); diff --git a/va108xx-hal/CHANGELOG.md b/va108xx-hal/CHANGELOG.md index c5db821..dd25ba2 100644 --- a/va108xx-hal/CHANGELOG.md +++ b/va108xx-hal/CHANGELOG.md @@ -8,9 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] -## [v0.9.0] 2024-10-07 +## [v0.9.0] - Deleted some HAL re-exports in the PWM module +- GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now + methods which mutable modify the pin instead of consuming and returning it. +- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion + methods. ## [v0.8.0] 2024-09-30 diff --git a/va108xx-hal/src/gpio/dynpin.rs b/va108xx-hal/src/gpio/dynpin.rs index 4c96e77..a76a3f6 100644 --- a/va108xx-hal/src/gpio/dynpin.rs +++ b/va108xx-hal/src/gpio/dynpin.rs @@ -172,7 +172,7 @@ pub struct DynPinId { /// /// This `struct` takes ownership of a [`DynPinId`] and provides an API to /// access the corresponding regsiters. -struct DynRegisters { +pub(crate) struct DynRegisters { id: DynPinId, } @@ -207,7 +207,7 @@ impl DynRegisters { /// This type acts as a type-erased version of [`Pin`]. Every pin is represented /// by the same type, and pins are tracked and distinguished at run-time. pub struct DynPin { - regs: DynRegisters, + pub(crate) regs: DynRegisters, mode: DynPinMode, } @@ -220,7 +220,7 @@ impl DynPin { /// must be at most one corresponding [`DynPin`] in existence at any given /// time. Violating this requirement is `unsafe`. #[inline] - unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { + pub(crate) unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { DynPin { regs: DynRegisters::new(id), mode, @@ -306,7 +306,69 @@ impl DynPin { self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); } - common_reg_if_functions!(); + #[inline] + pub fn datamask(&self) -> bool { + self.regs.datamask() + } + + #[inline] + pub fn clear_datamask(&mut self) { + self.regs.clear_datamask(); + } + + #[inline] + pub fn set_datamask(&mut self) { + self.regs.set_datamask(); + } + + #[inline] + pub fn is_high_masked(&self) -> Result { + self.regs.read_pin_masked() + } + + #[inline] + pub fn is_low_masked(&self) -> Result { + self.regs.read_pin_masked().map(|v| !v) + } + + #[inline] + pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.regs.write_pin_masked(true) + } + + #[inline] + pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.regs.write_pin_masked(false) + } + + pub(crate) fn irq_enb( + &mut self, + irq_cfg: crate::IrqCfg, + syscfg: Option<&mut va108xx::Sysconfig>, + irqsel: Option<&mut va108xx::Irqsel>, + ) { + if let Some(syscfg) = syscfg { + crate::clock::enable_peripheral_clock(syscfg, crate::clock::PeripheralClocks::Irqsel); + } + self.regs.enable_irq(); + if let Some(irqsel) = irqsel { + if irq_cfg.route { + match self.regs.id().group { + // Set the correct interrupt number in the IRQSEL register + DynGroup::A => { + irqsel + .porta0(self.regs.id().num as usize) + .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); + } + DynGroup::B => { + irqsel + .portb0(self.regs.id().num as usize) + .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); + } + } + } + } + } /// See p.53 of the programmers guide for more information. /// Possible delays in clock cycles: @@ -327,15 +389,16 @@ impl DynPin { /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state + #[inline] pub fn pulse_mode( - self, + &mut self, enable: bool, default_state: PinState, - ) -> Result { + ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Output(_) => { self.regs.pulse_mode(enable, default_state); - Ok(self) + Ok(()) } _ => Err(InvalidPinTypeError), } @@ -344,48 +407,50 @@ impl DynPin { /// See p.37 and p.38 of the programmers guide for more information. #[inline] pub fn filter_type( - self, + &mut self, filter: FilterType, clksel: FilterClkSel, - ) -> Result { + ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) => { self.regs.filter_type(filter, clksel); - Ok(self) + Ok(()) } _ => Err(InvalidPinTypeError), } } + #[inline] pub fn interrupt_edge( - mut self, + &mut self, edge_type: InterruptEdge, irq_cfg: IrqCfg, syscfg: Option<&mut pac::Sysconfig>, irqsel: Option<&mut pac::Irqsel>, - ) -> Result { + ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { self.regs.interrupt_edge(edge_type); self.irq_enb(irq_cfg, syscfg, irqsel); - Ok(self) + Ok(()) } _ => Err(InvalidPinTypeError), } } + #[inline] pub fn interrupt_level( - mut self, + &mut self, level_type: InterruptLevel, irq_cfg: IrqCfg, syscfg: Option<&mut pac::Sysconfig>, irqsel: Option<&mut pac::Irqsel>, - ) -> Result { + ) -> Result<(), InvalidPinTypeError> { match self.mode { DynPinMode::Input(_) | DynPinMode::Output(_) => { self.regs.interrupt_level(level_type); self.irq_enb(irq_cfg, syscfg, irqsel); - Ok(self) + Ok(()) } _ => Err(InvalidPinTypeError), } @@ -438,6 +503,21 @@ impl DynPin { fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> { self._write(true) } + + /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] + /// + /// There is no way for the compiler to know if the conversion will be + /// successful at compile-time. We must verify the conversion at run-time + /// or refuse to perform it. + #[inline] + pub fn upgrade(self) -> Result, InvalidPinTypeError> { + if self.regs.id == I::DYN && self.mode == M::DYN { + // The `DynPin` is consumed, so it is safe to replace it with the + // corresponding `Pin` + return Ok(unsafe { Pin::new() }); + } + Err(InvalidPinTypeError) + } } //================================================================================================== @@ -448,10 +528,8 @@ impl From> for DynPin { /// Erase the type-level information in a [`Pin`] and return a value-level /// [`DynPin`] #[inline] - fn from(_pin: Pin) -> Self { - // The `Pin` is consumed, so it is safe to replace it with the - // corresponding `DynPin` - unsafe { DynPin::new(I::DYN, M::DYN) } + fn from(pin: Pin) -> Self { + pin.downgrade() } } @@ -465,13 +543,7 @@ impl TryFrom for Pin { /// or refuse to perform it. #[inline] fn try_from(pin: DynPin) -> Result { - if pin.regs.id == I::DYN && pin.mode == M::DYN { - // The `DynPin` is consumed, so it is safe to replace it with the - // corresponding `Pin` - Ok(unsafe { Self::new() }) - } else { - Err(InvalidPinTypeError) - } + pin.upgrade() } } @@ -506,10 +578,12 @@ impl embedded_hal::digital::InputPin for DynPin { } impl embedded_hal::digital::StatefulOutputPin for DynPin { + #[inline] fn is_set_high(&mut self) -> Result { self._is_high() } + #[inline] fn is_set_low(&mut self) -> Result { self._is_low() } diff --git a/va108xx-hal/src/gpio/mod.rs b/va108xx-hal/src/gpio/mod.rs index 0d2a726..4bdbe41 100644 --- a/va108xx-hal/src/gpio/mod.rs +++ b/va108xx-hal/src/gpio/mod.rs @@ -26,81 +26,6 @@ #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct IsMaskedError; -macro_rules! common_reg_if_functions { - () => { - paste::paste!( - #[inline] - pub fn datamask(&self) -> bool { - self.regs.datamask() - } - - #[inline] - pub fn clear_datamask(self) -> Self { - self.regs.clear_datamask(); - self - } - - #[inline] - pub fn set_datamask(self) -> Self { - self.regs.set_datamask(); - self - } - - #[inline] - pub fn is_high_masked(&self) -> Result { - self.regs.read_pin_masked() - } - - #[inline] - pub fn is_low_masked(&self) -> Result { - self.regs.read_pin_masked().map(|v| !v) - } - - #[inline] - pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(true) - } - - #[inline] - pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { - self.regs.write_pin_masked(false) - } - - fn irq_enb( - &mut self, - irq_cfg: crate::IrqCfg, - syscfg: Option<&mut va108xx::Sysconfig>, - irqsel: Option<&mut va108xx::Irqsel>, - ) { - if syscfg.is_some() { - crate::clock::enable_peripheral_clock( - syscfg.unwrap(), - crate::clock::PeripheralClocks::Irqsel, - ); - } - self.regs.enable_irq(); - if let Some(irqsel) = irqsel { - if irq_cfg.route { - match self.regs.id().group { - // Set the correct interrupt number in the IRQSEL register - DynGroup::A => { - irqsel - .porta0(self.regs.id().num as usize) - .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); - } - DynGroup::B => { - irqsel - .portb0(self.regs.id().num as usize) - .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); - } - } - } - } - } - ); - }; -} - pub mod dynpin; pub use dynpin::*; diff --git a/va108xx-hal/src/gpio/pin.rs b/va108xx-hal/src/gpio/pin.rs index 944c7e8..c864544 100644 --- a/va108xx-hal/src/gpio/pin.rs +++ b/va108xx-hal/src/gpio/pin.rs @@ -72,6 +72,7 @@ //! and [`StatefulOutputPin`]. use super::dynpin::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode}; use super::reg::RegisterInterface; +use super::DynPin; use crate::{ pac::{Irqsel, Porta, Portb, Sysconfig}, typelevel::Sealed, @@ -321,8 +322,8 @@ macro_rules! pin_id { /// A type-level GPIO pin, parameterized by [PinId] and [PinMode] types pub struct Pin { - pub(in crate::gpio) regs: Registers, - mode: PhantomData, + inner: DynPin, + phantom: PhantomData<(I, M)>, } impl Pin { @@ -336,8 +337,8 @@ impl Pin { #[inline] pub(crate) unsafe fn new() -> Pin { Pin { - regs: Registers::new(), - mode: PhantomData, + inner: DynPin::new(I::DYN, M::DYN), + phantom: PhantomData, } } @@ -347,7 +348,7 @@ impl Pin { // Only modify registers if we are actually changing pin mode // This check should compile away if N::DYN != M::DYN { - self.regs.change_mode::(); + self.inner.regs.change_mode(N::DYN); } // Safe because we drop the existing Pin unsafe { Pin::new() } @@ -407,31 +408,78 @@ impl Pin { self.into_mode() } - common_reg_if_functions!(); + #[inline] + pub fn datamask(&self) -> bool { + self.inner.datamask() + } + + #[inline] + pub fn clear_datamask(&mut self) { + self.inner.clear_datamask() + } + + #[inline] + pub fn set_datamask(&mut self) { + self.inner.set_datamask() + } + + #[inline] + pub fn is_high_masked(&self) -> Result { + self.inner.is_high_masked() + } + + #[inline] + pub fn is_low_masked(&self) -> Result { + self.inner.is_low_masked() + } + + #[inline] + pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.inner.set_high_masked() + } + + #[inline] + pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> { + self.inner.set_low_masked() + } + + #[inline] + pub fn downgrade(self) -> DynPin { + self.inner + } + + fn irq_enb( + &mut self, + irq_cfg: crate::IrqCfg, + syscfg: Option<&mut va108xx::Sysconfig>, + irqsel: Option<&mut va108xx::Irqsel>, + ) { + self.inner.irq_enb(irq_cfg, syscfg, irqsel); + } #[inline] pub(crate) fn _set_high(&mut self) { - self.regs.write_pin(true) + self.inner.regs.write_pin(true) } #[inline] pub(crate) fn _set_low(&mut self) { - self.regs.write_pin(false) + self.inner.regs.write_pin(false) } #[inline] pub(crate) fn _toggle_with_toggle_reg(&mut self) { - self.regs.toggle(); + self.inner.regs.toggle(); } #[inline] pub(crate) fn _is_low(&self) -> bool { - !self.regs.read_pin() + !self.inner.regs.read_pin() } #[inline] pub(crate) fn _is_high(&self) -> bool { - self.regs.read_pin() + self.inner.regs.read_pin() } } @@ -524,27 +572,25 @@ impl AsMut

for SpecificPin

{ impl Pin> { pub fn interrupt_edge( - mut self, + &mut self, edge_type: InterruptEdge, irq_cfg: IrqCfg, syscfg: Option<&mut Sysconfig>, irqsel: Option<&mut Irqsel>, - ) -> Self { - self.regs.interrupt_edge(edge_type); + ) { + self.inner.regs.interrupt_edge(edge_type); self.irq_enb(irq_cfg, syscfg, irqsel); - self } pub fn interrupt_level( - mut self, + &mut self, level_type: InterruptLevel, irq_cfg: IrqCfg, syscfg: Option<&mut Sysconfig>, irqsel: Option<&mut Irqsel>, - ) -> Self { - self.regs.interrupt_level(level_type); + ) { + self.inner.regs.interrupt_level(level_type); self.irq_enb(irq_cfg, syscfg, irqsel); - self } } @@ -556,7 +602,7 @@ impl Pin> { /// - Delay 1 + Delay 2: 3 #[inline] pub fn delay(self, delay_1: bool, delay_2: bool) -> Self { - self.regs.delay(delay_1, delay_2); + self.inner.regs.delay(delay_1, delay_2); self } @@ -568,42 +614,38 @@ impl Pin> { /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state - pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self { - self.regs.pulse_mode(enable, default_state); - self + pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) { + self.inner.regs.pulse_mode(enable, default_state); } pub fn interrupt_edge( - mut self, + &mut self, edge_type: InterruptEdge, irq_cfg: IrqCfg, syscfg: Option<&mut Sysconfig>, irqsel: Option<&mut Irqsel>, - ) -> Self { - self.regs.interrupt_edge(edge_type); + ) { + self.inner.regs.interrupt_edge(edge_type); self.irq_enb(irq_cfg, syscfg, irqsel); - self } pub fn interrupt_level( - mut self, + &mut self, level_type: InterruptLevel, irq_cfg: IrqCfg, syscfg: Option<&mut Sysconfig>, irqsel: Option<&mut Irqsel>, - ) -> Self { - self.regs.interrupt_level(level_type); + ) { + self.inner.regs.interrupt_level(level_type); self.irq_enb(irq_cfg, syscfg, irqsel); - self } } impl Pin> { /// See p.37 and p.38 of the programmers guide for more information. #[inline] - pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self { - self.regs.filter_type(filter, clksel); - self + pub fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { + self.inner.regs.filter_type(filter, clksel); } } @@ -679,47 +721,6 @@ where } } -//================================================================================================== -// Registers -//================================================================================================== - -/// Provide a safe register interface for [`Pin`]s -/// -/// This `struct` takes ownership of a [`PinId`] and provides an API to -/// access the corresponding registers. -pub(in crate::gpio) struct Registers { - id: PhantomData, -} - -// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that -// each pin is a singleton, so this implementation is safe. -unsafe impl RegisterInterface for Registers { - #[inline] - fn id(&self) -> DynPinId { - I::DYN - } -} - -impl Registers { - /// Create a new instance of [`Registers`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`PinId`] - #[inline] - unsafe fn new() -> Self { - Registers { id: PhantomData } - } - - /// Provide a type-level equivalent for the - /// [`RegisterInterface::change_mode`] method. - #[inline] - pub(in crate::gpio) fn change_mode(&mut self) { - RegisterInterface::change_mode(self, M::DYN); - } -} - //================================================================================================== // Pin definitions //================================================================================================== diff --git a/va108xx-hal/src/gpio/reg.rs b/va108xx-hal/src/gpio/reg.rs index afc83a2..f194536 100644 --- a/va108xx-hal/src/gpio/reg.rs +++ b/va108xx-hal/src/gpio/reg.rs @@ -293,7 +293,7 @@ pub(super) unsafe trait RegisterInterface { /// Only useful for input pins #[inline] - fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) { + fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) { self.iocfg_port().modify(|_, w| { // Safety: Only write to register for this Pin ID unsafe { @@ -331,7 +331,7 @@ pub(super) unsafe trait RegisterInterface { /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state - fn pulse_mode(&self, enable: bool, default_state: PinState) { + fn pulse_mode(&mut self, enable: bool, default_state: PinState) { let portreg = self.port_reg(); unsafe { if enable {