Android 下通过jni读取i2c数据
Linux 平台:ubuntu 10.04(x86)
Android平台:AML18726-M3(Android4.0, linux kernel2.6.34)
JNI: Java Native Interface
原理:JNI允许在Java虚拟机上运行的Java代码操作其他语言编写的应用程序和本地库方法,从而能够直与接特定的操作系统和硬件平台进行交互。
方法:(这里是在linux下编译so库的方式)
一. Linux下jni编译环境搭建
1. 安装ndk.
a. 将c文件编译成so库的时候需要用到ndk-build命令,必须安装ndk
下载android-ndk-r6-linux-x86.tar,拷贝到ubuntu下,解压到/home/tony/sda6/m3tools目录
b. 添加ndk环境变量:
export PATH="$PATH:/home/tony/sda6/m3tools/android-ndk-r6"
添加完成后可以用ndk-build –v查看是否成功
二. Linux kernel 内核添加i2c设备
1. 根据原理图,i2c的mcu为i2c B, mcu芯片地址为0xa0
2. 在kernel中将设备注册到i2c B总线上,这样才能通过i2c访问到mcu上的数据。
修改kernel/inch/arch/arm/mach-meson3/board-m3-reff17.c,在i2c_board_info __initdata aml_i2c_bus_info_1[]结构体中注册设备(若是i2c A,则在i2c_bus_info_0[]中注册)
static struct i2c_board_info __initdata aml_i2c_bus_info_1[] = {
//elevator
{
I2C_BOARD_INFO("elevator ", 0xa0),
},
};
3. 重新编译内核。
make uImage 并更新uImage.
三. Jni程序编写
1. 以电梯应用程序amlogicAD为例,在amlogicAD目录下新建jni目录
2. 编写Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := i2c_elevator //name of so
LOCAL_SRC_FILES := i2c_elevator.c //c file
LOCAL_LDLIBS := -llog
LOCAL_C_INCLUDES := $(MY_ANDROID_SOURCE)/frameworks/base/core/jni/android/graphics \
$(MY_ANDROID_SOURCE)/external/skia/include/core \
$(MY_ANDROID_SOURCE)/external/skia/include/images \
$(MY_ANDROID_SOURCE)/frameworks/base/include \
$(MY_ANDROID_SOURCE)/system/core/include
include $(BUILD_SHARED_LIBRARY)
3. 利用javah生成jni头文件
a.用javah命令自动生成头文件需要在java代码中先声明该函数。(示例程序中 需要在SerialThread.java中调用jni来操作i2c,先用public native来声明open, read, write, colse函数,这里的open, read, write, close分别与i2c_elevator.c的函 数对应)
public static class I2c {
/**
* @param nodeName
* node path name
* @return return file hander else return <0 on fail
*/
public native int open(String nodeName);
/**
* @param fileHander
* @param i2c_adr
* slave addr
* @param buf
* @param Lenth
* of buf
* @return read length
*/
public native int read(int fileHander, int i2c_adr, byte buf[], int Length);
/**
* @param fileHander
* @param i2c_adr
* slave addr
* @param sub_adr
* sub addr
* @param buf
* @param Lenth
* of buf
* @return write length
*/
public native int write(int fileHander, int i2c_adr, int sub_adr, int buf[], int Length);
public native void close(int fileHander);
}
b.从linux下终端进入到项目目录
找到SerialThread.java对应的class文件
用javah命令自动生成头文件
javah -classpath bin/classes -d jni peter.amlogic.serial.SerialThread
其中:classpath 指定classes的目录,
-d :指定jni目录
运行此命令后,会在jni目录下生成符合jni规范的头文件
c.打开peter_amlogic_serial_SerialThread_I2c.h,里面有和前面在SerialThread.java中函数对应的函数名
在i2c_elevator.c中,函数名应该和peter_amlogic_serial_SerialThread_I2c.h一致,才能被识别
4. 在jni目录新建i2c_elevator.c 此c文件为jni核心,java层主要通过该文件中的函数来实现i2c的读写等操作。注意函数名字要用peter_amlogic_serial_SerialThread_I2c.h中的函数名
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CHIP_ADDR 0xa0 //mcu i2c addr
#define I2C_DEV "/dev/i2c-1" // register i2c B bus
#define LOG_TAG "i2c" //android logcat
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
static int read_eeprom(int fd, char buff[], int addr, int count)
{
int res;
int i;
for(i=0; iGetStringUTFChars(env, file, NULL);
if (str == NULL) {
LOGI("Can't get file name!");
return -1;
}
sprintf(fileName, "%s", str);
LOGI("will open i2c device node %s", fileName);
(*env)->ReleaseStringUTFChars(env, file, str);
return open(fileName, O_RDWR);
}
JNIEXPORT jint JNICALL Java_peter_amlogic_serial_SerialThread_00024I2c_read(JNIEnv * env, jobject obj, jint fileHander, jint slaveAddr, jintArray bufArr, jint len)
{
jint *bufInt;
char *bufByte;
int res = 0, i = 0, j = 0;
if (len <= 0) {
LOGE("I2C: buf len <=0");
goto err0;
}
bufInt = (jint *) malloc(len * sizeof(int));
if (bufInt == 0) {
LOGE("I2C: nomem");
goto err0;
}
bufByte = (char*) malloc(len);
if (bufByte == 0) {
LOGE("I2C: nomem");
goto err1;
}
(*env)->GetIntArrayRegion(env, bufArr, 0, len, bufInt);
memset(bufByte, '\0', len);
if ((j = read(fileHander, bufByte, len)) != len) {
LOGE("read fail in i2c read jni i = %d buf 4", i);
goto err2;
} else
{
for (i = 0; i < j ; i++)
bufInt[i] = bufByte[i];
LOGI("return %d %d %d %d in i2c read jni", bufByte[0], bufByte[1], bufByte[2], bufByte[3]);
(*env)->SetIntArrayRegion(env, bufArr, 0, len, bufInt);
}
free(bufByte);
free(bufInt);
return j;
err2:
free(bufByte);
err1:
free(bufInt);
err0:
return -1;
}
JNIEXPORT jint JNICALL Java_peter_amlogic_serial_SerialThread_00024I2c_write(JNIEnv *env, jobject obj, jint fileHander, jint slaveAddr, jint mode, jintArray bufArr, jint len)
{
#if 0
jint *bufInt;
char *bufByte;
int res = 0, i = 0, j = 0;
if (len <= 0) {
LOGE("I2C: buf len <=0");
goto err0;
}
bufInt = (jint *) malloc(len * sizeof(int));
if (bufInt == 0) {
LOGE("I2C: nomem");
goto err0;
}
bufByte = (char*) malloc(len + 1);
if (bufByte == 0) {
LOGE("I2C: nomem");
goto err1;
}
(*env)->GetIntArrayRegion(env, bufArr, 0, len, bufInt);
bufByte[0] = mode;
for (i = 0; i < len; i++)
bufByte[i + 1] = bufInt[i];
if ((j = write(fileHander, bufByte, len + 1)) != len + 1) {
LOGE("write fail in i2c");
goto err2;
}
LOGI("I2C: write %d byte", j);
free(bufByte);
free(bufInt);
return j - 1;
err2:
free(bufByte);
err1:
free(bufInt);
err0:
return -1;
#endif
}
JNIEXPORT void JNICALL Java_peter_amlogic_serial_SerialThread_00024I2c_close(JNIEnv *env, jobject obj, jint fileHander)
{
close(fileHander);
}
5. 编译i2c_elevator.c
在amlogicAD_iic目录下运行ndk-build, 成功之后在libs/armeabi目录下生成libi2c_elevator.so
四.如何在java中调用so库
1. SerialThread.java中添加libi2c_elevator.so . 添加的时候,名称只取i2c_elevator
{
System.loadLibrary("i2c_elevator");//
}
2. 此时在java中可以调用open, read, close了
五.权限权限chmod 777 /dev/i2c-1
操作设备之前要修改设备的权限,才能完成读写等操作。
修改权限可以修改device/amlogic/f17ref/init.rc脚本,加入chmod 0777 /dev/i2c-1命令(具体在文件什么位置没有限定,此处放在了on boot后)
注意:修改完成之后要make clean,然后重新编译
六.关于java调用jni时的参数传递
1. open函数参数为注册的i2c设备名,类型为String, 在jni中打开成功后返回一个文件描述符。
JAVA:
JNI:
2. read函数
read函数参数较多,fileHander为open device之后返回的描述符;
i2c_addr为i2c芯片上要去读取的地址,本项目中可以从0地址去读;
buf[ ] 中存放的是读取的数据。类型为byte,因此在jni中也应该定义为byte
GetByteArrayRegion:获得Java的byte[]类型参数,将Java传来的字节数组buf,复制len长度的数据到bufInt中。
SetByteArrayRegion:正好相反,将bufInt中的值复制len长度到Java中的bufArr字节数组。
3. close函数
与open函数对应。