Android平台读写i2c设备开发笔记二

二、 使用JNI在应用程序框架层添加服务访问接口

       APP应用不能直接访问HAL层,需要JNI层访问HAL模块并向上提供API接口。可以直接提供接口,但建议最好使用服务的方式提供访问。

       我们先看JNI如何访问刚才的HAL模块。

       进入源码根目录下的frameworks/base/service/jni目录,新建com_android_server_IICService.cpp,代码如下:

#include "jni.h"  
#include "JNIHelp.h"  
#include "android_runtime/AndroidRuntime.h"  
#include <utils/misc.h>  
#include <cutils/log.h>  
#include <hardware/hardware.h>  
#include <hardware/iic.h>  
#include <stdio.h>

namespace android  
{  
    /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/iic.h>*/  
        struct iic_device_t* iic_device = NULL;  
    /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/  
    static void iic_setVal(JNIEnv* env, jobject clazz, jstring val, jint slaveAddr, jint subAddr, jint len) {  
        const char *str = env->GetStringUTFChars(val, NULL);  
        LOGI("iic JNI: set value %s to device.", str);  
        if(!iic_device) {  
            LOGI("iic JNI: device is not open.");  
            return;  
        }  
        iic_device->iic_write(iic_device, (unsigned char*)str, slaveAddr, subAddr, len);  
	env->ReleaseStringUTFChars(val, str);  //注意释放资源
    }  

       /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/  
    static jstring iic_getVal(JNIEnv* env, jobject clazz, jint slaveAddr, jint len) {
	unsigned char* data = (unsigned char*)malloc(len);
	iic_device->iic_read(iic_device, data, slaveAddr, len);
        if(!iic_device) {  
            LOGI("iic JNI: device is not open.");  
        }
	int i = 0;
	for(;i<strlen((const char*)data);i++){
	  LOGI("data: %c ", data[i]);
	}  
        //LOGI("iic JNI: get value %s from device @ %x address!", data, subAddr);
	jstring tmp = env->NewStringUTF((const char*)data);
	free(data);
	data = NULL;
	return tmp;   
    } 
  
        /*通过硬件抽象层定义的硬件模块open接口打开硬件设备*/  
    static inline int iic_device_open(const hw_module_t* module, struct iic_device_t** device) {  
        return module->methods->open(module, IIC_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
    }  
        /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/  
    static jboolean iic_init(JNIEnv* env, jclass clazz) {  
        iic_module_t* module;  
          
        LOGI("iic JNI: initializing......");  
        if(hw_get_module(IIC_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
            LOGI("iic JNI: iic Stub found.");  
            if(iic_device_open(&(module->common), &iic_device) == 0) {  
                LOGI("eeprom JNI: iic device is opening...");  
                return 0;  
            }  
            LOGE("eeprom JNI: failed to open iic device.");  
            return -1;  
        }  
        LOGE("eeprom JNI: failed to get iic stub module.");  
        return -1;        
    }  
        /*JNI方法表*/  
    static const JNINativeMethod method_table[] = {  
        {"init_native", "()Z", (void*)iic_init},  
        {"setVal_native", "(Ljava/lang/String;III)V", (void*)iic_setVal},  
        {"getVal_native", "(III)Ljava/lang/String;", (void*)iic_getVal},  
    };  
        /*注册JNI方法*/  
    int register_android_server_IICService(JNIEnv *env) {  
            return jniRegisterNativeMethods(env, "com/android/server/IICService", method_table, NELEM(method_table));  
    }  
};

然后需要让android启动时加载此jni模块

在同目录下修改onload.cpp:

在namespace android中添加一行    int register_android_server_IICService(JNIEnv *env);

在JNI_onLoad方法中添加一行  register_android_server_IICService(env);

在同目录下修改Android.mk:

LOCAL_SRC_FILES增加一行   com_android_server_IICService \

编译命令:mmm frameworks/base/services/jni

注意:  HAL是根据iic_init中的IIC_HARDWARE_MODULE_ID加载相应模块。

然后,使用AIDL进行进程间通信,使APP能访问自定义的硬件服务。

我们需要在frameworks/base/core/java/android/os中新建IIICService.aidl(注意是III)

package android.os;  
interface IIICService {  
    void setVal(String val, int slaveAddr, int regAddr, int len);  
    String getVal(int slaveAddr, int len);  

它定义了服务的接口,接口在IICService中实现并关联到jni本地方法中。

同时我们需要修改frameworkd/base下的Android.mk编译文件,在LOCAL_SRC_FILES中增加 core/java/android/os/IIICService.aidl

编译命令: mmm frameworks/base


下面是AIDL的实现方法类:com.android.server.IICService 位置为:frameworks/base/services/java/com/android/server 代码如下:

package com.android.server;  
import android.content.Context;  
import android.os.IIICService;  
import android.util.Slog;  
public class IICService extends IIICService.Stub {  
    private static final String TAG = "IICService";  
    IICService() {  
        init_native();  
    }  
    public void setVal(String val,int slaveAddr, int regAddr, int len) {  
        setVal_native(val, slaveAddr, regAddr, len);  
    }     
    public String getVal(int slaveAddr,int len) {  
        return getVal_native( slaveAddr, len);  
    }  
     
    //本地方法 
    private static native boolean init_native();  
    private static native void setVal_native(String val, int slaveAddr, int regAddr, int len);  
    private static native String getVal_native(int slaveAddr, int len);  
}; 
从代码中我们可以看到它继承了IIICService.Stub,实现两个接口方法。因为硬件访问一般需要放在一个独立的线程中,这里使用了代理的方法来处理app与硬件服务的通信。

最后需要把新增的IICService服务加入到ServiceManager中,这样就可以通过ServiceManager进行调用。

修改frameworks/base/services/java/com/android/server下的SystemServer.java  在run()方法中添加

try{

    Slog.i(TAG, "IIC SERVICE");

    ServiceManager.addService("iic", new IICService());

}catch(Throwable e){

    Slog.e(TAG, "Failure starting IIC Service", e);

}
编译命令:mmm frameworks/base/services/java

或者使用另一种形式来调用服务:如同使用binder机制绑定service一样的方法, 具体就不详细写了。 


注意:有可能会编译不通过,因为这里修改了android的官方api, 需要运行make update-api更新frameworks/base/api/current.xml

打包后,app就可以使用IICService接口来访问硬件了。
下一节发上app相关代码

(待续)

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在 Linux 内核的 I2C 驱动中,通常会提供设备读写节点,以便用户空间程序可以通过文件系统接口来访问 I2C 设备。这些节点一般位于 `/dev` 目录下,命名规则为 `i2c-X`,其中 X 是 I2C 控制器的编号。 例如,如果系统中有一个名为 `i2c-1` 的 I2C 控制器,那么对应的设备节点为 `/dev/i2c-1`。 在 Android 上层,可以使用 Java 的 `FileInputStream` 和 `FileOutputStream` 类来读写 I2C 设备。需要先打开对应的设备节点,然后通过文件流进行读写操作。 以下是一个简单的读写示例: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class I2CExample { private static final String I2C_DEVICE = "/dev/i2c-1"; private static final int I2C_ADDRESS = 0x50; public static void main(String[] args) { try { // 打开 I2C 设备节点 FileInputStream input = new FileInputStream(I2C_DEVICE); FileOutputStream output = new FileOutputStream(I2C_DEVICE); // 写入数据 byte[] writeBuffer = { 0x00, 0x01, 0x02 }; output.write(writeBuffer); // 读取数据 byte[] readBuffer = new byte[3]; input.read(readBuffer); // 关闭文件流 input.close(); output.close(); System.out.println("Read data: " + toHexString(readBuffer)); } catch (IOException e) { e.printStackTrace(); } } private static String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString(); } } ``` 以上示例中,我们打开了 `/dev/i2c-1` 设备节点,并且使用地址 `0x50` 进行了一次写操作和一次读操作。读取到的数据会以十六进制字符串的形式输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值