美文网首页程序员
关于串口编程的总结

关于串口编程的总结

作者: 世外大帝 | 来源:发表于2019-05-17 17:04 被阅读0次

    这部分源码一直没变过,不过现在Android Studio已经支持Cmake方式了,所以不需要单独编译动态链接库了,可以直接修改Cmake文件和C文件

    说明

    开源库: https://github.com/cepr/android-serialport-api

    1. 参照AS的带C方式创建cpp文件夹,记得把配置也加上。
    2. 把开源库中的 SerialPort.cSerialPort.h 拷贝下来,放入cpp文件夹中
    3. 创建CMake文件,我这里直接拷贝的AS的 CMakeLists.txt

    经过以上3步,基本工作就做完了,这时候你的cpp库有3个文件: CMakeLists.txt 文件, c文件和头文件。

    然后剩下的就是定义调用串口的文件了

    修改代码

    修改SerialPort头文件和C代码

    以下是 SerialPort.c 文件,除了输出的 JNICALL 基本没啥要改的

    • 格式是Java_包名_类名_方法名 ,用下划线隔开
    • 下面要创建的java类及方法一定要严格对应
    • 头文件和C代码的 JNICALL 保持同名
    /*
     * Copyright 2009-2011 Cedric Priscal
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    #include <termios.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <jni.h>
    
    #include "SerialPort.h"
    
    #include "android/log.h"
    static const char *TAG="serial_port";
    #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
    #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
    #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
    
    static speed_t getBaudrate(jint baudrate)
    {
      switch(baudrate) {
      case 0: return B0;
      case 50: return B50;
      case 75: return B75;
      case 110: return B110;
      case 134: return B134;
      case 150: return B150;
      case 200: return B200;
      case 300: return B300;
      case 600: return B600;
      case 1200: return B1200;
      case 1800: return B1800;
      case 2400: return B2400;
      case 4800: return B4800;
      case 9600: return B9600;
      case 19200: return B19200;
      case 38400: return B38400;
      case 57600: return B57600;
      case 115200: return B115200;
      case 230400: return B230400;
      case 460800: return B460800;
      case 500000: return B500000;
      case 576000: return B576000;
      case 921600: return B921600;
      case 1000000: return B1000000;
      case 1152000: return B1152000;
      case 1500000: return B1500000;
      case 2000000: return B2000000;
      case 2500000: return B2500000;
      case 3000000: return B3000000;
      case 3500000: return B3500000;
      case 4000000: return B4000000;
      default: return -1;
      }
    }
    
    /*
     * Class:     android_serialport_SerialPort
     * Method:    open
     * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
     */
    JNIEXPORT jobject JNICALL Java_com_jiataoyuan_serialport_SerialPort_open
      (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
    {
      int fd;
      speed_t speed;
      jobject mFileDescriptor;
    
      /* Check arguments */
      {
          speed = getBaudrate(baudrate);
          if (speed == -1) {
              /* TODO: throw an exception */
              LOGE("Invalid baudrate");
              return NULL;
          }
      }
    
      /* Opening device */
      {
          jboolean iscopy;
          const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
          LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
          fd = open(path_utf, O_RDWR | flags);
          LOGD("open() fd = %d", fd);
          (*env)->ReleaseStringUTFChars(env, path, path_utf);
          if (fd == -1)
          {
              /* Throw an exception */
              LOGE("Cannot open port");
              /* TODO: throw an exception */
              return NULL;
          }
      }
    
      /* Configure device */
      {
          struct termios cfg;
          LOGD("Configuring serial port");
          if (tcgetattr(fd, &cfg))
          {
              LOGE("tcgetattr() failed");
              close(fd);
              /* TODO: throw an exception */
              return NULL;
          }
    
          cfmakeraw(&cfg);
          cfsetispeed(&cfg, speed);
          cfsetospeed(&cfg, speed);
    
          if (tcsetattr(fd, TCSANOW, &cfg))
          {
              LOGE("tcsetattr() failed");
              close(fd);
              /* TODO: throw an exception */
              return NULL;
          }
      }
    
      /* Create a corresponding file descriptor */
      {
          jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
          jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
          jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
          mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
          (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
      }
    
      return mFileDescriptor;
    }
    
    /*
     * Class:     cedric_serial_SerialPort
     * Method:    close
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_com_jiataoyuan_serialport_SerialPort_close
      (JNIEnv *env, jobject thiz)
    {
      jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
      jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
    
      jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
      jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
    
      jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
      jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
    
      LOGD("close(fd = %d)", descriptor);
      close(descriptor);
    }
    
    

    修改CMake文件

    • add_library

      • 1参设置库名(你要在java中加载的库名称),
      • 2参设置共享(基本都是SHARE),
      • 3参设置源文件的相对路径(因为在同一目录下,直接写文件名即可)
    • find_library

      这个东西我也没搞明白,看说明是用来搜索库路径的,但是因为有默认值,所以只需要指定NDK就行了,而且删了也不影响什么

    • target_link_libraries

      • 1参指定目标库,就是上面设置的库
      • 2参用来连接到日志库,已经包含在nkd中了,如果你的 find_library 默认的话,这里也默认就行了,如果 find_library 删掉的话,这里也删掉
    # For more information about using CMake with Android Studio, read the
    # documentation: https://d.android.com/studio/projects/add-native-code.html
    
    # Sets the minimum version of CMake required to build the native library.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    # Creates and names a library, sets it as either STATIC
    # or SHARED, and provides the relative paths to its source code.
    # You can define multiple libraries, and CMake builds them for you.
    # Gradle automatically packages shared libraries with your APK.
    
    add_library( # Sets the name of the library.
            SerialPort
            # Sets the library as a shared library.
            SHARED
            # Provides a relative path to your source file(s).
            SerialPort.c)
    
    
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because CMake includes system libraries in the search path by
    # default, you only need to specify the name of the public NDK library
    # you want to add. CMake verifies that the library exists before
    # completing its build.
    
    find_library( # Sets the name of the path variable.
            log-lib
    
            # Specifies the name of the NDK library that
            # you want CMake to locate.
            log)
    
    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.
    
    target_link_libraries( # Specifies the target library.
            SerialPort
            # Links the target library to the log library
            # included in the NDK.
            ${log-lib})
    

    创建SerialPort

    这个文件我改了一点,变化不大,你也可以直接用开源库中的,主要内容如下:

    • 获取串口:改动部分为将可读写改为可读写可执行
    • 获取读写流
    • 设置jni方法 ,打开和关闭,其中打开方法是私有方法
    /*
     * Copyright 2009 Cedric Priscal
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     * http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.jiataoyuan.serialport;
    
    import android.util.Log;
    
    import java.io.File;
    import java.io.FileDescriptor;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    public class SerialPort {
    
        private static final String TAG = "SerialPort";
    
        static {
            System.loadLibrary("SerialPort");
        }
    
        /*
         * Do not remove or rename the field mFd: it is used by native method close();
         */
        private FileDescriptor mFd; //文件描述
        private FileInputStream mFileInputStream;  // 输入流
        private FileOutputStream mFileOutputStream;  // 输出流
    
        /**
         * 获取串口
         *
         * @param device   设备
         * @param baudrate 波特率
         * @param flags    标志符
         * @throws SecurityException
         * @throws IOException
         */
        public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
    
            /* 检查访问权限 */
            if (!device.canRead() || !device.canWrite()) {
                try {
                    /* Missing read/write permission, trying to chmod the file */
                    Process su;
                    su = Runtime.getRuntime().exec("/system/bin/su");
                    String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"
                            + "exit\n";
                    su.getOutputStream().write(cmd.getBytes());
                    if ((su.waitFor() != 0) || !device.canRead()
                            || !device.canWrite()) {
                        throw new SecurityException();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new SecurityException();
                }
            }
    
            // 如果打不开返回null
            mFd = open(device.getAbsolutePath(), baudrate, flags);
            if (mFd == null) {
                Log.e(TAG, "native open returns null");
                throw new IOException();
            }
            mFileInputStream = new FileInputStream(mFd);
            mFileOutputStream = new FileOutputStream(mFd);
        }
    
        // Getters and setters
        public InputStream getInputStream() {
            return mFileInputStream;
        }
    
        public OutputStream getOutputStream() {
            return mFileOutputStream;
        }
    
        // JNI
        private native static FileDescriptor open(String path, int baudrate, int flags);
    
        public native void close();
    
    
    }
    
    

    封装SerialPortActivity

    为啥封装成这样子,因为我的串口调试助手,所以最后改成这样子了,通俗易懂

    /*
    * Copyright 2009 Cedric Priscal
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    
    package com.jiataoyuan.serialport;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.content.DialogInterface.OnClickListener;
    import android.os.Bundle;
    
    import com.jiataoyuan.dronetrack.R;
    import com.jiataoyuan.dronetrack.utils.MyConstantUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.security.InvalidParameterException;
    
    
    public abstract class SerialPortActivity extends Activity {
    
      protected SerialPort mSerialPort;
      protected OutputStream mOutputStream;
      private InputStream mInputStream;
      private ReadThread mReadThread;
    
      // 根据需要修改线程
      private class ReadThread extends Thread {
    
          @Override
          public void run() {
              super.run();
              int size;
              byte[] buffer = new byte[64];
              StringBuilder sb = new StringBuilder();
              while (!isInterrupted()) {
    
                  try {
    
                      if ((size = mInputStream.read(buffer)) != -1) {
                          sb.append(new String(buffer, 0, size));
                          onDataReceived(sb.toString());
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                      return;
                  }
              }
    
          }
      }
    
      // 打印错误消息弹框
      private void DisplayError(int resourceId) {
          AlertDialog.Builder b = new AlertDialog.Builder(this);
          b.setTitle("Error");
          b.setMessage(resourceId);
          b.setPositiveButton("OK", new OnClickListener() {
              public void onClick(DialogInterface dialog, int which) {
                  SerialPortActivity.this.finish();
              }
          });
          b.show();
      }
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
    
          try {
              if (null == mSerialPort) {
                  // 1参 串口 根据你的串口情况填入,如"/dev/ttyS4" , "/dev/ttyS1"
                  // 2参 波特率 根据约定填入,如19200 ,115200
                  // 3参 标志位 根据约定填入,没有填0
                  mSerialPort = new SerialPort(new File(MyConstantUtils.SerialPortPath), MyConstantUtils.baudrate, 0);
              }
              mOutputStream = mSerialPort.getOutputStream();
              mInputStream = mSerialPort.getInputStream();
    
              /* Create a receiving thread */
              mReadThread = new ReadThread();
              mReadThread.start();
          } catch (SecurityException e) {
              DisplayError(R.string.error_security);
          } catch (IOException e) {
              DisplayError(R.string.error_unknown);
          } catch (InvalidParameterException e) {
              DisplayError(R.string.error_configuration);
          }
      }
    
      // 定义接收的抽象方法
      protected abstract void onDataReceived(String buffer);
    
      // 发送消息
      public void sendMsg(byte[] msg) {
          try {
              mOutputStream = mSerialPort.getOutputStream();
              if (msg.length > 0) {
                  mOutputStream.write(msg);
                  mOutputStream.flush();
              }
    
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    
      // 关闭端口
      public void closeSerialPort() {
          if (mSerialPort != null) {
              mSerialPort.close();
              mSerialPort = null;
          }
      }
    
      @Override
      protected void onDestroy() {
          if (mReadThread != null)
              mReadThread.interrupt();
          closeSerialPort();
          mSerialPort = null;
          super.onDestroy();
      }
    }
    
    

    调用

    要调用的Activity需要继承 SerialPortActivity ,并实现 onDataReceived 方法

    • @Override
      protected void onDataReceived(String buffer) {
          mBuffer = buffer;
          Handler handler = new Handler(Looper.getMainLooper());
          handler.postDelayed(runnable, 100);
      
      }
      
    • private void send(String msg) {
          sendMsg(msg.getBytes());
      }
      
      private void send(byte[] msg) {
          sendMsg(msg);
          L.e(new String(msg));
      }
      
    • 开关串口都在 SerialPortActivity 的create和destroy方法中调用了,应用逻辑无需管理

    相关文章

      网友评论

        本文标题:关于串口编程的总结

        本文链接:https://www.haomeiwen.com/subject/dbboaqtx.html