Working with GPIO on the Wandboard and Writing an Android Driver for GPIO Interrupts

Following up on the last post “How to get started with Embedded Android on the Wandboard”, this article shows how to use the GPIO Pins of the Wandboard. It is based on the Android setup, however the generic part should run under other Linux based systems as well.

Finding the right GPIO Pin Number

In the userspace you are able to access GPIO functionality through the sysclass interface.

root@android:/ # ll /sys/class/gpio/                                           
--w------- root     root         4096 2013-06-20 10:57 export
lrwxrwxrwx root     root              2013-06-20 10:57 gpio101 -> ../../devices/virtual/gpio/gpio101
lrwxrwxrwx root     root              2013-06-20 10:57 gpio200 -> ../../devices/virtual/gpio/gpio200
lrwxrwxrwx root     root              2013-06-20 10:57 gpio24 -> ../../devices/virtual/gpio/gpio24
lrwxrwxrwx root     root              2013-06-20 10:57 gpio72 -> ../../devices/virtual/gpio/gpio72
lrwxrwxrwx root     root              2013-06-20 10:57 gpio75 -> ../../devices/virtual/gpio/gpio75
lrwxrwxrwx root     root              2013-06-20 10:57 gpio90 -> ../../devices/virtual/gpio/gpio90
lrwxrwxrwx root     root              2013-06-20 10:57 gpio91 -> ../../devices/virtual/gpio/gpio91
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip128 -> ../../devices/virtual/gpio/gpiochip128
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip160 -> ../../devices/virtual/gpio/gpiochip160
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip192 -> ../../devices/virtual/gpio/gpiochip192
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip32 -> ../../devices/virtual/gpio/gpiochip32
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip64 -> ../../devices/virtual/gpio/gpiochip64
lrwxrwxrwx root     root              2013-06-20 10:57 gpiochip96 -> ../../devices/virtual/gpio/gpiochip96
--w------- root     root         4096 2013-06-20 10:57 unexport

Take for instance “gpio101”. The number is set together by a Macro in the Kernel:

//file arch/arm/plat-mxc/include/mach/gpio.h
#define IMX_GPIO_NR(bank, nr)		(((bank) - 1) * 32 + (nr))

By having a look at the Schematic of the Wandbaord, the GPIO’s available are found on JP4

pins

In the Kernel the File board-wand.c gives further clues to which of the gpios are available in user space:

static __init void wand_init_external_gpios(void) {

        wand_mux_pads_init_external_gpios();

        gpio_request(IMX_GPIO_NR(3, 11), "external_gpio_0");
        gpio_export(IMX_GPIO_NR(3, 11), true);
        gpio_request(IMX_GPIO_NR(3, 27), "external_gpio_1");
        gpio_export(IMX_GPIO_NR(3, 27), true);
        gpio_request(IMX_GPIO_NR(6, 31), "external_gpio_2");
        gpio_export(IMX_GPIO_NR(6, 31), true);
        gpio_request(IMX_GPIO_NR(1, 24), "external_gpio_3");
        gpio_export(IMX_GPIO_NR(1, 24), true);
        gpio_request(IMX_GPIO_NR(7,  8), "external_gpio_4");
        gpio_export(IMX_GPIO_NR(7,  8), true);
        gpio_request(IMX_GPIO_NR(3, 26), "external_gpio_5");
        gpio_export(IMX_GPIO_NR(3, 26), true);
        gpio_request(IMX_GPIO_NR(3, 8), "external_gpio_6");
        gpio_export(IMX_GPIO_NR(3, 8), true);
        gpio_request(IMX_GPIO_NR(4, 5), "external_gpio_7");
        gpio_export(IMX_GPIO_NR(4, 5), true);
}

It seems like the schematic might not be entirely up to date as it would make a lot of sense if those 8 gpios exported would actually match all of those 8 pins on the right side of JP4.

After some testing it looks like this is the case and the correct mapping looks something like this:

# JP4.04 => /sys/class/gpio/gpio75   (3,11)
# JP4.06 => /sys/class/gpio/gpio91   (3,27)
# JP4.08 => /sys/class/gpio/gpio191  (6,31)
# JP4.10 => /sys/class/gpio/gpio24   (1,24)
# JP4.12 => /sys/class/gpio/gpio200  (7, 8)
# JP4.14 => /sys/class/gpio/gpio90   (3,26)
# JP4.16 => /sys/class/gpio/gpio72   (3, 8)
# JP4.18 => /sys/class/gpio/gpio101  (4, 5)

(again for refernece the pins)
pins
Configuring a GPIO as Back Key

There are cases where using a GPIO as an input key can be quite practical on an Embedded System. For this to work there need to be a few adjustments in the Kernel.

Have a look at the file “arch/arm/mach-mx6/board-wand.c”.
In there a few modifications need to made. They are based on the SABRE board from Freescale.

//this code goes right after #include "board-wand.h"
#include <linux/input.h>
#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake)    \
{                                                               \
        .gpio           = gpio_num,                             \
        .type           = EV_KEY,                               \
        .code           = ev_code,                              \
        .active_low     = act_low,                              \
        .desc           = "btn " descr,                         \
        .wakeup         = wake,                                 \
}

static struct gpio_keys_button ard_buttons[] = {
        GPIO_BUTTON(IMX_GPIO_NR(6, 31),    KEY_BACK,       1, "back",        0)   
};

static struct gpio_keys_platform_data ard_android_button_data = {
        .buttons        = ard_buttons,
        .nbuttons       = ARRAY_SIZE(ard_buttons),
};

static struct platform_device ard_android_button_device = {
        .name           = "gpio-keys",
        .id             = -1,
        .num_resources  = 0, 
        .dev            = {  
                .platform_data = &ard_android_button_data,
        }    
};

static void __init wand_add_android_device_buttons(void)
{
        platform_device_register(&ard_android_button_device);
}
#else
static void __init wand_add_android_device_buttons(void) {}
#endif

The upper code sets up a function to map IMX_GPIO_NR(6, 31) as BACK key. Again this implementation is almost directly copy-pasted from the SABRE Board. This relates to JP4.08 on the physical board.

Now the init function needs to be called during the board setup.

// this goes into the wand_board_init() function
        wand_init_external_gpios();
        wand_add_android_device_buttons();//<= new
        wand_init_spi();

To be able to use the GPIO(6, 31) as key, we need to remove it from the exported gpios.

static __init void wand_init_external_gpios(void) {

        wand_mux_pads_init_external_gpios();

        gpio_request(IMX_GPIO_NR(3, 11), "external_gpio_0");
        gpio_export(IMX_GPIO_NR(3, 11), true);
        gpio_request(IMX_GPIO_NR(3, 27), "external_gpio_1");
        gpio_export(IMX_GPIO_NR(3, 27), true);
        //gpio_request(IMX_GPIO_NR(6, 31), "external_gpio_2");
        //gpio_export(IMX_GPIO_NR(6, 31), true);
        gpio_request(IMX_GPIO_NR(1, 24), "external_gpio_3");
        gpio_export(IMX_GPIO_NR(1, 24), true);
        gpio_request(IMX_GPIO_NR(7,  8), "external_gpio_4");
        gpio_export(IMX_GPIO_NR(7,  8), true);
        gpio_request(IMX_GPIO_NR(3, 26), "external_gpio_5");
        gpio_export(IMX_GPIO_NR(3, 26), true);
        gpio_request(IMX_GPIO_NR(3, 8), "external_gpio_6");
        gpio_export(IMX_GPIO_NR(3, 8), true);
        gpio_request(IMX_GPIO_NR(4, 5), "external_gpio_7");
        gpio_export(IMX_GPIO_NR(4, 5), true);
}

Following the first compile in the article “How to get started with Embedded Android on the Wandboard”, the Kernel can be rebuilt and copied to the SD-Card.

cd /opt/android/
export ARCH=arm
export CROSS_COMPILE=/opt/android/prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-
make -j4 uImage
# this will take a while
sudo cp arch/arm/boot/uImage /media/user/BOOT/
sync && umount /media/user/*

Plug your SD-Card back into your Wandboard and hook up two wires: one to JP4.20 (GND) and one to JP4.08 (our gpio). If you touch the two wires, a “BACK” action on Android will be executed.

gpio

Using GPIOs from Userspace to catch Interrupts

In the big picture we would like to create an Android Application built on a GPIO driven Interrupt. In order to do so, we next create a C Application for the Android target using the Android NDK. With the NDK build script we create a “gpio” binary that “polls” a GPIO Pin and on a falling edge writes out the current value 1 or 0 to the Android Log.

First of all, the Android NDK is required. It can be optained from the Android Developer Website. Download and export it to your home directory to “ndk”.

cd ~
mv Downloads/ndk-..../ ndk

Now let’s create a directory for the C-Application.

cd ~
mkdir gpio
cd gpio
mkdir jni
cd jni

By creating a jni directory we are tricking the NDK build script into thinking we have an actual Android Application (which we don’t need just yet).

In the jni/ directory we create a small setup for our application. Most important is our source file “gpio.c”. It will contain a small routine to listen to a gpio interrupt.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "gpio.h"

#define LOG_TAG "GPIO"

#ifndef EXEC
#include <android/log.h>
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#else
#define LOGD(...) printf(">==< %s >==< ",LOG_TAG),printf(__VA_ARGS__),printf("\n")
#endif


void on_new_value(int val);
int read_gpio(char *path, void (*callback)(int));

int main(int argc, char **argv){
	LOGD("Hello!\n");
#ifdef HOST
	return 0;
#endif
	if(argc == 2)
		return read_gpio(argv[1], on_new_value);
	else
		LOGD("missing argument path");

	return -1;
}

int read_gpio(char *path, void (*callback)(int)){
	int fd = open(path, O_RDONLY);
	char buf[11];
	int res = 0;

	if(fd == -1){
		perror("error opening file");
		return -1;
	}

	struct pollfd gpio_poll_fd = {
		.fd = fd,
		.events = POLLPRI,
		.revents = 0
	};	

	for(;;){
		res = poll(&gpio_poll_fd,1,-1);
		if(res == -1){
			perror("error polling");
			break;
		}

		if((gpio_poll_fd.revents & POLLPRI)  == POLLPRI){
			LOGD("POLLPRI");
			int off = lseek(fd, 0, SEEK_SET);
			if(off == -1) break;
			memset(&buf[0], 0, 11);
			size_t num = read(fd, &buf[0], 10*sizeof(char));
			callback(atoi(buf));	
		}

		if((gpio_poll_fd.revents & POLLERR) == POLLERR){
			//seems always to be true ..
			//LOGD("POLLERR");
		}
	}
	return 0;
}

void on_new_value(int val){
	LOGD("interrupt received, val: %d", val);
}

In this code the function read_gpio is responsible to observe a gpio handle (for instance /sys/class/gpio/gpio91/value). The function poll will observe the file and every time an interrupt is triggered, the function returns with the resulting event in gpio_poll_fd.revents.

On what edge the interrupt should be triggered is defined by the value in “edge” (for instance /sys/class/gpio/gpio91/edge). It can be raising, falling or both. After the edge is triggered the value can be read using lseek on the opened file descriptor.

Let’s have a look at:

poll(&gpio_poll_fd,1,-1);

We theoretically could hand poll over a larger number of file descriptors to observe, however, for this sample one is enough. The third argument defines how long we block the process/thread until we get a timeout. We desire to be woken up as rarely as possible, this is why we chose to wait for an interrupt in the first place. By passing a value smaller than 0 we tell the underlying driver that we expect to wait an undefined time and therefore have no timeout at all.

The read_gpio function takes a function pointer that is called as soon as we read the new value. This way we have a modular way to later pass a function from a JNI module and can therefore separate the module nicely from the JNI functionality. For this main function “new_value” is the function that handles those callbacks, it only prints the current value of the gpio out.

The header file exports the “read_gpio” function to our future modules.

#ifndef _GPIO_H_
#define _GPIO_H_
int read_gpio(char *path, void (*callback)(int));
#endif

The last piece we require is an Android.mk file that defines how to build our project. This is one neat little trick to build a binary for Android instead of a library.

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := gpio_test
LOCAL_SRC_FILES := gpio.c
LOCAL_LDLIBS += -llog
LOCAL_CFLAGS += -DEXEC
include $(BUILD_EXECUTABLE)

Note that BUILD_EXECUTABLE will actually build us an executable file called test_gpio that we can run on the wandboard.
Now the project can be built using the ndk-build script from the Android NDK.

With “-DEXEC” we define a flag which indicates that we are building an executable. By checking it in the code we can use different approaches for the library or for the executable.

cd ~/gpio/jni
~/ndk/ndk-build
Compile thumb  : gpio_test <= gpio.c
Executable     : gpio_test
Install        : gpio_test => libs/armeabi/gpio_test

Copy the gpio_test binary onto the device and change permissions and stats. We are using JP4.06 (gpio_91) for this example. Make sure you are connected via ADB to use adb push to transfer the file to the wandboard (see next section for further infos).

cd ~/gpio
# make sure the rfs of the device is rw
adb connect 192.168.1.100
# adjust the IP to your board
# on the serial console use 
adb push libs/armeabi/gpio_test /system/bin/
adb shell
chmod 755 /system/bin/gpio_test
chown root.2000 /system/bin/gpio_test
# make sure the gpio is an input
# make sure the gpio is configured as interrupt
echo in > /sys/class/gpio/gpio91/direction
echo 1 > /sys/class/gpio/gpio91/active_low
echo falling > /sys/class/gpio/gpio91/edge
gpio_test /sys/class/gpio/gpio91/value

Each time a falling edge occurs on the gpio pin, a message should be printed out when you connect JP4.06 to GND and disconnect it again.

You can automate the setup process of the gpio by adding this to your init.wandboard.rc in the on boot part:

    write /sys/class/gpio/gpio91/direction in
    write /sys/class/gpio/gpio91/edge falling
    write /sys/class/gpio/gpio91/active_low 1

Integrating the code into an Android Java Application

The desired Java Application will dedicate a Thread to listening to the interrupt of the GPIO. An Android Activity will change the background color of the screen, as soon as an edge is detected to a random color, and display the gpio value.

First an Android Application project is created with the Android ADT Bundle. For this example we apply the Namespace “ch.nexuscomputing.android.simplegpio” and the Application name “SimpleGpio”. It should be located in “~/workspace/SimpleGpio” to match the exercise.

Add a new Class called NativeGpio to the Project (adjust the namespace according to your needs).

package ch.nexuscomputing.android.simplegpio;

public class NativeGpio {
	public static interface GpioInterruptCallback{
		void onNewValue(int value);
	}
	public static native void readGpio(String path, GpioInterruptCallback callback);
	
	static{
		System.loadLibrary("gpio");
	}
}

An interface for a callback function is defined. If the “readGpio” native function is called, it must be passed an actual implementation of the interface and its function “onNewValue”. The callback method will hence be called from native space and the code defined in the “onNewValue” Java method will be executed as soon as the C-side is calling it.

Note that a call to System.loadLibrary(“gpio”) is mandatory to let the native method to be called from Java space. Therefore a new native library must be created that contains the defined method.

In order to create the appropriate JNI C function the tool “javah” is used to generate a method header.
Assuming the Java Project is located in “~/workspace/SimpleGpio” do the following:

cd ~/workspace/SimpleGpio
javah -classpath bin/classes ch.nexuscomputing.android.simplegpio.NativeGpio
ls
AndroidManifest.xml
assets
bin
ch_nexuscomputing_android_simplegpio_NativeGpio_GpioInterruptCallback.h
ch_nexuscomputing_android_simplegpio_NativeGpio.h
gen
ic_launcher-web.png
obj
proguard-project.txt
project.properties
res
src

The file ch_nexuscomputing_android_simplegpio_NativeGpio_GpioInterruptCallback.h can be disregarded, it contains no relevant information.
The contents of the file ch_nexuscomputing_android_simplegpio_NativeGpio.h are on the other hand very relevant to the project.

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class ch_nexuscomputing_android_simplegpio_NativeGpio */

