无网不进  
软硬件开发

Android平台的串口通信之C链接库的创建过程--汇总版

 

http://blog.csdn.net/w837956/article/details/8216658

 Android发布初期,Google就表示其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以使用JNI调用自己的C动态库,但Google官方并没有明确表示支持开发者使用这种方法。终于在2009年6月,Google Android发布了NDK,它支持开发者使用C/C++语言开发Android程序。作为Android SDK的一个附加组件提供,开发者必须先安装Android SDK方可使用NDK。NDK的目的是为了增加代码的重用性及加快程序的运行速度,这有利于开发者从其他系统上移植软件到Android平台。
  1 Android NDK简介
  在Android上应用程序的开发大部分基于Java语言来实现。要使用C或是C++的程序或库,就需要使用NDK来实现。NDK是Native Development Kit的简称。它是一个工具集,集成了Android的交叉编译环境,并提供了一套比较方便的Makefile,可以帮助开发者快速开发C或是C++的动态库,并自动的将so动态库和java程序打包成apk,在Android上运行。有两个理由使用NDK:一是合理的重用现有的代码;二是在程序中某些关键的部分提高执行效率。
  Android NDK目前作为Android SDK的一个附加组件提供,开发者须先安装Android SDK方可使用NDK。在Windows平台下进行NDK开发通常会采用Cygwin。Cygwin是一套可以运行在Windows平台上的UNIX/Linux模拟器。运行Cygwin后会出现一个类似Windows CMD的Shell环境界面,可以使用大部分Linux软件和功能。使用它我们可以方便的在Windows平台编译出Linux平台的库文件或应用程序。
  2 安装和配置NDK开发环境
  2.1、安装NDK
  首先要完整安装SDK,尽量升级至最新版本的SDK。下载NDK,官网有三个版本分别是Windows、Mac OS X(intel)、Linux32/64(x86),下载后解压即可使用。文中使用Windows版本的NDK,版本为android-ndk-r8。将它解压到某个目录下,文中我们将NDK放到D:\android-ndk-r8c目录中。

直接给大家我的百度网盘的下载地址吧:

内容包括cygwin,NDK,和cygwin的离线安装包(仅有devel的)

http://pan.baidu.com/share/link?shareid=129521&uk=2467914304

文件名:android-ndk-r8c.zip

大家也可以去百度下载

  2.2、 安装Cygwin
  首先去Cygwin官网下载网络安装程序,下载下来以后点击直接运行。安装过程中最关键的是选择需要安装的包,为支持Android NDK的开发,选择Default安装后再安装以下模块autoconf2.1、automake1.10、binutils、gcc-core、gcc4-core、gdb、pcre、pcre-devel、GNU awk。 如果不确定需要哪些包,直接全部安装就行,下载速度可能会很慢,不要急,直接在晚上挂机下载第二天可能就好了。

官方地址:http://www.cygwin.com/

文件名:setup.exe

安装过程

1、运行Cygwin 安装程序setup.exe,然后选择“Install from Local Directory“,选择“下一步”,如图 所示。

 

 

2、选择安装方式、

 

 

3、选择Cygwin 的安装目录,注意Cygwin 的安装目录必须位于硬盘NTFS 分区(且尽量不要使用系统C 分区),否则会影响文件属性和权限操作,可能导致错误的结果。,直接选择“下一步”,如图 所示

4、选择安装包下载路径

5、选择使用的 Internet 连接类型。

6、选择一个镜像站点来下载安装包,或在User URL中输入网址,使用指定站点下载,如果不确定应该选择哪个站点,就选择地理位置比较近的站点。

7、稍等片刻,会列出站点可下载资源包,此处默认全部安装,可根据需要下载

8、点击下一步,开始下载,你就可以去睡觉了,下载完成后会自动安装

  9、工作界面

下面开始将Android NDK配置到Cygwin中。运行Cygwin,修改Cygwin目录下(/home/usrname)的.bash_profile文件,在文件尾部加入如下代码,
  NDK=/cygdrive/d/android-ndk-r8c  

