【Android应用开发】-(19)Android 串口编程原理和实现方式(附源码)
提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:FileDescriptor。下面我分别对JNI、FileDescriptor以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。
串口编程需要了解的基本知识点:对于串口编程,我们只需对串口进行一系列的设置,然后打开串口,这些操作我们可以参考串口调试助手的源码进行学习。在Java中如果要实现串口的读写功能只需操作文件设备类:FileDescriptor即可,其他的事都由驱动来完成不用多管!当然,你想了解,那就得看驱动代码了。这里并不打算对驱动进行说明,只初略阐述应用层的实现方式。
(一)JNI:
关于JNI的文章网上有很多,不再多做解释,想详细了解的朋友可以查看云中漫步的技术文章,写得很好,分析也很全面,那么在这篇拙文中我强调3点:
1、如何将编译好的SO文件打包到APK中?(方法很简单,直接在工程目录下新建文件夹 libs/armeabi,将SO文件Copy到此目录即可)
2、命名要注意的地方?(在编译好的SO文件中,将文件重命名为:libfilename.so即可。其中filename.so是编译好后生成的文件)
3、MakeFile文件的编写(不用多说,可以直接参考package/apps目录下用到JNI的相关项目写法)
这是关键的代码:
- <span style="font-size:18px;"> 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;
- }
- }
- </span>
(二)FileDescritor:
文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主要实际用途是创建一个包含该结构的FileInputStream
或FileOutputStream
。这是API的描述,不太好理解,其实可简单的理解为:FileDescritor就是对一个文件进行读写。
(三)实现串口通信细节
1) 建工程:SerialDemo包名:org.winplus.serial,并在工程目录下新建jni和libs两个文件夹和一个org.winplus.serial.utils,如下图:
2) 新建一个类:SerialPortFinder,添加如下代码:
- <span style="font-size:18px;">package org.winplus.serial.utils;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.LineNumberReader;
- import java.util.Iterator;
- import java.util.Vector;
- import android.util.Log;
- public class SerialPortFinder {
- private static final String TAG = "SerialPort";
- private Vector<Driver> mDrivers = null;
- public class Driver {
- public Driver(String name, String root) {
- mDriverName = name;
- mDeviceRoot = root;
- }
- private String mDriverName;
- private String mDeviceRoot;
- Vector<File> mDevices = null;
- public 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;
- }
- }
- 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();