레이블이 jni인 게시물을 표시합니다. 모든 게시물 표시
레이블이 jni인 게시물을 표시합니다. 모든 게시물 표시

2015년 4월 10일 금요일

WiringPi on ODROID-C1(Android)

WiringPi on ODROID-C1(Android)



ODROID-C1 Ubuntu에서 Raspberry Pi에서 사용되는 WiringPi를 사용할 수 있도록 porting 하였습니다.

http://odroid.com/dokuwiki/doku.php?id=en:c1_tinkering

wiringPi를 사용한 example-led 예제를 Android 에서도 사용할 수 있도록 작업을 해 보았습니다.

먼저 소스를 다운 받습니다.

git clone https://github.com/codewalkerster/example-led

다운 받은 소스를 eclipse에서 import 시킵니다.


그리고 NDK를 이용하여 wiringPi를 build합니다.

$ cd example-led/jni
$ ndk-build
Android NDK: WARNING: APP_PLATFORM android-16 is larger than android:minSdkVersion 7 in /media/codewalker/92fc070a-3bc1-43e0-af27-03d08ba9dd3e/home/codewalker/workspace/xxxx/example-led/AndroidManifest.xml  
[armeabi] Compile thumb  : wiringPi <= wiringPi.c
/media/codewalker/92fc070a-3bc1-43e0-af27-03d08ba9dd3e/home/codewalker/workspace/xxxx/example-led/jni/wiringPi/wiringPi.c:147:0: warning: "PAGE_SIZE" redefined [enabled by default]
 #define PAGE_SIZE   (4*1024)
 ^
In file included from /home/codewalker/projects/android-ndk-r10d/platforms/android-21/arch-arm/usr/include/signal.h:34:0,
                 from /home/codewalker/projects/android-ndk-r10d/platforms/android-21/arch-arm/usr/include/poll.h:34,
                 from /media/codewalker/92fc070a-3bc1-43e0-af27-03d08ba9dd3e/home/codewalker/workspace/xxxx/example-led/jni/wiringPi/wiringPi.c:59:
/home/codewalker/projects/android-ndk-r10d/platforms/android-21/arch-arm/usr/include/limits.h:119:0: note: this is the location of the previous definition
 #define PAGE_SIZE 4096
 ^
/media/codewalker/92fc070a-3bc1-43e0-af27-03d08ba9dd3e/home/codewalker/workspace/xxxx/example-led/jni/wiringPi/wiringPi.c: In function 'interruptHandler':
/media/codewalker/92fc070a-3bc1-43e0-af27-03d08ba9dd3e/home/codewalker/workspace/xxxx/example-led/jni/wiringPi/wiringPi.c:1865:7: warning: return makes pointer from integer without a cast [enabled by default]
       return wiringPiFailure (WPI_FATAL, "wiringPiISR: wiringPi has not been initialised. Unable to continue.\n") ;
       ^
[armeabi] Compile thumb  : wiringPi <= wiringShift.c
[armeabi] Compile thumb  : wiringPi <= piHiPri.c
[armeabi] Compile thumb  : wiringPi <= piThread.c
[armeabi] Compile thumb  : wiringPi <= wiringPiSPI.c
[armeabi] Compile thumb  : wiringPi <= wiringPiI2C.c
[armeabi] Compile thumb  : wiringPi <= softPwm.c
[armeabi] Compile thumb  : wiringPi <= softTone.c
[armeabi] Compile thumb  : wiringPi <= mcp23008.c
[armeabi] Compile thumb  : wiringPi <= mcp23016.c
[armeabi] Compile thumb  : wiringPi <= mcp23017.c
[armeabi] Compile thumb  : wiringPi <= mcp23s08.c
[armeabi] Compile thumb  : wiringPi <= mcp23s17.c
[armeabi] Compile thumb  : wiringPi <= sr595.c
[armeabi] Compile thumb  : wiringPi <= pcf8574.c
[armeabi] Compile thumb  : wiringPi <= pcf8591.c
[armeabi] Compile thumb  : wiringPi <= mcp3002.c
[armeabi] Compile thumb  : wiringPi <= mcp3004.c
[armeabi] Compile thumb  : wiringPi <= mcp4802.c
[armeabi] Compile thumb  : wiringPi <= mcp3422.c
[armeabi] Compile thumb  : wiringPi <= max31855.c
[armeabi] Compile thumb  : wiringPi <= max5322.c
[armeabi] Compile thumb  : wiringPi <= sn3218.c
[armeabi] SharedLibrary  : libwiringPi.so
[armeabi] Install        : libwiringPi.so => libs/armeabi/libwiringPi.so
[armeabi] Compile thumb  : wiringPiDev <= ds1302.c
[armeabi] Compile thumb  : wiringPiDev <= maxdetect.c
[armeabi] Compile thumb  : wiringPiDev <= piNes.c
[armeabi] Compile thumb  : wiringPiDev <= gertboard.c
[armeabi] Compile thumb  : wiringPiDev <= piFace.c
[armeabi] Compile thumb  : wiringPiDev <= lcd128x64.c
[armeabi] Compile thumb  : wiringPiDev <= lcd.c
[armeabi] Compile thumb  : wiringPiDev <= piGlow.c
[armeabi] SharedLibrary  : libwiringPiDev.so
[armeabi] Install        : libwiringPiDev.so => libs/armeabi/libwiringPiDev.so
[armeabi] Compile thumb  : wpi_android <= wpi_android.c
[armeabi] SharedLibrary  : libwpi_android.so
[armeabi] Install        : libwpi_android.so => libs/armeabi/libwpi_android.so 



