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的下载地址为http://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的工程,网上很多资料,只是这里需要注意,因为后边要用到所以专门弄出来说说。

2.在新建立的工程里面建立一个jni文件夹,该文件存放ndk需要编译的文件,具体为:在所建工程的名字LEDAPP上右键->New->folder,然后写入jni的名字,完成后,如图下图:


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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <fcntl.h> /*包括文件操作,如open() read() close() write()等*/
//----for output the debug log message
#include <android/log.h>


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 <jni.h>
/* 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应用程序的界面布局文件,具体的更新内容如下:

<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" >

    <Button
        android:id="@+id/led1_on"
        android:text="@string/myButton1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip"
    />
    
    <Button
        android:id="@+id/led1_off"
        android:text="@string/myButton2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="115dip"
    />
    
    <Button
        android:id="@+id/led2_on"
        android:text="@string/myButton3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="245dip"
    />
    
    <Button
        android:id="@+id/led2_off"
        android:text="@string/myButton4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="355dip"
    />
    
     <Button
        android:id="@+id/led3_on"
        android:text="@string/myButton5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dip"
        android:layout_marginLeft="5dip"
    />
    
    <Button
        android:id="@+id/led3_off"
        android:text="@string/myButton6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dip"
        android:layout_marginLeft="115dip"
    />
    
    <Button
        android:id="@+id/led4_on"
        android:text="@string/myButton7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dip"
        android:layout_marginLeft="245dip"
    />
    
    <Button
        android:id="@+id/led4_off"
        android:text="@string/myButton8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dip"
        android:layout_marginLeft="355dip"
    />

</RelativeLayout>

2.更新LEDAPP下的res/values/strings.xml的内容,这个里面的内容在前面的activity_main.xml的布局文件中需要,具体内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">LEDAPP</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    
    <string name="myButton1">LED1_ON</string>
    <string name="myButton2">LED1_OFF</string>
    <string name="myButton3">LED2_ON</string>
    <string name="myButton4">LED2_OFF</string>
    <string name="myButton5">LED3_ON</string>
    <string name="myButton6">LED3_OFF</string>
    <string name="myButton7">LED4_ON</string>
    <string name="myButton8">LED4_OFF</string>

</resources>
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源代码链接:http://download.csdn.net/detail/xie0812/6947967

smart210的led驱动链接:http://download.csdn.net/detail/xie0812/6947979

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。