最近在做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 plain copy
[cpp] view plain copy
  1. /*
  2. *Copyright2009CedricPriscal
  3. *
  4. *LicensedundertheApacheLicense,Version2.0(the"License");
  5. *youmaynotusethisfileexceptincompliancewiththeLicense.
  6. *YoumayobtainacopyoftheLicenseat
  7. *
  8. *http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. *Unlessrequiredbyapplicablelaworagreedtoinwriting,software
  11. *distributedundertheLicenseisdistributedonan"ASIS"BASIS,
  12. *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
  13. *SeetheLicenseforthespecificlanguagegoverningpermissionsand
  14. *limitationsundertheLicense.
  15. */
  16. #include<termios.h>
  17. #include<unistd.h>
  18. #include<sys/types.h>
  19. #include<sys/stat.h>
  20. #include<fcntl.h>
  21. #include<string.h>
  22. #include<jni.h>
  23. #include"android/log.h"
  24. staticconstchar*TAG="serial_port";
  25. #defineLOGI(fmt,args...)__android_log_print(ANDROID_LOG_INFO,TAG,fmt,##args)
  26. #defineLOGD(fmt,args...)__android_log_print(ANDROID_LOG_DEBUG,TAG,fmt,##args)
  27. #defineLOGE(fmt,args...)__android_log_print(ANDROID_LOG_ERROR,TAG,fmt,##args)
  28. staticspeed_tgetBaudrate(jintbaudrate)
  29. {
  30. switch(baudrate){
  31. case0:returnB0;
  32. case50:returnB50;
  33. case75:returnB75;
  34. case110:returnB110;
  35. case134:returnB134;
  36. case150:returnB150;
  37. case200:returnB200;
  38. case300:returnB300;
  39. case600:returnB600;
  40. case1200:returnB1200;
  41. case1800:returnB1800;
  42. case2400:returnB2400;
  43. case4800:returnB4800;
  44. case9600:returnB9600;
  45. case19200:returnB19200;
  46. case38400:returnB38400;
  47. case57600:returnB57600;
  48. case115200:returnB115200;
  49. case230400:returnB230400;
  50. case460800:returnB460800;
  51. case500000:returnB500000;
  52. case576000:returnB576000;
  53. case921600:returnB921600;
  54. case1000000:returnB1000000;
  55. case1152000:returnB1152000;
  56. case1500000:returnB1500000;
  57. case2000000:returnB2000000;
  58. case2500000:returnB2500000;
  59. case3000000:returnB3000000;
  60. case3500000:returnB3500000;
  61. case4000000:returnB4000000;
  62. default:return-1;
  63. }
  64. }
  65. /*
  66. *Class:cedric_serial_SerialPort
  67. *Method:open
  68. *Signature:(Ljava/lang/String;)V
  69. */
  70. JNIEXPORTjobjectJNICALLJava_android_serialport_SerialPort_open
  71. (JNIEnv*env,jobjectthiz,jstringpath,jintbaudrate)
  72. {
  73. intfd;
  74. speed_tspeed;
  75. jobjectmFileDescriptor;
  76. /*Checkarguments*/
  77. {
  78. speed=getBaudrate(baudrate);
  79. if(speed==-1){
  80. /*TODO:throwanexception*/
  81. LOGE("Invalidbaudrate");
  82. returnNULL;
  83. }
  84. }
  85. /*Openingdevice*/
  86. {
  87. jbooleaniscopy;
  88. constchar*path_utf=(*env)->GetStringUTFChars(env,path,&iscopy);
  89. LOGD("Openingserialport%s",path_utf);
  90. fd=open(path_utf,O_RDWR|O_DIRECT|O_SYNC);
  91. LOGD("open()fd=%d",fd);
  92. (*env)->ReleaseStringUTFChars(env,path,path_utf);
  93. if(fd==-1)
  94. {
  95. /*Throwanexception*/
  96. LOGE("Cannotopenport");
  97. /*TODO:throwanexception*/
  98. returnNULL;
  99. }
  100. }
  101. /*Configuredevice*/
  102. {
  103. structtermioscfg;
  104. LOGD("Configuringserialport");
  105. if(tcgetattr(fd,&cfg))
  106. {
  107. LOGE("tcgetattr()failed");
  108. close(fd);
  109. /*TODO:throwanexception*/
  110. returnNULL;
  111. }
  112. cfmakeraw(&cfg);
  113. cfsetispeed(&cfg,speed);
  114. cfsetospeed(&cfg,speed);
  115. if(tcsetattr(fd,TCSANOW,&cfg))
  116. {
  117. LOGE("tcsetattr()failed");
  118. close(fd);
  119. /*TODO:throwanexception*/
  120. returnNULL;
  121. }
  122. }
  123. /*Createacorrespondingfiledescriptor*/
  124. {
  125. jclasscFileDescriptor=(*env)->FindClass(env,"java/io/FileDescriptor");
  126. jmethodIDiFileDescriptor=(*env)->GetMethodID(env,cFileDescriptor,"<init>","()V");
  127. jfieldIDdescriptorID=(*env)->GetFieldID(env,cFileDescriptor,"descriptor","I");
  128. mFileDescriptor=(*env)->NewObject(env,cFileDescriptor,iFileDescriptor);
  129. (*env)->SetIntField(env,mFileDescriptor,descriptorID,(jint)fd);
  130. }
  131. returnmFileDescriptor;
  132. }
  133. /*
  134. *Class:cedric_serial_SerialPort
  135. *Method:close
  136. *Signature:()V
  137. */
  138. JNIEXPORTvoidJNICALLJava_android_serialport_SerialPort_close
  139. (JNIEnv*env,jobjectthiz)
  140. {
  141. jclassSerialPortClass=(*env)->GetObjectClass(env,thiz);
  142. jclassFileDescriptorClass=(*env)->FindClass(env,"java/io/FileDescriptor");
  143. jfieldIDmFdID=(*env)->GetFieldID(env,SerialPortClass,"mFd","Ljava/io/FileDescriptor;");
  144. jfieldIDdescriptorID=(*env)->GetFieldID(env,FileDescriptorClass,"descriptor","I");
  145. jobjectmFd=(*env)->GetObjectField(env,thiz,mFdID);
  146. jintdescriptor=(*env)->GetIntField(env,mFd,descriptorID);
  147. LOGD("close(fd=%d)",descriptor);
  148. close(descriptor);
  149. }


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

第三:

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

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

[java] view plain copy
  1. /*
  2. *Copyright2009CedricPriscal
  3. *
  4. *LicensedundertheApacheLicense,Version2.0(the"License");
  5. *youmaynotusethisfileexceptincompliancewiththeLicense.
  6. *YoumayobtainacopyoftheLicenseat
  7. *
  8. *http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. *Unlessrequiredbyapplicablelaworagreedtoinwriting,software
  11. *distributedundertheLicenseisdistributedonan"ASIS"BASIS,
  12. *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied.
  13. *SeetheLicenseforthespecificlanguagegoverningpermissionsand
  14. *limitationsundertheLicense.
  15. */
  16. packageandroid.serialport;
  17. importjava.io.File;
  18. importjava.io.FileDescriptor;
  19. importjava.io.FileInputStream;
  20. importjava.io.FileOutputStream;
  21. importjava.io.IOException;
  22. importjava.io.InputStream;
  23. importjava.io.OutputStream;
  24. importandroid.util.Log;
  25. publicclassSerialPort{
  26. privatestaticfinalStringTAG="SerialPort";
  27. /*
  28. *DonotremoveorrenamethefieldmFd:itisusedbynativemethodclose();
  29. */
  30. privateFileDescriptormFd;
  31. privateFileInputStreammFileInputStream;
  32. privateFileOutputStreammFileOutputStream;
  33. publicSerialPort(Filedevice,intbaudrate)throwsSecurityException,IOException{
  34. /*Checkaccesspermission*/
  35. if(!device.canRead()||!device.canWrite()){
  36. try{
  37. /*Missingread/writepermission,tryingtochmodthefile*/
  38. Processsu;
  39. su=Runtime.getRuntime().exec("/system/bin/su");
  40. Stringcmd="chmod777"+device.getAbsolutePath()+"\n"
  41. +"exit\n";
  42. /*Stringcmd="chmod777/dev/s3c_serial0"+"\n"
  43. +"exit\n";*/
  44. su.getOutputStream().write(cmd.getBytes());
  45. if((su.waitFor()!=0)||!device.canRead()
  46. ||!device.canWrite()){
  47. thrownewSecurityException();
  48. }
  49. }catch(Exceptione){
  50. e.printStackTrace();
  51. thrownewSecurityException();
  52. }
  53. }
  54. mFd=open(device.getAbsolutePath(),baudrate);
  55. if(mFd==null){
  56. Log.e(TAG,"nativeopenreturnsnull");
  57. thrownewIOException();
  58. }
  59. mFileInputStream=newFileInputStream(mFd);
  60. mFileOutputStream=newFileOutputStream(mFd);
  61. }
  62. //Gettersandsetters
  63. publicInputStreamgetInputStream(){
  64. returnmFileInputStream;
  65. }
  66. publicOutputStreamgetOutputStream(){
  67. returnmFileOutputStream;
  68. }
  69. //JNI
  70. privatenativestaticFileDescriptoropen(Stringpath,intbaudrate);
  71. publicnativevoidclose();
  72. static{
  73. System.loadLibrary("serial_port");
  74. }
  75. }

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

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

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

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

