关于串口编程的总结2

96
世外大帝
2019.05.18 15:34 字数 933

我们的程序收发十六进制命令,假设采用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];
}
Android手记
Gupao