【轉】Android通過JNI調用驅動程序(完全解析實例)


原文網址:http://blog.csdn.net/ok138ok/article/details/6560875

 要達到的目的:android系統中,用JAVA寫界面程序,調用jni中間庫提供的接口,去操作某個驅動節點,實現read,writer ioctl等操作!這對底層驅動開發人員是很重要的一個調試通道,也是android 系統下提供一些特殊功能接口的方法!

本文前提:我們假設已經寫了一個驅動程序,它是控制LED的亮滅的,並且創建了一個節點:/dev/vib,也就是通過open這個vib節點,可以read/write/ioctl 操作驅動程序實現LED燈的亮滅控制,具體可以看我另一篇博文《android驅動例子(LED燈控制)

開發環境 1、ubuntu下的NDK編譯環境,2、Esclips開發環境

一、編寫JNI模塊

當安裝好NDK編譯環境后,會在它的目錄下找到sample目錄,它里面有一些例子,可以參考這些例子來寫我們自已的模塊。

clip_image002

1、 source文件夾下,新建“LEDSJNI”文件夾。

2、 Source/LEDSJNI/jni/目錄下,新建“vib-jni.c”

vib-jni.c文件

 

#include <string.h>
#include <jni.h>
#include <fcntl.h> /*包括文件操作,如open() read() close() write()等*/
//----for output the debug log message
#include <android/log.h>
#define LOG_TAG "vib-jni"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define DEVICE_NAME "/dev/vib" //device point
#define VIB_ON 0x11
#define VIB_OFF 0x22
int fd;
jstring
Java_com_auly_control_vibClass_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI--Peter for vib!");//打印字符串
}

jint
Java_com_auly_control_vibClass_Init( JNIEnv* env )
{
LOGE("vibClass_Init() /n");
fd = open(DEVICE_NAME,O_RDWR);//打開設備
LOGE("vibClass_Init()-> fd = %d /n",fd);
if(fd == -1)
{
LOGE("open device %s error /n ",DEVICE_NAME);//打印調試信息
return 0;
}
else
{
return 1;
}
}

jint
Java_com_auly_control_vibClass_IOCTLVIB( JNIEnv* env, jobject thiz, jint controlcode )
{
int CTLCODE = controlcode;
LOGE("IOCTLVIB() = %x --vibClass_IOCTLVIB /n",CTLCODE);
switch(CTLCODE)
{
case VIB_ON:
{
ioctl(fd,VIB_ON);//調用驅動程序中的ioctrl接口,把命令VIB_ON傳下去,實現硬件操作
break;
}
case VIB_OFF:
{
ioctl(fd,VIB_OFF);//調用驅動程序中的ioctrl接口,把命令VIB_OFF傳下去,實現硬件操作
break;
}
default:break;
}
return 1;
}

 

 

3、相同目錄下的新建Android.mk如下

Android.mk文件

 

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := vib-jni
LOCAL_SRC_FILES := vib-jni.c
LOCAL_CFLAGS := -Werror
LOCAL_LDLIBS := -llog -lGLESv2 //__android_log_print 函數
include $(BUILD_SHARED_LIBRARY)

 

可以看到,主要是修改LOCAL_SRC_FILES指向源文件的名稱!

還有一點很重要,如果要使用調試LOG 打印,也就是__android_log_print 函數。要在LOCAL_LDLIBS中添加-llog,如上面的Android.mk所示。

4、編譯JNI模塊

#cd /home/workspace/android-ndk-r4b/sources/LEDSJNI

進到剛才寫的JNI目錄

 

#ndk-build

編譯JNI,編譯成功后,會在LEDSJNI文件夾下生成libs和obj兩個文件夾,並在

LEDSJNI/libs/armeabi下得到目標文件libvib-jni.so

clip_image002[8]

(目前LEDSJNI文件夾只有3個目錄jni,libs,obj)

二、JAVA程序

1、Eclipse新建工程

拷貝LEDSJNI目錄到Windows下,例如C盤下。然后在它里面新建Eclipse 工程。

clip_image002[10]
clip_image004

新鍵工程后,

如果出現如下錯誤:

ERROR: Unable to open class file C:/LEDSJNI/gen/com/auly/control/R.java: No such file or directory

解決方法如下:

對着該工程鼠標右鍵 》bulid path 》configure build path》java build path》order and Export

 

clip_image002[12]

把里面的android 2.1勾上,Build project,就OK了

clip_image002[14]

然后 Run as > Android application,就會出現android的模擬器了,里面跑個helloworld出來。

clip_image002[16]

2、加入button和文本輸出

程序到上面為止代碼是ADT自動生成的,似乎與我們一點關系也沒有。那我們來改一下代碼,因為我們調用JNI接口是為了訪問驅動程序操作硬件的,例如寫,讀,打開LED,關閉LED等等,由按鈕觸發的動作。

第一步是,增加兩個Button,,在main.xml里描述一下:打開Res > layout> main.xml 文件

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android

rientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button android:id="@+id/led_on"/*這表示需要一個唯一的UID來作為Button的ID,它的引用名是led_on。*/
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/LEDon">/*表示這個按鈕的文本是來源於另一個資源描述文件strings.xml里的文:字資源LEDon */
<requestFocus/>
</Button>
<Button android:id="@+id/led_off"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/LEDoff">
<requestFocus/>
</Button>
</LinearLayout>

 

 

實際代碼中,把注釋去掉,否則編譯不過的。

3、加入輸出字符串資源

工程 > values > strings.xml 文件

clip_image002[18]
修改如下

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Led 控制程序</string>
    <string name="app_name">LEDAPP</string>
<string name="LEDon">打開LED</string>
<string name="LEDoff">關閉LED</string>
</resources>

上面的”打開LED”等資源,就是用在按鈕上顯示出來的字符串

經過上面的修改,現在程序界面上,已經有如下效果了

鼠標右鍵工程名>Run as > Android application 運行程序。

clip_image004[11]

4、加入按鈕對應的動作

“打開LED”按扭:調用JNI的IOCTLVIB(VIB_ON);

“關閉LED”按鈕:調用JNI的 IOCTLVIB(VIB_OFF);

操作:

在LEDAPP > src > com.auly.control > vibrator.java文件

 

package com.auly.control;
/**定義頭文件*/
import android.widget.TextView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.util.Log;
import android.app.Activity;
import android.os.Bundle;
public class vibrator extends Activity {
/** 定義變量 */
public static final int VIB_ON = 0x11;
public static final int VIB_OFF = 0x22;
vibClass mvibClass;/**定義類*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/**----------------初始化------------- */
mvibClass = new vibClass();/*聲明類*/
mvibClass.Init(); //調用JNI庫里的初始化函數
/**----------------按鈕:打開LED------------- */
Button btn1 = (Button)findViewById(R.id.led_on);/*這里用到的ID,就是在main.xml里定義的 led_on*/
btn1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v) /**當按鈕按下*/
{
mvibClass.IOCTLVIB(VIB_ON);
}
});
/**----------------按鈕:關閉LED------------- */
Button btn2 = (Button)findViewById(R.id.led_off);/*聲明按鈕,id main.xml里有定義*/
btn2.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v) /**當按鈕按下*/
{
mvibClass.IOCTLVIB(VIB_OFF);
}
});
}
}

 

 

如果在保存時遇到說 save problems,無法保存,請先復制上面的代碼,然后,關閉vibrator.java,提示保存時選不保存,然后在左邊資源窗中再次雙擊打開該文件,把幾日才復制下來的內容,粘貼上去,一般就能正常保存,不知道是不是Eclipse的不穩定造成的。

5、添加類vibClass

鼠標右鍵com.auly.control > new > Class

clip_image002[20]

填參數:

clip_image002[22]

Finish后,在/src/com/auly/control/得到如下的類文件

vibClass.java

修改如下

 

package com.auly.control;
/*Class for Vibrator --peter*/
public class vibClass {
static {
System.loadLibrary("vib-jni");/*加載JNI庫*/
}
/*聲明 函數*/
public static native String stringFromJNI();/*輸出字符串
對應於JNI里面的
jstring Java_com_auly_control_vibClass_stringFromJNI( JNIEnv* env,jobject thiz )
*/
public static native int Init();/*初始化函數,對應於JNI里面的
jint Java_com_auly_control_vibClass_Init( JNIEnv* env )
*/
public static native int IOCTLVIB(int controlcode);
/*IO CTRL 接口
* 對應於JNI里的
jint Java_com_auly_control_vibClass_IOCTLVIB( JNIEnv* env, jobject thiz, jint controlcode )
*/
}

 

 

三、 編譯運行

鼠標右鍵工程名,彈出菜單,選擇 Run as > Android Application 就可以看到編譯過程,編譯完成后,會自動調用android模擬器,看到如下效果

clip_image002[24]

安裝到開發板:

在C:/LEDSJNI目錄下,會看到bin文件夾,里面的LEDAPP.apk就是這個程序的安裝文件,可以把它安裝的開發板上,運行本程序,看控制開發板上的LED燈的效果。

步驟:

1、開發板上跑的kenel就已經把了LED驅動編譯在里面了,

可以參考《android驅動例子(LED燈控制)》

2、開發板android跑起來后,PC機打開串口工具例如DNW,打開與開發板連接的COM口,然后敲打回車,就會在終端里看到“#”並有光標,表面進入了開發板的命令行終端,

輸入命令:

#chmod 777 /dev/vib 

      這是為了使得vib這個節點可以被我們寫的JNI操作,不然會open失敗的,因為APK安裝的JNI模塊,權限不夠,這個節點是我們的LED驅動生成的控制節點。

也可以在android文件系統yaffs編譯時,通過init.rc 文件來實現這個操作,就是在該文件里隨便一行,寫上面的命令行,啟動時會自動執行!這樣就不用手動的改變該節點的屬性了。

3、拷貝LEDAPP.apk到開發板上,通過安裝工具把它安裝到開發板上,如果不會安,可以GOOGLE一下,

4、運行程序,就能按程序上的近鈕來控制開發板上的LED亮滅了!

 


     要學好嵌入式,還是建議買一個開發板,因為在枯燥學習中,要實踐才會記憶深刻,而且,在看到自已學的知道能做出一些功能,會很有成就感,更能積累信心往深處學。例如學驅動開發,能在開發板上做出一個應用,調用自已寫的驅動程序,控制LED燈的亮滅,這就很能鼓勵自已,讓自已更有興趣往下學,這樣學習來會更快!

 

  推薦使用 著名開源項目:樹莓派3 卡片電腦,我個人覺得挺不錯的,也非常的便宜,

 介紹看這里

 http://www.superpi.org/chanpin/superpi3/superpi3.htm

 

csdn簽名


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM