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的框架,何乐而不为呢?

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

/*设备驱动模块加载函数*/

intglobalfifo_init(void)

{

intresult;

globalfifo_devp = kmalloc(sizeof(structglobalfifo_dev) ,GFP_KERNEL);

if(!globalfifo_devp) {

result = -ENOMEM;

}

memset(globalfifo_devp, 0, sizeof(structglobalfifo_dev));

globalfifo_devp->mdev = mdev_struct;

result = misc_register(&(globalfifo_devp->mdev));

if(result<0)

returnresult;

init_MUTEX(&globalfifo_devp->sem);   /*初始化信号量*/

init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/

init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/

return0;

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

/*globalfifo设备结构体*/

structglobalfifo_dev

{

//  struct cdev cdev; /*cdev结构体*/

structmiscdevice mdev;

unsigned intcurrent_len;/*fifo有效数据长度*/

unsigned charmem[GLOBALFIFO_SIZE];/*全局内存*/

structsemaphore sem;/*并发控制用的信号量*/

wait_queue_head_t r_wait; /*阻塞读用的等待队列头*/

wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/

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

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

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

/*文件操作结构体*/

staticconststructfile_operations globalfifo_fops =

{

.owner = THIS_MODULE,

.read = globalfifo_read,

.write = globalfifo_write,

.ioctl = globalfifo_ioctl,

.poll = globalfifo_poll,

.open = globalfifo_open,

.release = globalfifo_release,

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

structglobalfifo_dev *globalfifo_devp;/*设备结构体指针*/

/*文件打开函数*/

intglobalfifo_open(structinode *inode,structfile *filp)

{

/*将设备结构体指针赋值给文件私有数据指针*/

filp->private_data = globalfifo_devp;

return0;

}

/*文件释放函数*/

intglobalfifo_release(structinode *inode,structfile *filp)

{

return0;

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

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

/*globalfifo读函数*/

staticssize_t globalfifo_read(structfile *filp,char__user *buf,size_tcount,

loff_t *ppos)

{

intret;

structglobalfifo_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;

gotoout;

}

__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠

up(&dev->sem);

schedule(); //调度其他进程执行

if(signal_pending(current))

//如果是因为信号唤醒

{

ret = - ERESTARTSYS;

gotoout2;

}

down(&dev->sem);

}

/* 拷贝到用户空间 */

if(count > dev->current_len)

count = dev->current_len;

if(copy_to_user(buf, dev->mem, count))

{

ret = - EFAULT;

gotoout;

}

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);

returnret;

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

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

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

/* 等待FIFO非空 */

while(dev->current_len == 0)

{

if(filp->f_flags &O_NONBLOCK)

{

ret = - EAGAIN;

gotoout;

}

__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠

up(&dev->sem);

schedule(); //调度其他进程执行

if(signal_pending(current))

//如果是因为信号唤醒

{

ret = - ERESTARTSYS;

gotoout2;

}

down(&dev->sem);

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

/*globalfifo写操作*/

staticssize_t globalfifo_write(structfile *filp,constchar__user *buf,

size_tcount, loff_t *ppos)

{

structglobalfifo_dev *dev = filp->private_data;//获得设备结构体指针

intret;

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;

gotoout;

}

__set_current_state(TASK_INTERRUPTIBLE); //改变进程状态为睡眠

up(&dev->sem);

schedule(); //调度其他进程执行

if(signal_pending(current))

//如果是因为信号唤醒

{

ret = - ERESTARTSYS;

gotoout2;

}

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;

gotoout;

}

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,

#define FIFO_CLEAR 0x01

#define BUFFER_LEN 20

#define GLOBALFIFO_PATH "/dev/globalfifo"

intglobalfifo_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)

__android_log_print(ANDROID_LOG_INFO,"JNI","clear buff error!");

} else

__android_log_print(ANDROID_LOG_INFO,"JNI","open device error!");

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

JNIEXPORT jstring JNICALL

Java_com_liujun_globalfifo_read(JNIEnv *env, jobject obj)

