文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android串口使用2之使用Google官方库android-serialport-api

2022-06-06 13:54

关注

Google官方库android-serialport-api

GitHub:android-serialport-api
此库版本太旧了,而且还不是AS工程,也不支持奇偶检验、数据位和停止位设定。

想要这个库支持奇偶检验、数据位和停止位设定也很简单

修改SerialPort.h和SerialPort.c两个文件
/////////////////////////SerialPort.h/////////////////////////

JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
  (JNIEnv *, jclass, jstring, jint, jint, jint, jint);
/////////////////////////SerialPort.c/////////////////////////

JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
  (JNIEnv *env, jclass thiz, jstring path,  jint baudrate, jint parity, jint dataBits, jint stopBits)
{
    int fd;
    int flags;
    speed_t speed;
    jobject mFileDescriptor;
    flags = 0;
    
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            
            LOGE("Invalid baudrate");
            return NULL;
        }
        if (parity  2) {
            LOGE("Invalid parity");
            return NULL;
        }
        if (dataBits  8) {
            LOGE("Invalid dataBits");
            return NULL;
        }
        if (stopBits  2) {
            LOGE("Invalid stopBit");
            return NULL;
        }
    }
	
	{
        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)
        {
            
            LOGE("Cannot open port");
            
            return NULL;
        }
	}
	
	{
        struct termios cfg;
        LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
            LOGE("tcgetattr() failed");
            close(fd);
            
            return NULL;
        }
        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);
        
        switch (parity) {
            case 0:
                cfg.c_cflag &= ~PARENB;    //无奇偶校验
                break;
            case 1:
                cfg.c_cflag |= (PARODD | PARENB);   //奇校验
                break;
            case 2:
                cfg.c_iflag &= ~(IGNPAR | PARMRK); // 偶校验
                cfg.c_iflag |= INPCK;
                cfg.c_cflag |= PARENB;
                cfg.c_cflag &= ~PARODD;
                break;
            default:
                cfg.c_cflag &= ~PARENB;
                break;
        }
        switch (dataBits) {
            case 5: cfg.c_cflag |= CS5; break;
            case 6: cfg.c_cflag |= CS6; break;
            case 7: cfg.c_cflag |= CS7; break;
            case 8: cfg.c_cflag |= CS8; break;
            default: cfg.c_cflag |= CS8; break;
        }
        switch (stopBits) {
            case 1: cfg.c_cflag &= ~CSTOPB; break;
            case 2: cfg.c_cflag |= CSTOPB; break;
            default:cfg.c_cflag &= ~CSTOPB; break;
        }
        if (tcsetattr(fd, TCSANOW, &cfg))
        {
            LOGE("tcsetattr() failed");
            close(fd);
            
            return NULL;
        }
	}
	
	{
		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V");
		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
	}
	return mFileDescriptor;
}
修改SerialPort.java文件,将原来的构造方法修改成下面的

    public SerialPort(File device, int baudrate, int parity, int dataBits, int stopBits) throws SecurityException, IOException {
        
        if (!device.canRead() || !device.canWrite()) {
            try {
                
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + 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();
            }
        }
        mFd = open(device.getAbsolutePath(), baudrate, parity, dataBits, stopBits);
        if (mFd == null) {
            Log.e(TAG, "native open returns null");
            throw new IOException();
        }
        mFileInputStream = new FileInputStream(mFd);
        mFileOutputStream = new FileOutputStream(mFd);
    }

还有JNI的open方法

private native static FileDescriptor open(String path, int baudrate, int parity, int dataBits, int stopBits);
如何使用呢?

我用的是Android Studio 3.5,在新建项目的时候,是看不到Include C++ Support这个选项的,但也无妨,无论是新建的项目还是已经现有的项目都可以按照下面的步骤完成配置。

检查ndk配置
在这里插入图片描述

NDK(Native Develop Kit),Android NDK 是一套允许您使用原生代码语言(例如C和C++) 实现部分应用的工具集。在开发某些类型应用时,这有助于您重复使用C/C++语言编写的代码库。

如果还没有下载Android NDK的可以借助Android Studio下载或者网上找资源下载,比如官方网站https://developer.android.google.cn/ndk/downloads/。哪种方式方便快捷,就可以用哪种。
Android Studio找到Settings,或者使用快捷键Ctrl + Alt + S。搜索Android SDK,找到SDK Tools,最下面就是NDK的版本信息,勾选上之后,点击Apply,最后点OK。
NDK下载完成,按照上面的步骤配置好NDK路径。
在这里插入图片描述

