在常见的嵌入式外设中,串口通信是经常使用的一种通信机制,本篇文章给你带来,如何在Android系统中实现对串口设备的访问。
在Android中如何访问底层Linux的设备驱动,必然要用到HAL,即:硬件抽象层。关于HAL的概念及框架分析,请查看作者的下面几篇博文。
> 深入浅出 - Android系统移植与平台开发(七)- 初识HAL
http://blog.csdn.net/mr_raptor/article/details/8069588
> 深入浅出 - Android系统移植与平台开发(八)- HAL Stub框架分析
http://blog.csdn.net/mr_raptor/article/details/8074549
> 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
http://blog.csdn.net/mr_raptor/article/details/8082360
1. 首先,我们先定义出串口的API类,即:SerialService,它为串口访问应用程序提示了两个API接口:read()和write()方法。
@serial_app\src\cn\com\farsight\SerialService\SerialService.java
package cn.com.farsight.SerialService;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.util.Log;
public class SerialService {
private static final String TAG = "SerialService";
// 底层初始化状态
private boolean isInitOk = false;
// 加载本地库,read()和write()的实现通过JNI在本地代码实现
static {
Log.d(TAG, "System.loadLibrary()");
System.loadLibrary("serial_runtime");
}
// 构造器,用于初始化本地HAL及runtime
public SerialService(){
Log.d(TAG, "SerialService()");
// 初始化本地HAL
isInitOk = _init();
Log.d(TAG, "_init()");
}
// 读串口数据
public String read() {
Log.d(TAG, "read()");
if(!isInitOk)
return "串口初始化失败,请确认已经连接串口设备。";
// 由于 Java String是双字节字符,而串口数据是原始字节流,所以要进行转化
byte[] data = new byte[128];
// 从驱动读取字节流
_read(data, 128);
String ret;
try{
// 转化为Java String字符
ret = new String(data, "GBK");
}catch(UnsupportedEncodingException e1) {
return null;
}
return ret;
}
// 写入字符串数据
public int write(String s) {
Log.d(TAG, "write()");
int len;
try{
// 将Java String字符串转码成字节流后,写入串口
len = _write(s.getBytes("GBK"));
}catch(UnsupportedEncodingException e1) {
return -1;
}
return len;
}
private static native boolean _init();
private static native int _read(byte[] data, int len);
private static native int _write(byte[] data);
}
串口硬件抽象层头文件:
@serial_hal\include\serial.h
#include
#include
#include
#include
#include
#define serial_HARDWARE_MODULE_ID "serial"
/*每一个硬件模块都每必须有一个名为HAL_MODULE_INFO_SYM的数据结构变量,
它的第一个成员的类型必须为hw_module_t*/
struct serial_module_t {
struct hw_module_t common; //模块类型
};
/*见hardware.h中的hw_module_t定义的说明,
xxx_module_t的第一个成员必须是hw_module_t类型,
其次才是模块的一此相关信息,当然也可以不定义,
这里就没有定义模块相关信息
*/
/*每一个设备数据结构的第一个成员函数必须是hw_device_t类型,
其次才是各个公共方法和属性*/
struct serial_control_device_t {
struct hw_device_t common; //设备类型
/* supporting control APIs go here */
int (*serial_read_hal)(struct serial_control_device_t *dev, char *buf, int count);
/***************************************/
int (*serial_write_hal)(struct serial_control_device_t *dev, char *buf, int count);
/***************************************/
};
/*见hardware.h中的hw_device_t的说明,
要求自定义xxx_device_t的第一个成员必须是hw_device_t类型,
其次才是其它的一些接口信息
*/
@serial_runtime\cn_com_farsight_SerialService_SerialService.cpp
下面的代码用到JNI部分知识点,详情请看:
> 深入浅出 - Android系统移植与平台开发(九)- JNI介绍
http://blog.csdn.net/mr_raptor/article/details/8080606
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../include/serial.h"
int fd;
static int serial_device_close(struct hw_device_t* device)
{
LOGD("%s E", __func__);
struct serial_control_device_t* ctx = (struct serial_control_device_t*)device;
if (ctx) {
free(ctx);
}
close(fd);
LOGD("%s X", __func__);
return 0;
}
static int serial_read_drv(struct serial_control_device_t *dev, char *buf, int count)
{
LOGD("%s E", __func__);
int len = 0;
len = read(fd, buf, count);
if(len < 0)
{
perror("read");
}
LOGD("%s X", __func__);
return len;
}
static int serial_write_drv(struct serial_control_device_t *dev, char *buf, int size)
{
LOGD("%s E", __func__);
int len = write(fd, buf, size);
if(len < 0)
{
perror("write");
}
LOGD("%s X", __func__);
return len;
}
static int serial_device_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
LOGD("%s E", __func__);
struct serial_control_device_t *dev;
struct termios opt;
dev = (struct serial_control_device_t *)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
//HAL must init property
dev->common.tag= HARDWARE_DEVICE_TAG; //必须写这个
dev->common.version = 0;
dev->common.module= module;
dev->serial_read_hal = serial_read_drv;
dev->serial_write_hal = serial_write_drv;
*device= &dev->common;
// MichaelTang add for open ttyUSBx
char devname[PATH_MAX];
DIR *dir;
struct dirent *de;
dir = opendir("/dev");
if(dir == NULL)
return -1;
strcpy(devname, "/dev");
char * filename = devname + strlen(devname);
*filename++ = '/';
while((de = readdir(dir))) {
if(de->d_name[0] == '.' || strncmp(de->d_name, "ttyUSB", 6)) // start with . will ignor
continue;
strcpy(filename, de->d_name);
if((fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY)) < 0)
{
LOGE("open error");
return -1;
}else {
LOGD("open ok\n");
break;
}
}
//初始化串口
tcgetattr(fd, &opt);
//tcflush(fd, TCIOFLUSH);
cfsetispeed(&opt, B9600);
cfsetospeed(&opt, B9600);
//tcflush(fd, TCIOFLUSH);
opt.c_cflag |= (CLOCAL | CREAD);
opt.c_cflag &= ~CSIZE;
opt.c_cflag &= ~CRTSCTS;
opt.c_cflag |= CS8;
/*
opt.c_cflag |= PARENB; // enable; 允许输入奇偶校验
opt.c_cflag |= PARODD; // J check 对输入使用奇校验
opt.c_iflag |= (INPCK | ISTRIP); //
*/
opt.c_iflag |= IGNPAR;
opt.c_cflag &= ~CSTOPB;
opt.c_oflag = 0;
opt.c_lflag = 0;
tcsetattr(fd, TCSANOW, &opt);
LOGD("%s X", __func__);
return 0;
}
//定一个hw_module_methods_t结构体,关联入口函数
static struct hw_module_methods_t serial_module_methods = {
open: serial_device_open
};
//定义Stub入口
//注意必须使用:
//1。hw_module_t继承类
//2。必须使用HAL_MODULE_INFO_SYM这个名字
const struct serial_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: serial_HARDWARE_MODULE_ID,
//模块ID,上层的Service通过这个ID应用当前Stub
name: "serial HAL module",
author: "farsight",
methods: &serial_module_methods, //入口函数管理结构体
},
/* supporting APIs go here */
};
4. 相关代码下载:
http://download.csdn.net/detail/mr_raptor/7033385