在常见的嵌入式外设中,串口通信是经常使用的一种通信机制,本篇文章给你带来,如何在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. 相关代码下载: