android读写iic设备

http://blog.csdn.net/daniel117/article/details/17464783?locationNum=2&fps=1

 

Android开发和移植过程中,有时需要对某设备进行读写,但系统可能并未提供相应的服务。我们就需要自己开发硬件访问服务来控制设备。下面的例子是读写最简单的i2c设备eeprom的流程, i2c的驱动编写有两种方式,一种是利用系统提供的i2c-dev.c来实现一个i2c适配器的设备文件,然后通过在应用层操作I2C适配器来控制I2C设备;另一种是为I2C从设备独立编写一个设备驱动,不需要i2c-dev.c文件。由于前者比较简单通用性强,我们采用前者来展开。

    根据android层次划分,我们照例对开发分为如下几步:

    1. 添加HAL层接口模块访问设备

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

    3. 使用服务接口api开发应用程序

 

一. 添加HAL层接口模块访问设备

     首先确认物理设备正常。根据开发板说明书获知设备挂载在/dev/i2c-1上,检测到该设备的存在,则通用设备驱动正常。

     eeprom设备为at24c**系列,根据说明书获知设备从地址为0x50,准备工作完毕。

    1. 编写hal层接口模块头文件iic.h 

    进入源码根目录下hardware/libhardware/include/hardware目录新建iic.h,代码如下:

[cpp] view plaincopy

1.  #ifndef ANDROID_IIC_INTERFACE_H    

2.      #define ANDROID_IIC_INTERFACE_H    

3.      #include <hardware/hardware.h>    

4.         

5.     __BEGIN_DECLS    

6.         

7.     /*定义模块ID*/    

8.      #define IIC_HARDWARE_MODULE_ID "iic"    

9.         

10.     /*硬件模块结构体*/    

11.     struct iic_module_t {    

12.         struct hw_module_t common;    

13.     };    

14.         

15.     /*硬件接口结构体*/    

16.     struct iic_device_t {    

17.         struct hw_device_t common;    

18.         int fd;    

19.         int (*iic_write)(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len);    

20.         int (*iic_read)(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len);    

21.     };    

22.         

23.     __END_DECLS    

24.         

25.     #endif  

 

这里定义了iic_writeiic_read两个接口,头文件按照hal规范编写。

2. 编写hal层接口模块文件

    进入源码根目录下hardware/libhardware/modules目录新建iic目录,并在iic目录中添加iic.c,代码如下:

[cpp] view plaincopy

1.  #include <hardware/hardware.h>    

2.  #include <hardware/iic.h>    

3.  #include <fcntl.h>    

4.  #include <errno.h>    

5.  #include <cutils/log.h>    

6.  #include <cutils/atomic.h>   

7.  #include <stdio.h>  

8.  #include<linux/i2c.h>  

9.  #include<linux/i2c-dev.h>  

10. #include <stdlib.h>  

11. #include <linux/types.h>  

12. #include <unistd.h>  

13. #include <sys/types.h>  

14. #include <sys/ioctl.h>  

15. #include <string.h>  

[cpp] view plaincopy

1.  <span style="font-size:14px;">#define DEVICE_NAME "/dev/i2c-1"    

2.  #define MODULE_NAME "iic"    

3.  #define MODULE_AUTHOR "mfayz@sohu.com"    

4.   

5.   

6.  #define I2C_RETRIES 0x0701/* number of times a device address should be polled when not acknowledging */  

7.  #define I2C_TIMEOUT 0x0702/* set timeout in units of 10 ms */  

8.  #define I2C_RDWR         0x0707  

9.   

10.   

11. /*********定义struct i2c_rdwr_ioctl_datastruct i2c_msg,要和内核一致*******/  

12. struct i2c_msg  

13.        {  

14.          unsigned short addr;  

15.          unsigned short flags;  

16.          #define I2C_M_TEN 0x0010  

17.          #define I2C_M_RD 0x0001  

18.          unsigned short len;  

19.          unsigned char *buf;  

20.        };  

21.   

22.   

23. struct i2c_rdwr_ioctl_data {  

24. struct i2c_msg *msgs;/* pointers to i2c_msgs */  

25. int nmsgs; /* number of i2c_msgs */  

26. };   

27.   

28.   

29.     /*设备打开和关闭接口*/    

30. static int iic_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);    

31. static int iic_device_close(struct hw_device_t* device);    

32.         

33.     /*设备访问接口*/    

34. static int iic_write(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len);    

35. static int iic_read(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len);   

36.       

37.     /*模块方法表*/    

38. static struct hw_module_methods_t iic_module_methods = {    

39.         open: iic_device_open    

40. };    

41.   

42.   

43. struct i2c_rdwr_ioctl_data iic_data;  

44. int ret;  

45.   

46.   

