Android开发笔记——串口通讯

这是参考谷歌官网的开源项目和网上的串口通讯写的一个简单的demo。刚刚接触NDK编程以及串口通讯,可能很多写的不是很好,不喜勿喷。
大多数的的代码都在这个Google串口开源项目:https://code.google.com/p/android-serialport-api/
先新建一个带有c的项目。
这里写图片描述

勾选C++然后一路Next新建。
在编写代码前或者安装该项目前先将该项目使用的NDk换为NDK14版本,不然可能会出现串口打不开问题(本人现在不知道为什么,可能是环境配置问题),没有NDK14的先下载(在https://developer.android.google.cn/ndk/downloads/older_releases.html下载),然后在Android视图下,app——OPen Module Settings——SDK Location中重新选择NDK的路径。
将MainActivity中

static {    
System.loadLibrary("native-lib");
}​

的删除,在创建SerialPort类中添加

//在应用程序启动时,用于加载serial_port库
static {  
  System.loadLibrary("serial_port");//serial_port根据后面的CMakeLists.txt内容写 。
}​ 

将创建项目时的Cpp文件删除,再新建一个jni文件夹,然后在该文件夹中新建serial_port的.c文件 调用串口的读写主要就是open和close方法,类似于文件读写。
这里写图片描述
按照下面代码中的注释修改后,左边的绿红箭头表示java中的open方法和c中的open方法建立了联系。

#include <jni.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.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 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;
        default: return -1;
    }
}

/*
 * Java_com_smartlab_blue_serialdemo_SerialPort_open中的com_smartlab_blue_serialdemo是包名
 * SerialPort_open是SerialPort类中的private native static FileDescriptor open(...)方法,需要用"_"连接不能用"."
 * 有的可能直接在SerialPort类中进行Alt+Enter就可以在这个文件里面创建一个open方法,只需把这个类中我写的方法名换一下就行了,
 * 有的就需要按照我上面说的方法改方法名字。   下面的close方法配置一样
 * */
JNIEXPORT jobject JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_open(JNIEnv *env, jclass type, jstring path, jint baudrate, jint flags) {
    int fd;
    speed_t speed;      //引用#include <termios.h>
    jobject mFileDescriptor;

    /* Check arguments */
    {
        speed=getBaudrate(baudrate);
        if (speed==-1){

            LOGE("Invalid baudrate");
            return NULL;
        }
    }

    /* 开启 设备 */
    {
        jboolean iscapy;
        const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscapy);
        LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
        fd=open(path_utf,O_RDWR | flags | O_NDELAY);//O_RDWR 引入     #include <fcntl.h>
        LOGD("open() fd = %d", fd);
        (*env)->ReleaseStringUTFChars(env, path, path_utf );
        if (fd==-1){
            /* 引发异常 */
            LOGE("Cannot open port");
            /* TODO: throw an exception */
            return NULL;
        }

    }

    /* 配置 设备 */
    {
        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;
        }
    }

    /* 创建一个相应的文件描述符 */
    {
        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;
}

JNIEXPORT void JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_close(JNIEnv *env, jobject instance) {
    jclass SerialPortClass = (*env)->GetObjectClass(env, instance);
    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, instance, mFdID);
    jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

    LOGD("close(fd = %d)", descriptor);
    close(descriptor);
}

先将app ​ 的build,gradle中的 以下内容 注释

//externalNativeBuild {  
 // cmake {     
//       path "CMakeLists.txt" 
//   }
//}​

在创建该项目时生成的的CMakeLists.txt中进行以下修改
这里写图片描述
add_library( # Sets the name of the library.
serial_port​ //双引号中写的是这里的名字,在应用程序启动时,用于加载serial_port库

                # Provides a relative path to your source file(s).
               src/main/jni/serial_port.c  //你新建的.c文件的路径和名字。

target_link_libraries( # Specifies the target library.
serial_port //双引号中写的是这里的名字

app ​ 的build,gradle中添加一下内容
defaultConfig {
applicationId “com.smartlab.blue.serialdemo”
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName “1.0”
testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”
//添加

        ndk {
            moduleName "serial_port" ldLibs "log", "z", "m"
        }

}​

切换成Android视图在app上右键,然后选择Link C++ Project with Gradle
这里写图片描述

这里写图片描述

第一个选择CMake,路径选择刚刚修改的CMakeLists.txt,然后OK。
注意:有时候可能无法开启Android板上串口读写权限,需要自己去手动的添加权限
在调试中如果大家没有串口硬件的话可以使用PC机+模拟器完成调试实验。具体操作方法:
1、打开cmd进入到android开发的sdk目录下的tools文件夹下;
2、执行命令emulator @(你自己的模拟器的名字) -qemu -serial COM3(COM3是你的PC的一个串口通过命令挂载到你的模拟器上,当然你也可以是COM1跟你电脑对应);例:我的模拟器叫123,我给模拟器挂载的电脑上的串口是COM3,则执行:emulator @123 -qemu -serial COM3
这样你会看到模拟器已经启动,这时候你将程序部署上运行即可。
如果用程序打开串口,提示没有读写权限。应该在命令提示符下用linux命令赋予读写的权限: 进入shell:adb shell 进入设备目录:#cd dev 修改权限:#chmod 777 ttyS2即可。(这个我已经写入程序内,无需在添加,如果LOG日志打印显示无权限,那就只能你手动在执行一边,再解说以下这个进入shell,这个是adb shell,你的命令执行的目录下必须有adb shell.exe多的不解释了)
修改的地方基本上就上面那些,推荐一遍博客http://blog.csdn.net/qq_35071078/article/details/73065046,不知道串口的可以看下 。
下面是主要的代码

public class SerialPort {
    private static final String TAG = "SerialPort";

    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

    public SerialPort(File device, int baudrate, int flags)
            throws SecurityException, IOException {

        /* 检查是否又读写的权限 */
        if (!device.canRead() || !device.canWrite()) {
            /* 如果没有读写的权限, 尝试使用chmod来给文件权限 */
            Process su = null;
            try {

                su = Runtime.getRuntime().exec("/system/xbin/su");
                //su = Runtime.getRuntime().exec("su");
                String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
                        + "exit\n";
                su.getOutputStream().write(cmd.getBytes());
                if ((su.waitFor() != 0) || !device.canRead()
                        || !device.canWrite()) {
                    Log.e(TAG, "su.waitFor == " + su.waitFor() + " device.canRead = " + device.canRead() + " device.canWrite = " + device.canWrite());
                    throw new SecurityException();
                }
            } catch (Exception e) {
                Log.e(TAG, "Exception == " + e.toString());
                e.printStackTrace();
                throw new SecurityException();
            } finally {
                if (su != null) {
                    su.destroy();
                }
            }
        }

        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);

        Log.e(TAG, "****************** 串 口 打 开 成 功 !**********************");
    }

    // Getters and setters
    public InputStream getInputStream() {
        return mFileInputStream;
    }

    public OutputStream getOutputStream() {
        return mFileOutputStream;
    }


    /**
     * jni方法 打开串口
     *
     * @param path     串口文件的路径
     * @param baudrate 波特率
     * @param flags    端口
     * @return 文件描述符
     */
    private native static FileDescriptor open(String path, int baudrate,
                                              int flags);
    /**
     * jni方法 关闭串口
     */
    public native void close();

    //在应用程序启动时,用于加载serial_port库
    static {
        System.loadLibrary("serial_port");
    }

}


public abstract class SerialPortActivity  extends Activity {

    protected Application mApplication;
    protected SerialPort mSerialPort;
    protected OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;

    private class ReadThread extends Thread {

        @Override
        public void run() {
            super.run();
            while(!isInterrupted()) {
                int size;
                try {
                    byte[] buffer = new byte[64];
                    if (mInputStream == null) return;
                    Log.e("读取数据的线程","233333");
                    size = mInputStream.read(buffer);
                    if (size > 0) {
                        onDataReceived(buffer, size);
                    }
                } 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 DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                SerialPortActivity.this.finish();
            }
        });
        b.show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mApplication = (Application) getApplication();
        try {
            mSerialPort = mApplication.getSerialPort();
            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(final byte[] buffer, final int size);

    @Override
    protected void onDestroy() {
        if (mReadThread != null)
            mReadThread.interrupt();
        mApplication.closeSerialPort();
        mSerialPort = null;
        super.onDestroy();
    }
}


public class SerialPortFinder {
    private static final String TAG = "SerialPort";

    private Vector<Driver> mDrivers = null;

    private class Driver {
        private Driver(String name, String root) {
            mDriverName = name;
            mDeviceRoot = root;
        }

        private String mDriverName;
        private String mDeviceRoot;
        Vector<File> mDevices = null;

        private Vector<File> getDevices() {
            if (mDevices == null) {
                mDevices = new Vector<File>();
                File dev = new File("/dev");
                File[] files = dev.listFiles();
                int i;
                for (i = 0; i < files.length; i++) {
                    if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
                        Log.d(TAG, "Found new device: " + files[i]);
                        mDevices.add(files[i]);
                    }
                }
            }
            return mDevices;
        }

        public String getName() {
            return mDriverName;
        }
    }

    private Vector<Driver> getDrivers() throws IOException {
        if (mDrivers == null) {
            mDrivers = new Vector<Driver>();
            LineNumberReader r = new LineNumberReader(new FileReader(
                    "/proc/tty/drivers"));
            String l;
            while ((l = r.readLine()) != null) {
                // Issue 3:
                // Since driver name may contain spaces, we do not extract
                // driver name with split()
                String drivername = l.substring(0, 0x15).trim();
                String[] w = l.split(" +");
                if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
                    Log.d(TAG, "Found new driver " + drivername + " on "
                            + w[w.length - 4]);
                    mDrivers.add(new Driver(drivername, w[w.length - 4]));
                }
            }
            r.close();
        }
        return mDrivers;
    }

    public String[] getAllDevices() {
        Vector<String> devices = new Vector<String>();
        // Parse each driver
        Iterator<Driver> itdriv;
        try {
            itdriv = getDrivers().iterator();
            while (itdriv.hasNext()) {
                Driver driver = itdriv.next();
                Iterator<File> itdev = driver.getDevices().iterator();
                while (itdev.hasNext()) {
                    String device = itdev.next().getName();
                    String value = String.format("%s (%s)", device,
                            driver.getName());
                    devices.add(value);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return devices.toArray(new String[devices.size()]);
    }

    public String[] getAllDevicesPath() {
        Vector<String> devices = new Vector<String>();
        // Parse each driver
        Iterator<Driver> itdriv;
        try {
            itdriv = getDrivers().iterator();
            while (itdriv.hasNext()) {
                Driver driver = itdriv.next();
                Iterator<File> itdev = driver.getDevices().iterator();
                while (itdev.hasNext()) {
                    String device = itdev.next().getAbsolutePath();
                    devices.add(device);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return devices.toArray(new String[devices.size()]);
    }
}

源代码:http://download.csdn.net/download/qq_36462112/10161978

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值