JNI – the Java Native Interface

The Java Native Interface is a framework used to allow Java programs to integrate with other applications written in other programming languages like C and C++.

This feature is quite old now with regards to the JVM and was added in 1.1 to 1.2 of the JVM.

I have started using this feature with a Raspberry Pi as you can access the GPIO pins directly with C without other libraries and this can be integrated with a Java program to control the GPIO pins.

This feature is very useful, as the Pi4J library doesn’t work on all versions of Debian for the Raspberry Pi at the time of blogging this post, since they renamed some features, which broke Pi4J.

I will give an example of how to do this –

Create a file called HelloWorld.java

 
import com.sun.jna.Library;
import com.sun.jna.Native;

public class HelloWorld {
public interface CTest extends Library {
public void helloFromC();
}

public interface GPIO extends Library {
public int main();
}
public static void main(String[] args) {
CTest ctest = (CTest) Native.loadLibrary("ctest", CTest.class);
ctest.helloFromC();

GPIO gpio = (GPIO) Native.loadLibrary("gpio", GPIO.class);
gpio.main();
}
}


Create a file called gpio.c

//
// How to access GPIO registers from C-code on the Raspberry-Pi
// Example program
// 15-January-2012
// Dom and Gert
// Revised: 15-Feb-2013

// Access from ARM Running Linux

#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */

#include
#include
#include
#include
#include

#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)

int mem_fd;
void *gpio_map;

// I/O access
volatile unsigned *gpio;

// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))

#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0

#define GET_GPIO(g) (*(gpio+13)&(1<<g)) // 0 if LOW, (1<<g) if HIGH

#define GPIO_PULL *(gpio+37) // Pull up/pull down
#define GPIO_PULLCLK0 *(gpio+38) // Pull up/pull down clock

void setup_io();

void printButton(int g)
{
if (GET_GPIO(g)) // !=0 bit is 1 <- port is HIGH=3.3V
printf("Button pressed!\n");
else // port is LOW=0V
printf("Button released!\n");
}

int main(int argc, char **argv)
{
int g,rep;

// Set up gpi pointer for direct register access
setup_io();

// Switch GPIO 7..11 to output mode

/************************************************************************\
* You are about to change the GPIO settings of your computer. *
* Mess this up and it will stop working! *
* It might be a good idea to 'sync' before running this program *
* so at least you still have your code changes written to the SD-card! *
\************************************************************************/

// Set GPIO pins 7-11 to output
for (g=7; g<=11; g++)
{
INP_GPIO(g); // must use INP_GPIO before we can use OUT_GPIO
OUT_GPIO(g);
}

for (rep=0; rep<10; rep++)
{
for (g=7; g<=11; g++)
{
GPIO_SET = 1<<g;
sleep(1);
}
for (g=7; g<=11; g++)
{
GPIO_CLR = 1<<g;
sleep(1);
}
}

return 0;

} // main

//
// Set up a memory regions to access GPIO
//
void setup_io()
{
/* open /dev/mem */
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
printf("can't open /dev/mem \n");
exit(-1);
}

/* mmap GPIO */
gpio_map = mmap(
NULL, //Any adddress in our space will do
BLOCK_SIZE, //Map length
PROT_READ|PROT_WRITE,// Enable reading & writting to mapped memory
MAP_SHARED, //Shared with other processes
mem_fd, //File to map
GPIO_BASE //Offset to GPIO peripheral
);

close(mem_fd); //No need to keep mem_fd open after mmap

if (gpio_map == MAP_FAILED) {
printf("mmap error %d\n", (int)gpio_map);//errno also set!
exit(-1);
}

// Always use volatile pointer!
gpio = (volatile unsigned *)gpio_map;

} // setup_io


Run the Following commands:


javac HelloWorld.java


Create the jni headers file

javah -jni HelloWorld


Do not modify the file above

gcc -shared -I/usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/include -I/usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt/include/linux HelloWorld.c -o libHelloWorld.so


Then to run the application, use

java -Djava.library.path=. HelloWorld

If you would like more information, I initially used this website

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s