[New] Major refactoring

- Added GpioException which is thrown when something goes wrong - it is
a checked exception
- Made project create an executable Jar
- fixed thread safety
- added GpioBridge.writeValue()
- added setup scripts for BeagleBoneBlack
This commit is contained in:
Robert von Burg 2016-02-05 16:40:35 +01:00
parent 5168f4e07b
commit 82b5390e09
13 changed files with 427 additions and 72 deletions

View File

@ -1,2 +1,9 @@
# ch.eitchnet.beaglebone
ch.eitchnet.beaglebone
# BeagleBoneBlack Java Pin Bridge
## Setup
## Build
## Running

23
compile_dts.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
SLOTS=/sys/devices/platform/bone_capemgr/slots
PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins
# http://kilobaser.com/blog/2014-07-28-beaglebone-black-devicetreeoverlay-generator
# https://github.com/jadonk/validation-scripts/tree/master/test-capemgr
echo "Compiling pinctrl-eitchnet..."
dtc -O dtb -o pinctrl-eitchnet-00A0.dtbo -b 0 -@ pinctrl-eitchnet.dts
echo
echo "Install with:"
echo "$ sudo cp pinctrl-eitchnet-00A0.dtbo /lib/firmware/"
echo "$ echo pinctrl-eitchnet | sudo tee $SLOTS"
echo
echo "Remove with:"
echo "$ cat $SLOTS | grep pinctrl-eitchnet"
echo "15: P-O-L- 0 Override Board Name,00A0,Override Manuf,pinctrl-eitchnet"
echo "$ echo -15 | sudo tee $SLOTS"
exit 0

81
exportPins.sh Executable file
View File

@ -0,0 +1,81 @@
#!/bin/bash
set -e
echo -e "Exporting Pins to userspace..."
if [ "$(whoami)" != "root" ] ; then
echo "Please run as root!"
exit 1
fi
GPIO_DIR="/sys/class/gpio"
USER="ubuntu"
if [ ! -d "${GPIO_DIR}" ] ; then
echo -e "ERROR: Missing GPIO directory ${GPIO_DIR}"
fi
cd "${GPIO_DIR}"
function export_pin() {
direction="$1"
gpio_no="$2"
gpio_name="gpio${gpio_no}"
echo "Setting direction of ${gpio_name} to ${direction}..."
if [ ! -d "${gpio_name}" ] ; then
echo "${gpio_no}" > export
else
echo "GPIO ${gpio_no} was already exported, only verifying direction..."
fi
echo "${direction}" > "${gpio_name}/direction"
if [ "${direction}" == "in" ] ; then
echo "Current value of ${gpio_name} is $(cat ${gpio_name}/value)"
else
echo "Setting value of ${gpio_name} to 0"
fi
chown ${USER} "${gpio_name}/value"
}
##
## IN Pins
##
echo
echo "Setting input pins..."
# P8.7
export_pin in 66
# P8.8
export_pin in 67
# P8.9
export_pin in 69
##
## Out pins
##
echo
echo "Setting output pins..."
# P8.10
export_pin out 68
# P8.11
export_pin out 45
# P8.12
export_pin out 44
# P8.14
export_pin out 26
# P8.15
export_pin out 47
# P8.16
export_pin out 46
echo -e "Done."
exit 0

45
pinctrl-eitchnet.dts Normal file
View File

@ -0,0 +1,45 @@
/dts-v1/;
/plugin/;
/{
compatible = "ti,beaglebone", "ti,beaglebone-black";
/* identification */
part-number = "pinctrl-eitchnet";
/* https://github.com/derekmolloy/boneDeviceTree/blob/master/docs/BeagleboneBlackP9HeaderTable.pdf */
fragment@0 {
target = <&am33xx_pinmux>;
__overlay__ {
pinctrl_eitchnet: pinmux_pinctrl_eitchnet {
pinctrl-single,pins = <
/* 0x27 INPUT */
0x090 0x2f /* P8_07 66 INPUT MODE7 none */
0x094 0x2f /* P8_08 67 INPUT MODE7 none */
0x09c 0x2f /* P8_09 69 INPUT MODE7 none */
/* 0x07 OUTPUT */
0x098 0x0f /* P8_10 68 OUTPUT MODE7 none */
0x034 0x0f /* P8_11 45 OUTPUT MODE7 none */
0x030 0x0f /* P8_12 44 OUTPUT MODE7 none */
0x028 0x0f /* P8_14 26 OUTPUT MODE7 none */
0x03c 0x0f /* P8_15 47 OUTPUT MODE7 none */
0x038 0x0f /* P8_16 46 OUTPUT MODE7 none */
>;
};
};
};
fragment@1 {
target = <&ocp>;
__overlay__ {
pinctrl_eitchnet_pinmux {
compatible = "bone-pinmux-helper";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_eitchnet>;
status = "okay";
};
};
};
};

33
pinctrl-test-0.dts Normal file
View File

@ -0,0 +1,33 @@
/dts-v1/;
/plugin/;
/{
compatible = "ti,beaglebone", "ti,beaglebone-black";
/* identification */
part-number = "pinctrl-test-0";
/* https://github.com/derekmolloy/boneDeviceTree/blob/master/docs/BeagleboneBlackP9HeaderTable.pdf */
fragment@0 {
target = <&am33xx_pinmux>;
__overlay__ {
pinctrl_test: pinctrl_test_0_pins {
pinctrl-single,pins = <
0x164 0x00 /* P9_42 muxRegOffset, OUTPUT | MODE0 */
>;
};
};
};
fragment@1 {
target = <&ocp>;
__overlay__ {
test_helper: helper {
compatible = "bone-pinmux-helper";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
status = "okay";
};
};
};
};

80
pom.xml
View File

@ -1,27 +1,63 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ch.eitchnet</groupId>
<artifactId>ch.eitchnet.beaglebone</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<groupId>ch.eitchnet</groupId>
<artifactId>ch.eitchnet.beaglebone</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ch.eitchnet.beaglebone</name>
<url>http://maven.apache.org</url>
<name>ch.eitchnet.beaglebone</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>BeagleBone</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<!-- <classpathPrefix>lib/</classpathPrefix> -->
<mainClass>ch.eitchnet.beaglebone.GpioBridgeTest</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!-- <plugin> -->
<!-- <artifactId>maven-dependency-plugin</artifactId> -->
<!-- <executions> -->
<!-- <execution> -->
<!-- <phase>package</phase> -->
<!-- <goals> -->
<!-- <goal>copy-dependencies</goal> -->
<!-- </goals> -->
<!-- <configuration> -->
<!-- <includeScope>compile</includeScope> -->
<!-- <outputDirectory>${project.build.directory}/lib</outputDirectory> -->
<!-- </configuration> -->
<!-- </execution> -->
<!-- </executions> -->
<!-- </plugin> -->
</plugins>
</build>
</project>

30
setOutPin.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
set -e
GPIO_DIR="/sys/class/gpio"
if [ ! -d "${GPIO_DIR}" ] ; then
echo -e "ERROR: Missing GPIO directory ${GPIO_DIR}"
fi
cd "${GPIO_DIR}"
if [ "$#" != 2 ] ; then
echo "Usage: $0 <gpio_no> <value>"
exit 1
fi
gpio_no="$1"
gpio_name="gpio${gpio_no}"
value="$2"
direction="$(cat ${gpio_name}/direction)"
if [ "${direction}" != "out" ] ; then
echo "Current direction is not in, can't set output pin!"
fi
echo "Setting output pin ${gpio_name} to value ${value}"
echo ${value} | sudo tee "${GPIO_DIR}/${gpio_name}/value"
exit 0

View File

