Skip to content

Commit 1f8fccf

Browse files
bors[bot]Finomnis
andauthored
Merge #401
401: Add OpenDrainIO pin state (with InputPin capability) r=jonas-schievink a=Finomnis As already reported in #339, there is currently no way to create an I/O pin in nrf-hal. There are several reasons why having this would be useful. My personal usecase is the DHT22/AM2302 sensor. Controlling it requires a single-wire pulled-up open drain IO. Sadly, this means that all libraries that can interface with it [require `InputPin + OutputPin`](https://docs.rs/dht-sensor/0.2.1/dht_sensor/trait.InputOutputPin.html). # Solution There is no inherent reason why nrf chips shouldn't be able to implement this. The input buffer is always available and allows reading back the pin values during every pin configuration. Although for energy saving reasons, the input buffer is currently disabled during the `OpenDrain` state. My proposal is to add an `OpenDrainIO` state that does not disable the input buffer and implements `InputPin`. Co-authored-by: Finomnis <[email protected]>
2 parents 7b8387d + 2520dd4 commit 1f8fccf

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

nrf-hal-common/src/gpio.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub struct PushPull;
2525
/// Open drain output (type state).
2626
pub struct OpenDrain;
2727

28+
/// Open drain input/output (type state).
29+
pub struct OpenDrainIO;
30+
2831
/// Represents a digital input or output level.
2932
#[derive(Debug, Eq, PartialEq)]
3033
pub enum Level {
@@ -284,6 +287,40 @@ impl<MODE> Pin<MODE> {
284287
pin
285288
}
286289

290+
/// Convert the pin to be an open-drain input/output.
291+
///
292+
/// Similar to [`into_open_drain_output`](Self::into_open_drain_output), but can also be read from.
293+
///
294+
/// This method currently does not support configuring an internal pull-up or pull-down
295+
/// resistor.
296+
pub fn into_open_drain_input_output(
297+
self,
298+
config: OpenDrainConfig,
299+
initial_output: Level,
300+
) -> Pin<Output<OpenDrainIO>> {
301+
let mut pin = Pin {
302+
_mode: PhantomData,
303+
pin_port: self.pin_port,
304+
};
305+
306+
match initial_output {
307+
Level::Low => pin.set_low().unwrap(),
308+
Level::High => pin.set_high().unwrap(),
309+
}
310+
311+
// This is safe, as we restrict our access to the dedicated register for this pin.
312+
self.conf().write(|w| {
313+
w.dir().output();
314+
w.input().connect();
315+
w.pull().disabled();
316+
w.drive().variant(config.variant());
317+
w.sense().disabled();
318+
w
319+
});
320+
321+
pin
322+
}
323+
287324
/// Disconnects the pin.
288325
///
289326
/// In disconnected mode the pin cannot be used as input or output.
@@ -311,6 +348,18 @@ impl<MODE> InputPin for Pin<Input<MODE>> {
311348
}
312349
}
313350

351+
impl InputPin for Pin<Output<OpenDrainIO>> {
352+
type Error = Void;
353+
354+
fn is_high(&self) -> Result<bool, Self::Error> {
355+
self.is_low().map(|v| !v)
356+
}
357+
358+
fn is_low(&self) -> Result<bool, Self::Error> {
359+
Ok(self.block().in_.read().bits() & (1 << self.pin()) == 0)
360+
}
361+
}
362+
314363
impl<MODE> OutputPin for Pin<Output<MODE>> {
315364
type Error = Void;
316365

@@ -406,6 +455,7 @@ macro_rules! gpio {
406455
PullDown,
407456
PullUp,
408457
PushPull,
458+
OpenDrainIO,
409459

410460
PhantomData,
411461
$PX
@@ -555,6 +605,44 @@ macro_rules! gpio {
555605
pin
556606
}
557607

608+
/// Convert the pin to be an open-drain input/output
609+
///
610+
/// Similar to [`into_open_drain_output`](Self::into_open_drain_output), but can also be read from.
611+
///
612+
/// This method currently does not support configuring an
613+
/// internal pull-up or pull-down resistor.
614+
pub fn into_open_drain_input_output(self,
615+
config: OpenDrainConfig,
616+
initial_output: Level,
617+
)
618+
-> $PXi<Output<OpenDrainIO>>
619+
{
620+
let mut pin = $PXi {
621+
_mode: PhantomData,
622+
};
623+
624+
match initial_output {
625+
Level::Low => pin.set_low().unwrap(),
626+
Level::High => pin.set_high().unwrap(),
627+
}
628+
629+
// This is safe, as we restrict our access to the
630+
// dedicated register for this pin.
631+
let pin_cnf = unsafe {
632+
&(*$PX::ptr()).pin_cnf[$i]
633+
};
634+
pin_cnf.write(|w| {
635+
w.dir().output();
636+
w.input().connect();
637+
w.pull().disabled();
638+
w.drive().variant(config.variant());
639+
w.sense().disabled();
640+
w
641+
});
642+
643+
pin
644+
}
645+
558646
/// Disconnects the pin.
559647
///
560648
/// In disconnected mode the pin cannot be used as input or output.
@@ -586,6 +674,18 @@ macro_rules! gpio {
586674
}
587675
}
588676

677+
impl InputPin for $PXi<Output<OpenDrainIO>> {
678+
type Error = Void;
679+
680+
fn is_high(&self) -> Result<bool, Self::Error> {
681+
self.is_low().map(|v| !v)
682+
}
683+
684+
fn is_low(&self) -> Result<bool, Self::Error> {
685+
Ok(unsafe { ((*$PX::ptr()).in_.read().bits() & (1 << $i)) == 0 })
686+
}
687+
}
688+
589689
impl<MODE> From<$PXi<MODE>> for Pin<MODE> {
590690
fn from(value: $PXi<MODE>) -> Self {
591691
value.degrade()

0 commit comments

Comments
 (0)