47.     /*模块实例变量*/    

48. struct iic_module_t HAL_MODULE_INFO_SYM = {    

49.         common: {    

50.             tag: HARDWARE_MODULE_TAG,    

51.             version_major: 1,    

52.             version_minor: 0,    

53.             id: IIC_HARDWARE_MODULE_ID,    

54.             name: MODULE_NAME,    

55.             author: MODULE_AUTHOR,    

56.             methods: &iic_module_methods, //实现了一个open的方法供jni层调用,从而实例化eeprom_device_t   

57.         }    

58. };    

59.   

60.   

61. static int iic_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device){    

62.     struct iic_device_t* dev;  

63.     dev = (struct iic_device_t*)malloc(sizeof(struct iic_device_t));    

64.     if(!dev) {    

65.         LOGE("iic Stub: failed to alloc space");    

66.         return -EFAULT;    

67.     }else{  

68. LOGE("hal: alloc space succ!");  

69.     }    

70.     

71.     memset(dev, 0, sizeof(struct iic_device_t));    

72.     dev->common.tag = HARDWARE_DEVICE_TAG;    

73.     dev->common.version = 0;    

74.     dev->common.module = (hw_module_t*)module;    

75.     dev->common.close = iic_device_close;    

76.     dev->iic_write = iic_write;  

77.     dev->iic_read = iic_read;  

78.     *device = &dev->common;     //将实例化后的iic_device_t地址返回给jni,这样jni层就可以直接调用方法了。  

79.   

80.   

81.     if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {    

82.         LOGE("iic Stub hal: failed to open /dev/i2c-1 -- %s.", strerror(errno));  

83. free(dev);    

84.         return -EFAULT;    

85.     }else{      

86.         LOGI("iic Stub hal: open /dev/i2c-1 successfully.");   

87. iic_data.nmsgs=2;   

88. iic_data.msgs=(struct i2c_msg*)malloc(iic_data.nmsgs*sizeof(struct i2c_msg));   

89.   

90.   

91. if(!iic_data.msgs){  

92.                 LOGE("malloc error");  

93. close(dev->fd);  

94.                 exit(1);  

95.         }  

96. ioctl(dev->fd, I2C_TIMEOUT, 2);//设置超时时间  

97.         ioctl(dev->fd, I2C_RETRIES, 1);//设置重发次数  

98.     }  

99.     return 0;    

100. }   

101.   

102.   

103. static int iic_device_close(struct hw_device_t* device) {    

104.         struct iic_device_t* iic_device = (struct iic_device_t*)device;    

105.         

106.         if(iic_device) {    

107.             close(iic_device->fd);    

108.             free(iic_device);    

109.         }    

110.             

111.         return 0;    

112. }    

113.         

114. static int iic_write(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, unsigned short subAddr, int len) {  

115. int count = 0;  

116. unsigned char data[2];  

117. unsigned char bytes;  

118.   

119.   

120.         LOGI("iic Stub hal: set value %s to device.", dataBuf);   

121. iic_data.nmsgs=1;   

122.         (iic_data.msgs[0]).len=2; //写入地址位和数据长度  

123.         (iic_data.msgs[0]).addr=slaveAddr;// 设备地址0x50  

124.         (iic_data.msgs[0]).flags=0; //write  

125.         (iic_data.msgs[0]).buf=(unsigned char*)malloc(2);  

126. while(count<len){  

127.  bytes = 0;  

128.  data[bytes++] =  subAddr;//先写子地址   

129.  data[bytes]   = dataBuf[count];//再写value  

130.           LOGI("IIC write HAL: %x,%x", data[0],data[1]);  

131.           (iic_data.msgs[0]).buf=data;//the data to write   

132.           ret=ioctl(dev->fd,I2C_RDWR,(unsigned long)&iic_data);  

133.           if(ret<0){  

134.              LOGI("IIC HAL ioctl error");  

135.           }  

136.           count++;  

137.  subAddr++;  

138.  usleep(3000);//延迟3毫秒  

139.         }   

140.       LOGI("you have write %s into iic at %x address len: %d",dataBuf, subAddr, len);  

141.   

142.         return 0;    

143. }    

144.         

145. static int iic_read(struct iic_device_t* dev, unsigned char* dataBuf, unsigned short slaveAddr, int len){    

146.       int count = 0;  

147.         

148.       iic_data.nmsgs=1;  

149.         

150.       (iic_data.msgs[0]).len=1;   

151.       (iic_data.msgs[0]).addr=slaveAddr; //  设备地址  

152.       (iic_data.msgs[0]).flags=I2C_M_RD;//read  

153.       (iic_data.msgs[0]).buf=(unsigned char*)malloc(1);  

154.       while(count<len){  

155.          (iic_data.msgs[0]).buf= dataBuf++;  

156. if(ioctl(dev->fd,I2C_RDWR,(unsigned long)&iic_data)<0){  

157.                         LOGE("ioctl read error");  

158.          }  

159. LOGI("IIC read HAL: %x", dataBuf[count]);  

160. count++;  

161.       }   

162.         

163.         return 0;   

164. }</span>  

 

注意:需打开设备/dev/i2c-1权限,否则会碰到PemissionDenied错误。从源码根目录下进入system/core/rootdir目录,打开ueventd.rc添加一行:/dev/i2c-10666 root root (这里设备各开发板可能不同)

3. iic目录下编写android.mk进行编译

[cpp] view plaincopy

1.  LOCAL_PATH := $(call my-dir)  

2.       include $(CLEAR_VARS)  

3.       LOCAL_MODULE_TAGS := optional  

4.       LOCAL_PRELINK_MODULE := false  

5.       LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw  

6.       LOCAL_SRC_FILES := iic.c  

7.       LOCAL_MODULE := iic.default  

8.       include $(BUILD_SHARED_LIBRARY)  




编译命令:mmm -Bhardware/libhardware/module/iic 编译成功会得到iic.default.so,打包进img默认会被加载。

 

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

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

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

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

[cpp] view plaincopy

1.  #include "jni.h"    

2.  #include "JNIHelp.h"    

3.  #include "android_runtime/AndroidRuntime.h"    

4.  #include <utils/misc.h>    

5.  #include <cutils/log.h>    

6.  #include <hardware/hardware.h>    

7.  #include <hardware/iic.h>    

8.  #include <stdio.h>  

9.   

10. namespace android    