#ifndef _Included_ch_nexuscomputing_android_simplegpio_NativeGpio
#define _Included_ch_nexuscomputing_android_simplegpio_NativeGpio
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     ch_nexuscomputing_android_simplegpio_NativeGpio
 * Method:    readGpio
 * Signature: (Ljava/lang/String;Lch/nexuscomputing/android/simplegpio/NativeGpio/GpioInterruptCallback;)V
 */
JNIEXPORT void JNICALL Java_ch_nexuscomputing_android_simplegpio_NativeGpio_readGpio
  (JNIEnv *, jclass, jstring, jobject);

#ifdef __cplusplus
}
#endif
#endif

Now copy the jni directory created before into the project. Move the created header into the jni as jni_gpio.c. Start editing it.

cd ~/workspace/SimpleGpio
cp -a ~/gpio/jni .
rm ch_nexuscomputing_android_simplegpio_NativeGpio_GpioInterruptCallback
mv ch_nexuscomputing_android_simplegpio_NativeGpio.h jni/jni_gpio.c
vim jni/jni_gpio.c

Follow the implementation of the jni part.

#include <jni.h>
#include <stdlib.h>
/* Header for class nativehelper_NativeGpio */
#include "gpio.h"

#define LOG_TAG "GPIO"

#ifndef HOST
#include <android/log.h>
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#else
#define LOGD(...) printf(">==< %s >==< ",LOG_TAG),printf(__VA_ARGS__),printf("\n")
#endif

#ifndef _Included_nativehelper_NativeGpio
#define _Included_nativehelper_NativeGpio
#ifdef __cplusplus
extern "C" {
#endif

static void on_new_value(int val);

static jmethodID cb_method_id;
static jclass cb_class;
static jobject cb_object;
static JNIEnv *cb_save_env;


/*
 * Class:     nativehelper_NativeGpio
 * Method:    readGpio
 * Signature: (Ljava/lang/String;Lnativehelper/NativeGpio/GpioInterruptCallback;)V
 */
JNIEXPORT void JNICALL Java_ch_nexuscomputing_android_simplegpio_NativeGpio_readGpio
  (JNIEnv *env, jclass cls, jstring path, jobject callback){
	cb_class = (*env)->GetObjectClass(env, callback);
	if(cb_class == NULL){
		LOGD("callback interface not found");
		return;
	}

	cb_method_id = (*env)->GetMethodID(env, cb_class, "onNewValue", "(I)V");
	if(cb_method_id == NULL){
		LOGD("could not find callback method");
		return;
	}

	cb_object = callback;
	cb_save_env = env;

	const char *fname = (*env)->GetStringUTFChars(env, path, NULL);	
	LOGD("path is %s", fname);
	read_gpio((char *)fname, on_new_value);
	(*env)->ReleaseStringUTFChars(env, path, fname);
}


static void on_new_value(int val){
	LOGD("interrupt received, val: %d", val);
	(*cb_save_env)->CallVoidMethod(cb_save_env, cb_object, cb_method_id, (jint)val);
}
#ifdef __cplusplus
}
#endif

In this code we start polling the gpio for interrupts. We pass as a callback the function on_new_value and in there we call the Java function out of the native C space. In order to do so, the environment needs to be cached, as well as the callback object and the method of the class thereof.

In order to create a library, the Android.mk needs to be adjusted. If -DEXEC is not defined, then the android log function is used, which makes much more sense if a library is created.

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE := gpio_test
LOCAL_SRC_FILES := gpio.c
LOCAL_LDLIBS += -llog
LOCAL_CFLAGS += -DEXEC
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)
LOCAL_MODULE := libgpio
LOCAL_SRC_FILES := gpio.c jni_gpio.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)

The module can be built using NDK-Build. Now additionally to the gpio_test binary, the library libgpio will be created.

cd ~/workspace/SimpleGpio/jni
~/ndk/ndk-build
Compile thumb  : gpio <= gpio.c
Compile thumb  : gpio <= jni_gpio.c
SharedLibrary  : libgpio.so
Install        : libgpio.so => libs/armeabi/libgpio.so
Compile thumb  : gpio_test <= gpio.c
Executable     : gpio_test
Install        : gpio_test => libs/armeabi/gpio_test

The native part is now created and now can be used from the Java side.

Modify from eclipse the activity_main.xml Layout (cosmetics).

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="40sp"
        android:textAlignment="center"
        android:gravity="center"
        android:id="@+id/gpio"
        android:text="@string/gpio" />

</RelativeLayout>

And now we create the final part of the Application. The Android Activity which calls from a separate Thread the blocking “readGpio”, while passing a callback object.

package ch.nexuscomputing.android.simplegpio;

import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import ch.nexuscomputing.android.simplegpio.NativeGpio.GpioInterruptCallback;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

	private static View sBackground;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		sBackground = findViewById(android.R.id.content);
		if(sGpioThread == null){
			sGpioThread = new Thread(mGpioRunnable, "GPIO-Thread");
			sGpioThread.start();
		}
	}
	
	@Override
	protected void onDestroy() {
		sBackground = null;
		super.onDestroy();
	}
	
	private void updateView(){
		runOnUiThread(mColorRunnable);
	}
	
	private final Runnable mColorRunnable = new Runnable() {
		private Random mRand = new Random();
		@Override
		public void run() {
			int r = mRand.nextInt(256);
			int g = mRand.nextInt(256);
			int b = mRand.nextInt(256);
			
			if(sBackground != null){
				sBackground.setBackgroundColor(Color.rgb(r, g, b));
				sBackground.postInvalidate();	
				((TextView)sBackground.findViewById(R.id.gpio)).setText(
						String.format("# interrupts %d\nvalue is %d", 
								sInterruptCount, sCurrentValue.get()));
				
			}
		}
	};
	
	

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	private static final AtomicInteger sCurrentValue = new AtomicInteger(0); 
	private static int sInterruptCount = 0;
	private static Thread sGpioThread;
	private final Runnable mGpioRunnable = new Runnable() {
		@Override
		public void run() {
			while(true){
				NativeGpio.readGpio("/sys/class/gpio/gpio91/value", new GpioInterruptCallback(){
					@Override
					public void onNewValue(int value) {
						sInterruptCount++;
						sCurrentValue.set(value);
						updateView();
					}
				});		
			}
		}
	};

}

The application is quite simple. A Thread is started polling “readGpio”. Whenever a new Value is received, the TextView and the Background are updated. By checking if sGpioThread == null, we assure that if the Thread is running already, it’s not started a second time (by closing and reopening the application).

Note that shutting down the application is not handled in this example, however is good practice in an actual implementation.

Liked this post? Add a comment below to let us know!
Looking for professional Android Training or Android Engineering? See Nexus-Computing for more.

9 Replies to “Working with GPIO on the Wandboard and Writing an Android Driver for GPIO Interrupts”

  1. Hello Manuel
    I am very interested in the communication between a Wandboard and Arduino cards.
    Is your book “Android mit Arduinoâ„¢ Due” is available in US language ? It seems very very attractive.
    Regards

  2. Manuel, Very good information.
    Have you use a lcd touchscreen with the wandaboard?

    it will be interesting to see a wandaboard + LCD + OsciPrime…. the ultimate pack.

    Thx

  3. Manuel,
    The info is very helpful. I ahve a question. Where do you get the information that J4:16 is translated to IMX_GPIO_NR(3, 8) and gpio72 in Linux? From the wandboard schematic, the pin is called GPIO18 schematic. I want to add additional GPIOs that is not defined, how do I find from the schematic the needed pin number?

    Thanks

    1. Hello Norm,

      Thank you very much. Hope the post got you off a good start 🙂

      From the exported gpio’s in the Kernel it seemed to make sense, that all the 8 gpios exported (in that order) match the headers of the board. However, the schematic seems to be of an older version and some of the pins numbers seemed to be wrong in the schematic.

      As for translating the pin numbers:

      IMX_GPIO_NR(bank, nr)       (((bank) - 1) * 32 + (nr))
      

      meaning: (3,8) = > (3-1)*32 + 8 = 72 => gpio_72

      Be sure to double check that your custom pin is nowhere used in the Kernel.

      Hope that answers your question 🙂

      All the best
      Manuel

  4. Hello,

    Could you set this GPIO code in an Android eclipse project online?
    When I copy this code in a project, it can compile without errors,
    but when running the code in Android, there are problems with loading the package.

    Best regards,

  5. Thanks so much for your code. It was help me a lot. But, additionaly I need a method to write in GPIO’s. Can you help me with it?

  6. Hi Manuel,
    Thanks for a solid tutorial.
    I’m using your solution on another board (still with the IMX6SL). When I apply a voltage the GPIO pin’s value goes from 0 to 1 but when I remove the voltage it does not return to 0. Is this something you experienced?

Leave a Reply

Your email address will not be published. Required fields are marked *


*