Android 设备上实现串口的移植

转自: http://blog.csdn.net/vv0_0vv/article/details/7449594


de项目需要,要实现在Android中实现串口的收发功能,有几种方法可以参考使用。

1. 标准的Android HAL层思想,把串口的功能加入framework的API中(类似于android中sensor的实现)

    a. 确保驱动层中基于tty的串口驱动可以正常read、write、poll数据,当然了,也可以自己写一个字符驱动来实现串口的读写功能。

    b. 在BSP的HAL层中添加串口读写功能的回调函数(linux 应用层 c/c++)

    c. Android framework中添加jni层,解析HAL中生成的module,然后对回调函数进行封装,生成.so库,提供给java层。

    d. 添加远程调用接口,使用aidl在framework中添加远程调用

    e. 添加serviceManagement

2. 绕过HAL,直接使用JNI来完成读写等回调函数,之后同1 。

3. 绕过android系统,直接编写jni库,在应用程序中直接调用jni接口,完成串口的收发。

------------------------------------------------------------------------------

以上都是可用的方法,这里我采用最简单的第三种方法,其中第一种方法最繁琐,但也是android最标准的方法,之后我会在can bus的移植中使用(先打个哑谜^0^),OK 废话不多说,开始码代码,工作!

首先是驱动层,我使用的是fsl的开发板,这边freescale已经帮我们实现了驱动,可以在/dev/下发现ttymxc0,ttymxc1.。。。这些就是CPU上各个串口的驱动文件,可以尝试echo "123" > /dev/mxctty0 之后可以看到串口终端上会打印出“123”。

但是,我们做驱动的不能就这样拿着别人的东西就用,咱要分析,要学习,要膜拜,要抄袭,要。。。貌似我最喜欢干这种事情了,好吧,这里我自己照着Linux设备驱动详解这书写了一个虚拟的字符驱动,当做我们的串口吧。

提供了跟串口同样的功能,这个驱动中我使用阻塞的方式来读写数据,一边看书,一边学习,一边自己写代码,一边学习jni,一边学习android的框架,何乐而不为呢?

首先,我们要注册一个字符驱动,然后初始化等待队列,初始化信号量,初始化变量,给结构体分配内存空间,老一套了。。。是个写驱动的都知道要干这些事情。

[cpp] view plain copy print ?
  1. /*设备驱动模块加载函数*/  
  2. int globalfifo_init(void)  
  3. {  
  4.     int result;  
  5.     globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev) ,GFP_KERNEL);  
  6.     if(!globalfifo_devp) {  
  7.         result = -ENOMEM;  
  8.     }  
  9.     memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));  
  10.       
  11.     globalfifo_devp->mdev = mdev_struct;  
  12.   
  13.     result = misc_register(&(globalfifo_devp->mdev));  
  14.     if(result<0)  
  15.         return result;  
  16.   
  17.     init_MUTEX(&globalfifo_devp->sem);   /*初始化信号量*/  
  18.     init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/  
  19.     init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/  
  20.   
  21.     return 0;  
  22.   
  23. }  
/*设备驱动模块加载函数*/
int globalfifo_init(void)
{
	int result;
	globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev) ,GFP_KERNEL);
	if(!globalfifo_devp) {
		result = -ENOMEM;
	}
	memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
	
	globalfifo_devp->mdev = mdev_struct;

	result = misc_register(&(globalfifo_devp->mdev));
	if(result<0)
		return result;

	init_MUTEX(&globalfifo_devp->sem);   /*初始化信号量*/
	init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/
	init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/

	return 0;

}

看到没,这里使用了miscdevice驱动,这个简单容易实现,HOHO~~偷懒了。这里给我们的全局结构体分配了内存空间,然后把结构体操作函数挂到我们的全局结构体变量中,最后注册这个miscdevice驱动。

[cpp] view plain copy print ?
  1. /*globalfifo设备结构体*/  
  2. struct globalfifo_dev                                       
  3. {                                                          
  4. //  struct cdev cdev; /*cdev结构体*/                          
  5.     struct miscdevice mdev;  
  6.     unsigned int current_len;    /*fifo有效数据长度*/  
  7.     unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/          
  8.     struct semaphore sem; /*并发控制用的信号量*/             
  9.     wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/       
  10.     wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/       
  11. };  
/*globalfifo设备结构体*/
struct globalfifo_dev                                     
{                                                        
//	struct cdev cdev; /*cdev结构体*/                       
	struct miscdevice mdev;
	unsigned int current_len;    /*fifo有效数据长度*/
	unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/        
	struct semaphore sem; /*并发控制用的信号量*/           
	wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/     
	wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/     
};

看到我们的globalfifo结构体的定义了吧,这里,就是这里,所以在init函数中,我们要初始化信号量,初始化读写等待队列头。要不咱先来讲讲这里的阻塞的概念吧。

顾名思义,就是堵在那边不动了,其实是真的不动了,利用等待队列实现设备的阻塞,当用户进程访问系统资源的时候,当这个资源不能被访问,我们又不想让之后的事情继续发生,这样的话我们就可以阻塞在那边,放心,我们可以让该进程进入休眠,这样的话就不会浪费CPU的资源了,然而等到这个资源可以访问的时候,我们就可以唤醒该阻塞的进程,继续让他执行下去,如果没有地方唤醒他,那他就真的“堵死”在那边了。

简单的介绍了下,接下来看看我们要实现哪些功能函数

[cpp] view plain copy print ?
  1. /*文件操作结构体*/  
  2. static const struct file_operations globalfifo_fops =  
  3. {  
  4.     .owner = THIS_MODULE,  
  5.     .read = globalfifo_read,  
  6.     .write = globalfifo_write,  
  7.     .ioctl = globalfifo_ioctl,  
  8.     .poll = globalfifo_poll,  
  9.     .open = globalfifo_open,  
  10.     .release = globalfifo_release,  
  11. };  
/*文件操作结构体*/
static const struct file_operations globalfifo_fops =
{
	.owner = THIS_MODULE,
	.read = globalfifo_read,
	.write = globalfifo_write,
	.ioctl = globalfifo_ioctl,
	.poll = globalfifo_poll,
	.open = globalfifo_open,
	.release = globalfifo_release,
};

咱有读,写,打开。。。。等函数,继续往下分析。

[cpp] view plain copy print ?
  1. struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/  
  2. /*文件打开函数*/  
  3. int globalfifo_open(struct inode *inode, struct file *filp)  
  4. {  
  5.     /*将设备结构体指针赋值给文件私有数据指针*/  
  6.     filp->private_data = globalfifo_devp;  
  7.     return 0;  
  8. }  
  9. /*文件释放函数*/  
  10. int globalfifo_release(struct inode *inode, struct file *filp)  
  11. {  
  12.     return 0;  
  13. }  
struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/
/*文件打开函数*/
int globalfifo_open(struct inode *inode, struct file *filp)
{
	/*将设备结构体指针赋值给文件私有数据指针*/
	filp->private_data = globalfifo_devp;
	return 0;
}
/*文件释放函数*/
int globalfifo_release(struct inode *inode, struct file *filp)
{
	return 0;
}

open和release函数没什么好说的了,其实这里还是蛮有讲究的,比如说这个设备我们只能让一个用户进行访问,那我们可以再open函数里面做点手脚,一般我们读内核驱动模型的时候都会看到很多时候在open函数中都会设计引用计数自加1,这样的话可以更好的管控我们设备被打开次数。

但是这里我们没做什么,我们只是把我们的全局结构体变量赋值给了这里filp的一个私有成员变量中,这样的话我们可以再每一个功能函数中取出这个私有成员,有利于代码的可读性,release就不讲了。

[cpp] view plain copy print ?
  1. /*globalfifo读函数*/  
  2. static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,  
  3. loff_t *ppos)  
  4. {  
  5.     int ret;  
  6.     struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针   
  7.     DECLARE_WAITQUEUE(wait, current); //定义等待队列   
  8.   
  9.     down(&dev->sem); //获得信号量   
  10.     add_wait_queue(&dev->r_wait, &wait); //进入读等待队列头   
  11.   
  12.     /* 等待FIFO非空 */  
  13.     while (dev->current_len == 0)  
  14.     {  
  15.         if (filp->f_flags &O_NONBLOCK)  
  16.         {  
  17.             ret = - EAGAIN;  
  18.             goto out;  
  19.         }   
  20.         __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠   
  21.         up(&dev->sem);  
  22.   
  23.         schedule(); //调度其他进程执行   
  24.         if (signal_pending(current))  
  25.         //如果是因为信号唤醒   
  26.         {  
  27.             ret = - ERESTARTSYS;  
  28.             goto out2;  
  29.         }  
  30.   
  31.         down(&dev->sem);  
  32.     }  
  33.   
  34.     /* 拷贝到用户空间 */  
  35.     if (count > dev->current_len)  
  36.         count = dev->current_len;  
  37.   
  38.     if (copy_to_user(buf, dev->mem, count))  
  39.     {  
  40.         ret = - EFAULT;  
  41.         goto out;  
  42.     }  
  43.     else  
  44.     {  
  45.         memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifo数据前移   
  46.         dev->current_len -= count; //有效数据长度减少   
  47.         printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);  
  48.   
  49.         wake_up_interruptible(&dev->w_wait); //唤醒写等待队列   
  50.   
  51.         ret = count;  
  52.     }  
  53. out:   
  54.     up(&dev->sem); //释放信号量   
  55. out2:  
  56.     remove_wait_queue(&dev->w_wait, &wait); //从附属的等待队列头移除   
  57.     set_current_state(TASK_RUNNING);  
  58.     return ret;  
  59. }  
/*globalfifo读函数*/
static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
	int ret;
	struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
	DECLARE_WAITQUEUE(wait, current); //定义等待队列

	down(&dev->sem); //获得信号量
	add_wait_queue(&dev->r_wait, &wait); //进入读等待队列头

	/* 等待FIFO非空 */
	while (dev->current_len == 0)
	{
		if (filp->f_flags &O_NONBLOCK)
		{
			ret = - EAGAIN;
			goto out;
		} 
		__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
		up(&dev->sem);

		schedule(); //调度其他进程执行
		if (signal_pending(current))
		//如果是因为信号唤醒
		{
			ret = - ERESTARTSYS;
			goto out2;
		}

		down(&dev->sem);
	}

	/* 拷贝到用户空间 */
	if (count > dev->current_len)
		count = dev->current_len;

	if (copy_to_user(buf, dev->mem, count))
	{
		ret = - EFAULT;
		goto out;
	}
	else
	{
		memcpy(dev->mem, dev->mem + count, dev->current_len - count); //fifo数据前移
		dev->current_len -= count; //有效数据长度减少
		printk(KERN_INFO "read %d bytes(s),current_len:%d\n", count, dev->current_len);

		wake_up_interruptible(&dev->w_wait); //唤醒写等待队列

		ret = count;
	}
out: 
	up(&dev->sem); //释放信号量
out2:
	remove_wait_queue(&dev->w_wait, &wait); //从附属的等待队列头移除
	set_current_state(TASK_RUNNING);
	return ret;
}

然后这就是我们的读函数,在进行读之前,我们把等待队列加进我们的队列链表中,然后检查我们的buff是否为空,如果为空的话,那就没什么好读的了,所以我们让进城休眠,当有货给我们读了,再唤醒我们的队列。

首先是把当前进城加入等待队列中add_wait_queue(&dev->r_wait, &wait); 

没东西读的时候,使进程睡眠,在调度到别的任务去

[cpp] view plain copy print ?
  1. /* 等待FIFO非空 */  
  2. while (dev->current_len == 0)  
  3. {  
  4.     if (filp->f_flags &O_NONBLOCK)  
  5.     {  
  6.         ret = - EAGAIN;  
  7.         goto out;  
  8.     }   
  9.     __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠   
  10.     up(&dev->sem);  
  11.   
  12.     schedule(); //调度其他进程执行   
  13.     if (signal_pending(current))  
  14.     //如果是因为信号唤醒   
  15.     {  
  16.         ret = - ERESTARTSYS;  
  17.         goto out2;  
  18.     }  
  19.   
  20.     down(&dev->sem);  
  21. }  
	/* 等待FIFO非空 */
	while (dev->current_len == 0)
	{
		if (filp->f_flags &O_NONBLOCK)
		{
			ret = - EAGAIN;
			goto out;
		} 
		__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
		up(&dev->sem);

		schedule(); //调度其他进程执行
		if (signal_pending(current))
		//如果是因为信号唤醒
		{
			ret = - ERESTARTSYS;
			goto out2;
		}

		down(&dev->sem);
	}

这段代码比较关键,与写函数中一样,当我们的buff被写满时,我们也会发生阻塞。

[cpp] view plain copy print ?
  1. /*globalfifo写操作*/  
  2. static ssize_t globalfifo_write(struct file *filp, const char __user *buf,  
  3. size_t count, loff_t *ppos)  
  4. {  
  5.     struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针   
  6.     int ret;  
  7.     DECLARE_WAITQUEUE(wait, current); //定义等待队列   
  8.   
  9.     down(&dev->sem); //获取信号量   
  10.     add_wait_queue(&dev->w_wait, &wait); //进入写等待队列头   
  11.   
  12.     /* 等待FIFO非满 */  
  13.     while (dev->current_len == GLOBALFIFO_SIZE)  
  14.     {  
  15.         if (filp->f_flags &O_NONBLOCK)  
  16.         //如果是非阻塞访问   
  17.         {  
  18.             ret = - EAGAIN;  
  19.             goto out;  
  20.         }   
  21.         __set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠   
  22.         up(&dev->sem);  
  23.   
  24.         schedule(); //调度其他进程执行   
  25.         if (signal_pending(current))  
  26.         //如果是因为信号唤醒   
  27.         {  
  28.             ret = - ERESTARTSYS;  
  29.             goto out2;  
  30.         }  
  31.   
  32.         down(&dev->sem); //获得信号量   
  33.     }  
  34.   
  35.     /*从用户空间拷贝到内核空间*/  
  36.     if (count > GLOBALFIFO_SIZE - dev->current_len)  
  37.         count = GLOBALFIFO_SIZE - dev->current_len;  
  38.   
  39.     if (copy_from_user(dev->mem + dev->current_len, buf, count))  
  40.     {  
  41.         ret = - EFAULT;  
  42.         goto out;  
  43.     }  
  44.     else  
  45.     {  
  46.         dev->current_len += count;  
  47.         printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev->current_len);  
  48.   
  49.         wake_up_interruptible(&dev->r_wait); //唤醒读等待队列  
/*globalfifo写操作*/
static ssize_t globalfifo_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos)
{
	struct globalfifo_dev *dev = filp->private_data; //获得设备结构体指针
	int ret;
	DECLARE_WAITQUEUE(wait, current); //定义等待队列

	down(&dev->sem); //获取信号量
	add_wait_queue(&dev->w_wait, &wait); //进入写等待队列头

	/* 等待FIFO非满 */
	while (dev->current_len == GLOBALFIFO_SIZE)
	{
		if (filp->f_flags &O_NONBLOCK)
		//如果是非阻塞访问
		{
			ret = - EAGAIN;
			goto out;
		} 
		__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠
		up(&dev->sem);

		schedule(); //调度其他进程执行
		if (signal_pending(current))
		//如果是因为信号唤醒
		{
			ret = - ERESTARTSYS;
			goto out2;
		}

		down(&dev->sem); //获得信号量
	}

	/*从用户空间拷贝到内核空间*/
	if (count > GLOBALFIFO_SIZE - dev->current_len)
		count = GLOBALFIFO_SIZE - dev->current_len;

	if (copy_from_user(dev->mem + dev->current_len, buf, count))
	{
		ret = - EFAULT;
		goto out;
	}
	else
	{
		dev->current_len += count;
		printk(KERN_INFO "written %d bytes(s),current_len:%d\n", count, dev->current_len);

		wake_up_interruptible(&dev->r_wait); //唤醒读等待队列

写函数最后会唤醒我们的等待队列,因为写进去东西了,就可以去读了,就是这样,这部分跟我们的串口收发相同。

别的功能我就不说了,OK,驱动完成之后,我们加载进去,然后进行测试下。

首先我们去cat /dev/globalfifo

发生阻塞,一直停在那,这时候我们再打开一个终端,去写数据

echo "123" > /dev/globalfifo

写完之后,我们立马会发现之前的cat有东西出来了,每次都会把数据全部读出来。

==================================================

下面是我们的jni,首先咱要明确我们做的事情,打开设备,读设备,最后不用的话就关闭设备,所以我们至少要实现这3个API,

[cpp] view plain copy print ?
  1. #define FIFO_CLEAR 0x01   
  2. #define BUFFER_LEN 20   
  3. #define GLOBALFIFO_PATH "/dev/globalfifo"   
  4.   
  5. int globalfifo_fd = -1;  
  6.   
  7. JNIEXPORT jint JNICALL  
  8. Java_com_liujun_globalfifo_init(JNIEnv *env, jobject obj)  
  9. {  
  10.     globalfifo_fd = open(GLOBALFIFO_PATH, O_RDONLY); // | O_NOBLOCK   
  11.     if(globalfifo_fd != -1)  
  12.     {  
  13.         __android_log_print(ANDROID_LOG_INFO,"JNI","open device done.");  
  14.         //clear the buff   
  15.         if(ioctl(globalfifo_fd, FIFO_CLEAR, 0) < 0)  
  16.             __android_log_print(ANDROID_LOG_INFO,"JNI","clear buff error!");  
  17.     } else  
  18.         __android_log_print(ANDROID_LOG_INFO,"JNI","open device error!");  
  19. }  
#define FIFO_CLEAR 0x01
#define BUFFER_LEN 20
#define GLOBALFIFO_PATH	"/dev/globalfifo"

int globalfifo_fd = -1;

JNIEXPORT jint JNICALL
Java_com_liujun_globalfifo_init(JNIEnv *env, jobject obj)
{
	globalfifo_fd = open(GLOBALFIFO_PATH, O_RDONLY); // | O_NOBLOCK
	if(globalfifo_fd != -1)
	{
		__android_log_print(ANDROID_LOG_INFO,"JNI","open device done.");
		//clear the buff
		if(ioctl(globalfifo_fd, FIFO_CLEAR, 0) < 0)
			__android_log_print(ANDROID_LOG_INFO,"JNI","clear buff error!");
	} else
		__android_log_print(ANDROID_LOG_INFO,"JNI","open device error!");
}

这是我们的初始化函数,定义了一个全局的文件描述符,init函数只做了open的动作。

[cpp] view plain copy print ?
  1. JNIEXPORT jstring JNICALL  
  2. Java_com_liujun_globalfifo_read(JNIEnv *env, jobject obj)  
  3. {  
  4.     int nread = 0;  
  5.     char buff[512] = "";  
  6.     __android_log_print(ANDROID_LOG_INFO,"JNI","read !");  
  7.     nread = read(globalfifo_fd, buff, sizeof(buff));  
  8.     if(nread != -1)  
  9.         __android_log_print(ANDROID_LOG_INFO,"JNI","===> %s", buff);  
  10.       
  11.     return (*env)->NewStringUTF(env, buff);  
  12. }  
JNIEXPORT jstring JNICALL
Java_com_liujun_globalfifo_read(JNIEnv *env, jobject obj)
{
	int nread = 0;
	char buff[512] = "";
	__android_log_print(ANDROID_LOG_INFO,"JNI","read !");
	nread = read(globalfifo_fd, buff, sizeof(buff));
	if(nread != -1)
		__android_log_print(ANDROID_LOG_INFO,"JNI","===> %s", buff);
	
	return (*env)->NewStringUTF(env, buff);
}

这个API封装了read函数,然后把读到的buff转换成为java中能识别的string,最后返回到java层的是string类型的字符串。

[cpp] view plain copy print ?
  1. JNIEXPORT jint JNICALL  
  2. Java_com_liujun_globalfifo_exit(JNIEnv *env, jobject obj)  
  3. {     
  4.     close(globalfifo_fd);  
  5.     __android_log_print(ANDROID_LOG_INFO,"JNI","close done!");  
  6.     return 0;  
  7. }  
JNIEXPORT jint JNICALL
Java_com_liujun_globalfifo_exit(JNIEnv *env, jobject obj)
{	
	close(globalfifo_fd);
	__android_log_print(ANDROID_LOG_INFO,"JNI","close done!");
	return 0;
}

最后这是我们的exit函数,调用了close来关闭我们的设备。然后编写Android.mk文件,最后编译,生成globalfifo库

===========================================

接下来我们创建一个Android 工程,导入jni库并且定义native API

[cpp] view plain copy print ?
  1. public native int init();  
  2. public native String read();  
  3. public native int exit();  
  4. static {    
  5.     System.loadLibrary("globalfifo");    
  6. }    
    public native int init();
    public native String read();
    public native int exit();
    static {  
        System.loadLibrary("globalfifo");  
    }  

然后在适当的地方调用。设置3个按键,先试打开,然后read,按下read按键的时候开启一个thread去读数据。

[cpp] view plain copy print ?
  1. public class MyThread implements Runnable{  
  2.     public void run() {  
  3.         // TODO Auto-generated method stub   
  4.         while (true) {  
  5.             try {  
  6.                 Thread.sleep(100);//   
  7.                 string = read();  
  8.                 Message message=new Message();  
  9.                 message.what=1;  
  10.                 handler.sendMessage(message);//發送消息   
  11.                    
  12.             } catch (InterruptedException e) {  
  13.                 // TODO Auto-generated catch block   
  14.                 e.printStackTrace();  
  15.             }  
  16.         }  
  17.     }  
  18. }  
  19.   
  20. class MyButtonListener implements OnClickListener{    
  21.          
  22.     public void onClick(View v) {  
  23.         if(v.getId() == R.id.start ){    
  24.             init();  
  25.         }    
  26.         if(v.getId() == R.id.read) {  
  27. //string = read();   
  28. //Toast.makeText(mContext, string, Toast.LENGTH_SHORT).show();     
  29.             new Thread(new MyThread()).start();  
  30.         }   
  31.         if(v.getId() == R.id.close) {  
  32.             exit();  
  33.         }  
  34.     }    
  35. }   
    public class MyThread implements Runnable{
    	public void run() {
    		// TODO Auto-generated method stub
    		while (true) {
    			try {
    				Thread.sleep(100);//
    				string = read();
    	    	    Message message=new Message();
    	    	    message.what=1;
    	    	    handler.sendMessage(message);//發送消息
    				 
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    class MyButtonListener implements OnClickListener{  
    	   
        public void onClick(View v) {
            if(v.getId() == R.id.start ){  
            	init();
            }  
            if(v.getId() == R.id.read) {
				//string = read();
				//Toast.makeText(mContext, string, Toast.LENGTH_SHORT).show();  
            	new Thread(new MyThread()).start();
            } 
            if(v.getId() == R.id.close) {
            	exit();
            }
        }  
    } 

安装apk,然后运行程序,点击open,然后点击read,使用adb shell进入系统,然后往里面写东西

echo "Jay Zhang" > dev/globalfifo

可以看到有Jay Zhang 吐出来。

=================================================

这样就模拟了串口,之后我们会用标准的android流程来完成can bus在android 设备上的开发。

你可能感兴趣的:(Android 设备上实现串口的移植)