From bf4e0d8aba367944bb6552563d3e9d0afb3c9517 Mon Sep 17 00:00:00 2001 From: Maciej Kucharczyk Date: Sat, 2 Nov 2024 15:21:17 +0100 Subject: [PATCH] Initial DriveMode implementation --- .../lib/jgpioit/LineSessionWriteDriveIT.java | 137 ++++++++++++++++++ jgpio/src/jextract/gpiod-includes.txt | 1 + .../java/eu/softpol/lib/jgpio/DriveMode.java | 9 ++ .../main/java/eu/softpol/lib/jgpio/Line.java | 2 + .../softpol/lib/jgpio/LineOutputSession.java | 2 + .../lib/jgpio/internal/gpiod/GpiodLine.java | 7 + .../gpiod/GpiodLineOutputSession.java | 35 +++++ 7 files changed, 193 insertions(+) create mode 100644 jgpio-it/src/test/java/eu/softpol/lib/jgpioit/LineSessionWriteDriveIT.java create mode 100644 jgpio/src/main/java/eu/softpol/lib/jgpio/DriveMode.java diff --git a/jgpio-it/src/test/java/eu/softpol/lib/jgpioit/LineSessionWriteDriveIT.java b/jgpio-it/src/test/java/eu/softpol/lib/jgpioit/LineSessionWriteDriveIT.java new file mode 100644 index 0000000..4c4b33f --- /dev/null +++ b/jgpio-it/src/test/java/eu/softpol/lib/jgpioit/LineSessionWriteDriveIT.java @@ -0,0 +1,137 @@ +package eu.softpol.lib.jgpioit; + +import static eu.softpol.lib.jgpio.DriveMode.OPEN_DRAIN; +import static eu.softpol.lib.jgpio.DriveMode.OPEN_DRAIN_PULL_UP; +import static eu.softpol.lib.jgpio.DriveMode.OPEN_SOURCE; +import static eu.softpol.lib.jgpio.DriveMode.OPEN_SOURCE_PULL_DOWN; +import static eu.softpol.lib.jgpio.DriveMode.PUSH_PULL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.params.ParameterizedTest.INDEX_PLACEHOLDER; +import static org.junit.jupiter.params.provider.Arguments.of; + +import eu.softpol.lib.jgpio.Bias; +import eu.softpol.lib.jgpio.DriveMode; +import eu.softpol.lib.jgpio.Jgpio; +import eu.softpol.lib.jgpioit.util.TestPin; +import eu.softpol.lib.jgpioit.util.TwoPins; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class LineSessionWriteDriveIT { + + private static final List CONNECTED_PINS = List.of( + Defs.CONNECTED_PINS + ); + + private static Stream args() { + return Stream.of( + // PUSH_PULL + of(PUSH_PULL, Bias.HIGH_IMPEDANCE, false, false), + of(PUSH_PULL, Bias.HIGH_IMPEDANCE, true, true), + of(PUSH_PULL, Bias.PULL_UP, false, false), + of(PUSH_PULL, Bias.PULL_UP, true, true), + of(PUSH_PULL, Bias.PULL_DOWN, false, false), + of(PUSH_PULL, Bias.PULL_DOWN, true, true), + // OPEN_DRAIN + of(OPEN_DRAIN, Bias.HIGH_IMPEDANCE, false, false), + //of(OPEN_DRAIN, Bias.HIGH_IMPEDANCE, true, undefined), + of(OPEN_DRAIN, Bias.PULL_UP, false, false), + of(OPEN_DRAIN, Bias.PULL_UP, true, true),//??? + of(OPEN_DRAIN, Bias.PULL_DOWN, false, false), + of(OPEN_DRAIN, Bias.PULL_DOWN, true, false), + // OPEN_DRAIN_PULL_UP + of(OPEN_DRAIN_PULL_UP, Bias.HIGH_IMPEDANCE, false, false), + of(OPEN_DRAIN_PULL_UP, Bias.HIGH_IMPEDANCE, true, true), + of(OPEN_DRAIN_PULL_UP, Bias.PULL_UP, false, false), + of(OPEN_DRAIN_PULL_UP, Bias.PULL_UP, true, true), + of(OPEN_DRAIN_PULL_UP, Bias.PULL_DOWN, false, false), + //of(OPEN_DRAIN_PULL_UP, Bias.PULL_DOWN, true, undefined), + // OPEN_SOURCE + //of(OPEN_SOURCE, Bias.HIGH_IMPEDANCE, false, undefined), + of(OPEN_SOURCE, Bias.HIGH_IMPEDANCE, true, true), + of(OPEN_SOURCE, Bias.PULL_UP, false, true), + of(OPEN_SOURCE, Bias.PULL_UP, true, true), + of(OPEN_SOURCE, Bias.PULL_DOWN, false, false),//??? + of(OPEN_SOURCE, Bias.PULL_DOWN, true, true), + // OPEN_SOURCE_PULL_DOWN + of(OPEN_SOURCE_PULL_DOWN, Bias.HIGH_IMPEDANCE, false, false), + of(OPEN_SOURCE_PULL_DOWN, Bias.HIGH_IMPEDANCE, true, true), + //of(OPEN_SOURCE_PULL_DOWN, Bias.PULL_UP, false, undefined), + of(OPEN_SOURCE_PULL_DOWN, Bias.PULL_UP, true, true), + of(OPEN_SOURCE_PULL_DOWN, Bias.PULL_DOWN, false, false), + of(OPEN_SOURCE_PULL_DOWN, Bias.PULL_DOWN, true, true) + ).flatMap(it -> + CONNECTED_PINS.stream() + .map(pins -> of(Stream.concat( + Stream.of( + pins.pin1(), + pins.pin2() + ), + Arrays.stream(it.get()) + ).toArray())) + ); + } + + @ParameterizedTest(name = "[" + INDEX_PLACEHOLDER + "] " + + "driveMode={2}, " + + "bias={3}, " + + "writeValue={4}, " + + "readValue={5} ") + @MethodSource("args") + void should_read_value_depending_on_bias_and_drive_mode_on_coupled_line( + TestPin inputPin, + TestPin outputPin, + DriveMode driveMode, + Bias bias, + boolean writeValue, + boolean readValue + ) { + var jgpio = Jgpio.getInstance(); + try (var outputChip = jgpio.openChipByName(outputPin.chipName()); + var outputLine = outputChip.getLine(outputPin.lineOffset()) + .openAsOutput(driveMode); + var inputChip = jgpio.openChipByName(inputPin.chipName()); + var inputLine = inputChip.getLine(inputPin.lineOffset()) + .openAsInput(bias); + ) { + outputLine.write(writeValue); + assertThat(inputLine.read()) + .as("Input line level") + .isEqualTo(readValue); + } + } + + @ParameterizedTest(name = "[" + INDEX_PLACEHOLDER + "] " + + "driveMode={2}, " + + "bias={3}, " + + "writeValue={4}, " + + "readValue={5} ") + @MethodSource("args") + void should_read_value_depending_on_bias_and_drive_mode_on_coupled_line2( + TestPin inputPin, + TestPin outputPin, + DriveMode driveMode, + Bias bias, + boolean writeValue, + boolean readValue + ) { + var jgpio = Jgpio.getInstance(); + try (var outputChip = jgpio.openChipByName(outputPin.chipName()); + var outputLine = outputChip.getLine(outputPin.lineOffset()) + .openAsOutput(); + var inputChip = jgpio.openChipByName(inputPin.chipName()); + var inputLine = inputChip.getLine(inputPin.lineOffset()) + .openAsInput(bias); + ) { + outputLine.setDriveMode(driveMode); + outputLine.write(writeValue); + assertThat(inputLine.read()) + .as("Input line level") + .isEqualTo(readValue); + } + } +} diff --git a/jgpio/src/jextract/gpiod-includes.txt b/jgpio/src/jextract/gpiod-includes.txt index b9f02ce..89fc99d 100644 --- a/jgpio/src/jextract/gpiod-includes.txt +++ b/jgpio/src/jextract/gpiod-includes.txt @@ -37,6 +37,7 @@ --include-function gpiod_line_request_input --include-function gpiod_line_request_input_flags --include-function gpiod_line_request_output +--include-function gpiod_line_request_output_flags --include-function gpiod_line_get_value --include-function gpiod_line_set_value --include-function gpiod_line_set_flags diff --git a/jgpio/src/main/java/eu/softpol/lib/jgpio/DriveMode.java b/jgpio/src/main/java/eu/softpol/lib/jgpio/DriveMode.java new file mode 100644 index 0000000..e402d3f --- /dev/null +++ b/jgpio/src/main/java/eu/softpol/lib/jgpio/DriveMode.java @@ -0,0 +1,9 @@ +package eu.softpol.lib.jgpio; + +public enum DriveMode { + PUSH_PULL, + OPEN_DRAIN, + OPEN_DRAIN_PULL_UP, + OPEN_SOURCE, + OPEN_SOURCE_PULL_DOWN; +} diff --git a/jgpio/src/main/java/eu/softpol/lib/jgpio/Line.java b/jgpio/src/main/java/eu/softpol/lib/jgpio/Line.java index b1a0c2e..e78565c 100644 --- a/jgpio/src/main/java/eu/softpol/lib/jgpio/Line.java +++ b/jgpio/src/main/java/eu/softpol/lib/jgpio/Line.java @@ -64,4 +64,6 @@ public interface Line { /// @return the session for the output line, which must be closed after use LineOutputSession openAsOutput(); + LineOutputSession openAsOutput(DriveMode driveMode); + } diff --git a/jgpio/src/main/java/eu/softpol/lib/jgpio/LineOutputSession.java b/jgpio/src/main/java/eu/softpol/lib/jgpio/LineOutputSession.java index 47b5746..907e345 100644 --- a/jgpio/src/main/java/eu/softpol/lib/jgpio/LineOutputSession.java +++ b/jgpio/src/main/java/eu/softpol/lib/jgpio/LineOutputSession.java @@ -23,6 +23,8 @@ /// The methods of this interface should not be called after [Chip#close()]. public interface LineOutputSession extends Closeable { + void setDriveMode(DriveMode driveMode); + /// Writes the new value of the output line. /// /// @param value the signal level: `true` if the output line needs to be high, `false` if the diff --git a/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLine.java b/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLine.java index d5900fb..71e21fd 100644 --- a/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLine.java +++ b/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLine.java @@ -22,6 +22,7 @@ import eu.softpol.lib.jgpio.Line; import eu.softpol.lib.jgpio.LineInputSession; import eu.softpol.lib.jgpio.LineOutputSession; +import eu.softpol.lib.jgpio.DriveMode; import eu.softpol.lib.jgpio.internal.ffm.libgpiod.gpiod_h; import java.lang.foreign.MemorySegment; import org.jspecify.annotations.Nullable; @@ -99,6 +100,12 @@ public LineOutputSession openAsOutput() { return new GpiodLineOutputSession(chip, linePtr); } + @Override + public LineOutputSession openAsOutput(DriveMode driveMode) { + throwWhenChipClosed(); + return new GpiodLineOutputSession(chip, linePtr, driveMode); + } + @Override public String toString() { if (chip.isClosed()) { diff --git a/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLineOutputSession.java b/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLineOutputSession.java index 2a311c1..bbabfa6 100644 --- a/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLineOutputSession.java +++ b/jgpio/src/main/java/eu/softpol/lib/jgpio/internal/gpiod/GpiodLineOutputSession.java @@ -17,6 +17,7 @@ import eu.softpol.lib.jgpio.JgpioException; import eu.softpol.lib.jgpio.LineOutputSession; +import eu.softpol.lib.jgpio.DriveMode; import eu.softpol.lib.jgpio.internal.ffm.libgpiod.gpiod_h; import java.lang.System.Logger; import java.lang.System.Logger.Level; @@ -39,6 +40,29 @@ public GpiodLineOutputSession(GpiodChip chip, MemorySegment linePtr) { logger.log(Level.DEBUG, "Line requested"); } + public GpiodLineOutputSession(GpiodChip chip, MemorySegment linePtr, DriveMode driveMode) { + super(chip, linePtr); + int flags = toFlags(driveMode); + try (var arena = Arena.ofConfined()) { + var consumerPtr = arena.allocateFrom(GpiodChip.CONSUMER_NAME, StandardCharsets.US_ASCII); + if (gpiod_h.gpiod_line_request_output_flags(this.linePtr, consumerPtr, flags, 0) != 0) { + throw new JgpioException("JGPIO line request failed"); + } + } + logger.log(Level.DEBUG, "Line requested"); + } + + @Override + public void setDriveMode(DriveMode driveMode) { + throwWhenChipClosed(); + throwWhenLineSessionClosed(); + int flags = toFlags(driveMode); + logger.log(Level.DEBUG, "Set driveMode to {0}", driveMode); + if (gpiod_h.gpiod_line_set_flags(linePtr, flags) != 0) { + throw new JgpioException("Cannot set driveMode"); + } + } + @Override public void write(boolean value) { throwWhenChipClosed(); @@ -49,4 +73,15 @@ public void write(boolean value) { } } + private int toFlags(DriveMode driveMode) { + return switch (driveMode) { + case PUSH_PULL -> 0; + case OPEN_DRAIN -> gpiod_h.GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN(); + case OPEN_DRAIN_PULL_UP -> gpiod_h.GPIOD_LINE_REQUEST_FLAG_OPEN_DRAIN() + + gpiod_h.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP(); + case OPEN_SOURCE -> gpiod_h.GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE(); + case OPEN_SOURCE_PULL_DOWN -> gpiod_h.GPIOD_LINE_REQUEST_FLAG_OPEN_SOURCE() + + gpiod_h.GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN(); + }; + } }