@ -3,7 +3,7 @@ package ch.eitchnet.beaglebone;
public class Gpio {
private final Pin pin;
private final String name;
private final String kernelName;
private final Direction direction;
private String label;
private Signal signal;
@ -11,8 +11,8 @@ public class Gpio {
public Gpio(Pin pin, Direction direction) {
this.pin = pin;
this.direction = direction;
this.name = "gpio" + pin.getGpioNr();
this.label = name;
this.kernelName = "gpio" + pin.getGpioNr();
this.label = kernelName;
this.signal = Signal.LOW;
}
@ -20,8 +20,8 @@ public class Gpio {
return this.pin;
}
public String getName() {
return this.name;
public String getKernelName() {
return this.kernelName;
}
public Gpio setLabel(String label) {
@ -45,4 +45,9 @@ public class Gpio {
this.signal = signal;
return this;
}
@Override
public String toString() {
return this.pin.toString();
}
}

View File

@ -2,6 +2,7 @@ package ch.eitchnet.beaglebone;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
@ -23,12 +24,20 @@ public class GpioBridge {
this.listeners = Collections.synchronizedMap(new HashMap<>());
}
public void writeValue(Gpio gpio, Signal signal) {
private File getGpioValuePath(Gpio gpio) {
return new File(GpioBridgeTest.GPIO_PATH, gpio.getKernelName() + "/value");
}
private File getGpioDirectionPath(Gpio gpio) {
return new File(GpioBridgeTest.GPIO_PATH, gpio.getKernelName() + "/direction");
}
public void writeValue(Gpio gpio, Signal signal) throws GpioException {
if (gpio.getDirection() != Direction.OUT)
throw new IllegalArgumentException("For writing the direction must be " + Direction.OUT);
File file = new File(GpioBridgeTest.GPIO_PATH, gpio.getName() + "/value");
File file = getGpioValuePath(gpio);
try (FileOutputStream out = new FileOutputStream(file)) {
out.write(signal.getValueS().getBytes());
@ -37,12 +46,35 @@ public class GpioBridge {
gpio.setSignal(signal);
} catch (Exception e) {
e.printStackTrace();
throw new GpioException("Failed to write GPIO " + gpio + " with signal " + signal, e);
}
System.out.println("Set GPIO " + gpio.getPin() + " signal to " + gpio.getSignal());
}
public Signal readValue(Gpio gpio) throws GpioException {
synchronized (gpio) {
if (gpio.getDirection() != Direction.IN)
throw new IllegalArgumentException("For reading the direction must be " + Direction.IN);
File file = getGpioValuePath(gpio);
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
String valueS = fin.readLine();
Signal signal = Signal.getSignal(valueS);
if (!gpio.getSignal().equals(signal))
gpio.setSignal(signal);
return signal;
} catch (Exception e) {
throw new GpioException("Failed to read GPIO " + gpio, e);
}
}
}
public void start() {
this.run = true;
@ -52,8 +84,10 @@ public class GpioBridge {
synchronized (this) {
try {
wait(1000l);
} catch (Exception e) {
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("Was interrupted. Stopping thread.");
this.run = false;
break;
}
}
} else {
@ -62,19 +96,18 @@ public class GpioBridge {
synchronized (this.listeners) {
for (Gpio gpio : this.listeners.keySet()) {
File file = new File(GpioBridgeTest.GPIO_PATH, gpio.getName() + "/value");
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
String valueS = fin.readLine();
Signal signal = Signal.getSignal(valueS);
if (!gpio.getSignal().equals(signal)) {
gpio.setSignal(signal);
changes.add(gpio);
try {
synchronized (gpio) {
Signal currentSignal = gpio.getSignal();
Signal newSignal = readValue(gpio);
if (currentSignal != newSignal)
changes.add(gpio);
}
} catch (Exception e) {
System.out.println("Failed to read GPIO " + gpio + " due to:");
e.printStackTrace();
this.run = false;
break;
}
}
}
@ -89,20 +122,28 @@ public class GpioBridge {
+ ". Notifying " + listeners.size() + " listeners.");
for (GpioSignalListener listener : listeners) {
listener.notify(gpio);
try {
listener.notify(gpio);
} catch (Exception e) {
System.out.println("Failed to update listener " + listener + " due to:");
e.printStackTrace();
}
}
}
}
try {
Thread.sleep(200l);
} catch (Exception e) {
e.printStackTrace();
} catch (InterruptedException e) {
System.out.println("Was interrupted. Stopping thread.");
this.run = false;
break;
}
}
}
} , "gpio_reader");
this.thread.start();
System.out.println("Started GPIO bridge.");
}
public void stop() {
@ -111,38 +152,66 @@ public class GpioBridge {
try {
this.thread.join(5000l);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Was interrupted while waiting for thread to stop?!");
}
}
public Gpio getGpio(Pin pin, Direction direction) {
public synchronized Gpio getGpio(Pin pin, Direction direction) throws GpioException {
Gpio gpio = this.cache.get(pin);
if (gpio == null) {
gpio = new Gpio(pin, direction);
File file = new File(GpioBridgeTest.GPIO_PATH, gpio.getName() + "/direction");
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
// validate direction
validateDirection(pin, direction, gpio);
String directionS = fin.readLine();
Direction dir = Direction.getDirection(directionS);
if (dir != direction)
throw new IllegalArgumentException(
"Actual direction of GPIO " + gpio.getPin() + " is " + dir + " not " + directionS);
} catch (IOException e) {
e.printStackTrace();
}
// validate file permissions
validateFilePermissions(direction, gpio);
this.cache.put(pin, gpio);
System.out.println("Initialized pin " + pin + " with direction " + direction + ".");
}
return gpio;
}
public void register(Gpio gpio, GpioSignalListener listener) {
private void validateDirection(Pin pin, Direction direction, Gpio gpio) throws GpioException {
File file = getGpioDirectionPath(gpio);
try (BufferedReader fin = new BufferedReader(new FileReader(file))) {
String directionS = fin.readLine();
Direction dir = Direction.getDirection(directionS);
if (dir != direction)
throw new GpioException(
"Actual direction of GPIO " + gpio.getPin() + " is " + dir + " not " + directionS);
} catch (FileNotFoundException e) {
throw new GpioException("GPIO " + pin + " does not exist, was the pin exported to user space?", e);
} catch (IOException e) {
throw new GpioException("Failed to open GPIO " + pin, e);
}
}
private void validateFilePermissions(Direction direction, Gpio gpio) throws GpioException {
File gpioValuePath = getGpioValuePath(gpio);
if (direction == Direction.IN) {
if (!gpioValuePath.canRead())
throw new GpioException("GPIO " + gpio + " has direction " + direction
+ " and is not readable. Are the file permissions ok?");
} else if (direction == Direction.OUT) {
if (!gpioValuePath.canWrite())
throw new GpioException("GPIO " + gpio + " has direction " + direction
+ " and is not writable. Are the file permissions ok?");
} else {
throw new RuntimeException("Unhandled Direction " + direction);
}
}
public void register(Gpio gpio, GpioSignalListener listener) throws GpioException {
if (gpio.getDirection() != Direction.IN)
throw new IllegalArgumentException("For reading the direction must be " + Direction.IN);
throw new GpioException("For reading the direction must be " + Direction.IN);
synchronized (this.listeners) {
List<GpioSignalListener> listeners = this.listeners.get(gpio);

View File

@ -0,0 +1,14 @@
package ch.eitchnet.beaglebone;
public class GpioException extends Exception {
private static final long serialVersionUID = 1L;
public GpioException(String message, Throwable cause) {
super(message, cause);
}
public GpioException(String message) {
super(message);
}
}

View File

@ -1,5 +1,5 @@
package ch.eitchnet.beaglebone;
public interface GpioSignalListener {
public void notify(Gpio gpio);
public void notify(Gpio gpio) throws Exception;
}

View File

@ -2,13 +2,13 @@ package ch.eitchnet.beaglebone;
public enum Pin {
P8_3("P8.3", 1, 6),
P8_4("P8.4", 1, 7),
P8_5("P8.5", 1, 2),
P8_6("P8.6", 1, 3),
P8_7("P8.7", 2, 2),
P8_8("P8.8", 2, 3),
P8_9("P8.9", 2, 2),
P8_03("P8.03", 1, 6),
P8_04("P8.04", 1, 7),
P8_05("P8.05", 1, 2),
P8_06("P8.06", 1, 3),
P8_07("P8.07", 2, 2),
P8_08("P8.08", 2, 3),
P8_09("P8.09", 2, 5),
P8_10("P8.10", 2, 4),
P8_11("P8.11", 1, 13),
P8_12("P8.12", 1, 12),
@ -68,8 +68,10 @@ public enum Pin {
P9_29("P9.29", 3, 15),
P9_30("P9.30", 3, 16),
P9_31("P9.31", 3, 14),
P9_41("P9.41", 0, 20),
P9_42("P9.42", 0, 7);
P9_41A("P9.41A", 0, 20),
P9_41B("P9.41B", 3, 20),
P9_42A("P9.42A", 0, 7),
P9_42B("P9.42B", 3, 18);
private String label;
private int chip;
@ -96,4 +98,9 @@ public enum Pin {
public int getGpioNr() {
return this.chip * 32 + this.pin;
}
@Override
public String toString() {
return this.label;
}
}

View File

@ -52,4 +52,9 @@ public enum Signal {
return HIGH;
throw new IllegalArgumentException("No signal for value " + valueS);
}
@Override
public String toString() {
return name();
}
}