Skip to content

Commit 2bb0a55

Browse files
JurajSadelbugadani
andauthored
Introduce PinGuard and reconnecting an output signal to a different pin now clears previous connections (#3012)
* wip * Fix and clean up * Introduce PinGuard and reconnecting an output signal to a different pin clears previous connections * Add demft for PinGuard * Keep pins around in SpiDma * Simplify i2c * Changelog * Clean up * Don't use PeripheralRef directly * Handle RTS pin --------- Co-authored-by: Dániel Buga <[email protected]>
1 parent fc2815b commit 2bb0a55

File tree

6 files changed

+224
-51
lines changed

6 files changed

+224
-51
lines changed

esp-hal/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- `timer::wait` is now blocking (#2882)
2424
- By default, set `tx_idle_num` to 0 so that bytes written to TX FIFO are always immediately transmitted. (#2859)
2525
- `Rng` and `Trng` now implement `Peripheral<P = Self>` (#2992)
26+
- SPI, UART, I2C: `with_<pin>` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012)
27+
- SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012)
28+
2629
- `Async` drivers are no longer `Send` (#2980)
2730
- GPIO drivers now take configuration structs, and their constructors are fallible (#2990)
2831

esp-hal/src/gpio/interconnect.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{
1313
OutputPin,
1414
OutputSignalType,
1515
Pin,
16+
PinGuard,
1617
Pull,
1718
FUNC_IN_SEL_OFFSET,
1819
GPIO_FUNCTION,
@@ -106,7 +107,6 @@ impl gpio::OutputSignal {
106107
pub fn connect_to(self, pin: impl Peripheral<P = impl PeripheralOutput>) {
107108
crate::into_mapped_ref!(pin);
108109

109-
// FIXME: disconnect previous connection(s)
110110
pin.connect_peripheral_to_output(self);
111111
}
112112

@@ -718,4 +718,20 @@ impl OutputConnection {
718718
fn disconnect_from_peripheral_output(&mut self, signal: gpio::OutputSignal);
719719
}
720720
}
721+
722+
pub(crate) fn connect_with_guard(
723+
this: impl Peripheral<P = impl PeripheralOutput>,
724+
signal: crate::gpio::OutputSignal,
725+
) -> PinGuard {
726+
crate::into_mapped_ref!(this);
727+
match &this.0 {
728+
OutputConnectionInner::Output(pin) => {
729+
PinGuard::new(unsafe { pin.pin.clone_unchecked() }, signal)
730+
}
731+
OutputConnectionInner::DirectOutput(pin) => {
732+
PinGuard::new(unsafe { pin.pin.clone_unchecked() }, signal)
733+
}
734+
OutputConnectionInner::Constant(_) => PinGuard::new_unconnected(signal),
735+
}
736+
}
721737
}

esp-hal/src/gpio/mod.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,56 @@ impl CFnPtr {
112112
}
113113
}
114114

115+
/// Represents a pin-peripheral connection that, when dropped, disconnects the
116+
/// peripheral from the pin.
117+
///
118+
/// This only needs to be applied to output signals, as it's not possible to
119+
/// connect multiple inputs to the same peripheral signal.
120+
#[derive(Debug)]
121+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
122+
pub(crate) struct PinGuard {
123+
pin: u8,
124+
signal: OutputSignal,
125+
}
126+
127+
impl crate::private::Sealed for PinGuard {}
128+
impl Peripheral for PinGuard {
129+
type P = Self;
130+
131+
unsafe fn clone_unchecked(&self) -> Self::P {
132+
Self {
133+
pin: self.pin,
134+
signal: self.signal,
135+
}
136+
}
137+
}
138+
139+
impl PinGuard {
140+
pub(crate) fn new(mut pin: AnyPin, signal: OutputSignal) -> Self {
141+
signal.connect_to(&mut pin);
142+
Self {
143+
pin: pin.number(),
144+
signal,
145+
}
146+
}
147+
148+
pub(crate) fn new_unconnected(signal: OutputSignal) -> Self {
149+
Self {
150+
pin: u8::MAX,
151+
signal,
152+
}
153+
}
154+
}
155+
156+
impl Drop for PinGuard {
157+
fn drop(&mut self) {
158+
if self.pin != u8::MAX {
159+
let mut pin = unsafe { AnyPin::steal(self.pin) };
160+
self.signal.disconnect_from(&mut pin);
161+
}
162+
}
163+
}
164+
115165
/// Event used to trigger interrupts.
116166
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
117167
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

esp-hal/src/i2c/master/mod.rs

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ use fugit::HertzU32;
3636
use crate::{
3737
asynch::AtomicWaker,
3838
clock::Clocks,
39-
gpio::{interconnect::PeripheralOutput, InputSignal, OutputSignal, Pull},
39+
gpio::{
40+
interconnect::{OutputConnection, PeripheralOutput},
41+
InputSignal,
42+
OutputSignal,
43+
PinGuard,
44+
Pull,
45+
},
4046
interrupt::{InterruptConfigurable, InterruptHandler},
4147
pac::i2c0::{RegisterBlock, COMD},
4248
peripheral::{Peripheral, PeripheralRef},
@@ -439,6 +445,8 @@ pub struct I2c<'d, Dm: DriverMode> {
439445
phantom: PhantomData<Dm>,
440446
config: Config,
441447
guard: PeripheralGuard,
448+
sda_pin: PinGuard,
449+
scl_pin: PinGuard,
442450
}
443451

444452
#[cfg(any(doc, feature = "unstable"))]
@@ -542,27 +550,35 @@ impl<'d, Dm: DriverMode> I2c<'d, Dm> {
542550
}
543551

544552
/// Connect a pin to the I2C SDA signal.
545-
pub fn with_sda(self, sda: impl Peripheral<P = impl PeripheralOutput> + 'd) -> Self {
553+
///
554+
/// This will replace previous pin assignments for this signal.
555+
pub fn with_sda(mut self, sda: impl Peripheral<P = impl PeripheralOutput> + 'd) -> Self {
546556
let info = self.driver().info;
547557
let input = info.sda_input;
548558
let output = info.sda_output;
549-
self.with_pin(sda, input, output)
559+
Self::connect_pin(sda, input, output, &mut self.sda_pin);
560+
561+
self
550562
}
551563

552564
/// Connect a pin to the I2C SCL signal.
553-
pub fn with_scl(self, scl: impl Peripheral<P = impl PeripheralOutput> + 'd) -> Self {
565+
///
566+
/// This will replace previous pin assignments for this signal.
567+
pub fn with_scl(mut self, scl: impl Peripheral<P = impl PeripheralOutput> + 'd) -> Self {
554568
let info = self.driver().info;
555569
let input = info.scl_input;
556570
let output = info.scl_output;
557-
self.with_pin(scl, input, output)
571+
Self::connect_pin(scl, input, output, &mut self.scl_pin);
572+
573+
self
558574
}
559575

560-
fn with_pin(
561-
self,
576+
fn connect_pin(
562577
pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
563578
input: InputSignal,
564579
output: OutputSignal,
565-
) -> Self {
580+
guard: &mut PinGuard,
581+
) {
566582
crate::into_mapped_ref!(pin);
567583
// avoid the pin going low during configuration
568584
pin.set_output_high(true);
@@ -572,9 +588,8 @@ impl<'d, Dm: DriverMode> I2c<'d, Dm> {
572588
pin.pull_direction(Pull::Up);
573589

574590
input.connect_to(&mut pin);
575-
output.connect_to(&mut pin);
576591

577-
self
592+
*guard = OutputConnection::connect_with_guard(pin, output);
578593
}
579594
}
580595

@@ -588,11 +603,16 @@ impl<'d> I2c<'d, Blocking> {
588603

589604
let guard = PeripheralGuard::new(i2c.info().peripheral);
590605

606+
let sda_pin = PinGuard::new_unconnected(i2c.info().sda_output);
607+
let scl_pin = PinGuard::new_unconnected(i2c.info().scl_output);
608+
591609
let i2c = I2c {
592610
i2c,
593611
phantom: PhantomData,
594612
config,
595613
guard,
614+
sda_pin,
615+
scl_pin,
596616
};
597617

598618
i2c.driver().setup(&i2c.config)?;
@@ -652,6 +672,8 @@ impl<'d> I2c<'d, Blocking> {
652672
phantom: PhantomData,
653673
config: self.config,
654674
guard: self.guard,
675+
sda_pin: self.sda_pin,
676+
scl_pin: self.scl_pin,
655677
}
656678
}
657679

@@ -904,6 +926,8 @@ impl<'d> I2c<'d, Async> {
904926
phantom: PhantomData,
905927
config: self.config,
906928
guard: self.guard,
929+
sda_pin: self.sda_pin,
930+
scl_pin: self.scl_pin,
907931
}
908932
}
909933

0 commit comments

Comments
 (0)