export NDK


  然后重新启动Cygwin。输入cd $NDK,如果输出上面配置的/cygdrive/e/android-ndk-r5信息,则表明环境变量设置成功了,如图。接下来就可以用 Cygwin 来编译我们的NDK代码了。


  3、Android NDK开发  

目前为止,用C/C++编写Android应用程序有两种方式:

1. 用C/C++编写主要的逻辑层,再用java编写界面层并调用C/C++的库;

2. 用C/C++直接开发完整的应用程序,完全不用java。(仅Android 2.3之后的版本支持)

在此介绍第一种方法:

       流程~

        1)JNI接口设计;<即Linuxc.java文件中需要写的方法>
  2) 使用C/C++实现本地方法;
  3) 生成动态链接库;
  4) 将动态链接库复制到Java工程,生成.apk文件。
 1、 首先,创建一个NDK工程,然后在这个文件夹下建立jni和src两个目录,jni用来存放我们的C文件(注:该文件夹下还存放有Android.mk文件和*.h头文件,其中h文件是自动生成的,不能对其进行修改,*.mk和C文件是自己编译的,),src是调用C库的Java接口文件。接着创建com_example_linux_Linuxc.h.c,该文件的主要作用是完成串口的打开和关闭。部分关键代码如下:

[cpp] view plain copy
 
  1. #include<stdio.h>  
  2.   
  3. #include<stdlib.h>  
  4.   
  5. #include<fcntl.h>  
  6.   
  7. #include<errno.h>  
  8.   
  9. #include<unistd.h>  
  10.   
  11. #include"com_example_linux_Linuxc.h"  
  12.   
  13. #include<sys/types.h>  
  14.   
  15. #include<sys/stat.h>  
  16.   
  17. #include<string.h>  
  18.   
  19. #include<stdint.h>  
  20.   
  21. #include<termios.h>  
  22.   
  23. #include<android/log.h>  
  24.   
  25. //#include <time.h>  
  26.   
  27.    
  28.   
  29. #include<sys/ioctl.h>  
  30.   
  31. #undef TCSAFLUSH  
  32.   
  33. #define TCSAFLUSH TCSETSF  
  34.   
  35. #ifndef _TERMIOS_H_  
  36.   
  37. #define _TERMIOS_H_  
  38.   
  39. #endif  
  40.   
  41.    
  42.   
  43. #define MAX_RECEIVE_BUF_LEN    1024  
  44.   
  45.    
  46.   
  47. char buffer[MAX_RECEIVE_BUF_LEN];  
  48.   
  49. char buffer1[2*MAX_RECEIVE_BUF_LEN];  
  50.   
  51.    
  52.   
  53. wchar_t w_buffer[4*MAX_RECEIVE_BUF_LEN];  
  54.   
  55.    
  56.   
  57. int fd;  
  58.   
  59. struct termios newtio,oldtio;  
  60.   
  61.    
  62.   
  63. JNIEXPORT jint JNICALL Java_com_example_linux_Linuxc_openUart(JNIEnv *env,jobject mc,jint i, jint mode)  
  64.   
  65. {  
  66.   
  67.         int fd = -1;  
  68.   
  69.         if(mode == 0)  
  70.   
  71.         {  
  72.   
  73.             if(i == 0)  
  74.   
  75.             {  
  76.   
  77.                     fd=open("/dev/ttySAC0",O_RDWR, 0666);//O_NOCTTY  
  78.   
  79.                     return fd;  
  80.   
  81.             }  
  82.   
  83.             elseif(i==1)  
  84.   
  85.             {  
  86.   
  87.                 fd=open("/dev/ttySAC1",O_RDWR, 0666);//O_NOCTTY  
  88.   
  89.                 return fd;  
  90.   
  91.             }  
  92.   
  93.             elseif(i==2)  
  94.   
  95.             {  
  96.   
  97.                 fd=open("/dev/ttySAC2",O_RDWR, 0666);  
  98.   
  99.                 return fd;  
  100.   
  101.             }  
  102.   
  103.             elseif(i==3)  
  104.   
  105.           {  
  106.   
  107.               fd=open("/dev/ttySAC3",O_RDWR, 0666);  
  108.   
  109.               return fd;  
  110.   
  111.           }  
  112.   
  113.           elseif(i==4)  
  114.   
  115.           {  
  116.   
  117.               fd=open("/dev/ttySAC4",O_RDWR, 0666);  
  118.   
  119.               return fd;  
  120.   
  121.           }  
  122.   
  123.    
  124.   
  125.          }  
  126.   
  127.          elseif(mode == 1)  
  128.   
  129.          {  
  130.   
  131.             if(i == 0)  
  132.   
  133.             {  
  134.   
  135.                 fd=open("/dev/ttyUSB0",O_RDWR, 0666);//O_NOCTTY  
  136.   
  137.                 return fd;  
  138.   
  139.             }  
  140.   
  141.             elseif(i==1)  
  142.   
  143.             {  
  144.   
  145.                 fd=open("/dev/ttyUSB1",O_RDWR, 0666);//O_NOCTTY  
  146.   
  147.                 return fd;  
  148.   
  149.             }  
  150.   
  151.             elseif(i==2)  
  152.   
  153.             {  
  154.   
  155.                 fd=open("/dev/ttyUSB2",O_RDWR, 0666);  
  156.   
  157.                 return fd;  
  158.   
  159.             }  
  160.   
  161.             elseif(i==3)  
  162.   
  163.           {  
  164.   
  165.               fd=open("/dev/ttyUSB3",O_RDWR, 0666);  
  166.   
  167.               return fd;  
  168.   
  169.           }  
  170.   
  171.           elseif(i==4)  
  172.   
  173.           {  
  174.   
  175.               fd=open("/dev/ttyUSB4",O_RDWR, 0666);  
  176.   
  177.               return fd;  
  178.   
  179.           }  
  180.   
  181.    
  182.   
  183.          }  
  184.   
  185.    
  186.   
  187.     return fd;  
  188.   
  189. }  
  190.   
  191.    
  192.   
  193. JNIEXPORT void JNICALL Java_com_example_linux_Linuxc_closeUart(JNIEnv *env,jobject mc,jint fd)  
  194.   
  195. {  
  196.   
  197.     close(fd);  
  198.   
  199. }  
  200.   
  201. Java_android_serialport_SerialPort_open(JNIEnv *env, jobject thiz, jstring path, jint baudrate) {  
  202.   
  203.   ……  
  204.   
  205.   /* Opening device */  
  206.   
  207.   const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);  
  208.   
  209.   LOGD("Opening serial port %s", path_utf);  
  210.   
  211.   fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);  
  212.   
  213.   LOGD("open() fd = %d", fd);  
  214.   
  215.   (*env)->ReleaseStringUTFChars(env, path, path_utf);  
  216.   
  217.   ……  
  218.   
  219.   /* Configure device */  
  220.   
  221.   ……  
  222.   
  223.   struct termios cfg;  
  224.   
  225.   cfmakeraw(&cfg);  
  226.   
  227.   cfsetispeed(&cfg, speed);  
  228.   
  229.   cfsetospeed(&cfg, speed);  
  230.   
  231.   }  



在文件中,函数名这样定义:
  jobject JNICALL Java_com_example_linux_Linuxc_openUart,这个是JNI的标准,定义需要按照如下格式:Java_packagename_classname_methodname
 2、 接着创建文件jni/Android.mk.这个文件是我们本地c代码的Makefile。文件内容如下:

[plain] view plain copy
 
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include$(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE := uart  
  6.   
  7. LOCAL_SRC_FILES := com_example_uart_Linuxc.c  
  8.   
  9. LOCAL_LDLIBS += -llog  
  10.   
  11. LOCAL_LDLIBS +=-lm  
  12.   
  13. include$(BUILD_SHARED_LIBRARY)    

 

LOCAL_PATH:=$(callmy-dir)这句用来指定编译的路径通过调用宏my-dir获取到当前工作的路径。
  include$(CLEAR_VARS) CLEAR_VARS这个变量是编译系统提供的用来指明一个GNU makefile文件添加这句主要的目的是清理所有的LOCAL_XXX,比如LOCAL_MODULE、LOCAL_SRC_FILES等。在每个新模块的开始处需要添加这句。
  LOCAL_MODULE := serial_port这句定义了模块名称,将来编译的库或者可执行程序就以此命名。如果编译的是动态库或者静态库,那么库名就是libserial_port.so或者libserial_port.a。需要注意的是系统会在生成动态库或者静态库的时候自动添加lib的前缀。
  LOCAL_SRC_FILES := SerialPort.c是列出需要编译的源码文件名。这里不需要列出头文件和被包含文件,因为编译系统会自动为你添加。
  include$(BUILD_SHARED_LIBRARY)这句说明将来产生的库是共享库即动态链接库。
3、 接着,.编译Linuxc.java文件,在cmd中用命令进到该java文件的目录,javacLinuxc.java,回车。jvm将编译出.class文件,将.class和.java相同的位置。

4、生成.h文件,用命令进入,javah + Linuxc.java所在的文件夹下 (包名一定要加上,无后缀)。如:

5、此时产生的.h后,jni的过程就完成了,jni:java native interface,为java提供调用本地方法的接口。完成界面:

将.h文件复制到jni目录下,我们就可以在cygwin下编译生成库文件了。如图所示,进入到工程目录下,运行ndk-build命令,生成了名为libserial_port.so的文件。


 然后在src目录下编写的serialport.java文件,该文件用于JNI接口调用。关键代码如下:

[java] view plain copy
 
  1. package com.example.linux;  
  2.   
  3.    
  4.   
  5. //import android.util.Log;  
  6.   
  7.    
  8.   
  9. publicclass Linuxc {  
  10.     //打开串口库  
  11.     static  
  12.     {  
  13.       try  
  14.       {  
  15.            System.loadLibrary("uart");  
  16.            //Log.i("JIN","Trying to load libuart.so");  
  17.        }  
  18.        catch(UnsatisfiedLinkError ule)  
  19.        {  
  20.            //Log.e("JIN","WARNING:could not load libuart.so");  
  21.        }  
  22.     }  
  23.     publicstaticnativeint openUart(int i,int j);// 打开串口   
  24.     publicstaticnativevoid closeUart(int fd);//关闭串口  
  25.     publicstaticnativeint setUart(int fd,int burd,int returntimeout, int returnminlen);//设置串口  
  26.     publicstaticnativeint sendMsgUart(int fd,String msg);//发送串口信息  
  27.     publicstaticnativeint sendMsgUartHex(int fd,String msg,int len);//发送串口信息hex  
  28.    publicstaticnative String receiveMsgUart(int fd);//接受串口信息  
[java] view plain copy
 
  1. publicstaticnative String receiveMsgUartHex(int fd);//接收串口信息hex  

System.loadLibrary("serial_port")这句就是用来加载我们的c动态库的。上面声明方法的具体实现就在我们加载的库中。在完成了上述工作后,我们就可以针对具体应用来使用串口完成数据通信了。

        编译运行该工程,就可以生成apk文件了。将apk文件和libserial_port.so安装到Android平台后,就可以运行该应用程序了

 注:

android开发用ndk编译so库时,有时直接从别的地方拷贝Android.mk文件,会报:

make: *** 没有规则可以创建“obj/local/armeabi/objs/a/a.o”需要的目标“/a.c”。停止。

出现这个这个错误,可能是因为android.mk的文件格式可能是windows下拷贝,或是从网页copy,只要用vi在linux下打开Android.mk文件把行尾一些多余的看不见字符清除即可。

 
最后、给一个使用cygwin错误汇总的链接:http://wenku.baidu.com/view/5d20212d3169a4517723a380.html

 本文部分内容来自网络

posted on 2018-01-10 18:16  无网不进  阅读(930)  评论(0编辑  收藏  举报