代码如下:

[java] view plain copy
  1. packageandroid.serialport;
  2. importjava.io.File;
  3. importjava.io.FileInputStream;
  4. importjava.io.FileOutputStream;
  5. importjava.io.IOException;
  6. importandroid.app.Activity;
  7. importandroid.os.Bundle;
  8. //importandroid.serialport.sample.R;
  9. importandroid.serialport.R;
  10. importandroid.view.View;
  11. importandroid.widget.Button;
  12. importandroid.widget.EditText;
  13. importandroid.widget.Toast;
  14. publicclassMyserialActivityextendsActivity{
  15. /**Calledwhentheactivityisfirstcreated.*/
  16. EditTextmReception;
  17. FileOutputStreammOutputStream;
  18. FileInputStreammInputStream;
  19. SerialPortsp;
  20. @Override
  21. publicvoidonCreate(BundlesavedInstanceState){
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.main);
  24. finalButtonbuttonSetup=(Button)findViewById(R.id.ButtonSetup);
  25. buttonSetup.setOnClickListener(newView.OnClickListener(){
  26. publicvoidonClick(Viewv){
  27. mReception=(EditText)findViewById(R.id.EditTextRec);
  28. try{
  29. sp=newSerialPort(newFile("/dev/ttyS2"),9600);
  30. }catch(SecurityExceptione){
  31. //TODOAuto-generatedcatchblock
  32. e.printStackTrace();
  33. }catch(IOExceptione){
  34. //TODOAuto-generatedcatchblock
  35. e.printStackTrace();
  36. }
  37. mOutputStream=(FileOutputStream)sp.getOutputStream();
  38. mInputStream=(FileInputStream)sp.getInputStream();
  39. Toast.makeText(getApplicationContext(),"open",
  40. Toast.LENGTH_SHORT).show();
  41. }
  42. });
  43. finalButtonbuttonsend=(Button)findViewById(R.id.ButtonSent1);
  44. buttonsend.setOnClickListener(newView.OnClickListener(){
  45. publicvoidonClick(Viewv){
  46. try{
  47. mOutputStream.write(newString("send").getBytes());
  48. mOutputStream.write('\n');
  49. }catch(IOExceptione){
  50. e.printStackTrace();
  51. }
  52. Toast.makeText(getApplicationContext(),"send",
  53. Toast.LENGTH_SHORT).show();
  54. }
  55. });
  56. finalButtonbuttonrec=(Button)findViewById(R.id.ButtonRec);
  57. buttonrec.setOnClickListener(newView.OnClickListener(){
  58. publicvoidonClick(Viewv){
  59. intsize;
  60. try{
  61. byte[]buffer=newbyte[64];
  62. if(mInputStream==null)return;
  63. size=mInputStream.read(buffer);
  64. if(size>0){
  65. onDataReceived(buffer,size);
  66. }
  67. }catch(IOExceptione){
  68. e.printStackTrace();
  69. return;
  70. }
  71. }
  72. });
  73. }
  74. voidonDataReceived(finalbyte[]buffer,finalintsize){
  75. runOnUiThread(newRunnable(){
  76. publicvoidrun(){
  77. if(mReception!=null){
  78. mReception.append(newString(buffer,0,size));
  79. }
  80. }
  81. });
  82. }
  83. }

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

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

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


然后进入adb shell

cd /dev

chmod 777 ttyS2

运行后结果:

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

然后运行程序:

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

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


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

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

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

更多相关文章

  1. Android(安卓)SharedPreferences总结及优化
  2. Android开发,ArcGis for Android的一些常见操作
  3. 【源码分享下载】每日更新之Android应用源码比较不错的新闻客户
  4. 【Android(安卓)界面效果9】9patch图片
  5. Android(安卓)创建及调用自己的 ContentProvider
  6. Android(安卓)Jetpack系列——ViewModel源码分析
  7. Android应用程序启动过程——Launcher源码分析
  8. Android中AIDL实现进程通信(附源码下载)
  9. Android(安卓)网络图片加载

随机推荐

  1. HttpURLConnection类的保持session会话
  2. Android(安卓)MediaStore检索视频并播放
  3. MULTI-INTERFACE CONNECTIVITY ON ANDROI
  4. android 自定义PickerView 日期滑动选择
  5. 对android:textAlignmentStyle 属性参数
  6. Android(安卓)Material Design初步认识
  7. MediaPlayer的使用 带有seekBar
  8. Android(安卓)Studio忽略文件配置
  9. android Watchdog 实现剖析
  10. ADT 0.9下载