将整个工程目录切换至Project视图,默认是Android视图。找到src/main,右键main文件夹,选择New,找到Folder,选择JNI Folder
在这里插入图片描述

JNI(Java Native Interface),即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。

将官方库android-serialport-api下载下来,找到jni文件夹,将里面所有的文件复制到上面新建的jni文件夹里。如果想要支持奇偶检验、数据位和停止位设定,按照我上面的代码修改就可以实现。
在这里插入图片描述

配置app/build.gradle
android {
    ...
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
}

4.最后一步,在src/main/java根目录下,新建一个文件夹android_serialport_api,名字千万不要改哦,因为这个名字链接着这个api库,改变之后,java代码无法调用它,会报错的。
新建一个SerialPort类或者将官方原来的SerialPort.java文件复制过去,如果有需要,也可以将SerialPortFinder.java这个文件复制过去,这个是查找串口驱动文件路径。

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 parity, int dataBits, int stopBits) throws SecurityException, IOException {
        
        if (!device.canRead() || !device.canWrite()) {
            try {
                
                Process su;
                su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + 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();
            }
        }
        mFd = open(device.getAbsolutePath(), baudrate, parity, dataBits, stopBits);
        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 parity, int dataBits, int stopBits);
	public native void close();
	static {
		System.loadLibrary("serial_port");
	}
}

到这里,Google官方库android-serialport-api基本配置完成了,剩下的就是写工具类进行调用了,实现打开串口、监听数据、发送数据和关闭串口了。

如果不知道如何下手的,我下面写了SerialPortHelper,可以参考参考下。

public abstract class SerialPortHelper
{
    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;
    private boolean _isOpen = false;
    //串口配置
    private String sPort;
    private int iBaudRate;
    private int parity = 0; //默认无校验
    private int dataBits = 8;//默认数据位8位
    private int stopBits = 1;//默认停止位1位
    public SerialPortHelper(String sPort, int iBaudRate) {
        this.sPort = sPort;
        this.iBaudRate = iBaudRate;
    }
    public SerialPortHelper(String sPort, int iBaudRate, int parity, int dataBits, int stopBits) {
        this.sPort = sPort;
        this.iBaudRate = iBaudRate;
        this.parity = parity;
        this.dataBits = dataBits;
        this.stopBits = stopBits;
    }
    public void open() throws SecurityException, IOException {
        this.mSerialPort = new SerialPort(new File(sPort), iBaudRate, parity, dataBits, stopBits);
        this.mOutputStream = this.mSerialPort.getOutputStream();
        this.mInputStream = this.mSerialPort.getInputStream();
        this.mReadThread = new ReadThread();
        this.mReadThread.start();
        this._isOpen = true;
    }
    public void close() {
        if (this.mReadThread != null) {
            this.mReadThread.interrupt();
        }
        if (this.mSerialPort != null) {
            this.mSerialPort.close();
            this.mSerialPort = null;
        }
        this._isOpen = false;
    }
    public void send(byte[] bOutArray) {
        try {
            this.mOutputStream.write(bOutArray);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void sendHex(String sHex) {
        byte[] bOutArray = ByteUtil.HexToByteArr(sHex);
        send(bOutArray);
    }
    public void sendTxt(String sTxt) {
        byte[] bOutArray = sTxt.getBytes();
        send(bOutArray);
    }
    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                try {
                    if (SerialPortHelper.this.mInputStream == null) {
                        return;
                    }
                    int available = SerialPortHelper.this.mInputStream.available();
                    if (available > 0) {
                        byte[] buffer = new byte[available];
                        int size = SerialPortHelper.this.mInputStream.read(buffer);
                        if (size > 0) {
                            SerialPortHelper.this.onDataReceived(buffer, size);
                        }
                    } else {
                        SystemClock.sleep(50);
                    }
                } catch (Throwable e) {
                    Log.e("error", e.getMessage());
                    return;
                }
            }
        }
    }
    public boolean isOpen() {
        return this._isOpen;
    }
    protected abstract void onDataReceived(byte[] buffer, int size);
}

初始化串口


private SerialPortHelper mSerialPortHelper;
mSerialPortHelper = new SerialPortHelper("dev/ttyS4", 9600) {
            @Override
            protected void onDataReceived(byte[] buffer, int size) {
                 //todo 业务处理,解析接收的数据
            }
};

打开串口

mSerialPortHelper.open();

发送数据

mSerialPortHelper.send(byte[]/Hex String/Txt String);

关闭串口

mSerialPortHelper.close();

如果觉得上面的步骤比较繁琐,开发效率不高的话,可以试试别人造的轮子。
快速使用Android串口


作者:Steven Jon


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