zoukankan      html  css  js  c++  java
  • android串口编程

    最近在Android项目中要使用到串口编程,开始的时候为了省事,直接把以前在linux下用纯C写得串口程序封装成so库,再在JNI中调用so库,一点也没有问题。

    虽说没有什么问题,总觉得在JAVA中使用纯C实现串口所有的操作很像是在“挂羊头卖狗肉”,而且也有点繁琐,想说JAVA应该把这些东西直接封装成API,于是在网上查资料,想找到类似于windows下的CreateFile的API接口,未果。

    还好JAVA之中有个FileDescriptor类,可以把串口当作一个FileDescriptor,打开之后操作FileDescriptor就相当于操作串口。

    不管是windows、linux、或是android操作系统,串口编程无非是以下几步:

    1. 打开串口
    2. 串口配置
    3. 串口操作(读写)
    4. 关闭串口

    这是典型的流驱动设备,操作很简单,在C语言里无非就是open、write、read、close几个操作就能轻松搞定的东西。这跟文件的输入输出流其实是一个概念,所以在windows中可以用CreateFile操作串口,同样的道理,在java中可以使用FileDescriptor操作串口。

    下面是android 串口编程的具体步骤,先创建一个串口类SerialPort:

    [java] view plain copy
     
    1. private static final String TAG = "SerialPort";  
    2.   
    3. /* 
    4.  * Do not remove or rename the field mFd: it is used by native method close(); 
    5.  */  
    6. private FileDescriptor mFd;  
    7. private FileInputStream mFileInputStream;  
    8. private FileOutputStream mFileOutputStream;  
    9.   
    10. public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {  
    11.   
    12.     /* Check access permission */  
    13.     if (!device.canRead() || !device.canWrite()) {  
    14.         try {  
    15.             /* Missing read/write permission, trying to chmod the file */  
    16.             Process su;  
    17.             su = Runtime.getRuntime().exec("/system/bin/su");  
    18.             String cmd = "chmod 666 " + device.getAbsolutePath() + " "  
    19.                     + "exit ";  
    20.             su.getOutputStream().write(cmd.getBytes());  
    21.             if ((su.waitFor() != 0) || !device.canRead()  
    22.                     || !device.canWrite()) {  
    23.                 throw new SecurityException();  
    24.             }  
    25.         } catch (Exception e) {  
    26.             e.printStackTrace();  
    27.             throw new SecurityException();  
    28.         }  
    29.     }  
    30.   
    31.     mFd = open(device.getAbsolutePath(), baudrate, flags);  
    32.     if (mFd == null) {  
    33.         Log.e(TAG, "native open returns null");  
    34.         throw new IOException();  
    35.     }  
    36.     mFileInputStream = new FileInputStream(mFd);  
    37.     mFileOutputStream = new FileOutputStream(mFd);  
    38. }  
    39.   
    40. // Getters and setters  
    41. public InputStream getInputStream() {  
    42.     return mFileInputStream;  
    43. }  
    44.   
    45. public OutputStream getOutputStream() {  
    46.     return mFileOutputStream;  
    47. }  
    48.   
    49. // JNI  
    50. private native static FileDescriptor open(String path, int baudrate, int flags);  
    51. public native void close();  
    52. static {  
    53.     System.loadLibrary("serial_port");  
    54. }  


    这个类中主要干了两件事情:
    创建了打开串口和关闭串口的本地方法

    [java] view plain copy
     
    1. private native static FileDescriptor open(String path, int baudrate, int flags);  
    2. public native void close();  

    关联串口的文件描述符(FileDescriptor),并把文件的输入输出流与之关联,在代码之中

    [java] view plain copy
     
    1. private FileDescriptor mFd;  
    2. private FileInputStream mFileInputStream;  
    3. private FileOutputStream mFileOutputStream;  
    4.   
    5. mFd = open(device.getAbsolutePath(), baudrate, flags);  
    6. mFileInputStream = new FileInputStream(mFd);  
    7. mFileOutputStream = new FileOutputStream(mFd);  

    这段代码的意思就是把串口打开,并取得串口的输入输出操作(读写操作)。

    到了这里,有个问题,如何打开串口?

    代码上可知,打开串口的操作用的是一个本地方法open,那只能在JNI中实现这个接口,下面是jni中的实现:

    [cpp] view plain copy
     
    1. static speed_t getBaudrate(jint baudrate)  
    2. {  
    3.     switch(baudrate) {  
    4.     case 0: return B0;  
    5.     case 50: return B50;  
    6.     case 75: return B75;  
    7.     case 110: return B110;  
    8.     case 134: return B134;  
    9.     case 150: return B150;  
    10.     case 200: return B200;  
    11.     case 300: return B300;  
    12.     case 600: return B600;  
    13.     case 1200: return B1200;  
    14.     case 1800: return B1800;  
    15.     case 2400: return B2400;  
    16.     case 4800: return B4800;  
    17.     case 9600: return B9600;  
    18.     case 19200: return B19200;  
    19.     case 38400: return B38400;  
    20.     case 57600: return B57600;  
    21.     case 115200: return B115200;  
    22.     case 230400: return B230400;  
    23.     case 460800: return B460800;  
    24.     case 500000: return B500000;  
    25.     case 576000: return B576000;  
    26.     case 921600: return B921600;  
    27.     case 1000000: return B1000000;  
    28.     case 1152000: return B1152000;  
    29.     case 1500000: return B1500000;  
    30.     case 2000000: return B2000000;  
    31.     case 2500000: return B2500000;  
    32.     case 3000000: return B3000000;  
    33.     case 3500000: return B3500000;  
    34.     case 4000000: return B4000000;  
    35.     default: return -1;  
    36.     }  
    37. }  
    38.   
    39. /* 
    40.  * Class:     android_serialport_SerialPort 
    41.  * Method:    open 
    42.  * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; 
    43.  */  
    44. JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open  
    45.   (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)  
    46. {  
    47.     int fd;  
    48.     speed_t speed;  
    49.     jobject mFileDescriptor;  
    50.   
    51.     /* Check arguments */  
    52.     {  
    53.         speed = getBaudrate(baudrate);  
    54.         if (speed == -1) {  
    55.             /* TODO: throw an exception */  
    56.             LOGE("Invalid baudrate");  
    57.             return NULL;  
    58.         }  
    59.     }  
    60.   
    61.     /* Opening device */  
    62.     {  
    63.         jboolean iscopy;  
    64.         const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);  
    65.         LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);  
    66.         fd = open(path_utf, O_RDWR | flags);  
    67.         LOGD("open() fd = %d", fd);  
    68.         (*env)->ReleaseStringUTFChars(env, path, path_utf);  
    69.         if (fd == -1)  
    70.         {  
    71.             /* Throw an exception */  
    72.             LOGE("Cannot open port");  
    73.             /* TODO: throw an exception */  
    74.             return NULL;  
    75.         }  
    76.     }  
    77.   
    78.     /* Configure device */  
    79.     {  
    80.         struct termios cfg;  
    81.         LOGD("Configuring serial port");  
    82.         if (tcgetattr(fd, &cfg))  
    83.         {  
    84.             LOGE("tcgetattr() failed");  
    85.             close(fd);  
    86.             /* TODO: throw an exception */  
    87.             return NULL;  
    88.         }  
    89.   
    90.         cfmakeraw(&cfg);  
    91.         cfsetispeed(&cfg, speed);  
    92.         cfsetospeed(&cfg, speed);  
    93.   
    94.         if (tcsetattr(fd, TCSANOW, &cfg))  
    95.         {  
    96.             LOGE("tcsetattr() failed");  
    97.             close(fd);  
    98.             /* TODO: throw an exception */  
    99.             return NULL;  
    100.         }  
    101.     }  
    102.   
    103.     /* Create a corresponding file descriptor */  
    104.     {  
    105.         jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");  
    106.         jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");  
    107.         jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");  
    108.         mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);  
    109.         (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);  
    110.     }  
    111.   
    112.     return mFileDescriptor;  
    113. }  
    114.   
    115. /* 
    116.  * Class:     cedric_serial_SerialPort 
    117.  * Method:    close 
    118.  * Signature: ()V 
    119.  */  
    120. JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close  
    121.   (JNIEnv *env, jobject thiz)  
    122. {  
    123.     jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);  
    124.     jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");  
    125.   
    126.     jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");  
    127.     jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");  
    128.   
    129.     jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);  
    130.     jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);  
    131.   
    132.     LOGD("close(fd = %d)", descriptor);  
    133.     close(descriptor);  
    134. }  


    在Java_android_1serialport_1api_SerialPort_open接口中,能发现一些熟悉的东西:

    [cpp] view plain copy
     
    1. fd = open(path_utf, O_RDWR | flags);  
    2. tcgetattr(fd, &cfg)  
    3. cfmakeraw(&cfg);  
    4. cfsetispeed(&cfg, speed);  
    5. cfsetospeed(&cfg, speed);  
    [cpp] view plain copy
     
    1. ...  


    这些都是在linux下的串口编程,分别是打开串口设备节点,配置数据流控、波特率等,这些东西其实不是重点,重点是如何把打开的串口设备与FileDescriptor关联,即把fd封装到FileDescriptor中去:

    [cpp] view plain copy
     
    1. /* Create a corresponding file descriptor */  
    2. {  
    3.     jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");  
    4.     jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");  
    5.     jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");  
    6.     mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);  
    7.     (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);  
    8. }  
    9.   
    10. return mFileDescriptor;  

    上面的代码简单解释来说就是mFileDescriptor与fd关联并返回给JAVA层。

    至此,其实串口的JAVA封装已经完成,这个类其实已经可以完成串口的所有操作(打开,配置,读写,关闭),接下来的事情只是逻辑层面的事情了。

  • 相关阅读:
    消息中间件(一)MQ详解及四大MQ比较
    SIP协议
    PAT (Basic Level) Practice 1008 数组元素循环右移问题
    LeetCode-Algorithms 1. 两数之和
    PAT (Basic Level) Practice 1040 有几个PAT
    PAT (Basic Level) Practice 1023 组个最小数
    PAT (Basic Level) Practice 1021 个位数统计
    PAT (Basic Level) Practice 1007 素数对猜想
    PAT (Basic Level) Practice 1006 换个格式输出整数
    PAT (Basic Level) Practice 1004 成绩排名
  • 原文地址:https://www.cnblogs.com/cliuwei/p/5764788.html
Copyright © 2011-2022 走看看