{

intnread = 0;

charbuff[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类型的字符串。

JNIEXPORT jint JNICALL

Java_com_liujun_globalfifo_exit(JNIEnv *env, jobject obj)

{

close(globalfifo_fd);

__android_log_print(ANDROID_LOG_INFO,"JNI","close done!");

return0;

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

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

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

publicnativeintinit();

publicnative String read();

publicnativeintexit();

static{

System.loadLibrary("globalfifo");

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

publicclassMyThread implements Runnable{

publicvoidrun() {

// TODO Auto-generated method stub

while(true) {

try{

Thread.sleep(100);//

string = read();

Message message=newMessage();

message.what=1;

handler.sendMessage(message);//發送消息

} catch(InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

classMyButtonListener implements OnClickListener{

publicvoidonClick(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();

newThread(newMyThread()).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几个串口设备,Android 设备上实现串口的移植相关推荐

  1. android最新能调试吗,android – 不能在同一台设备上安装调试和发布版本

    我已经使用applicationIdSuffix在我的项目中配置了构建版本,以便我可以在我的设备上安装调试版本和发行版本. 这里是我的build.gradle(相关部分): buildTypes { ...

  2. Android错误:无法在设备上安装* .apk *:超时

    我不时收到这个错误,不知道是什么导致这个: 当试图在真实设备上运行/调试Android应用程序时(在我的情况下是Galaxy Samsung S)我在控制台中收到以下错误: 无法在设备*上安装* .a ...

  3. android设备启动不起来,android – app不会在设备上启动

    我是 Java, Eclipse和 Android应用程序开发的老手.我正在浏览 Android app tutorial,但似乎无法在我的设备上启动第一课.在解决了一些阻止项目编译的问题后,我能够让 ...

  4. HTML5定稿了,终于有一种编程语言开发的程序可以在Android和IOS两种设备上运行了

    过去这些年,HTML5 颠覆了 PC 互联网的格局,优化了移动互联网的体验,接下来,HTML5 将颠覆原生 App 世界.这听起来有点危言耸听,但若认真分析 HTML5 的发展史,你会发现,这个世界的 ...

  5. android23是什么设备,Android TextView DrawableTint在v23设备上

    此答案是基于@kris larson的建议. 我使用下面的方法,它在所有设备上工作正常. setTintedCompoundDrawable这是一种自定义方法,它将TextView设置为您要设置复合可 ...

  6. 在Android和iOS设备上调用C++代码

    不少Android和iOS项目中,因为种种原因不得不调用C/C++代码.这篇文章主要讲述如何通过Objective-C++.NDK技术在iOS及Android设备上调用C/C++代码. 主要工作原理 ...

  7. Android debug.apk在别的手机上 闪退 ClassNotFoundException 直接Run正常

    最近工作时遇到一个非常坑爹的问题,每次打包给测试的app-debug.apk总会在一部分机器上闪退,但是直接在出问题的机型上run又没有任何问题. 如果你遇到跟我一样的情况,可以往下看看,说不定能得到 ...

  8. android连接otg摄像头,Android系统OTG_usb链接摄像头的驱动和使用

    这段时间做一个Android项目,需要在一台Android 4.4.4系统设备上链接usb摄像头,用作人脸识别, 于是作为Android项目组"骨干"(哈哈臭屁一下^-^)的我开始 ...

  9. 设置android启动器,教程:在任意 Android 设备上安装 HTC 专属桌面启动器

    HTC 手机的标志性 Sense UI 几乎是伴随着 Android 系统成长起来的,BlinkFeed 作为 Sense 5 的新特性进入了人们的视野之中.经历了几代更新,BlinkFeed 受到更 ...

最新文章

  1. kettle组件-输出
  2. debian 9系统安装配置iptables
  3. 1、初识Server API for JavaScript
  4. python编程思维培养_Python教学:编程如何培养学生计算思维-最新教育资料
  5. 前缀和优化+计蒜客 泡咖啡
  6. Shell脚本完成hadoop的集群安装
  7. 两相四线步进电机C语言程序,两相四线步进电机时序
  8. linux 内核移植和根文件系统的制作
  9. dz论坛服务器限制上传大文件,Discuz 如何设置允许上传附件大小
  10. PhD Debate-11 预告 | 回顾与展望神经网络的后门攻击与防御
  11. Tumblr面试流程
  12. 2019厦门大学计算机复试,2019年厦门大学考研复试分数线已出现
  13. 读《乔布斯的魔力演讲》
  14. 已分区的硬盘如何重新合并, 分出去的盘怎么重新合并
  15. Cisco-win10安装ezv_p_n客户端过程介绍与常见问题汇总
  16. catkin_make 编译包无效(没反应)
  17. 新宜中国携手fone,踏上预算管理新征程
  18. 专用VPX背板连接器
  19. 5.8架构设计原则案例分析
  20. 【程序源代码】外卖侠源码

热门文章

  1. 小黑小波比.自学android安卓遇到的问题以及解决方案
  2. ASTER GDEM镶嵌
  3. ASUSIROG 枪神7 Plus 超竞版G834JZ JY G634JZ JY原厂系统
  4. 强化学习基础 | (18) 基于模拟的搜索与蒙特卡罗树搜索(MCTS)
  5. 城市新56族 你是哪一族?
  6. BadApple(控制台改进版,消除闪烁)
  7. Graphics.MeasureString 不能获得精确宽度的问题
  8. 消息疯狂堆积!RocketMQ出Bug了?
  9. Unity3D中文手册1
  10. RTL8812AU安装驱动Kali Linux 2020 内核Linux 5.9.0-kali1-amd64 x86_64