在常见的嵌入式外设中,串口通信是经常使用的一种通信机制,本篇文章给你带来,如何在Android系统中实现对串口设备的访问。
在Android中如何访问底层Linux的设备驱动,必然要用到HAL,即:硬件抽象层。关于HAL的概念及框架分析,请查看作者的下面几篇博文。
> 深入浅出 - Android系统移植与平台开发(七)- 初识HAL
> 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析
> 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
1. 首先,我们先定义出串口的API类,即:SerialService,它为串口访问应用程序提示了两个API接口:read()和write()方法。
@serial_app\src\cn\com\farsight\SerialService\SerialService.java
1. package cn.com.farsight.SerialService;
2. import java.io.BufferedReader;
3. import java.io.UnsupportedEncodingException;
4. import java.io.IOException;
5. import java.io.InputStream;
6. import java.io.InputStreamReader;
7.
8. import android.util.Log;
9.
10. public class SerialService {
11. private static final String TAG = "SerialService";
12. // 底层初始化状态
13. private boolean isInitOk = false;
14.
15. // 加载本地库,read()和write()的实现通过JNI在本地代码实现
16. static {
17. "System.loadLibrary()");
18. "serial_runtime");
19. }
20.
21. // 构造器,用于初始化本地HAL及runtime
22. public SerialService(){
23. "SerialService()");
24. // 初始化本地HAL
25. isInitOk = _init();
26. "_init()");
27. }
28.
29. // 读串口数据
30. public String read() {
31. "read()");
32. if(!isInitOk)
33. return "串口初始化失败,请确认已经连接串口设备。";
34. // 由于 Java String是双字节字符,而串口数据是原始字节流,所以要进行转化
35. byte[] data = new byte[128];
36. // 从驱动读取字节流
37. 128);
38.
39. String ret;
40. try{
41. // 转化为Java String字符
42. new String(data, "GBK");
43. catch(UnsupportedEncodingException e1) {
44. return null;
45. }
46. return ret;
47. }
48.
49. // 写入字符串数据
50. public int write(String s) {
51. "write()");
52. int len;
53. try{
54. // 将Java String字符串转码成字节流后,写入串口
55. "GBK"));
56. catch(UnsupportedEncodingException e1) {
57. return -1;
58. }
59. return len;
60. }
61.
62. private static native boolean _init();
63. private static native int _read(byte[] data, int len);
64. private static native int _write(byte[] data);
65. }
2. 编写SerialService的运行时代码,即:cn_com_farsight_SerialService_SerialService.cpp,实现JNI调用函数:_init(),_read(),_write()。
串口硬件抽象层头文件:
@serial_hal\include\serial.h
1. #include <hardware/hardware.h>
2. #include <fcntl.h>
3. #include <errno.h>
4. #include <cutils/log.h>
5. #include <cutils/atomic.h>
6.
7. #define serial_HARDWARE_MODULE_ID "serial"
8.
9.
10. /*每一个硬件模块都每必须有一个名为HAL_MODULE_INFO_SYM的数据结构变量,
11. 它的第一个成员的类型必须为hw_module_t*/
12. struct serial_module_t {
13. struct hw_module_t common; //模块类型
14.
15. };
16. /*见hardware.h中的hw_module_t定义的说明,
17. xxx_module_t的第一个成员必须是hw_module_t类型,
18. 其次才是模块的一此相关信息,当然也可以不定义,
19. 这里就没有定义模块相关信息
20. */
21.
22.
23. /*每一个设备数据结构的第一个成员函数必须是hw_device_t类型,
24. 其次才是各个公共方法和属性*/
25. struct serial_control_device_t {
26. struct hw_device_t common; //设备类型
27. /* supporting control APIs go here */
28.
29. int (*serial_read_hal)(struct serial_control_device_t *dev, char *buf, int count);
30. /***************************************/
31. int (*serial_write_hal)(struct serial_control_device_t *dev, char *buf, int count);
32. /***************************************/
33. };
34. /*见hardware.h中的hw_device_t的说明,
35. 要求自定义xxx_device_t的第一个成员必须是hw_device_t类型,
36. 其次才是其它的一些接口信息
37. */
@serial_runtime\cn_com_farsight_SerialService_SerialService.cpp
下面的代码用到JNI部分知识点,详情请看:
> 深入浅出 - Android系统移植与平台开发(九)- JNI介绍
1. #include <hardware/hardware.h>
2. #include <fcntl.h>
3. #include <termios.h>
4. #include <errno.h>
5. #include <cutils/log.h>
6. #include <cutils/atomic.h>
7. #include <sys/ioctl.h>
8. #include <errno.h>
9. #include <string.h>
10. #include <dirent.h>
11. #include "../include/serial.h"
12.
13. int fd;
14.
15. static int serial_device_close(struct hw_device_t* device)
16. {
17. "%s E", __func__);
18. struct serial_control_device_t* ctx = (struct serial_control_device_t*)device;
19. if (ctx) {
20. free(ctx);
21. }
22. close(fd);
23. "%s X", __func__);
24. return 0;
25. }
26.
27. static int serial_read_drv(struct serial_control_device_t *dev, char *buf, int count)
28. {
29. "%s E", __func__);
30. int len = 0;
31. len = read(fd, buf, count);
32. if(len < 0)
33. {
34. "read");
35. }
36. "%s X", __func__);
37. return len;
38. }
39.
40. static int serial_write_drv(struct serial_control_device_t *dev, char *buf, int size)
41. {
42. "%s E", __func__);
43. int len = write(fd, buf, size);
44. if(len < 0)
45. {
46. "write");
47. }
48. "%s X", __func__);
49. return len;
50. }
51.
52. static int serial_device_open(const struct hw_module_t* module, const char* name,
53. struct hw_device_t** device)
54. {
55. "%s E", __func__);
56. struct serial_control_device_t *dev;
57. struct termios opt;
58.
59. struct serial_control_device_t *)malloc(sizeof(*dev));
60. sizeof(*dev));
61.
62. //HAL must init property
63. //必须写这个
64. dev->common.version = 0;
65. dev->common.module= module;
66.
67. dev->serial_read_hal = serial_read_drv;
68.
69. dev->serial_write_hal = serial_write_drv;
70. *device= &dev->common;
71.
72. // MichaelTang add for open ttyUSBx
73. char devname[PATH_MAX];
74. DIR *dir;
75. struct dirent *de;
76. "/dev");
77. if(dir == NULL)
78. return -1;
79. "/dev");
80. char * filename = devname + strlen(devname);
81. '/';
82. while((de = readdir(dir))) {
83. if(de->d_name[0] == '.' || strncmp(de->d_name, "ttyUSB", 6)) // start with . will ignor
84. continue;
85. strcpy(filename, de->d_name);
86. if((fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
87. {
88. "open error");
89. return -1;
90. else {
91. "open ok\n");
92. break;
93. }
94. }
95.
96.
97. //初始化串口
98. tcgetattr(fd, &opt);
99. //tcflush(fd, TCIOFLUSH);
100. cfsetispeed(&opt, B9600);
101. cfsetospeed(&opt, B9600);
102.
103. //tcflush(fd, TCIOFLUSH);
104.
105. opt.c_cflag |= (CLOCAL | CREAD);
106.
107. opt.c_cflag &= ~CSIZE;
108. opt.c_cflag &= ~CRTSCTS;
109. opt.c_cflag |= CS8;
110.
111. /*
112. opt.c_cflag |= PARENB; // enable; 允许输入奇偶校验
113. opt.c_cflag |= PARODD; // J check 对输入使用奇校验
114. opt.c_iflag |= (INPCK | ISTRIP); //
115. */
116. opt.c_iflag |= IGNPAR;
117.
118. opt.c_cflag &= ~CSTOPB;
119.
120. opt.c_oflag = 0;
121. opt.c_lflag = 0;
122.
123. tcsetattr(fd, TCSANOW, &opt);
124.
125. "%s X", __func__);
126.
127. return 0;
128. }
129.
130. //定一个hw_module_methods_t结构体,关联入口函数
131. static struct hw_module_methods_t serial_module_methods = {
132. open: serial_device_open
133. };
134.
135. //定义Stub入口
136. //注意必须使用:
137. //1。hw_module_t继承类
138. //2。必须使用HAL_MODULE_INFO_SYM这个名字
139.
140. const struct serial_module_t HAL_MODULE_INFO_SYM = {
141. common: {
142. tag: HARDWARE_MODULE_TAG,
143. version_major: 1,
144. version_minor: 0,
145. id: serial_HARDWARE_MODULE_ID,
146. //模块ID,上层的Service通过这个ID应用当前Stub
147. "serial HAL module",
148. "farsight",
149. //入口函数管理结构体
150. },
151. /* supporting APIs go here */
152. };
4. 相关代码下载: