Android开发笔记——串口通讯

来源:互联网 发布:空心软件测试平台 编辑:程序博客网 时间:2024/05/20 17:07

这是参考谷歌官网的开源项目和网上的串口通讯写的一个简单的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 JNICALLJava_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 JNICALLJava_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

原创粉丝点击