Android 串口开发知识总结(未完待续)

一 、Android与串口设备通信的四种方案

  1. 直接用SDK的BluetoothSocket类来进行蓝牙通信,外部设备再用蓝牙转串口进行控制。这种方式有较高延时,蓝牙模块需要供电,低带宽
  2. 使用USB转RS232方式(使用内核驱动和使用Android驱动两种方式),这种方式不需要硬件改动,不需要另外的供电,延时很小且有较高带宽。但是Android设备需要硬件上支持USB Host接口(一般Android平板支持,Android手机一般是没有的),另外可能需要root以改变/dev/ttyUSB0文件权限来加载一个内核模块。开发需要使用android_serialport_api。
  3. 第三种是最容易的方案,串口直接进行连接,但是这种方式兼容性不好,只有少数设备支持,而且串口不支持流控制(由Android提供的USB Host API决定的)。使用时也用android_serialport_api。
  4. 第四种是将Android作为USB从机,外部设备作为USB主机与之通信,这种方式几乎与所有Android设备兼容(一般都有USB从口),无需root,低延迟,高带宽。

目前项目中所用的平板设备,其串口的本质还是USB转串口(文件):ttyUSB


==串口通信无需root==

个人使用总结:

  1. 实际项目中采用第二种方案,未root平板(USB口)接串口(RS232)设备
  2. 平板架构为X86
  3. 串口设置通过SharedPreference类保存 后续未采用

二、串口通信基础知识

参考文章

  1. 串口参数的具体含义及程序配置 主要是C语言,程序如何配置参数
  2. 串口参数详解:波特率,数据位,停止位,奇偶校验位 帮助了解概念
  3. General Terminal Interface
  4. 比特率 波特率 数据传输速率 区别 对串口基础概念讲解的很清楚,重点参考

串口通信的概念非常简单,串口按位(bit)发送和接收字节。

RS232是要用在近距离传输上最大距离为30M

RS485用在长距离传输最大距离1200M

串口(SerialPort)重要的参数。
  • 串口通信模式:异步通信和同步通信

    异步通信:按字符传送

  • 传输过程:程序编码以包为单位发送,一包为一个字节。在串口编码时,起始位、停止位、奇偶校验、波特率都是串口打开前设置好的,配置好串口文件就可以,个人理解:底层协议会将发送的数据位信息按照配置参数分好包再发送,所以实际编程配置好参数,做好数据位信息(byte)的处理就可以了。无需关心起始位和停止位、奇偶校验位的发送问题。

  • :起始位+数据位+停止位+(奇偶校验位)

  • 比特率(Bit rate):传信率、信息传输速率(简称信息速率,information rate)。其定义是:通信线路(或系统)单位时间(每秒)内传输的信息量,即每秒能传输的二进制位数,通常用Rb表示,其单位是比特/秒(bit/s或b/s,英文缩略语为bps)。

在二进制系统中,信息速率(比特率)与信号速率(波特率)相等.在无调制的情况下,比特率等于波特率;采用调相技术时,比特率不等于波特率。通信系统的发送设备和接收设备必须在相同的波特率下工作,否则会出现帧同步错误。

  • 波特率(Baud rate):又称传码率、码元传输速率(简称码元速率)、信号传输速率(简称信号速率,signaling rate)或调制速率。其定义是:通信线路(或系统)单位时间(每秒)内传输的码元(脉冲)个数;或者表示信号调制过程中,单位时间内调制信号波形的变换次数,通常用RB表示,单位是波特(Bd或Baud,前者规范)。如果每秒传输1个码元就称为1Bd;如果1码元的时间长短为200ms,则每秒可传输5个码元,那么码元速率(波特率)就是5Bd。

波特率(码元速率)并没有限定是何种进制的码元,所以给出波特率时必须说明这个码元的进制。
对于M进制码元,比特率(信息速率)Rb波特率(码元速率)RB的关系式为

Rb=RB·lbM
式中:lbM=log2M,表示M的以2为底的对数。

显然,对于二进制码元,由于lb2=1,所以Rb=RB,即波特率与比特率在数值上相等,但单位不同,也即二者代表的意义不同。
例如,波特率为600Bd,则在二进制时,比特率也为600bit/s;在四进制时,由于lb4=2,所以比特率为1200bit/s。可见,在一个码元中可以传送多个比特。

  • 数据传输速率(data transfer rate):又称数据传输速率、数据传送率。其定义是:通信线路(或系统)单位时间(每秒)内传输的字符个数;或者单位时间(每秒)内传输的码组(字块)数或比特数。其单位是字符/秒;或者码组/秒、比特/秒(可见,当数据传输率用“bit/s”作单位时,即等于比特率)。

例如,在某计算机异步串行通信系统中,数据传输率为960字符/s,每个字符包括1个起始位、8个数据位、1个停止位,则对应的比特率为10×960位/s=9600位/s=9600bit/s;因为是二进制编码,所以对应的波特率也为9600Bd。

  • 波特,码元,比特

波特(Bd)是计量单位,用于量度调制解调器等设备每秒信号变化次数的多少,即表示每秒时间内通信线路状态改变的次数,而不代表传输数据的多少。“波特”一词源于一位法国人的名字——Baudot。他于1877年为法国电报系统开发了一种编码方案。如果每1次信号改变,调制解调器传送1bit(位)数据,那么该系统的比特率(信息速率)与波特率(码元速率)相同。但是采用编码技术后,能使1Bd(1次信号改变)表示2bit或更多的比特(位)。每波特表示2bit(位)称作双位编码,每波特表示3bit(位)称作三位编码。也就是说,1次电压(或电流)波形的变化可能包括了几位数据,因此,不能把波特率和比特率混为一谈;前者指电压(或电流)变化次数(变化量),后者指传输数据量的多少。

码元(code cell)是携带信息的数字单位,是指在数字信道中传送数字信号的一个波形符号,也即“时间轴上的一个信号编码单元”。码元可能是二进制的,也可能是多进制的

比特(bit)是“信息量”的计量单位,1位二进制数所携带的信息量即为1bit(比特),例如,10010110是8位二进制数字,所携带的信息量为8bit。

  • 起始位、停止位:起始位,0(低电平);停止位:1(高电平)。无信息传输,串口空闲时,串口线一直为高电平,一直为1,所以是根据0起始位判断是否有数据的。

  • 奇偶校验位:有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。==奇偶校验概念需继续了解,不是很明白==

  • 数据位:TODO 待编辑

三、串口编程

1. Android

Android是基于Linux内核的系统,对串口的操作与Linux无多大区别。

==串口编程实际上是对串口文件的操作。==

在Android开发中,对串口数据的读取和写入,实际上是是通过I/O流读取、写入文件数据。
串口用完记得关闭(文件关闭)。 串口关闭,即是文件流关闭。

在串口开发中的简要操作流程:

graph LR
打开串口文件-->配置串口参数
配置串口参数-->写入/读取
写入/读取-->关闭

通俗易懂 编程基础教程 :
Android串口通信 抱歉,学会它真的可以为所欲为
源文章

入门指南:原文地址-上半年最好的Android串口开发入门指南
上半年最好的Android串口开发入门指南 这是保存的有道笔记

导入Google SerialPort项目,CMake进行JNI开发
// TODO 待后续更新 另一个项目 音频编解码开发中也用到CMake,待更新

串口数据的读取/写入方法

a. FileInputStream(read)和FileOutputStream(write)

通过JNI获取串口文件的FileDescriptor,对FileInputStream/FileOutputStream初始化,通过操作FileInputStream/FileOutputStream来操作串口。

b. 通过操作底层的read/write方法来 读取/写入 串口数据

串口数据的处理

波特率不同(比特率)写入串口的数据需要的时间不同。如果读取数据的缓冲区较大,读取频繁,那么有可能每次读取的有效数据长度不一致。对于需要的完整信息(有头尾的一帧数据)有可能每次都被截断。对于后续的业务处理会造成不便。根据实际业务情况可做两种处理:
读取时间其实由有业务处理层所耗时间决定。如果不做处理,串口会频繁读取。

a. 读取数据不频繁

给出一个简单的计算公式:

一个字节(数据位)的传输时间。如前面所提,串口一个包的数据包括:
起始位+数据位+停止位+(奇偶校验)= 10 | 11位
以不包含奇偶校验位为例:

t(ms/byte) = 1/bitRate * 10 * 1000

以115200波特率为例,一个字节的传输(写入时间:1/115200 * 8 *1000 = 0.086 ms
假设需要的有效信息帧为100个字节,为了每次保包含一个完整的信息帧,缓冲区需要至少设置为200个字节,需要读200个字节。写入200个字节的时间为0.086 * 200 = 17ms左右。

如果业务数据的处理时间较大于17ms,保证了每次读取的有效字节>=200,那么可以按照字节读取,在提取其中的有效信息。否则,会造成读取的信息帧丢失,造成业务损失。

b.读取较为频繁(业务处理较快)

这种情况还是按照字节读取较为稳妥,每次进行字节判断,根据信息帧的头尾提取有效字节,再进行组装。

串口开发遇到的问题、思考点

1.串口读写数据分离,在两个线程中操作,需要同步锁。

2.串口突然断开(异常)怎么办? 表现:读取数据位-1,报异常。需要进行软件处理。
关闭串口,提示用户,再次打开串口。

3.发送信息的频率/周期

4.数据信息缓存,消息队列格式,要求:后进先出,队列满,从队列头依次剔除队列数据
目前可将收发线程放在通过一个Activity中,通过锁对象(队列)实现 读写分离锁

5.串口意外断开处理方法 思路

1.根据FileInputStream的read读取的数据-1次数,达到阈值判定串口异常,关闭串口,并重新连接

2.采用JNI原生方法read()读取数据,在Linux 串口c_cc定义了控制字符,包含以下内容:包含VTIME超时变量。可设定读取超时时间。

6. 项目中采用的平板遇到的串口问题
只是针对项目机型,其他设备不一定出现,但也作为记录
Android 5.1 系统。
1. 本设备的串口其实内部还是USB转串口(ttyUSB),所以打开串口的时候需要注意,非ttySn串口
2. 项目设备的U口不能带串口设备启动,否则会造成该U口占用其串口文件,造成其硬件COM口的串口号变动,例:设备出厂的串口号是ttyUSB1,U口的串口号是ttyUSB0;如果U口带串口线(或串口设备启动),可能造成U口的串口号变为ttyUSB1,COM口的串口号变为ttyUSB0。COM口带串口线(设备)无此问题。

  1. Android系统能否与Windows系统一样,能够判断出是哪个串口设备已接入,这样对于Android端的串口调试助手来说,不需要列出无用的串口号。(未解决)