前段时间做了一个移动开发的嵌入式项目,目前有时间所以写这篇博客,第一次写,多有不对望指正
好入正题,希望通俗易懂
安卓系统是架构在Linux上的,如果要将一个串口设备让应用层APP识别,或者说将数据读取出来应该有一下的几个必要的事要做
1、APP是用java写的,因此要实现Java调用C/Cpp代码,
2、应该有C/Cpp编写的Linux应用
3、应该有Linux驱动
4、应该要定义协议
——————————————————————————————>
上面的4步实际上google[今天这小伙18岁了],在设计安卓的时候已经做好了,我们只需要依葫芦画瓢而已。
1、 实际上是android的JNI开发,有两种方式:NDK和直接使用CPP写一个native方法的调用表然后再JNI代码中实现。
2、 这就对应着android中的HAL开发,和开发Linux的应用是一样的。
3、 这个通常不需要去开发,因为linux自带PL2303的USB转串口的驱动,如果是纯粹的串口设备的话需要自己去开发驱动。
4、协议的定义是许多外设IC和SOC通信时必须做的如GPRS模块的AT指令,GPS的nema数据,但是这里指的是和Cyber_M0板子通信的协议。
——————————————————————————————>
设计的思路是这四步,但是凡事总有仙人发明的轮子,linux哲学就是不要发明重复的轮子,哈哈,实际上android的GPS代码和我们要开发的几乎是一样的。
一、 JNI的开发:(设计从简,因为把握主线理解这种移动开发模式为主,不再模仿GPS那种直接将他做成安卓的服务,做成Framework层的东西)。

static JNINativeMethod zgbMethods[] = {{"InitZgb", "()I", (void *)Init_zgb},{"CloseZgb", "()I", (void *)close_zgb},{"Ledctrl", "(I)I", (void *)Ledctrl_zgb},{"Fanctrl", "(I)I", (void *)Fanctrl_zgb},
};

代码只设计打开四个native的接口。
InitZgb:负责初始化设备,主要是打开设备节点,绑定串口的一次参数。

/*------------------------------------------------------
注册相应的回调方法
加载HAL库
获取HAL的接口
打开设备创建设备节点
成功返回0
失败返回-1
------------------------------------------------------*/
jint Init_zgb(JNIEnv *env, jobject thiz)
{ALOGD("-----%s--------\n", __FUNCTION__);jint ret,err;/*将从主线程传递过来的对象做成本地全局的*/if (!mCallbacksObj)mCallbacksObj = env->NewGlobalRef(thiz);if (!mCallbacksObj)ALOGD("mCallbacksObj get error \n");#ifdef NEVER/*这里将数据单独做成一个类在JNI无法找到,水很深没研究透*/jclass SensorCls = env->FindClass("com/wl/zgb/bean/SensorData");
#else/*因为接口简单所以Java里面的方法设计在native类里面*/jclass SensorCls = env->FindClass("com/android/zgb/ZgbComCtrl");
#endif method_reportHumTemp = env->GetMethodID(SensorCls, "ReportHumTemp", "(Ljava/lang/String;)V");ret = hw_get_module(ZGB_MODULE_ID,(const struct hw_module_t * * )&pModule);if(ret == 0){ALOGD("hw_get_module ok\n");pModule->common.methods->open(&pModule->common, NULL,(struct hw_device_t** )&pDevice);ALOGD("zgb_module_open ok\n");if(pDevice != NULL){sZgbInterface = pDevice->get_zgb_interface(pDevice);ALOGD("Init_zgb get_hal_interface ok\n");}if (!sZgbInterface || sZgbInterface->init() < 0){ALOGD("Init_zgb hal init error\n");return -1;}/*从主界面退出时希望能结束*/isRunflag = 1;/*创建用于异步接收的线程*/
if(pthread_create(&JNI_thread_id,NULL,JNI_thread,NULL) != 0) {ALOGD("Init_zgb pthread_create fail !");return -1;}}else{ALOGD("hw_get_module error\n");return -1;}ALOGD("every ok\n");return 0;}

I、从上面的代码看当时自己的设计是从传统的设计方式出发的,不过一些常规的做法:使用两个指针将HAL层的设备HMI【硬件抽象层模块接口】对象拿到,再调用sZgbInterface = pDevice->get_zgb_interface(pDevice);获取HAL的接口,然后从接口中调用sZgbInterface->init() 方法打开设备方法打开初始化硬件设备。
II、这里也有还创建了一个线程JNI_thread,具体下面会分析。

二、 JNI里面实现线程

static void*  JNI_thread(void *arg)
{char buff[1024]="wellcome";JNIEnv *env;ALOGD("start-----%s--------\n", __FUNCTION__);  //从全局的JavaVM中获取到环境变量gJavaVM->AttachCurrentThread(&env, NULL);int cnt=0;//线程循环while(isRunflag) {ALOGD("JNI_thread:------%d--\nbuff:%s",cnt++ ,buff);    //调用HAL层的方法获取数据memset(buff,0,1024);if(sZgbInterface!=NULL)sZgbInterface->zgb_recvmsg(buff);//回调Java层的函数if(strlen(buff)>0)env->CallVoidMethod(mCallbacksObj,method_reportHumTemp,env->NewStringUTF((const char *)buff));
//      checkAndClearExceptionFromCallback(env, __FUNCTION__);//休眠1秒
//      sleep(1);}gJavaVM->DetachCurrentThread();ALOGD("end-----%s--------\n", __FUNCTION__);        return (void*)0;
}

1、设计思路:许多时候当外面的事件是异步发生的时候我们希望有一种中断的机制来处理这件事,所以在当APP层使用Handler向界面更新【不理解的请了解Android的APP里面的Handler对象的作用】希望是一种中断模式,这也是回调的设计初衷,如同select到epoll的改进也差不多这个设计模式【这个设计模式被广泛采纳了】。
2、JNI中创建线程方法:在jni中创建线程需要搞明白几个东西;这里有篇博客需要您先阅读下http://www.2cto.com/kf/201407/319308.html主要介绍的是JNI中的几个量{JNIEnv,JavaVM}的概念和作用,贴个图

关于JNI创建线程的方式很多,这里列举两种可以尝试
a) gJavaVM->AttachCurrentThread(&env, NULL);绑定的
b) 也有使用AndroidRuntime去创建的。
在线程中主要是调用HAL接口区监控读取相应串口设备的fd,当有数据的时候就回调java的回调函数进行上报。这样一来上层就可以异步的做自己的事情了。

三、控制命令往下传递的过程
数据往下传递一般是通信的数据或命令,用于控制相关的底层硬件
1、java里面操作硬件有多种做法,这里列举出两中
a) 参数传递方式,java调用native方法直接将参数往下传递,类也可以传递到JNI层
b) 使用UNIX的域套接字在线程间直接通信,这样一来上层【一般在framework】和下层JNI就不再有层之间关系了,都是进程,可以发送一些简单的命令数据,不能发送大量的数据。有人可能会想当往上层送数据是否也可以?哈哈我没有探究过,有兴趣的可以试试。Android的GPS子系统便是使用了这种方式。
这里采用的是传统的第一种参数传递。

/*从上往下传递数据使用的是传参方式jint on命令*/
jint Ledctrl_zgb(JNIEnv *env, jobject thiz,jint on)
{int ret = -1; if(sZgbInterface){ret = sZgbInterface->zgb_ledctrl(on);ALOGD("--------------Ledctrl_zgb\n");}
    return ret;
}

其中在调用了HAL封装的接口sZgbInterface->zgb_ledctrl(on);去控制相关的write 方法或者ioctrl去调用Linux驱动中的相关函数。

四、HAL中接口实现和数据协议解析

HAL(Hardware Abstract Layer)这一层在Android里面是用来规避GPL保护厂商的利益的,感觉也有点意思,知道那个意思就好:这里做硬件抽象的,将硬件做成一种类,含有方法和属性,这里不多说,上代码。

/*对该模块抽象的接口:打开初始化、控制、处理*/
static const ZgbInterface  MyZgbInterface = {sizeof(ZgbInterface),zgb_devopen,zgb_ledctrl,zgb_fanctrl,zgb_rcvmsg,
};/*传接口给JNI使用*/
const ZgbInterface* zgb_get_interface(struct zgb_hw_device_t* dev)
{dev=dev;return &MyZgbInterface;
}

这里设计依赖JNI,这样设计他们两没法分开。

