要用Android控制自定義的硬件,如何實現呢?用JNI即可。
1、准備工作
好了,先做些准備工作。准備工作無非就是搭建下環境,下載些東西。請看些鏈接。點我點我!
2、led驅動
照理說,點燈的程序,我不應該貼出來的,但是,考慮到有同學做Android沒學過驅動,我就貼出來,僅供參考哈:
[cpp]
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "leds"
static int led_gpios[] = {
S5PV210_GPJ2(0),
S5PV210_GPJ2(1),
S5PV210_GPJ2(2),
S5PV210_GPJ2(3),
};
#define LED_NUM ARRAY_SIZE(led_gpios)
static long gec210_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > LED_NUM) {
return -EINVAL;
}
gpio_set_value(led_gpios[arg], !cmd);
//printk(DEVICE_NAME": %d %d\n", arg, cmd);
break;
default:
return -EINVAL;
}
return 0;
}
static struct file_operations gec210_led_dev_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = gec210_leds_ioctl,
};
static struct miscdevice gec210_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &gec210_led_dev_fops,
};
static int __init gec210_led_dev_init(void) {
int ret;
int i;
for (i = 0; i < LED_NUM; i++) {
ret = gpio_request(led_gpios[i], "LED");
if (ret) {
printk("%s: request GPIO %d for LED failed, ret = %d\n", DEVICE_NAME,
led_gpios[i], ret);
return ret;
}
s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
gpio_set_value(led_gpios[i], 1);
}
ret = misc_register(&gec210_led_dev);
printk(DEVICE_NAME"\tinitialized\n");
return ret;
}
static void __exit gec210_led_dev_exit(void) {
int i;
for (i = 0; i < LED_NUM; i++) {
gpio_free(led_gpios[i]);
}
misc_deregister(&gec210_led_dev);
}
module_init(gec210_led_dev_init);
module_exit(gec210_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GEC Inc.");
Makefile也附出來了:
[plain]
ifneq ($(KERNELRELEASE),)
obj-m :=led_drv.o
else
module-objs :=led_drv.o
KERNELDIR :=/home/gec/linux_kernel/linux-2.6.35.7/
PWD :=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
$(RM) *.ko *.mod.c *.mod.o *.o *.order *.symvers *.cmd
那個linux-2.6.35.7是指GEC210板上Android系統的Linux內核版本,而且在PC機的Linux下也要有這個內核的源碼,路徑按Makefile裡面放,也可以改Makefile需要的同學可以自行下載哈。
好了。執行make,之後,就能得到一個.ko結尾的文件,把這個.ko文件放進GEC210板的文件系統裡,怎麼放進去?SD卡也可以,網線nfs也可以,串口線也可以。這裡不詳說。
3、編寫Android應用程序
不多說,看代碼吧,都是最基本的。2.2版的
LedDemoTestActivity.java
[java]
package com.gec.leddemotest.activity;
import android.app.Activity;
import android.os.Bundle;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.Toast;
public class LedDemoTestActivity extends Activity {
private RadioGroup radioGroupLed01;
private RadioGroup radioGroupLed02;
private RadioGroup radioGroupLed03;
private RadioGroup radioGroupLed04;
static{
System.loadLibrary("leddemotest");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
openRunLed();
radioGroupLed01=(RadioGroup)findViewById(R.id.radioGroupLed01);
radioGroupLed01.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
if(checkedId==R.id.chooseLed01_1)
{
startRunLed(0,1);
Toast.makeText(LedDemoTestActivity.this, R.string.led1_on, Toast.LENGTH_LONG).show();
}else if(checkedId==R.id.chooseLed01_2)
{
startRunLed(0,0);
Toast.makeText(LedDemoTestActivity.this, R.string.led1_off, Toast.LENGTH_LONG).show();
}
}
});
radioGroupLed02=(RadioGroup)findViewById(R.id.radioGroupLed02);
radioGroupLed02.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
if(checkedId==R.id.chooseLed02_1)
{
startRunLed(1,1);
Toast.makeText(LedDemoTestActivity.this, R.string.led2_on, Toast.LENGTH_LONG).show();
}else if(checkedId==R.id.chooseLed02_2)
{
startRunLed(1,0);
Toast.makeText(LedDemoTestActivity.this, R.string.led2_off, Toast.LENGTH_LONG).show();
}
}
});
radioGroupLed03=(RadioGroup)findViewById(R.id.radioGroupLed03);
radioGroupLed03.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
if(checkedId==R.id.chooseLed03_1)
{
startRunLed(2,1);
Toast.makeText(LedDemoTestActivity.this, R.string.led3_on, Toast.LENGTH_LONG).show();
}else if(checkedId==R.id.chooseLed03_2)
{
startRunLed(2,0);
Toast.makeText(LedDemoTestActivity.this, R.string.led3_off, Toast.LENGTH_LONG).show();
}
}
});
radioGroupLed04=(RadioGroup)findViewById(R.id.radioGroupLed04);
radioGroupLed04.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(RadioGroup group, int checkedId) {
// TODO Auto-generated method stub
if(checkedId==R.id.chooseLed04_1)
{
startRunLed(3,1);
Toast.makeText(LedDemoTestActivity.this, R.string.led4_on, Toast.LENGTH_LONG).show();
}else if(checkedId==R.id.chooseLed04_2)
{
startRunLed(3,0);
Toast.makeText(LedDemoTestActivity.this, R.string.led4_off, Toast.LENGTH_LONG).show();
}
}
});
}
protected void onDestroy() {
closeRunLed();
}
public native void startRunLed(int whichLed,int on);
public native void openRunLed();
public native void closeRunLed();
}
要在工程目錄下創建jni文件,把下面的文件放進去。
appleds.c
[cpp]
#include
#include
#include
#include
#include
#include
#define LED_ON 1
#define LED_OFF 0
int fd;
JNIEXPORT void JNICALL Java_com_gec_leddemotest_activity_LedDemoTestActivity_openRunLed
(JNIEnv *env, jobject this,jint whichLed,jint on)
{
fd = open("/dev/leds", 0);
}
JNIEXPORT void JNICALL Java_com_gec_leddemotest_activity_LedDemoTestActivity_startRunLed
(JNIEnv *env, jobject this,jint whichLed,jint on)
{
if (fd < 0) {
perror("open device leds");
exit(1);
}
ioctl(fd, on,whichLed);
}
JNIEXPORT void JNICALL Java_com_gec_leddemotest_activity_LedDemoTestActivity_closeRunLed
(JNIEnv *env, jobject this,jint whichLed,jint on)
{
close(fd);
}
上面的JNIEXPORT是必須的,void是返回類型,JNICALL也是必須的,後面的Java也是必須的,Java後面跟的就是com.gec.leddemotest.activity包下的LedDemoTestActivity下調用的函數名,把.改為_就可以了。大概是這意思。在JNI的.c文件中,所以要調用的函數的參數最少是兩個,一個是JNIEnv *env,一個是jobject this.具體有什麼用,大伙們自己上網查一下JNI即可。另外,像int這樣的參數,在JNI裡要寫成jint,雖然int跟jint其實是一樣的,但是JNI格式還是選擇jint比較適合,也就是在JNI的.c中,用到的所有數據類型都可以在上面加個j。當然頭文件要包jni.h才行。
4、編寫Android.mk
Android.mk是編譯.so文件的重要部分,分別有兩個。
下面是JNI文件夾下的Android.mk
[plain]
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libleddemotest a
LOCAL_SRC_FILES :=appleds.c b
LOCAL_C_INCLUDES += \ c
$(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY) d
a:生成C 動態庫名稱libleddemotest(JAVA層就是通過加載此庫名稱來實現互調)
b:編譯C文件
c:加載jni庫頭文件
d:生成libleddemotest動態庫
下面是工程目錄下的Android.mk
[plain]
LOCAL_PATH:= $(call my-dir) a
include $(CLEAR_VARS) b
LOCAL_SRC_FILES := $(call all-subdir-java-files) c
LOCAL_PACKAGE_NAME := LedDemoTest d
LOCAL_JNI_SHARED_LIBRARIES := libleddemotest e
include $(BUILD_PACKAGE) f
include $(LOCAL_PATH)/jni/Android.mk g
# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH)) h
a:一個Android.mk文件首先必須定義好LOCAL_PATH,獲得當前目錄
b:用來初始化Android.mk文件中”LOCAL_XXX”的變量
c:編譯Java文件
d:生成Android應用apk文件名稱
e:生成Android應用apk文件名稱
f:生成Android應用
g:編譯jni目錄裡面的Android.mk文件
h:編譯此工程裡面所有的Android.mk文件
5、執行ndk-build
在cygwin中執行,用linux命令,進入工程目錄,執行ndk-build.
6、將leds設備文件設置權限
用串口線,連接GEC210板,執行insmod XXXX.ko文件,將設備文件裝進內核,然後在/dev下會有一個leds的設備文件,
用chmod 777 leds,將leds的設備權限加大,好了。可以將應用程序運行在GEC210板上了。享受吧。
過會會把源代碼附上。