jni/Android.mk 에 libwingPi.so와 libwingPiDev.so 빌드하도록 추가 하였습니다.

include $(CLEAR_VARS)
LOCAL_C_INCLUDES += \
    $(NDK_PATH)/platforms/android-21/arch-arm/usr/include \
    $(LOCAL_PATH)/wiringPi

LOCAL_MODULE    := wiringPi
LOCAL_SRC_FILES := \
    wiringPi/wiringPi.c \
    wiringPi/wiringShift.c \
    wiringPi/piHiPri.c \
    wiringPi/piThread.c \
    wiringPi/wiringPiSPI.c \
    wiringPi/wiringPiI2C.c \
    wiringPi/softPwm.c \
    wiringPi/softTone.c \
    wiringPi/mcp23008.c \
    wiringPi/mcp23016.c \
    wiringPi/mcp23017.c \
    wiringPi/mcp23s08.c \
    wiringPi/mcp23s17.c \
    wiringPi/sr595.c \
    wiringPi/pcf8574.c \
    wiringPi/pcf8591.c \
    wiringPi/mcp3002.c \
    wiringPi/mcp3004.c \
    wiringPi/mcp4802.c \
    wiringPi/mcp3422.c \
    wiringPi/max31855.c \
    wiringPi/max5322.c \
    wiringPi/sn3218.c

LOCAL_CFLAGS    += -UNDEBUG -DANDROID

LOCAL_LDLIBS    := -ldl -llog
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_C_INCLUDES += \
    $(NDK_PATH)/platforms/android-21/arch-arm/usr/include \
    $(LOCAL_PATH)/wiringPi

LOCAL_MODULE    := wiringPiDev
LOCAL_SRC_FILES := \
    devLib/ds1302.c \
    devLib/maxdetect.c \
    devLib/piNes.c \
    devLib/gertboard.c \
    devLib/piFace.c \
    devLib/lcd128x64.c \
    devLib/lcd.c \
    devLib/piGlow.c

LOCAL_SHARED_LIBRARIES := libwiringPi

LOCAL_CFLAGS    += -UNDEBUG
include $(BUILD_SHARED_LIBRARY)

그리고 wirinPi에서 사용해야 하는 function을 jni 형식으로 만듭니다.

jint Java_com_hardkernel_wiringpi_MainActivity_analogRead(JNIEnv* env, jobject obj, jint port) {
    return analogRead(port);
}

void Java_com_hardkernel_wiringpi_MainActivity_digitalWrite(JNIEnv* env, jobject obj, jint port, jint onoff) {
    digitalWrite(port, onoff);
}

int Java_com_hardkernel_wiringpi_MainActivity_wiringPiSetupSys(JNIEnv* env, jobject obj) {
    wiringPiSetupSys();
    return 0;
}

사용한 GPIO를 선언합니다.

    private final int ledPorts[] = {
        97, // GPIOX.BIT0(#97)
        108, // GPIOX.BIT11(#108)
        100, // GPIOX.BIT3(#100)
        101, // GPIOX.BIT4(#101)
        105, // GPIOX.BIT8(#105)
        106, // GPIOX.BIT9(#106)
        107, // GPIOX.BIT10(#107)
        115,  // GPIOX.BIT18(#115)
        116,  // GPIOX.BIT19(#116)
        88,  // GPIOY.BIT8(#88)
        83,  // GPIOY.BIT3(#83)
        87,  // GPIOY.BIT7(#87)
        104,  // GPIOX.BIT7(#104)
        102,  // GPIOX.BIT5(#102)
        103,  // GPIOX.BIT6(#103)
        117, // GPIOX.BIT20(#117)
        99, // GPIOX.BIT2(#99)
        118, // GPIOX.BIT21(#118)
        98, // GPIOX.BIT1(#98)
    };

MainActivity에 jni function들을 선언합니다.

    public native int wiringPiSetupSys();
    public native int analogRead(int port);
    public native void digitalWrite(int port, int onoff);

    static {
        System.loadLibrary("wpi_android");
    }

/sys/class/gpio/export를 이용하여 GPIO sysfs 노드를 만듭니다.
sysfs 방식은 pinMode()를 사용할 수 없기 때문에 directionout으로 설정 합니다.
그리고 value, direction의 접근 권한을 바꿉니다.

    boolean exportGPIO() {
        try {
            DataOutputStream os = new DataOutputStream(mProcess.getOutputStream());
            for (int port: ledPorts) {
                os.writeBytes("echo " +  port + " > /sys/class/gpio/export\n");
                os.writeBytes("chmod 666 /sys/class/gpio/gpio" + port + "/direction\n");
                os.writeBytes("echo out > /sys/class/gpio/gpio" + port + "/direction\n");
                os.writeBytes("chmod 666 /sys/class/gpio/gpio" + port + "/value\n");
            }
            os.flush();
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
            return false;
        }

        return true;
    }

wiringPi의 "/dev/mem"을 open 하여 mmap을 사용하는 방식으로 만들려고 하였으니 "/dev/mem"은 root가 아니면 접근을 제한하도록 되어 있어서 불가능합니다.
jni의 library에는 root 권한을 줄 방법이 없습니다.

그래서 wiringPiSetupSys()를 호출 하여 wiringPi library를 초기화 합니다.


주기적으로 update()를 호출 하도록 만들고 UI와 LED를 control 합니다.

    public void update() {
        int i = 0;
        int adcValue = 0;
        int ledPos = 0;
        if ((adcValue = analogRead (PORT_ADC1)) > 0) {
            ledPos = (adcValue * ledPorts.length * 1000) / 1024;
            ledPos = (ledPorts.length - (ledPos / 1000));
            mADC.setProgress(adcValue);
        } else
            ledPos = 0;

        for (i = 0; i < ledPorts.length; i++) {
            digitalWrite (ledPorts[i], 0);
            mLeds.get(i).setChecked(false);
        }

        for (i = 0; i < ledPos; i++) {
            digitalWrite (ledPorts[i], 1);
            mLeds.get(i).setChecked(true);
        }

        if (!mStop)
            handler.postDelayed(runnable, 100);
    }


2013년 7월 31일 수요일

JNI int,String array parameter, return value

int array를 parameter로 넘겨기 예제(java->jni)

---------------------------------------------------------------------------------------
java file

public class Test {
    static {
        System.loadLibrary("test");
    }

    public static void func1(int arr[]) {
        native_func1(core);
    }

 private native static void native_func1(int arr[]);


---------------------------------------------------------------------------------------
cpp file

static void native_func1(JNIEnv* env, jobject obj, jintArray arr) {
    int size = env->GetArrayLength(arr);

    jint *value = env->GetIntArrayElements(arr, NULL);

    for (int i = 0; i < size; i++) {
        value[i] = i;
    }
 
    env->ReleaseIntArrayElements(arr, value, 0);
    return;
}

static const JNINativeMethod g_methods[] = { 
    { "native_func1", "([I)V", (void*)native_func1 }, 
}; 
  
---------------------------------------------------------------------------------------
//배열 생성 
int[] arr = new int[4];
//배열 전달
Test.func1(arr);


String array를 return value로 받기(jni->java)

---------------------------------------------------------------------------------------
java file
static jobjectArray native_func2(JNIEnv* env, jobject obj) {
    char *temp[4] = { "apple, "banana", tomato", "melon"};

    jobjectArray ret = (jobjectArray)env->NewObjectArray(4, env->FindClass("java/lang/String"),
            env->NewStringUTF(""));

    for (int i = 0; i < 4; i++) {
        env->SetObjectArrayElement(ret, i, env->NewStringUTF(temp[i]));
    }
    return ret;
}

static const JNINativeMethod g_methods[] = {
    { "native_func2", "()[Ljava/lang/String;", (jobjectArray*)native_func2 },
};


---------------------------------------------------------------------------------------
java file

public class Test {
    static {
        System.loadLibrary("test");
    }

    public static String[] func2() {
        return native_func2();
    }

    private native static String[] native_func2();



---------------------------------------------------------------------------------------
String[] str = new String[4];
str = Test.func2();

2010년 10월 5일 화요일

android JNI을 이용하여 serial 읽어 오기

http://developer.android.com/sdk/ndk/index.html

위에 홈페이지에서 ndk를 다운 받고 적당한 디렉토리에 압축을 푼다.

eclipse에서 프로젝트를 하나 생성한다.

프로젝트 생성시 반드시 minSdkVersion="3"을 추가 해야 한다.

AndroidManifest.xml에 추가한다.

project 폴더에 jni 폴더를 만들다.

ndk/sample/ 폴더에 예제를 참조 하여 Android.mk를 생성한다.

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := gps_serial
LOCAL_SRC_FILES := gps_serial.c \
jni_serial.c

LOCAL_LDLIBS := -ldl -llog

include $(BUILD_SHARED_LIBRARY)



jni 폴더에 소스를 생성한다.

gps serial를 읽어 오는 gps_serial.h/c를 생성한다.

//---------------gps_serial.h-----------------------//
#ifndef GPS_SERIAL_H
#define GPS_SERIAL_H

char* read_serial();

#endif //GPS_SERIAL_H



//---------------gps_serial.c-----------------------//
#include
#include
#include
#include
#include
#include

#define GPS_SERIAL_NODE "/dev/ttyACM0"
#define READ_COUNT 256
#define LOG_TAG "GPS Serial"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

int fd = -1;
char buf[READ_COUNT];

int openGPS()
{
LOGI("oepnGPS()");

if (fd = open(GPS_SERIAL_NODE, O_RDONLY) >= 0) {
return fd;
} else {
LOGI("failed to oepn!");
return fd;
}
}

void closeGPS()
{
LOGI("closeGPS()");

if (fd)
close(fd);

fd = -1;
}

char* read_serial()
{
int read_count = 0;
int idx = 0;

memset(buf, 0x00, READ_COUNT);

if (fd == -1)
goto out;

read_count = read(fd, buf, READ_COUNT);
LOGI("count %d", read_count);
LOGI("%s", buf);

LOGI("read success!!");

out:
return buf;
}

여기까지는 일반적인 linux 프로그램이다. 단 printf로 메세지를 확인 할 수 없기 때문에 __android_log_print()를 이용하여 log를 확인 해야 한다.

그리고 java와 c library를 연결 할 jni 함수를 만든다.

//---------------jni_serial.c-----------------------/

#include
#include

#include

void Java_com_hardkernel_gpsserial_GPSSerialActivity_openGPS(JNIEnv* env, jobject obj)
{
openGPS();
}

jstring Java_com_hardkernel_gpsserial_GPSSerialActivity_readGPS(JNIEnv* env, jobject obj)
{
return (*env)->NewStringUTF(env, read_serial());
}

void Java_com_hardkernel_gpsserial_GPSSerialActivity_closeGPS(JNIEnv* env, jobject obj)
{
closeGPS();
}

JNI 함수의 이름 규칙은 Java_ 이후에 package name 그리고 jni를 호출 할 class 이름 그리고 함수 이름을 붙여 주면 됩니다.

Java_com_hardkernel_gpsserial_GPSSerialActivity_openGPS
_____패키지이름_______________class 이름_______함수 이름

이제 ndk를 이용하여 build를 해 보자.

jni 폴더에서 [android-ndk가 설치된 폴더]/ndk-build 명령으로 build를 한다.
하고 나면 아래의 그림과 같이 libs 폴더가 생성된다.


//---------------GPSSerialActivity.java-----------------------/
package com.hardkernel.gpsserial;

import com.hardkernel.gpsserial.R;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.TextView;

public class GPSSerialActivity extends Activity {
private TextView mTv;
private Thread mReadThread = null;
private String mString;
boolean mRunning = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTv = (TextView)findViewById(R.id.gps_textview);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
if (mReadThread != null && mReadThread.isAlive()) {
Log.w("codewalker", "onPause()");
mRunning = false;
mReadThread = null;
}
closeGPS();
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
Log.w("codewalker", "onResume()");
super.onResume();
openGPS();
if (mReadThread == null) {
mRunning = true;
mReadThread = new Thread(new Runnable() {
public void run() {
while (mRunning) {
try {
Thread.sleep(500);
} catch (InterruptedException ignore) {
}
mString = readGPS();
mHandler.sendMessage(mHandler.obtainMessage());
}
}
});
mReadThread.start();
}
}

@Override
protected void onDestroy() {
if (mReadThread != null && mReadThread.isAlive())
mReadThread.destroy();
// TODO Auto-generated method stub
super.onDestroy();
}
Handler mHandler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
mTv.setText(mString);
};
};

public native String readGPS();
public native void openGPS();
public native void closeGPS();

static {
System.loadLibrary("gps_serial");
}
}

jni 함수를 위와 같이 등록해 주고 load해 주는 코드를 추가 한다.