安卓与串口通信-基础篇

前言

安卓并不仅仅只是一个手机操作系统,在很多领域都能见到安卓的身影。

无论是车载系统、工控系统、屏控系统还是物联网设备基本都有安卓的一席之地。

在所谓的寒冬之下,纯粹的安卓开发似乎已经不再吃香,于是越来越多的安卓开发者转向了车载、工控等领域。

而在这些领域,绕不开串口通信相关知识,无论是车载系统与车身各个传感器或者 ECU 通信还是工控系统与 PLC 交互,都离不开串口。

说来惭愧,虽然我勉强算是一个做安卓工控的吧,但是实际上干的活却是调轮子,调参数,对于其中的基本原理一直是一知半解的。

正好最近有空,我决定先从串口通信学起,慢慢夯实我的基础知识。

在学习过程中也会把自己学到的东西结合自己工作中积累的为数不多的经验分享出来。

因为对这方面其实我也不甚了解,所以难免会有纰漏,如有错误还望各位大佬指正。

本文中的部分图表来自于文末标注的参考资料

串口基础知识

串行与并行

如果我们想让两台设备(单片机、电脑等)相互通信,一般可以有两种方式:串行与并行。

例如我们想从设备A中发送一条 8 bit 的数据到设备 B。

如果是串行的方式则会从第1位到第8位依次排队从一根线(注意,这里的一根线不是物理意义上的一根线,可以理解成一根完整的数据线)上发送过去。

如果使用并行的方式,则可能设备A和设备B之间有8条线相连接,发送时这 8 bit 数据会同时从8根线上发送到设备B。

直接说可能不太好理解,但是看这张图就能秒懂:

serial_parallel

从上面的图也不难看出,并行传输相比于串行传输速度会更快,但是相应的线路成本也更高,而且抗干扰能力也没有串行强,所以并行的通信距离没有串行的距离长。

同时,由于串行协议的外设简单,成本低通信距离远,所以我们在单片机或者说工控设备之间通常使用的都是串行传输。

而串行传输又可以分为同步串行和异步串行。

同步串行与异步串行

同步串行指的是通信双方的时钟频率保持一致,接收方时刻准备接收数据,并且只需要在接受到开始传输的信号后即可连续传输数据,数据之间没有间隙,不需要起始位和结束位,传输效率高,可以进行一对多通信。常用的同步串行有 I2C 和 SPI。

而异步串行指的是双方时钟频率不一致,传输数据时采用帧格式传输,发送方发送每一帧数据时需要附加起始位和结束位,接收方通过读取起始位和结束位来实现与发送方的信息同步,传输效率低,只能1对1通信。通常一帧数据由起始位、数据位、校验位、停止位组成。常用的异步串行有 UART ,即我们俗称的串口通信。

串口通信(UART)

为了实现串口通信,我们需要在设备之间连接三条线:TX(发送)、RX(接收)、GND(接地)。

分别将设备A的TX和设备B的RX相连;设备A的RX和设备B的TX相连;设备A、B的GND相连。

由于每个设备之间都同时有发送和接收端,所以串口通信是全双工通信,即支持同时发送和接收数据。

假如我们想要由设备A向设备B发送一个字符 “E”,则发送效果如图:

1

即通过设备A的发送端(TX)向设备B的接收端(RX)发送一段数据。

此时如果测量发送数据时的这条线上的电平,将得到这么一个波形图:

2

此即二进制的 “01000101” 换算成十进制则为 69 ,在 ASCII 中表示字母 “E” :

3

而这里电平中读到的 0 和 1 是怎么定义呢?一般来说我们会定义高电平为1,低电平为0,具体怎么定义下面会讲,现在先按下不表。

事实上,这里的波形图并不完整,还记得我们上面介绍异步串行时说过的吗?它是采用帧传输的方式来传输数据的。也就是说,一个完整的数据帧至少还应该包含前面的起始位(1位低电平)和最后的结束位(1-2位高电平):

4

另外,一帧数据中数据位可选5-8位,一般来说使用的是 8 位;在数据位之后结束位之前还可以有一个校验位用于校验数据传输是否正确(奇偶校验、CRC校验等),当空闲时数据线输出高电平,称为空闲位:

5

那么问题来了,接收端怎么知道使用的数据位是多少位、是否使用校验、结束位有几位?

答案是不知道,也无法知道。

所以通常在串口通信开始之前,传输双方需要约定好使用的 数据位、校验位、停止位。

一般使用时都是 8位数据位、无校验、1位停止位,即俗称的 “8N1”。

除了上面说的三个参数外,在开始通信前还需要约定一个参数,那就是“波特率”。

波特率即每秒传输多少个bit的数据,单位为 bps(bits per second)。

回到我们上面说的传输字母 “E” 的图上,虽然从图中可以很直观的看出电平的变化,但是,接收方应该怎么知道“多长一段”是一个 bit 呢?这就需要用到我们的波特率了。

例如我们上面设置的波特率是 9600 bps,则表示每秒发送 9600 bit,即一个 bit 宽度为 104 微秒。

此时接收方在读取到起始位后,会开始每间隔104微秒读取一次数据,直至读取到结束位。

(事实上,在读取到起始位后通常还会延迟 52 微秒才会开始读取,此后每隔 104 微秒读取一次,这是为了避免开始读取时数据不稳定)

自此,串口通信的基本原理就全部讲完了。

是不是觉得少了点什么呢?

对的,上面还留了一个问题,就是我们怎么去界定高电平和低电平呢?

这就引出了我们的下一个主角 RS232 。

RS232

一般来说,串口通信使用的是 TTL 电平。

TTL 即 Transistor-transistor logic ,晶体管逻辑,其电路由双极晶体管构成,工作电压为 5V。所以我们一般将 +5V 电压认为是高电平(1),0V认为是低电平(0)。但是,TTL电路输出的高电平最小为 2.4V、低电平最大为0.4V,这就导致高低电平之间的差值最小可能只有2V,所以其传输数据时极易受到干扰,例如此时受到静电干扰,可能就会把低电平变为了高电平。所以此种传输方式的距离非常短,一般在1米以内,只适用于芯片和芯片之间或设备与电脑之间的数据传输。

为了改善这些缺点,美国电子工业联盟制定了一个标准:RS232 。

RS232改善的方法非常简单,就是将电平提高。

为了提高传输的电平,我们只需要在原本的串口传输设备之间增加一个电平转换芯片即可。

此时,如果发送设备发送一个 5V 的高电平给转换芯片,会被提高到 -15V 后发送给接收设备;反之,如果这个芯片接收到 -15V 的高电平,又会将其转换为 5V 发送给设备。

这就完成了将 TTL 电平转换为 RS232 电平的操作。

6

在 RS232 中,高电平(1)为 -3V至-15V;低电平(0)为 +3V至+15V。

7

RS232 只是改变了电平大小,使得高低电平之间相差了将近 20V ,这样一来就让 RS232 的抗干扰能力大大增强,传输距离也达到了理论 15 米,同时传输数据格式也没有改变。但是这也导致 RS232 的最大波特率只能使用 19200 bps,使得传输速率有限,且抗共模干扰能力依旧没有改善。

所以,又诞生了 RS485 标准。

RS485

与 RS232 相同的是,RS485也是通过在原本的串口通讯基础上添加了电平转换芯片来实现。

8

不同的是, RS485 不再采用固定的电平高低判断方式,而是使用差分信号来表示数据。

RS485 接线只需要两根线A与B。当A与B之间的电压差在2V到6V之间时表示逻辑1;差值在-6V到-2V之间时表示逻辑0 。

同时 RS485 通常采用双绞线的形式接线,这就使得它的抗共模干扰能力大大增强,因为当受到干扰时 A和B的电压都会改变,此时他们的电压差值就基本不会受到影响。

9

由于 RS485 使用差分信号的特点,使得它的可传输信号最低能达到 200mV、数据最高传输率能达到 10Mbps、传输距离可以达到1200米。

不过由于 RS485 只使用了两根线,且两根线都用于传输同一个数据的特性,使得它只支持半双工通信,即同一时间只能发送或接收数据,不能同时发送与接收数据;但是也因为这个特点,使得 RS485 不同于 RS232 只能两个设备点对点通信,而是可以一对多通信,即一个主机可以同时与多个从机通信。

10

总结

自此我们已经对串口通信有了一个基本的认识。

从上文中我们可以发现,UART定义了数据传输格式标准,RS232和RS485定义物理层面的数据传输方式与接线方式:

11

也就是说,无论是串口通信还是 RS232 还是 RS485,其实它们的数据格式与内容都是一样的。

对于我们安卓开发人员来说,无论使用什么标准几乎都对我们没有影响,我们需要做的只是解析好通过串口传输过来的数据即可。

如何进行测试

讲解完了上述的串口通信原理,那么我们应该怎么测试呢?

最好的方式当然是有一个实机能够测试,但是有时候我们手头暂时没有设备或者不方便使用真实设备进行测试,此时我们就可以使用虚拟串口进行模拟测试。

概述

在 Windows 上进行模拟测试相对于 MAC 上简单的多。

大致流程为:使用软件创建虚拟串口-启动安卓模拟器并与创建的虚拟串口连接-完成安卓与虚拟串口的通讯。

由于我目前主力机使用的是 MAC ,所以我尝试按照上述步骤在 MAC 上进行模拟测试。

但是却卡死在了第一步,即创建虚拟串口。

我尝试查找了一番,发现在 MAC 上并没有可用的虚拟串口软件,好不容易找到一款,却已经停止维护,因为新版本的 MAC 权限收紧,无法再创建虚拟串口。

好在,我手头还有两块 ESP32 的开发版,所以其实不使用虚拟串口模拟也行,我可以直接使用 ESP32 开发版进行测试。

那么,现在问题又来到了第二个步骤。

但是经过我的尝试,MAC 始终无法在启动安卓模拟器与串口设备连接。

不过虽然无法使用,但是过程中踩了一些坑,也找到了解决方案,所以这里还是大致讲一下。

MAC 启动模拟器与串口连接

首先,我们需要创建好安卓模拟器,这一步直接在 Android Studio 中按照常规方式创建就可以了

创建好模拟器后,我们不能直接在 Android Studio 中启动,而是必须在终端中启动,因为我们需要指定启动参数。

启动需要执行 emulator 命令,这个命令的可执行为文件在 AndroidSdk/tools 中,例如我的路径是 /Users/equationl/Library/Android/sdk/tools 。如果你不知道你的 AndroidSdk 目录在哪儿,你可以直接在 Android Studio 的设置中搜索 “sdk” 后,在 “Android SDK Location” 选项中看到这个路径:

sdk_location

找到你的路径后执行以下命令即可启动模拟器:

cd /Users/equationl/Library/Android/sdk/tools

./emulator -avd Pixel_6_Pro_API_31

其中的 “Pixel_6_Pro_API_31” 就是你之前创建的模拟器的名称。

当然,这里只是单纯的启动了模拟器,并没有连接到串口,所以此时你启动的模拟器是找不到你插入设备的串口的。

如果我们需要连接串口,则需要执行:

./emulator -avd Pixel_6_Pro_API_31 -qemu -serial /dev/tty.wchusbserial544C0047221

其中的 /dev/tty.wchusbserial544C0047221 即为你的串口路径。

在 MAC 上查看可用串口的命令为:

ls /dev/tty.*

然而,如果你的 MAC 是 ARM CPU的话,大概率启动模拟器时会报错:

launcher_emulator_fail

这应该是谷歌的一个小 BUG,从错误信息也能看出,错误的原因是不存在 darwin-x86_64/qemu-system-aarch64 这个文件。

我们来到报错的路径,可以看到,压根没有叫 darwin-x86_64 的文件夹,但是有一个叫 darwin-aarch64 的文件夹,里面就有 qemu-system-aarch64 文件。

哈哈哈,其实到这里估计已经有人反应过来了,我们明明是 ARM 的处理器,怎么会有 x86 文件夹嘛。

所以我们直接把 darwin-aarch64 软连接到 darwin-x86_64 就行了。

执行命令:

cd /Users/equationl/Library/Android/sdk/emulator/qemu

ln -s darwin-aarch64 darwin-x86_64

然后再返回 tools 目录,执行启动模拟器的命令,可以看到,启动成功了。

但是,连接串口失败,并报错:

serial_error

可以看到,有两个错误,一个是无法识别 serial 命令;另一个是无法连接到我们指定的串口。

关于这个错误,我也是搜索了好久,也尝试了各种方法,但是都无法解决。

不过我发现另外一种实现方法,那就是使用单独的虚拟机开启一个安卓的模拟器,然后由这个虚拟机创建一个虚拟串口给安卓模拟器使用,并将这个虚拟串口连接至物理设备。

啊这?搁这套娃呢?太麻烦了!果断放弃!

感兴趣的可以去尝试一下,教程地址:【Android】mac下Android的串口及NDK调试探索

总结

因为上面的原因,最终我选择的测试方案是 使用安卓真机+ESP32开发版进行测试。

对于同样是使用 MAC 的用户,我的建议是直接使用真机+ESP32开发版,或者其他更便宜的开发版进行测试学习,毕竟一块开发版也就十几块钱,真没必要为了省这十几块钱而去折腾这个神坑的无底洞。

而对于 Windows 用户就方便多了,不仅可以使用和 MAC 一样的真机测试方案,还可以很方便的使用虚拟串口以及安卓模拟器进行测试。

总结

我们今天主要讲解了串口的一些基本知识,并且大概介绍了应该怎么去测试串口通信。

所有知识点都是循序渐进的,设备间的通信有串行通信和并行通信,一般我们在实际中使用的是串行比较多,而串行分为同步串行与异步串行,常用的是异步串行,异步串行常用协议是 UART 即常说的串口,为了解决串口通信的问题,又衍生出了 RS232、RS485等协议。

参考资料

  1. How can I launch Android Emulator without android studio on Mac M1
  2. 5分钟看懂!串口RS232 RS485最本质的区别!
  3. 串行通信接口之RS-232、RS-485与RS-422
  4. 3分钟理解通信协议之串口UART到底是个啥?
  5. 超简单的一种通信,2分钟搞懂,串口通讯的工作原理!
  6. UART协议详解
  7. UART接口的基础知识详解
  8. 串口通信(UART)介绍
  9. 什么是串口通讯?
  10. 串口通讯详解
  11. 串口通讯详解