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

关于串口编程的总结2

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

    我们的程序收发十六进制命令,假设采用16个字节,一个包头(1字节),一个控制(命令字1字节),一个具体命令(数据13字节),一个校验位(1字节),由于java本身对内部控制的应用较少,所以这个前后总共花费了五六个小时才搞定,特此记录一下。

    这个程序还是有一些缺点的,就是它并不是一次性的返回所有数据,我尝试了很多方式都不行,最后通过判断的方式只接受一条完整有效的命令。

    类型转换

    java的操作一般都是对字符串或数组的操作为主,这里的转换有两种方式:

    数组

    对数组的操作,就是定义一个16字节数组,然后对每一个字节单独赋值。

    优点很明显,就是可以准确的控制到每一个字节,校验比对都很方便

    缺点也同样明显,当命令较多时,编写不易,而且校验起来很麻烦。

    byte[] buffer = new byte[16];
    buffer[0] = (byte) 0xFD;
    ...
    

    字符串

    字符串是java中最常用的,优缺点也同样明显,但我还是用字符串了

    首先命令得是确定值,因为字符串的修改,并不如数组那样方便,为了方便,我们在每个字节中加个空格,方便查看:

    String commandStr = "FD E1 01 00 00 00 00 00 00 00 00 00 00 00 00 E2";
    

    然后就是把它转换为byte数组,但是字符串有个问题,就是每次读取的是一个char,比如FD,会被分成两个部分,那么在编写转换类的时候,就需要把它们合成一个。

    1. 一个字节是8位, 那么我们首先取得F,然后左移4位,放在高4位的位置,低4位归零,D通过转换后放在第四位的位置上,这样就合并为一个完整的字节了:高4位 0x0f0,低4位 0x ff
    2. 对位置来说,我们每次需要取得两位进行合并,所以每次需要索引到字符串中对应的位置,下面的代码中封装为toByte方法
    3. 将Byte数组转换为hexString就太特么方便了,一下就 体现出java那强大的特性
    // 十六进制字符串转二进制数组
    public static byte[] hexStringToByte(String hex) {
        hex = hex.replace(" ", "");
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }
    
    //获取char的索引
    private static int toByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
    
    // byte数组转换为十六进制字符串,用java提供的转换类即可
    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
    
    

    对SerialPortActivity的修改

    之前对串口的封装及代码已经在 关于串口编程的总结 中了

    对于read的无数次测试,最后我发现,还是没找到一次性读取的办法,如果有大神有好的办法可留言告诉我一声,万分感谢!!!

    基本每次都读取一个字符的东西,但是有个问题,就是并不是所有的都是一个字节,有可能超出1个字节,为了防止这种事情发生,最好限定每次只读一个字节

    size = mInputStream.read(buffer, 0, 1);
    

    对应用类的修改

    1. 为了防止越界,还是把抽象方法加上一个size比较好,虽然已经是稳定的1个字节了..
    2. 判断包头是否正确
    3. 拷贝数组到全局变量,并线性偏移
    @Override
    protected void onDataReceived(byte[] buffer, int size) {
        try {
    
            if ((mSize == 0) && (buffer[0] == (byte) 0xFD)) {
                System.arraycopy(buffer, 0, mBuffer, mSize, size);
                mSize++;
            } else if (mSize > 0) {
                System.arraycopy(buffer, 0, mBuffer, mSize, size);
                mSize++;
            } else {
                L.e("初始值不是FD " + ConvertUtils.bytesToHexString(buffer));
            }
    
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(runnable);
    
        } catch (ArrayIndexOutOfBoundsException e) {
            L.e("error: over 16 bit.");
        }
    
    }
    

    对于runnable来说,收到的消息同样要进行处理:

    1. 当runnable收到数据时,我们可预知命令大小是16个字节的,用全局size判断是否收取完整即可
    2. 包头 已经判断成功了,收取完整后通过约定的规则去判定校验位是否完整
    3. 对全局变量归零
    
    if (mSize == 16) {
        byte checkBit = (byte) (mBuffer[1] + mBuffer[2]);
        if (checkBit == mBuffer[15]) {
            // do something..
            L.e("校验成功");
    
        }
        String mString = ConvertUtils.bytesToHexString(mBuffer);
        L.e("command:" + mString);
    
        mSize = 0;
        mBuffer = new byte[16];
    }
    

    相关文章

      网友评论

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

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