Android 串口开发知识总结

/ Android / 没有评论 / 1728浏览

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

二、串口通信基础知识

参考文章

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

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

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

串口(SerialPort)重要的参数。

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

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

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

显然,对于二进制码元,由于lb2=1,所以Rb=RB,即波特率与比特率在数值上相等,但单位不同,也即二者代表的意义不同。 例如,波特率为600Bd,则在二进制时,比特率也为600bit/s;在四进制时,由于lb4=2,所以比特率为1200bit/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。

三、串口编程

1. Android

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

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

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

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

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

通俗易懂 编程基础教程 : 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. 项目中采用的平板遇到的串口问题 只是针对项目机型,其他设备不一定出现,但也作为记录。Android 5.1 系统。
  1. Android系统能否与Windows系统一样,能够判断出是哪个串口设备已接入,这样对于Android端的串口调试助手来说,不需要列出无用的串口号。(未解决)