Android串口开发的一些事

基础知识

说到串口,那就有点嵌入式了,说点概念。

上位机:

上位机是指可以直接发出操控命令的设备

下位机:

下位机一般是指被操控的设备(例如单片机)

波特率

波特率串行每秒传输数据的速率,例如,波特率是9600那么它传输一位0或1的时间就是1/9600秒,也就是每秒能传9600bit。波特率越高,代表传输信号频率越高。相同的电缆,信号频率越高时,感抗越大,信号在传输线上的损耗也就越大。因此,波特率越高,传输距离越短。

比特和字节(bit&byte)

计算机就是各种电路的开关,每个开关自然就只有两种状态,开和关,在计算机中1表示的是开,0表示是关。而计算机最小的存储单位是字节,每个字节等于8比特(至于为啥自己查),那8比特用二进制表示当然就是0000 0000-1111 1111,那是不表达为十进制就是0-255呢?(ps 255 是由2的7次方减1来的 公式你懂的),当然不是,负数给狗吃了么☻。

原码、反码、补码

定律:
原码就是二进制本身 例如十进制的1等于0000 0001
对于正数来说 :
原码 = 反码 = 补码
对于负数来说 :
原码就是正数的符号位取1
反码 = 原码的非符号位取反(取反就是把1变成0 把0变成1)
补码 = 反码+1

一些小知识 两个16进制代表一个字节
异或:两者相等为0,不等为1
如a=a^b=1011^1001=0010

对于计算机来说,一个二进制的它的最高位是它的符号位,0代表正数,1代表负数,所以一个字节8比特范围区间应该是 1111 1111 - 0111 1111 也就是-127到127,你以为这就是对的么,其实也不是(我真的不是特意耍你)答案是-128到127,为什么呢,这就涉及到了计算机二进制的运算了。
对于计算机来说,假如要识别符号位来做加还是减,很麻烦,所以计算机只能做加法,例如1-1=1+(-1)。

计算机内部是用补码来存储一个数的,为什么呢,因为二进制的计算就是补码的加法,至于为啥用补码,举个例子1-1它的原码直接相加会等于 1000 0010 等于-2,肯定不科学,那反码呢,反码的1-1等于 1111 1111 转为原码 1000 0000 也就是 -0也不科学,补码就不一样了 补码的 1-1 等于1 0000 0000 超过8位的会被截掉,那结果就是0000 0000 转回原码还是0000 0000 那就正确了。而127加1二进制就会表示 1000 0000 负数么显然不科学,于是中央就决定1000 0000 就是-128了。所以128转字节会变成-128,也就是溢出了。

安卓的串口通讯

串口通讯顾名思义就是给串口发送或者接收数据,而串口的数据格式一般来说有
包头+数据+校验+包尾
或者
包头+数据长度+数据+校验值
等不同的格式,不同的设备有可能不同,当然安卓的串口通讯涉及了jni,也就是java调用c/c++的操作,不过不用担心,谷歌n年前已经帮我们封装好了,https://github.com/cepr/android-serialport-api ,至于怎么导入,自己搜素。当然,如果你像我一样那么懒,直接用别人弄好的arr包吧。

导入

1
2
3
4
5
6
7
8
9
10
11
//根build.gradle添加
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
//模块build.gradle添加
dependencies {
compile 'com.github.licheedev.Android-SerialPort-API:serialport:1.0.1'
}

具体使用

我这里就提个大概吧,首先给谷歌提供的串口工具类提供串口地址,波特率。

1
2
3
4
5
6
7
8
9
//获取所有串口地址 返回字符串数组,你设备插了那个口你需要自己尝试。
String[] mDevices=new SerialPortFinder().getAllDevicesPath()
SerialPort mSerialPort = new SerialPort(device, baurate, 0);//设备地址,波特率,标志位
BufferedInputStream mInputStream=new BufferedInputStream(mSerialPort.getInputStream()); //读取数据的流,需要开个线程不停的读,怎么读相信知道java io流的人都懂的
OutputStream mOutputStream=mSerialPort.getOutputStream();//写入数据的流
mOutputStream.write(datas);//发送数据
//最后当然有关闭各种流的操作,这里就不写了

是不是很简单哈哈,更详细参考这个开源项目吧 https://github.com/licheedev/Android-SerialPort-Tool
当然串口通讯也是有坑的(编程怎么可能没坑)
会有分包 粘包的坑,至于分包 粘包是啥,下面的图画的很清楚。

具体可看https://blog.csdn.net/qq513036862/article/details/53640317
对于串口分包,我的思路就是根据头和数据长度来拼接,具体思路为,开两个StringBuffer若出现数据长度不足,有以下情况:
1.有包头且在数据起始位的
2.有包头且不在数据起始位的
3.没包头的
StringBuffer1就负责把 1,(2的包头前部分或者3拼接起来,因为2 3在一条完整数据不可能同时存在,所以用或)
StringBuffer2 就负责把 2的包头后部分(含包头存起来)
当StringBuffer1长度够后,检验数据,正确后取出,然后StringBuffer1清空,把StringBuffer2 的数据导入StringBuffer1,StringBuffer2数据清空即可。

讲完啦~