`
天空之城
  • 浏览: 398111 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android串口操作,简化android-serialport-api的demo

    博客分类:
  • Java
 
阅读更多

  最近在做android串口的开发,找到一个开源的串口类android-serialport-api。其主页在这里http://code.google.com/p/android-serialport-api/  ,这里可以下到APK及对源码。

    但是下载源码之后发现源码不能直接使用,而且源码结构较为复杂。关于串口的操作不外乎几步:

   1.打开串口(及配置串口);

   2.读串口;

   3.写串口;

   4.关闭串口。

android-serialport-api的代码使用了继承等复杂的行为,不容易使初学者很快掌握关于串口的上述4步,所以我特别自己写了一个demo,只有一个activity,其中包含了打开串口,写串口,读串口的操作,对于关闭串口,大家一开就会不明白怎么写了。

这篇文章主要参考http://blog.csdn.net/tangcheng_ok/article/details/7021470

还有http://blog.csdn.net/jerome_home/article/details/8452305

 

下面言归正传:

 

第一:

  说道android 串口,就不得不提JNI技术,它使得java中可以调用c语言写成的库。为可在android中使用串口,android-serialport-api的作者自己写了一个c语言的动态链接库serial_port.so(自动命名成libserial_port.so),并把它放在了libs/aemeabi 里,其c源文件在JNI中,大家在下载了android-serialport-api的源代码后,将这两个文件夹copy到自己新建的工程中即可。


 

第二:

然后将调用c语言写成的动态链接库的java类放入到src文件夹下的android.serialport包下,这里一定要将包名命名成这个,因为对JNI有一定了解的人就会知道,在写c语言链接库时候,函数的命名是和调用它的类所在的包名相关的,一旦包名与链接库中函数的命名不相符,就不能调用链接库的函数。这里可以打开jni中的.c文件(他就是动态链接库的源文件),可以看到源码:

[cpp] view plaincopy
 
  1.   
[cpp] view plaincopy
 
  1. /* 
  2.  * Copyright 2009 Cedric Priscal 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  * http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. #include <termios.h>  
  18. #include <unistd.h>  
  19. #include <sys/types.h>  
  20. #include <sys/stat.h>  
  21. #include <fcntl.h>  
  22. #include <string.h>  
  23. #include <jni.h>  
  24.   
  25. #include "android/log.h"  
  26. static const char *TAG="serial_port";  
  27. #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)  
  28. #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)  
  29. #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)  
  30.   
  31. static speed_t getBaudrate(jint baudrate)  
  32. {  
  33.     switch(baudrate) {  
  34.     case 0: return B0;  
  35.     case 50: return B50;  
  36.     case 75: return B75;  
  37.     case 110: return B110;  
  38.     case 134: return B134;  
  39.     case 150: return B150;  
  40.     case 200: return B200;  
  41.     case 300: return B300;  
  42.     case 600: return B600;  
  43.     case 1200: return B1200;  
  44.     case 1800: return B1800;  
  45.     case 2400: return B2400;  
  46.     case 4800: return B4800;  
  47.     case 9600: return B9600;  
  48.     case 19200: return B19200;  
  49.     case 38400: return B38400;  
  50.     case 57600: return B57600;  
  51.     case 115200: return B115200;  
  52.     case 230400: return B230400;  
  53.     case 460800: return B460800;  
  54.     case 500000: return B500000;  
  55.     case 576000: return B576000;  
  56.     case 921600: return B921600;  
  57.     case 1000000: return B1000000;  
  58.     case 1152000: return B1152000;  
  59.     case 1500000: return B1500000;  
  60.     case 2000000: return B2000000;  
  61.     case 2500000: return B2500000;  
  62.     case 3000000: return B3000000;  
  63.     case 3500000: return B3500000;  
  64.     case 4000000: return B4000000;  
  65.     defaultreturn -1;  
  66.     }  
  67. }  
  68.   
  69. /* 
  70.  * Class:     cedric_serial_SerialPort 
  71.  * Method:    open 
  72.  * Signature: (Ljava/lang/String;)V 
  73.  */  
  74. JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_open  
  75.   (JNIEnv *env, jobject thiz, jstring path, jint baudrate)  
  76. {  
  77.     int fd;  
  78.     speed_t speed;  
  79.     jobject mFileDescriptor;  
  80.   
  81.     /* Check arguments */  
  82.     {  
  83.         speed = getBaudrate(baudrate);  
  84.         if (speed == -1) {  
  85.             /* TODO: throw an exception */  
  86.             LOGE("Invalid baudrate");  
  87.             return NULL;  
  88.         }  
  89.     }  
  90.   
  91.     /* Opening device */  
  92.     {  
  93.         jboolean iscopy;  
  94.         const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);  
  95.         LOGD("Opening serial port %s", path_utf);  
  96.         fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);  
  97.         LOGD("open() fd = %d", fd);  
  98.         (*env)->ReleaseStringUTFChars(env, path, path_utf);  
  99.         if (fd == -1)  
  100.         {  
  101.             /* Throw an exception */  
  102.             LOGE("Cannot open port");  
  103.             /* TODO: throw an exception */  
  104.             return NULL;  
  105.         }  
  106.     }  
  107.   
  108.     /* Configure device */  
  109.     {  
  110.         struct termios cfg;  
  111.         LOGD("Configuring serial port");  
  112.         if (tcgetattr(fd, &cfg))  
  113.         {  
  114.             LOGE("tcgetattr() failed");  
  115.             close(fd);  
  116.             /* TODO: throw an exception */  
  117.             return NULL;  
  118.         }  
  119.   
  120.         cfmakeraw(&cfg);  
  121.         cfsetispeed(&cfg, speed);  
  122.         cfsetospeed(&cfg, speed);  
  123.   
  124.         if (tcsetattr(fd, TCSANOW, &cfg))  
  125.         {  
  126.             LOGE("tcsetattr() failed");  
  127.             close(fd);  
  128.             /* TODO: throw an exception */  
  129.             return NULL;  
  130.         }  
  131.     }  
  132.   
  133.     /* Create a corresponding file descriptor */  
  134.     {  
  135.         jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");  
  136.         jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>""()V");  
  137.         jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor""I");  
  138.         mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);  
  139.         (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);  
  140.     }  
  141.   
  142.     return mFileDescriptor;  
  143. }  
  144.   
  145. /* 
  146.  * Class:     cedric_serial_SerialPort 
  147.  * Method:    close 
  148.  * Signature: ()V 
  149.  */  
  150. JNIEXPORT void JNICALL Java_android_serialport_SerialPort_close  
  151.   (JNIEnv *env, jobject thiz)  
  152. {  
  153.     jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);  
  154.     jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");  
  155.   
  156.     jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd""Ljava/io/FileDescriptor;");  
  157.     jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor""I");  
  158.   
  159.     jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);  
  160.     jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);  
  161.   
  162.     LOGD("close(fd = %d)", descriptor);  
  163.     close(descriptor);  
  164. }  


可以看到,函数的命名规则直接和包名有关。

 

 

第三:

android.serialport包下,有两个类,分别是SerialPort.java 和SerialPortFinder.java。

其中,SerialPort.java,这个类主要用来加载SO文件,通过JNI的方式打开关闭串口。

[java] view plaincopy
 
  1. /* 
  2.  * Copyright 2009 Cedric Priscal 
  3.  *  
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  *  
  8.  * http://www.apache.org/licenses/LICENSE-2.0 
  9.  *  
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License.  
  15.  */  
  16.   
  17. package android.serialport;  
  18.   
  19. import java.io.File;  
  20. import java.io.FileDescriptor;  
  21. import java.io.FileInputStream;  
  22. import java.io.FileOutputStream;  
  23. import java.io.IOException;  
  24. import java.io.InputStream;  
  25. import java.io.OutputStream;  
  26.   
  27. import android.util.Log;  
  28.   
  29. public class SerialPort {  
  30.   
  31.     private static final String TAG = "SerialPort";  
  32.   
  33.     /* 
  34.      * Do not remove or rename the field mFd: it is used by native method close(); 
  35.      */  
  36.     private FileDescriptor mFd;  
  37.     private FileInputStream mFileInputStream;  
  38.     private FileOutputStream mFileOutputStream;  
  39.   
  40.     public SerialPort(File device, int baudrate) throws SecurityException, IOException {  
  41.   
  42.         /* Check access permission */  
  43.         if (!device.canRead() || !device.canWrite()) {  
  44.             try {  
  45.                 /* Missing read/write permission, trying to chmod the file */  
  46.                 Process su;  
  47.                 su = Runtime.getRuntime().exec("/system/bin/su");  
  48.                 String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"  
  49.                         + "exit\n";  
  50.                 /*String cmd = "chmod 777 /dev/s3c_serial0" + "\n" 
  51.                 + "exit\n";*/  
  52.                 su.getOutputStream().write(cmd.getBytes());  
  53.                 if ((su.waitFor() != 0) || !device.canRead()  
  54.                         || !device.canWrite()) {  
  55.                     throw new SecurityException();  
  56.                 }  
  57.             } catch (Exception e) {  
  58.                 e.printStackTrace();  
  59.                 throw new SecurityException();  
  60.             }  
  61.         }  
  62.   
  63.         mFd = open(device.getAbsolutePath(), baudrate);  
  64.         if (mFd == null) {  
  65.             Log.e(TAG, "native open returns null");  
  66.             throw new IOException();  
  67.         }  
  68.         mFileInputStream = new FileInputStream(mFd);  
  69.         mFileOutputStream = new FileOutputStream(mFd);  
  70.     }  
  71.   
  72.     // Getters and setters  
  73.     public InputStream getInputStream() {  
  74.         return mFileInputStream;  
  75.     }  
  76.   
  77.     public OutputStream getOutputStream() {  
  78.         return mFileOutputStream;  
  79.     }  
  80.   
  81.     // JNI  
  82.     private native static FileDescriptor open(String path, int baudrate);  
  83.     public native void close();  
  84.     static {  
  85.         System.loadLibrary("serial_port");  
  86.     }  
  87. }  

可以看到System.loadLibrary("serial_port");一句,这一句就是用来加载动态链接库。我们的串口操作就是要给予这个类来实现。

 

 

含有一个类SerialPortFinder.java,这个类是用来找到系统中可以用的串口的,如果你知道的android设备有什么串口,就不必使用这个类来查找串口了,一次简化我们的demo。

 

第四:加入我们自己的Activity类

  为了方便我记在android.serialport包下加入了我自己的MyserialActivity.java,大家从上面的图中也可以看见。

代码如下:

[java] view plaincopy
 
  1. package android.serialport;  
  2.   
  3.   
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileOutputStream;  
  7. import java.io.IOException;  
  8.   
  9. import android.app.Activity;  
  10.   
  11. import android.os.Bundle;  
  12.   
  13.   
  14.   
  15. //import android.serialport.sample.R;  
  16. import android.serialport.R;  
  17.   
  18. import android.view.View;  
  19. import android.widget.Button;  
  20. import android.widget.EditText;  
  21. import android.widget.Toast;  
  22.   
  23. public class MyserialActivity extends Activity {  
  24.     /** Called when the activity is first created. */  
  25.       
  26.       
  27.      EditText mReception;  
  28.      FileOutputStream mOutputStream;  
  29.      FileInputStream mInputStream;  
  30.      SerialPort sp;  
  31.        
  32.     @Override  
  33.      
  34.     public void onCreate(Bundle savedInstanceState) {  
  35.         super.onCreate(savedInstanceState);  
  36.         setContentView(R.layout.main);  
  37.     
  38.       
  39.     final Button buttonSetup = (Button)findViewById(R.id.ButtonSetup);  
  40.     buttonSetup.setOnClickListener(new View.OnClickListener() {  
  41.         public void onClick(View v) {  
  42.             mReception = (EditText) findViewById(R.id.EditTextRec);  
  43.                 
  44.               try {  
  45.             sp=new SerialPort(new File("/dev/ttyS2"),9600);  
  46.             } catch (SecurityException e) {  
  47.                 // TODO Auto-generated catch block  
  48.                 e.printStackTrace();  
  49.             } catch (IOException e) {  
  50.                 // TODO Auto-generated catch block  
  51.                 e.printStackTrace();  
  52.             }     
  53.                 
  54.               
  55.               mOutputStream=(FileOutputStream) sp.getOutputStream();  
  56.               mInputStream=(FileInputStream) sp.getInputStream();  
  57.               
  58.                Toast.makeText(getApplicationContext(), "open",  
  59.                         Toast.LENGTH_SHORT).show();  
  60.               
  61.         }  
  62.     });  
  63.       
  64.       
  65.       
  66.     final Button buttonsend= (Button)findViewById(R.id.ButtonSent1);  
  67.     buttonsend.setOnClickListener(new View.OnClickListener() {  
  68.         public void onClick(View v) {  
  69.               
  70.             try {  
  71.                 mOutputStream.write(new String("send").getBytes());  
  72.                 mOutputStream.write('\n');  
  73.             } catch (IOException e) {  
  74.                 e.printStackTrace();  
  75.             }  
  76.                 
  77.              
  78.               Toast.makeText(getApplicationContext(), "send",  
  79.                          Toast.LENGTH_SHORT).show();  
  80.               
  81.         }  
  82.     });  
  83.       
  84.       
  85.     final Button buttonrec= (Button)findViewById(R.id.ButtonRec);  
  86.     buttonrec.setOnClickListener(new View.OnClickListener() {  
  87.         public void onClick(View v) {  
  88.             int size;  
  89.               
  90.             try {  
  91.             byte[] buffer = new byte[64];  
  92.             if (mInputStream == nullreturn;  
  93.             size = mInputStream.read(buffer);  
  94.             if (size > 0) {  
  95.                 onDataReceived(buffer, size);  
  96.                   
  97.             }  
  98.         } catch (IOException e) {  
  99.             e.printStackTrace();  
  100.             return;  
  101.         }  
  102.               
  103.         }  
  104.     });  
  105.     }  
  106.     void onDataReceived(final byte[] buffer, final int size) {  
  107.         runOnUiThread(new Runnable() {  
  108.             public void run() {  
  109.                 if (mReception != null) {  
  110.                     mReception.append(new String(buffer, 0, size));  
  111.                 }  
  112.             }  
  113.         });  
  114.     }  
  115.       
  116.       
  117. }  

 

可以看见,功能比较简单,只有三个按钮,分别用来打开串口(buttonsetup),写串口(buttonsend),读串口(buttonrec),一个文本框用来显示串口接收到的信息。功能已经简化到了最简。

 

下面先说说在模拟器中使用串口的方法:

应先使用-serial选项打开你的模拟器,如图(修改你模拟器的名字)


然后进入adb shell 

  cd /dev

chmod 777 ttyS2

运行后结果:

相比大家都懂得,我们的串口就是ttyS2,使用chmod命令来获取对它的操作,否则之后你的应用可能没有串口的操作权限。

然后运行程序:

其中Console就是打开串口(原谅我偷懒,忘改名字了)。

你可以把你的电脑的COM1连接到另一台电脑的串口上,并在那台电脑上打开你的串口助手之类的软件,配置好串口(参数不难从源代码里看出来)。按下模拟器中的send键,就能在那台电脑的串口助手中看到:


 

同样,从那台电脑向这台电脑发送数据也可以显示

 

至此,这个小demo就完毕了。

  我的源码在这里:   http://download.csdn.net/detail/akunainiannian/5202173

分享到:
评论
6 楼 u011073441 2017-08-23  
如何才能知道,我的安卓设备连接的是什么接口?android 设备也没有像计算机一样可以查看设备?
5 楼 sinat_31332985 2015-09-24  
大神请问一下,在真机测试中,我没有串口设备,然后我设置dev/ttyS2,且取得了root权限,并chmod 777 dev/ttyS2, 但是还是报出不能读写的错误。这是因为真机没有串口设备的原因吗?谢谢!!!
4 楼 骑着蜗牛狂奔 2015-07-06  
在调用close的时候,出现了这个
JNI WARNING: JNI method called with exception pending
              in Lcom/uxun/serialport/SerialPort;.CloseSerial:()V (GetFieldID)
Pending exception is:
java.lang.NoSuchFieldError: no field with name='mFd' signature='Ljava/io/FileDescriptor;' in class Lcom/uxun/serialport/SerialPort;

怎么回事,如何解决??
3 楼 l475334176 2015-06-08  
怎么对多个串口进行监听额?  就是同时接受或者发送数据给多个串口。
2 楼 冰雨的日子 2015-04-22  
你好,我执行su.getOutputStream().write(cmd.getBytes());抛出异常, su在/system/xbin/su, 请问是什么原因?
1 楼 天空之城 2015-01-27  
不错,多谢楼主分享~~  

相关推荐

    android-serialport-api 串口demo.zip

    这是很据android-serialport-api 自己简化的一个demo ,可以使用。原来android-serial-api的程序很多人反映都不能使用,所以自己写了这个,只有一个activity,可以做为你的学习参考。

    Android代码-安卓串口打开、接收与发送

    Android串口操作,简化android-serialport-api的demo(附源码) 我把文中的源码导入 android studio 源码来自谷歌:android-serialport-api 涉及到的 jni 开发可以参考:【Android 应用开发】Android 开发 之 JNI...

    android串口demo

    这是很据android-serialport-api 自己简化的一个demo ,可以使用。原来android-serial-api的程序很多人反映都不能使用,所以自己写了这个,只有一个activity。

    android串口开发demo(开关读写)

    公司业务是开发智能快递柜的,最近几天再研究串口编程。这是很据android-serialport-api 自己简化的一个demo ,主要实现 串口连接的 开启,关闭,发送数据,获取数据等。可以使用。

    Android串口通信Demo

    1,此Demo是使用 android studio 2.3.3 编写,可以直接作为Module导入到项目中使用;...2,此Demo是对google推荐的串口通信开源项目android_serialport_api的简化,便于初次接触串口开发的童鞋借鉴学习;

    Android串口通信超简单的Demo

    1,本Demo是使用 android studio 2.1 编写,可以...2,本Demo是对google推荐的串口通信开源项目android_serialport_api的简化,便于学习; 3,使用方法见博客:https://blog.csdn.net/haiger_xxx/article/details/51597237

    android串口

    这是很据android-serialport-api 自己简化的一个demo ,可以使用。原来android-serial-api的程序很多人反映都不能使用,所以自己写了这个,只有一个activity。

    MyApplication.rar

    Android串口通信超简单的Demo 评分: ...2,本Demo是对google推荐的串口通信开源项目android_serialport_api的简化,便于学习; 1.打开串口(及配置串口); 2.读串口; 3.写串口; 4.关闭串口。

Global site tag (gtag.js) - Google Analytics