实现目标:
写一个简单的测试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
#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
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源代码链接:http://download.csdn.net/detail/xie0812/6947967
smart210的led驱动链接:http://download.csdn.net/detail/xie0812/6947979
下面是在smart210上截取效果图片: