android 串口編程


    最近在做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
  1.   
[cpp]  view plain copy
  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 plain copy
  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 plain copy
  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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM