Android應用程序通過JNI調用驅動程序(友善Smart210)

實現目標:

寫一個簡單的測試smart210上LED的應用程序,應用程序通過JNI調用Android系統下的Linux內核中的LED的驅動程序,實現在應用程序上控制開發板上4個LED的目的。

開發環境:

win7 32位的系統;

開發板:友善Smart210(s5pv210);

Android版本:Android-4.0.3;

Linux內核版本:Linux-3.0.8

環境搭建以及完成JNI部分:

這裡假設你的電腦上已經裝好瞭開發應用程序的環境,其中Android sdk的下載地址為https://developer.android.com/sdk/index.html 根據自己電腦的系統來下載適合自己的sdk吧。

ndk的簡介:(參考網絡博客)

NDK全稱:Native Development Kit。

1、NDK是一系列工具的集合。
NDK提供瞭一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。這些工具對開發者的幫助是巨大的。
NDK集成瞭交叉編譯器,並提供瞭相應的mk文件隔離CPU、平臺、ABI等差異,開發人員隻需要簡單修改mk文件(指出“哪些文件需要編譯”、“編 譯特性要求”等),就可以創建出so。
NDK可以自動地將so和Java應用一起打包,極大地減輕瞭開發人員的打包工作。

2、NDK提供瞭一份穩定、功能有限的API頭文件聲明。
Google明確聲明該API是穩定的,在後續所有版本中都穩定支持當前發佈的API。從該版本的NDK中看出,這些API支持的功能非常有限,包含有:C標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、Log庫(liblog)。

對於Windows環境下NDK的開發,如果使用的NDK是r7之前的版本,必須要安裝Cygwin才能使用NDK,所以為Eclipse需要配置的builder,其實是執行Cygwin,然後傳遞ndk-build作為參數。在NDKr7開始,Google的Windows版的NDK提供瞭一個ndk-build.cmd的腳本,這樣,就可以直接利用這個腳本編譯,而不需要使用Cygwin瞭。隻需要為Eclipse Android工程添加一個Builders,就能讓Eclipse自動編譯NDK。

現在我們來講解怎麼搭建ndk的環境,ndk的下載地址為:https://developer.android.com/tools/sdk/ndk/index.html 下載後解壓相應的ndk,我解壓在D盤的android-ndk目錄下,解壓後的的文件為android-ndk-r9c,如下圖:

1.解壓完ndk後,接下來我們來在Eclipse中怎麼把ndk的部分設置進去,首先打開Eclipse,創建一個新工程,我去的工程名字為:LEDAPP,包的名字為:com.ndk.led,如下圖:

至於具體怎麼在Eclipse下建立Android的工程,網上很多資料,隻是這裡需要註意,因為後邊要用到所以專門弄出來說說。vcD4KPHA+Mi7U2tDCvajBorXEuaSzzMDvw+a9qMGi0ru49mpuac7EvP680KOsuMPOxLz+tOa3xW5ka9Do0qqx4NLrtcTOxLz+o6y+38zlzqqjutTay/m9qLmks8y1xMP719ZMRURBUFDJz9PSvPwtPk5ldy0+Zm9sZGVyo6zIu7rz0LTI62puabXEw/vX1qOszeqzybrzo6zI5828z8LNvKO6PC9wPgo8cD48aW1nIGFsdD0=”” src=”/uploadfile/Collfiles/20140223/2014022309214782.png”>

3.建立並配置Builder

(a)右鍵LEDAPP->Properties->Builders,如下圖:

(b)點擊右邊的New,如下圖:

(c)然後點擊”Program”,並點擊OK按鈕,出現下圖:

(d)在彈出的【EditConfiguration】對話框中,配置選項卡【Main】。
在“Name“中輸入新builders的名稱(我取名為ndk_Builder)。
在“Location”中輸入nkd-build.cmd的路徑。
(我的是D:\android-ndk\android-ndk-r9c\ndk-build.cmd,根據各自的ndk路徑設置,也可以點擊“Browser File System…”來選取這個路徑)。在“WorkingDiretcoty”中輸入${workspace_loc:/LEDAPP}(也可以點擊“Browse Workspace”來選取LEDAPP目錄)。具體設置完成後,如下圖:

(e)【EditConfiguration】對話框中,配置選項卡【Refresh】。
勾選“Refresh resources upon completion”,
勾選“The entire workspace”,
勾選“Recuresively include sub-folders”。 具體入下圖:

(f)【Edit Configuration】對話框中,配置選項卡【Build options】。
勾選“After a “Clean””,
勾選“During manual builds”,
勾選“During auto builds”,
勾選“Specify working set of relevantresources”。
點擊“Specify Resources…”
勾選LEDAPP工程的“jni“目錄,點擊”finish“。 具體設置完如下圖:

(g)點擊上圖中的OK按鈕後,再點擊剛開始出現的對話框的OK按鈕,這樣就設置完成瞭

4.在LEDAPP下新建一個JniLed.class,這就是存放Android應用程序所調用的類的地方,步驟為:右鍵LEDAPP->New->class,具體如下圖:

在“name”的輸入框中輸入:JniLed,在package輸入框中,選擇com.ndk.led,然後點擊finish,完成JniLed.class的創建。接下來雙擊剛創建的JniLed.class,在裡面輸入如下的內容:

package com.ndk.led;

public class JniLed {
    static public native int LedInit();
    static public native int LedIOCTL(int cmd, int led_num);
}

其中JniLed中的那兩個函數,就是我們對led驅動程序經過封裝後的函數,你從名字當中也可以看出來,第一個函數就是初始化led,第二個就是通過命令和選擇哪一個led進行控制

5.生成com_ndk_led_JniLed.h文件,具體為通過cmd命令進入windows的命令行控制界面,然後進入到工程所在的目錄下,具體為進入D盤->test-android/LEDAPP/bin/classes,然後輸入:javah com.ndk.led.JniLed,再回車。這樣會在classes文件下面生成:com_ndk_led_JniLed.h文件,這就是生成的c/c++文件的頭文件。

6.在工程中的jni文件夾下,建立Android.mk文件,名字一定是這個,不能是其他的名字。並且輸入如下的內容:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := LEDAPP
LOCAL_SRC_FILES := com_ndk_led_JniLed.c
include $(BUILD_SHARED_LIBRARY)

這就是Android下面的Makefile,註意第三行中的“LEDAPP”和第四行中的“com_ndk_led_JniLed.c”,一個是模塊的名字,另外一個是要編譯的c語言的源文件,要和上面的.h文件保存一致。

7.把在classes下面生成的com_ndk_led_JniLed.h文件拷貝到剛開始所建立的jni文件夾下,同時建立com_ndk_led_JniLed.c的文件,這個c文件就是通過調用Linux內核中的驅動函數,以一定的方式進行封裝,使Android應用程序能調用驅動的關鍵。這個文件中的函數要看具體的驅動函數來完成,比如說,咱們現在就得看友善所提供的smart210的Linux-3.0.8內核中的led的驅動函數,具體位置為:友善的Linux-3.0.8/char/mini210_leds.c,我會把這個文件共享,方便大傢查看,下面說說com_ndk_led_JniLed.c文件的內容,具體如下:

#include "com_ndk_led_JniLed.h"
#include 
#include 
#include 
#include 
#include  /*包括文件操作,如open() read() close() write()等*/
//----for output the debug log message
#include 


int fd;   //文件標識符

#define DEVICE_NAME  "/dev/leds"   //這是linux下面的設備節點,Android應用程序就是通過這個來識別驅動程序的

#ifdef __cplusplus
extern "C"
{
#endif

JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedInit(JNIEnv *env, jclass arg)
{
	fd = open(DEVICE_NAME,O_RDWR);//打開設備

	ioctl(fd,1,2);
    if(fd == -1)
		return 0;
    else
	    return 1;
}

JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedIOCTL(JNIEnv *env, jclass arg, jint cmd, jint led_num)
{
   int CTLCODE = led_num;

   switch(CTLCODE)
   {
      case 0:
    	  if(cmd==0)
    		  ioctl(fd,0,0);  //調用led驅動函數中的ioctl,這裡需要註意,要按驅動裡的函數保持一致
    	  else if(cmd==1)
    		  ioctl(fd,1,0);
    	  break;
      case 1:
          if(cmd==0)
          	  ioctl(fd,0,1);
          else if(cmd==1)
          	  ioctl(fd,1,1);
          break;
      case 2:
          if(cmd==0)
          	  ioctl(fd,0,2);
          else if(cmd==1)
          	  ioctl(fd,1,2);
          break;
      case 3:
          if(cmd==0)
              ioctl(fd,0,3);
          else if(cmd==1)
          	  ioctl(fd,1,3);
          break;
      default: break;
   }

   return 1;
}

#ifdef __cplusplus
}
#endif

8.同時要更新com_ndk_led_JniLed.h文件中的內容,具體的更新代碼如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_ndk_led_JniLed */

#ifndef _Included_com_ndk_led_JniLed
#define _Included_com_ndk_led_JniLed
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ndk_led_JniLed
 * Method:    LedInit
 * Signature: ()I
 */
//這就是.c文件中實現函數的聲明,註意要一致啊,負責會出現“unfortunately,xxx has stopped!”的錯誤
JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedInit
  (JNIEnv *, jclass);

/*
 * Class:     com_ndk_led_JniLed
 * Method:    LedIOCTL
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedIOCTL
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

以上就是JNI的部分,下面我將進行Android應用程序的開發

Android的應用開發

這一部分我們粗糙的講一下,就是弄幾個按鍵,然後調用剛才生成的類,然後下載到smart210上做一個測試。

1.更新LEDAPP下面的res/layout/activity_main.xml,這是Android應用程序的界面佈局文件,具體的更新內容如下:


    

2.更新LEDAPP下的res/values/strings.xml的內容,這個裡面的內容在前面的activity_main.xml的佈局文件中需要,具體內容如下:



    LEDAPP
    Settings
    Hello world!
    
    LED1_ON
    LED1_OFF
    LED2_ON
    LED2_OFF
    LED3_ON
    LED3_OFF
    LED4_ON
    LED4_OFF


3.現在看看MainActivity.java文件,在他裡面的具體調用剛生成的類,具體的代碼如下:

package com.ndk.led;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	static {
        System.loadLibrary("LEDAPP");
    }//上面的System.loadLibrary("LEDAPP")這一句一定要加上,這是調用剛才生成的庫的函數,負責會出現“unfortunately,xxx has stopped!”的錯誤
	Button Button1,Button2,Button3,Button4,Button5,Button6,Button7,Button8;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        JniLed.LedInit();
        
        Button1 = (Button)findViewById(R.id.led1_on);
        Button2 = (Button)findViewById(R.id.led1_off);
        Button3 = (Button)findViewById(R.id.led2_on);
        Button4 = (Button)findViewById(R.id.led2_off);
        Button5 = (Button)findViewById(R.id.led3_on);
        Button6 = (Button)findViewById(R.id.led3_off);
        Button7 = (Button)findViewById(R.id.led4_on);
        Button8 = (Button)findViewById(R.id.led4_off);
        
        class ButtonClick implements OnClickListener
        {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				switch(v.getId())
				{
					case R.id.led1_on:
						//Toast.makeText(MainActivity.this, "按鈕1", Toast.LENGTH_LONG).show();
						JniLed.LedIOCTL(1,0);
						break;
					case R.id.led1_off:
						JniLed.LedIOCTL(0,0);
						break;
					case R.id.led2_on:
						JniLed.LedIOCTL(1,1);
						break;
					case R.id.led2_off:
						JniLed.LedIOCTL(0,1);
						break;
					case R.id.led3_on:
						JniLed.LedIOCTL(1,2);
						break;
					case R.id.led3_off:
						JniLed.LedIOCTL(0,2);
						break;
					case R.id.led4_on:
						JniLed.LedIOCTL(1,3);
						break;
					case R.id.led4_off:
						JniLed.LedIOCTL(0,3);
						break;
				}
			}
        }
        
        Button1.setOnClickListener(new ButtonClick());
        Button2.setOnClickListener(new ButtonClick());
        Button3.setOnClickListener(new ButtonClick());
        Button4.setOnClickListener(new ButtonClick());
        Button5.setOnClickListener(new ButtonClick());
        Button6.setOnClickListener(new ButtonClick());
        Button7.setOnClickListener(new ButtonClick());
        Button8.setOnClickListener(new ButtonClick());

    }  
}

這裡再簡單第說一下引起“unfortunately,xxxhas stopped!”的錯誤,一般是函數的名字不一致,沒有加載創建的庫所引起的。好吧,就這麼多吧,希望對大傢有用,下面是Android下的源代碼以及smart210的led驅動文件,鏈接如下:

Android源代碼鏈接:https://download.csdn.net/detail/xie0812/6947967

smart210的led驅動鏈接:https://download.csdn.net/detail/xie0812/6947979

下面是在smart210上截取效果圖片:

發佈留言