11. {    

12.     /*在硬件抽象层中定义的硬件访问结构体,参考<hardware/iic.h>*/    

13.         struct iic_device_t* iic_device = NULL;    

14.     /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/    

15.     static void iic_setVal(JNIEnv* env, jobject clazz, jstring val, jint slaveAddr, jint subAddr, jint len) {    

16.         const char *str = env->GetStringUTFChars(val, NULL);    

17.         LOGI("iic JNI: set value %s to device.", str);    

18.         if(!iic_device) {    

19.             LOGI("iic JNI: device is not open.");    

20.             return;    

21.         }    

22.         iic_device->iic_write(iic_device, (unsigned char*)str, slaveAddr, subAddr, len);    

23.     env->ReleaseStringUTFChars(val, str);  //注意释放资源  

24.     }    

25.   

26.        /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/    

27.     static jstring iic_getVal(JNIEnv* env, jobject clazz, jint slaveAddr, jint len) {  

28.     unsigned char* data = (unsigned char*)malloc(len);  

29.     iic_device->iic_read(iic_device, data, slaveAddr, len);  

30.         if(!iic_device) {    

31.             LOGI("iic JNI: device is not open.");    

32.         }  

33.     int i = 0;  

34.     for(;i<strlen((const char*)data);i++){  

35.       LOGI("data: %c ", data[i]);  

36.     }    

37.         //LOGI("iic JNI: get value %s from device @ %x address!", data, subAddr);  

38.     jstring tmp = env->NewStringUTF((const char*)data);  

39.     free(data);  

40.     data = NULL;  

41.     return tmp;     

42.     }   

43.     

44.         /*通过硬件抽象层定义的硬件模块open接口打开硬件设备*/    

45.     static inline int iic_device_open(const hw_module_t* module, struct iic_device_t** device) {    

46.         return module->methods->open(module, IIC_HARDWARE_MODULE_ID, (struct hw_device_t**)device);    

47.     }    

48.         /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/    

49.     static jboolean iic_init(JNIEnv* env, jclass clazz) {    

50.         iic_module_t* module;    

51.             

52.         LOGI("iic JNI: initializing......");    

53.         if(hw_get_module(IIC_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {    

54.             LOGI("iic JNI: iic Stub found.");    

55.             if(iic_device_open(&(module->common), &iic_device) == 0) {    

56.                 LOGI("eeprom JNI: iic device is opening...");    

57.                 return 0;    

58.             }    

59.             LOGE("eeprom JNI: failed to open iic device.");    

60.             return -1;    

61.         }    

62.         LOGE("eeprom JNI: failed to get iic stub module.");    

63.         return -1;          

64.     }    

65.         /*JNI方法表*/    

66.     static const JNINativeMethod method_table[] = {    

67.         {"init_native""()Z", (void*)iic_init},    

68.         {"setVal_native""(Ljava/lang/String;III)V", (void*)iic_setVal},    

69.         {"getVal_native""(III)Ljava/lang/String;", (void*)iic_getVal},    

70.     };    

71.         /*注册JNI方法*/    

72.     int register_android_server_IICService(JNIEnv *env) {    

73.             return jniRegisterNativeMethods(env, "com/android/server/IICService", method_table, NELEM(method_table));    

74.     }    

75. };  

然后需要让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)

packageandroid.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代码如下:

[java] view plaincopy

1.  package com.android.server;    

2.  import android.content.Context;    

3.  import android.os.IIICService;    

4.  import android.util.Slog;    

5.  public class IICService extends IIICService.Stub {    

6.     private static final String TAG = "IICService";    

7.     IICService() {    

8.         init_native();    

9.     }    

10.     public void setVal(String val,int slaveAddr, int regAddr, int len) {    

11.         setVal_native(val, slaveAddr, regAddr, len);    

12.     }       

13.     public String getVal(int slaveAddr,int len) {    

14.         return getVal_native( slaveAddr, len);    

15.     }    

16.        

17.     //本地方法   

18.     private static native boolean init_native();    

19.     private static native void setVal_native(String val, int slaveAddr, int regAddr, int len);    

20.     private static native String getVal_native(int slaveAddr, int len);    

21. };   

从代码中我们可以看到它继承了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(Throwablee){

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

}
编译命令:mmmframeworks/base/services/java

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

 

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

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

三、app调用服务接口访问硬件

上主要代码EEPROMActivity.java  

[java] view plaincopy

1.  package com.zkgd.eeprom;  

2.   

3.  import android.app.Activity;  

4.  import android.os.Bundle;  

5.  import android.os.ServiceManager;  

6.  import android.os.IIICService;  

7.  import android.os.RemoteException;  

8.  import android.util.Log;  

9.  import android.view.View;  

10. import android.view.View.OnClickListener;  

11. import android.widget.Button;  

12. import android.widget.EditText;  

13.   

14. public class EEPROMActivity extends Activity  implements OnClickListener{  

15. private final static String LOG_TAG = "com.zkgd.eeprom";    

16.       

17.     private IIICService iicService = null;    

18.     

19.     private EditText valueText = null;    

20.     private Button readButton = null;    

21.     private Button writeButton = null;    

22.     private Button clearButton = null;    

23.     int len = 1;  

24.     /** Called when the activity is first created. */    

25.     @Override    

26.     public void onCreate(Bundle savedInstanceState) {    

27.         super.onCreate(savedInstanceState);    

28.         setContentView(R.layout.main);    

29.     

30.         iicService = IIICService.Stub.asInterface(    

31.         ServiceManager.getService("iic"));    

32.             

33.         valueText = (EditText)findViewById(R.id.edit_value);    

34.         readButton = (Button)findViewById(R.id.button_read);    

35.         writeButton = (Button)findViewById(R.id.button_write);    

36.         clearButton = (Button)findViewById(R.id.button_clear);    

37.     

38.     readButton.setOnClickListener(this);    

39.     writeButton.setOnClickListener(this);    

40.     clearButton.setOnClickListener(this);    

41.             

42.         Log.i(LOG_TAG, "Activity Created");    

43.     }    

44.         

45.     public void onClick(View v) {    

46.         if(v.equals(readButton)) {    

47.         try {    

48.         len = 1;  

49.                 //在从设备中读取数据  

50.                 String val =  iicService.getVal(0x50,len);      

51.                 valueText.setText(val);    

52.         } catch (RemoteException e) {    

53.             Log.e(LOG_TAG, "Remote Exception while reading value from device.");    

54.         }           

55.         }    

56.         else if(v.equals(writeButton)) {    

57.         try {    

58.                 String val = valueText.getText().toString();    

59.                 len = val.length();   

60.                 //在从设备的子地址处开始写入数据  

61.                 iicService.setVal(val,0x50,0x10,len);    

62.         } catch (RemoteException e) {    

63.             Log.e(LOG_TAG, "Remote Exception while writing value to device.");    

64.         }    

65.         }    

66.         else if(v.equals(clearButton)) {    

67.             String text = "";    

68.             valueText.setText(text);    

69.         }    

70.     }    

71. }  

工程eeprom放置在源码目录package/app/

编译命令:mmm package/app/eeprom

打包,烧写固件至开发板,启动就可以看到该应用的图标了。

 

小结:

整个调用流程为:app<---AIDL访问服务<---JNI本地方法实现<---HALso文件<---硬件

一个问题,这种方法改动了android原生api,毕竟是访问了硬件。如果想做通用app又想使用c/c++提高效率,直接进行NDK开发,功能编译成库文件打进app应用的工程中。

另一个问题,硬件访问会遭遇到权限问题。如果做通用app,需要设备root了,然后在代码里添加权限修改操作,例如:"chmod777 "+getPackageCodePath(); "chmod 777 /dev/i2c-1";

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值