Adds interrupts to Java

Implements the JNI bindings for java
Adds integration tests for Digital Inputs and AnalogTriggers.
Adds the ability to get the value and message from errno in java using the HALUtil JNI class.

Change-Id: I853529fdab9744ce95ee15d4cc73dc3953265552
This commit is contained in:
Jonathan Leitschuh
2014-08-04 14:19:01 -04:00
parent 23b6a980c2
commit 8ba0eada17
18 changed files with 834 additions and 213 deletions

View File

@@ -92,6 +92,7 @@ public class AnalogTriggerOutput extends DigitalSource {
@Override
public void free() {
}
/**
@@ -113,8 +114,8 @@ public class AnalogTriggerOutput extends DigitalSource {
}
@Override
public int getModuleForRouting() {
return m_trigger.m_index >> 2;
public byte getModuleForRouting() {
return (byte) (m_trigger.m_index >> 2);
}
@Override
@@ -122,27 +123,6 @@ public class AnalogTriggerOutput extends DigitalSource {
return true;
}
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* the interrupt service routine
* @param param
* a parameter for the ISR
*/
// public void requestInterrupts(/*tInterruptHandler*/Object handler, Object
// param) {
// TODO: add interrupt support
// TODO: throw exception
// }
/**
* Request interrupts synchronously on this digital input.
*/
// public void requestInterrupts() {
// TODO: throw exception
// }
/**
* Defines the state in which the AnalogTrigger triggers
* @author jonathanleitschuh

View File

@@ -12,14 +12,10 @@ import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptHandlerFunction;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.util.AllocationException;
import edu.wpi.first.wpilibj.util.CheckedAllocationException;
/**
* Class to read a digital input. This class will read digital inputs and return
@@ -67,97 +63,15 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
return m_channel;
}
@Override
public boolean getAnalogTriggerForRouting() {
return false;
}
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* The address of the interrupt handler function of type
* tInterruptHandler that will be called whenever there is an
* interrupt on the digitial input port. Request interrupts in
* synchronus mode where the user program interrupt handler will
* be called when an interrupt occurs. The default is interrupt
* on rising edges only.
*/
public void requestInterrupts(InterruptHandlerFunction handler) {
// TODO: add interrupt support
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
allocateInterrupts(false);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, (byte) getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
setUpSourceEdge(true, false);
InterruptJNI.attachInterruptHandler(m_interrupt, handler, null, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Request interrupts synchronously on this digital input. Request
* interrupts in synchronus mode where the user program will have to
* explicitly wait for the interrupt to occur. The default is interrupt on
* rising edges only.
*/
public void requestInterrupts() {
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
allocateInterrupts(true);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, (byte) getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
}
/**
* Set which edge to trigger interrupts on
*
* @param risingEdge
* true to interrupt on rising edge
* @param fallingEdge
* true to interrupt on falling edge
*/
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
if (m_interrupt != null) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.setInterruptUpSourceEdge(m_interrupt,
(byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0),
status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
} else {
throw new IllegalArgumentException(
"You must call RequestInterrupts before setUpSourceEdge");
}
}
/*
* Live Window code, only does anything if live window is activated.
*/
@Override
public String getSmartDashboardType() {
return "Digital Input";
}
@@ -167,6 +81,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void initTable(ITable subtable) {
m_table = subtable;
updateTable();
@@ -175,6 +90,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void updateTable() {
if (m_table != null) {
m_table.putBoolean("Value", get());
@@ -184,6 +100,7 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public ITable getTable() {
return m_table;
}
@@ -191,12 +108,14 @@ public class DigitalInput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void startLiveWindowMode() {
}
/**
* {@inheritDoc}
*/
@Override
public void stopLiveWindowMode() {
}
}

View File

@@ -7,24 +7,21 @@
package edu.wpi.first.wpilibj;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
//import com.sun.jna.Pointer;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
import edu.wpi.first.wpilibj.communication.UsageReporting;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.PWMJNI;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.hal.PWMJNI;
import edu.wpi.first.wpilibj.livewindow.LiveWindowSendable;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
//import com.sun.jna.Pointer;
import edu.wpi.first.wpilibj.communication.FRCNetworkCommunicationsLibrary.tResourceType;
/**
* Class to write digital outputs. This class will wrtie digital outputs. Other
* Class to write digital outputs. This class will write digital outputs. Other
* devices that are implemented elsewhere will automatically allocate digital
* inputs and outputs as required.
*/
@@ -48,6 +45,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* Free the resources associated with a digital output.
*/
@Override
public void free() {
// disable the pwm only if we have allocated it
if (m_pwmGenerator != null) {
@@ -104,6 +102,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
* @param pulseLength
* The length of the pulse.
*/
@Deprecated
public void pulse(final int channel, final int pulseLength) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
@@ -212,6 +211,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/*
* Live Window code, only does anything if live window is activated.
*/
@Override
public String getSmartDashboardType() {
return "Digital Output";
}
@@ -222,6 +222,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void initTable(ITable subtable) {
m_table = subtable;
updateTable();
@@ -230,6 +231,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public ITable getTable() {
return m_table;
}
@@ -237,6 +239,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void updateTable() {
// TODO: Put current value.
}
@@ -244,8 +247,10 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void startLiveWindowMode() {
m_table_listener = new ITableListener() {
@Override
public void valueChanged(ITable itable, String key, Object value,
boolean bln) {
set(((Boolean) value).booleanValue());
@@ -257,6 +262,7 @@ public class DigitalOutput extends DigitalSource implements LiveWindowSendable {
/**
* {@inheritDoc}
*/
@Override
public void stopLiveWindowMode() {
// TODO: Broken, should only remove the listener from "Value" only.
m_table.removeTableListener(m_table_listener);

View File

@@ -7,9 +7,8 @@
package edu.wpi.first.wpilibj;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.hal.DIOJNI;
import edu.wpi.first.wpilibj.hal.HALUtil;
@@ -54,6 +53,7 @@ public abstract class DigitalSource extends InterruptableSensorBase {
HALUtil.checkStatus(status.asIntBuffer());
}
@Override
public void free() {
channels.free(m_channel);
ByteBuffer status = ByteBuffer.allocateDirect(4);
@@ -69,6 +69,7 @@ public abstract class DigitalSource extends InterruptableSensorBase {
*
* @return channel routing number
*/
@Override
public int getChannelForRouting() {
return m_channel;
}
@@ -78,15 +79,16 @@ public abstract class DigitalSource extends InterruptableSensorBase {
*
* @return 0
*/
public int getModuleForRouting() {
@Override
public byte getModuleForRouting() {
return 0;
}
/**
* Is this an analog trigger
*
* @return true if this is an analog trigger
*/
@Override
public boolean getAnalogTriggerForRouting() {
return false;
}

View File

@@ -0,0 +1,57 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008-2014. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptJNIHandlerFunction;
/**
* It is recommended that you use this class in conjunction with classes from
* {@link java.util.concurrent.atomic} as these objects are all thread safe.
*
* @author Jonathan Leitschuh
*
* @param <T> The type of the parameter that should be returned to the the
* method {@link #interruptFired(int, Object)}
*/
public abstract class InterruptHandlerFunction<T>{
/**
* The entry point for the interrupt. When the interrupt fires the
* {@link #apply(int, Object)} method is called.
* The outer class is provided as an interface to allow the implementer to
* pass a generic object to the interrupt fired method.
* @author Jonathan Leitschuh
*/
private class Function implements InterruptJNIHandlerFunction{
@SuppressWarnings("unchecked")
@Override
public void apply(int interruptAssertedMask, Object param) {
interruptFired(interruptAssertedMask, (T)param);
}
}
final Function function = new Function();
/**
* This method is run every time an interrupt is fired.
* @param interruptAssertedMask
* @param param The parameter provided by overriding the {@link #overridableParamater()}
* method.
*/
abstract void interruptFired(int interruptAssertedMask, T param);
/**
* Override this method if you would like to pass a specific
* parameter to the {@link #interruptFired(int, Object)} when it is fired by the interrupt.
* This method is called once when {@link InterruptableSensorBase#requestInterrupts(InterruptHandlerFunction)}
* is run.
* @return The object that should be passed to the interrupt when it runs
*/
public T overridableParamater(){
return null;
}
}

View File

@@ -7,26 +7,40 @@
package edu.wpi.first.wpilibj;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.hal.InterruptJNI.InterruptHandlerFunction;
import edu.wpi.first.wpilibj.hal.HALUtil;
import edu.wpi.first.wpilibj.hal.InterruptJNI;
import edu.wpi.first.wpilibj.util.AllocationException;
import edu.wpi.first.wpilibj.util.CheckedAllocationException;
/**
* Base for sensors to be used with interrupts
*/
public abstract class InterruptableSensorBase extends SensorBase {
/**
* This is done to store the JVM variable in the InterruptJNI
* This is done because the HAL must have access to the JVM variable
* in order to attach the newly spawned thread when an interrupt is fired.
*/
static{
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.initializeInterruptJVM(status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* The interrupt resource
*/
protected ByteBuffer m_interrupt;
protected ByteBuffer m_interrupt = null;
/**
* The interrupt manager resource
* Flags if the interrupt being allocated is synchronous
*/
protected InterruptHandlerFunction m_manager;
protected boolean m_isSynchronousInterrupt = false;
/**
* The index of the interrupt
*/
@@ -40,25 +54,102 @@ public abstract class InterruptableSensorBase extends SensorBase {
* Create a new InterrupatableSensorBase
*/
public InterruptableSensorBase() {
m_manager = null;
m_interrupt = null;
}
/**
* @return
*/
abstract boolean getAnalogTriggerForRouting();
/**
* @return
*/
abstract int getChannelForRouting();
/**
* @return
*/
abstract byte getModuleForRouting();
/**
* Request interrupts asynchronously on this digital input.
*
* @param handler
* The {@link #InterruptHandlerFunction} that contains the method
* {@link InterruptHandlerFunction#interruptFired(int, Object)} that
* will be called whenever there is an interrupt on this device.
* Request interrupts in synchronus mode where the user program
* interrupt handler will be called when an interrupt occurs. The
* default is interrupt on rising edges only.
*/
public void requestInterrupts(InterruptHandlerFunction<?> handler) {
if(m_interrupt != null){
throw new AllocationException("The interrupt has already been allocated");
}
allocateInterrupts(false);
assert (m_interrupt != null);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
InterruptJNI.attachInterruptHandler(m_interrupt, handler.function, handler.overridableParamater(), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Request interrupts synchronously on this digital input. Request
* interrupts in synchronous mode where the user program will have to
* explicitly wait for the interrupt to occur. The default is interrupt on
* rising edges only.
*/
public void requestInterrupts() {
if(m_interrupt != null){
throw new AllocationException("The interrupt has already been allocated");
}
allocateInterrupts(true);
assert (m_interrupt != null);
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(),
getChannelForRouting(),
(byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
setUpSourceEdge(true, false);
}
/**
* Allocate the interrupt
*
* @param watcher
* @param watcher true if the interrupt should be in synchronous mode where the user
* program will have to explicitly wait for the interrupt to occur.
*/
public void allocateInterrupts(boolean watcher) {
if (!watcher) {
throw new IllegalArgumentException(
"Interrupt callbacks not yet supported");
protected void allocateInterrupts(boolean watcher) {
try {
m_interruptIndex = interrupts.allocate();
} catch (CheckedAllocationException e) {
throw new AllocationException(
"No interrupts are left to be allocated");
}
// Expects the calling leaf class to allocate an interrupt index.
IntBuffer status = IntBuffer.allocate(1);
m_isSynchronousInterrupt = watcher;
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
m_interrupt = InterruptJNI.initializeInterrupts(m_interruptIndex,
(byte) (watcher ? 1 : 0), status);
HALUtil.checkStatus(status);
(byte) (watcher ? 1 : 0), status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -66,13 +157,15 @@ public abstract class InterruptableSensorBase extends SensorBase {
* structures and disables any interrupts.
*/
public void cancelInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.cleanInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.cleanInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
m_interrupt = null;
interrupts.free(m_interruptIndex);
}
/**
@@ -82,12 +175,13 @@ public abstract class InterruptableSensorBase extends SensorBase {
* Timeout in seconds
*/
public void waitForInterrupt(double timeout) {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.waitForInterrupt(m_interrupt, (float) timeout, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.waitForInterrupt(m_interrupt, (float) timeout, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -96,24 +190,32 @@ public abstract class InterruptableSensorBase extends SensorBase {
* other options before starting to field interrupts.
*/
public void enableInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.enableInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
if(m_isSynchronousInterrupt){
throw new IllegalStateException("You do not need to enable synchronous interrupts");
}
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.enableInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
* Disable Interrupts without without deallocating structures.
*/
public void disableInterrupts() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
InterruptJNI.disableInterrupts(m_interrupt, status);
HALUtil.checkStatus(status);
if(m_isSynchronousInterrupt){
throw new IllegalStateException("You can not disable synchronous interrupts");
}
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.disableInterrupts(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
}
/**
@@ -123,12 +225,36 @@ public abstract class InterruptableSensorBase extends SensorBase {
* @return Timestamp in seconds since boot.
*/
public double readInterruptTimestamp() {
if (m_interrupt == null || m_manager == null) {
throw new IllegalStateException();
if (m_interrupt == null) {
throw new IllegalStateException("The interrupt is not allocated.");
}
IntBuffer status = IntBuffer.allocate(1);
double timestamp = InterruptJNI.readInterruptTimestamp(m_interrupt, status);
HALUtil.checkStatus(status);
ByteBuffer status = ByteBuffer.allocateDirect(4);
status.order(ByteOrder.LITTLE_ENDIAN);
double timestamp = InterruptJNI.readInterruptTimestamp(m_interrupt, status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
return timestamp;
}
/**
* Set which edge to trigger interrupts on
*
* @param risingEdge
* true to interrupt on rising edge
* @param fallingEdge
* true to interrupt on falling edge
*/
public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) {
if (m_interrupt != null) {
ByteBuffer status = ByteBuffer.allocateDirect(4);
// set the byte order
status.order(ByteOrder.LITTLE_ENDIAN);
InterruptJNI.setInterruptUpSourceEdge(m_interrupt,
(byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0),
status.asIntBuffer());
HALUtil.checkStatus(status.asIntBuffer());
} else {
throw new IllegalArgumentException(
"You must call RequestInterrupts before setUpSourceEdge");
}
}
}

View File

@@ -1,7 +1,7 @@
package edu.wpi.first.wpilibj.hal;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class HALUtil extends JNIWrapper {
public static final int NULL_PARAMETER = -5;
@@ -28,6 +28,12 @@ public class HALUtil extends JNIWrapper {
public static native boolean getFPGAButton(IntBuffer status);
public static native String getHALErrorMessage(int code);
public static native int getHALErrno();
public static native String getHALstrerror(int errno);
public static String getHALstrerror(){
return getHALstrerror(getHALErrno());
}
public static void checkStatus(IntBuffer status)
{

View File

@@ -1,12 +1,13 @@
package edu.wpi.first.wpilibj.hal;
import java.nio.IntBuffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
public class InterruptJNI extends JNIWrapper {
public interface InterruptHandlerFunction {
void apply(int interruptAssertedMask, ByteBuffer param);
public interface InterruptJNIHandlerFunction {
void apply(int interruptAssertedMask, Object param);
};
public static native void initializeInterruptJVM(IntBuffer status);
public static native ByteBuffer initializeInterrupts(int interruptIndex, byte watcher, IntBuffer status);
public static native void cleanInterrupts(ByteBuffer interrupt_pointer, IntBuffer status);
public static native void waitForInterrupt(ByteBuffer interrupt_pointer, double timeout, IntBuffer status);
@@ -14,6 +15,6 @@ public class InterruptJNI extends JNIWrapper {
public static native void disableInterrupts(ByteBuffer interrupt_pointer, IntBuffer status);
public static native double readInterruptTimestamp(ByteBuffer interrupt_pointer, IntBuffer status);
public static native void requestInterrupts(ByteBuffer interrupt_pointer, byte routing_module, int routing_pin, byte routing_analog_trigger, IntBuffer status);
public static native void attachInterruptHandler(ByteBuffer interrupt_pointer, InterruptHandlerFunction handler, ByteBuffer param, IntBuffer status);
public static native void attachInterruptHandler(ByteBuffer interrupt_pointer, InterruptJNIHandlerFunction handler, Object param, IntBuffer status);
public static native void setInterruptUpSourceEdge(ByteBuffer interrupt_pointer, byte risingEdge, byte fallingEdge, IntBuffer status);
}