diff --git a/java/libraries/io/examples/SimpleInput/SimpleInput.pde b/java/libraries/io/examples/SimpleInput/SimpleInput.pde index d7a420ce1..80aa5894f 100644 --- a/java/libraries/io/examples/SimpleInput/SimpleInput.pde +++ b/java/libraries/io/examples/SimpleInput/SimpleInput.pde @@ -5,7 +5,10 @@ import processing.io.*; // see setup.png in the sketch folder for wiring details void setup() { - GPIO.pinMode(4, GPIO.INPUT); + // INPUT_PULLUP enables the built-in pull-up resistor for this pin + // left alone, the pin will read as HIGH + // connected to ground (via e.g. a button or switch) it will read LOW + GPIO.pinMode(4, GPIO.INPUT_PULLUP); } void draw() { diff --git a/java/libraries/io/src/native/iface.h b/java/libraries/io/src/native/iface.h index 32e235e1e..74decc185 100644 --- a/java/libraries/io/src/native/iface.h +++ b/java/libraries/io/src/native/iface.h @@ -47,6 +47,30 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile (JNIEnv *, jclass, jstring, jbyteArray); +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemRead + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead + (JNIEnv *, jclass, jint); + +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemWrite + * Signature: (III)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite + (JNIEnv *, jclass, jint, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemWrite + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias + (JNIEnv *, jclass, jint, jint); + /* * Class: processing_io_NativeInterface * Method: pollDevice diff --git a/java/libraries/io/src/native/impl.c b/java/libraries/io/src/native/impl.c index 0f9402ab9..53268a0f4 100644 --- a/java/libraries/io/src/native/impl.c +++ b/java/libraries/io/src/native/impl.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,143 @@ JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile } +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead + (JNIEnv *env, jclass cls, jint offset) +{ + // validate offset + if (4096 <= offset) { + return -EINVAL; + } + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + uint32_t value = mem[offset]; + + munmap(mem, 4096); + close(file); + return value; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite + (JNIEnv *env, jclass cls, jint offset, jint mask, jint value) +{ + // validate offset + if (4096 <= offset) { + return -EINVAL; + } + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + mem[offset] = (mem[offset] & ~mask) | (value & mask); + + munmap(mem, 4096); + close(file); + return 1; // number of bytes written +} + + +#define BCM2835_GPPUD_OFFSET (0x94 >> 2) +#define BCM2835_GPPUDCLK0_OFFSET (0x98 >> 2) +#define BCM2835_GPPUDCLK1_OFFSET (0x9c >> 2) + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias + (JNIEnv *env, jclass cls, jint gpio, jint mode) +{ + int ret = 0; // success + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + // validate arguments + if (gpio < 0 || 53 < gpio) { + ret = -EINVAL; + goto out; + } + + // see BCM2835 datasheet, p. 101 + uint32_t pud; + if (mode == 0) { + pud = 0; // floating + } else if (mode == 2) { + pud = 2; // pull-up + } else if (mode == 3) { + pud = 1; // pull-down + } else { + ret = -EINVAL; + goto out; + } + + /* + * From the BCM2835 datasheet, p. 101: + * + * The following sequence of events is required: + * 1. Write to GPPUD to set the required control signal (i.e. Pull-up or + * Pull-Down or neither to remove the current Pull-up/down) + * 2. Wait 150 cycles – this provides the required set-up time for the + * control signal + * 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads + * you wish to modify – NOTE only the pads which receive a clock will + * be modified, all others will retain their previous state. + * 4. Wait 150 cycles – this provides the required hold time for the + * control signal + * 5. Write to GPPUD to remove the control signal + * 6. Write to GPPUDCLK0/1 to remove the clock + */ + + // python-gpiozero uses a delay of 214 ns, so we do the same + struct timespec wait; + wait.tv_sec = 0; + wait.tv_nsec = 214; + + mem[BCM2835_GPPUD_OFFSET] = pud; + nanosleep(&wait, NULL); + if (gpio < 32) { + mem[BCM2835_GPPUDCLK0_OFFSET] = 1 << gpio; + } else { + mem[BCM2835_GPPUDCLK1_OFFSET] = 1 << (gpio-32); + } + nanosleep(&wait, NULL); + mem[BCM2835_GPPUD_OFFSET] = 0; + if (gpio < 32) { + mem[BCM2835_GPPUDCLK0_OFFSET] = 0; + } else { + mem[BCM2835_GPPUDCLK1_OFFSET] = 0; + } + +out: + munmap(mem, 4096); + close(file); + return ret; +} + + JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice (JNIEnv *env, jclass cls, jstring _fn, jint timeout) { diff --git a/java/libraries/io/src/processing/io/GPIO.java b/java/libraries/io/src/processing/io/GPIO.java index b4e0d476b..54e41bb1c 100644 --- a/java/libraries/io/src/processing/io/GPIO.java +++ b/java/libraries/io/src/processing/io/GPIO.java @@ -328,7 +328,7 @@ public class GPIO { /** * Configures a pin to act either as input or output * @param pin GPIO pin - * @param mode GPIO.INPUT or GPIO.OUTPUT + * @param mode GPIO.INPUT, GPIO.INPUT_PULLUP, GPIO.INPUT_PULLDOWN, or GPIO.OUTPUT * @see digitalRead * @see digitalWrite * @see releasePin @@ -369,6 +369,10 @@ public class GPIO { String out; if (mode == INPUT) { out = "in"; + + // attempt to disable any pre-set pullups on the Raspberry Pi + NativeInterface.raspbianGpioMemSetPinBias(pin, mode); + } else if (mode == OUTPUT) { if (values.get(pin)) { out = "high"; @@ -376,9 +380,18 @@ public class GPIO { out = "low"; } } else if (mode == INPUT_PULLUP || mode == INPUT_PULLDOWN) { + out = "in"; + + // attempt to set pullups on the Raspberry Pi + ret = NativeInterface.raspbianGpioMemSetPinBias(pin, mode); + if (ret == -2) { // NOENT + System.err.println("Setting pullup or pulldown resistors is currently only supported on the Raspberry Pi running Raspbian. Continuing without."); + } else if (ret < 0) { + System.err.println("Error setting pullup or pulldown resistors: " + NativeInterface.getError(ret) + ". Continuing without."); + } // currently this can't be done in a non-platform-specific way, see // http://lists.infradead.org/pipermail/linux-rpi-kernel/2015-August/002146.html - throw new RuntimeException("Not yet implemented"); + } else { throw new IllegalArgumentException("Unknown mode"); } diff --git a/java/libraries/io/src/processing/io/NativeInterface.java b/java/libraries/io/src/processing/io/NativeInterface.java index 3b1b3e0c3..a05a71ecd 100644 --- a/java/libraries/io/src/processing/io/NativeInterface.java +++ b/java/libraries/io/src/processing/io/NativeInterface.java @@ -62,6 +62,9 @@ public class NativeInterface { } /* GPIO */ + public static native int raspbianGpioMemRead(int offset); + public static native int raspbianGpioMemWrite(int offset, int mask, int value); + public static native int raspbianGpioMemSetPinBias(int gpio, int mode); public static native int pollDevice(String fn, int timeout); /* I2C */ public static native int transferI2c(int handle, int slave, byte[] out, byte[] in);