/*打开设备节点和配置串口,成功返回>0的数失败返回 -1
*/
static int
zgb_devopen(void)
{char buff[50]="in HAL zgb_devopen";/*只打开设备节点*/ALOGD("start-----%s--------\n", __FUNCTION__);struct termios p, newp; if( (zgb_fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY)) < 0) {       ALOGE("open device in hal error\n");ALOGD("end-----%s--------\n", __FUNCTION__);return -1;}   /*相关串口编程很多默认的参数需要修改的*/bzero(&p, sizeof(p));tcgetattr(zgb_fd, &p);cfsetispeed(&p, B115200);cfsetospeed(&p, B115200);newp = p;newp.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);newp.c_oflag &= ~OPOST;newp.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);newp.c_cflag &= ~(CSIZE|PARENB);newp.c_cflag |= CS8;//newp.c_iflag |= IGNBRK;tcsetattr(zgb_fd, TCSANOW, &newp);ALOGD("open device in hal ok the fd = %d\n",zgb_fd);ALOGD("end-----%s--------\n", __FUNCTION__);return zgb_fd;
}

这里就是打开设备节点和配置相关串口【这个需要和串口设备模块配合,如果是GPS模块的话你可能需要查看提供商的数据手册】,相关串口编程的参数非常之多不再此处多说了,可以查看先关博客
http://blog.csdn.net/mr_raptor/article/details/8349323
介绍的很清楚。
这里需要注意的是当使用的模块是单工模式的时候在读写串口是需要加锁【线程间的互斥量也可以】保护不让数据被破坏。

/*自定义帧识别收消息*/
void zgb_rcvmsg(char *storedata)
{int nread = 0;int sumrcv = 0;char temp;if((NULL==storedata)||(zgb_fd < 0))return;memset(storedata,0,1024);while(1){/*一个字节一个字节的读取*/nread = read(zgb_fd, &temp, 1);ALOGD("in recv  nread = %d sumrcv=%d\n",nread,sumrcv);if(nread==0||nread < 0 ){ALOGD("/dev/ttyUSB0 close!!\n");break;}if(sumrcv>1020){memset(storedata,0,1024);break;/*这一帧数据太长了*/}if(nread == 1){if(temp=='$'){/*帧头*/ALOGD("frame start\n");memset(storedata,0,1024);nread=0;sumrcv=0;}else if(temp=='#'){/*帧尾*/ALOGD("frame end\n");nread=0;break;}else{/*数据段*/storedata[sumrcv]=temp;sumrcv+=nread;nread=0;/*bug here fix me with crc check*/}}}ALOGD("complete frame(len:%d): %s\n",strlen(storedata),storedata);
}

这里定义了一个简单的协议,将$作为帧的开始,#作为帧的结束,这里是一个字节一个字节的读取串口的数据的,主要是因为Zigbee模块在一次性读取多个的时候协议判别难写还会出现读到下一帧的情况,这里牺牲了相关的性能来获取稳定性。
这个借口会被JNI的线程来回的调用,在JNI的线程中会由此接口获取串口的数据,并回调java的方法对数据进行显示

//调用HAL的接口
if(sZgbInterface!=NULL)sZgbInterface->zgb_recvmsg(buff);
//回调Java层的函数
if(strlen(buff)>0)
env->CallVoidMethod(mCallbacksObj,method_reportHumTemp,env->NewStringUTF((const char *)buff));

在定义协议的时候这里可以使用相对合适方式使得编程简化,例如将发送过来的字符串一JSON格式封包,当数据传递到上层的时候解析就会变得十分的简单,但是会失去性能,在小数据量的项目中可以使用单数数据量大了这可以优化或者重新定义一种协议或技术。
到此基本介绍完了,谢谢!

在安卓中实现Zigbee串口设备采集模块相关推荐

  1. 安卓中实现两端对齐,中间fill_parent的方法

    安卓中实现两端对齐,中间fill_parent的方法 <?xml version="1.0″ encoding="utf-8″?> <LinearLayout x ...

  2. android密码dakay,安卓中按钮点击事件onClick的两种实现方式

    很多的语言都有一些共同的特点,比如OnClick这个东西,可能我们能在js中见到,当然在安卓中也有,可能其他的编程语言也会有这个东西,刚好今天学了这个玩意在安卓中的写法. 点击事件大多用在Button ...

  3. Android:在安卓中使用TFLite模型

    在安卓中使用TFLite不需要再进行loadlibrary. 参考:基于Android搭建tensorflow lite,实现官网的Demo以及运行自定义tensorflow模型(一) 1.安装最新版 ...

  4. android 增删改查错误,安卓中使用HttpURLConnection进行增删改查操作(包括后端讲解)(一)...

    在安卓中我们使用HttpURLConnection来进行请求 我们看主activity的代码:public class TestHttpActivity extends Activity implem ...

  5. 安卓中fragment的使用全解

    全栈工程师开发手册 (作者:栾鹏) 安卓教程全解 安卓中Fragment可以没有UI,不需要manifest中注册,只能嵌套在一个activity存在. 在Fragment基础上,系统派生处理另外几种 ...

  6. 安卓中dumpsys命令使用

    安卓中dumpsys命令使用 adb shell dumpsys,默认打印出当前系统所有的服务名,在后面加上具体的服务名. 一,需要列出当前运行的服务,可运行: adb shell dumpsys | ...

  7. 安卓中图片占用内存大小分析

    相关概念: 位深 色彩空间 颜色通道 int型占用字节 位深: 位是二进制的位.位深是指计算机系统中图片的一个像素点占用的二进制位数.例如位深32,就是使用2^8 = 32 位二进制来表示像素值.例如 ...

  8. 安卓中的对称加密,非对称加密,MD5加密的算法

    转自:http://blog.csdn.net/fengkaungdewoniu/article/details/52846025 安卓中使用的加密算法可以说有三种:对称加密.非对称加密,及MD5加密 ...

  9. matlab edittext 回车,安卓中的虚拟键盘实现,KeyEvent的事件分发、处理机制。EditText是如何将KeyEvent事件转为字符输入的?...

    目录 一.实现一个可以模拟输入的软键盘 一开始,我们的需求是在用户经常使用的部分界面中,增加虚拟软键盘,减少用户对于外接键盘的依赖 如图,在整单改价界面右侧增加了方便用户快捷输入的软键盘,用户不需要使 ...

最新文章

  1. AngularJS学习笔记(3)——通过Ajax获取JSON数据
  2. java脚本_写一个在线Java脚本执行器
  3. 基于高德地图Windows Phone API 快速开发地图相关APP(二)
  4. java静态路由_Linux添加静态路由两种实现方法解析
  5. Linux下oracle数据库启动和关闭操作
  6. SAP S/4HANA生产订单状态含义
  7. javascript中令人迷惑的this
  8. c java json_cJSON_json包的C语言解析库
  9. 安全测试===sqlmap(壹)转载
  10. python下载过程中最后一步执行opencv出错怎么回事_PyCharm安装opencv-python和opencv-contrib-python的一些问题和解决方法_2018-09-27...
  11. linux下组态软件,linux组态软件入门使用
  12. JAVA缓存机制浅析
  13. MATLAB代码:基于分布式优化的多产消者非合作博弈能量共享
  14. github 更纱黑体_更纱黑体v0.12.6
  15. 问题解决:wireshark之npcap无法安装、winpcap无法安装问题解决
  16. C语言与三菱plc通讯案例,三菱PLC的通讯与编程案例
  17. SD卡容量变小恢复方法
  18. Flickr网站架构分析
  19. 【数据压缩】使用Audacity软件分析浊音、清音爆破音的时域及频域特性。
  20. 开氏温度与摄氏度换算_为什么体温表要甩?探秘温度计、湿度计的玄机!

热门文章

  1. ppt中的表格行高批量设置
  2. Simon IELTS: Reading
  3. 横向扩展 纵向扩展 数据库_理解数据库扩展模式的指南
  4. day21-学习总结
  5. 国产麒麟系统忘记密码重置办法(5步解决)
  6. 设置div高度为浏览器可视窗口的高度
  7. 最高月薪15K!当过老师、卖过保险的退伍小哥,用三个月开启技术人生!
  8. 2017计算机信息类ei,2017年EI收录的中国期刊目录.pdf
  9. 商城购物系统【用户登录注册,购物页面,购物车页面,订单页面】
  10. 华为2019开发者大会内容小记