Android 车载串口开发笔记:UART、RS232、RS485、串口配置与数据通信

Android 车载串口开发笔记:UART、RS232、RS485、串口配置与数据通信

Android 工控机、车载终端、RK 系列开发板和嵌入式设备经常通过串口连接 PLC、传感器、仪表、控制器等设备。串口开发主要涉及:UART + TTL/RS232/RS485 + 设备节点 + 波特率 + 数据位 + 停止位 + 校验位 + 收发方向 + 数据协议

典型链路:Android 应用 → 串口驱动 → UART → RS232/RS485 收发器 → 外部设备

UART

UART(Universal Asynchronous Receiver/Transmitter)负责异步串行数据传输,常见信号为 TX + RX + GND。通信双方需要保持 波特率 + 数据位 + 停止位 + 校验位 一致。

常见配置 9600 8N1 表示:9600 波特率 + 8bit 数据位 + 无校验 + 1bit 停止位

UART 描述的是串行数据传输方式,不等于 RS232,也不等于 RS485。常见硬件关系:UART + TTLUART + RS232 收发器UART + RS485 收发器

TTL 串口

TTL UART 常见于开发板内部接口,典型接线:TX → RX + RX → TX + GND → GND。常见逻辑电平为 3.3V5V,不同 IO 电平不能默认直接连接。

TTL UART 与 RS232 使用的电气电平不同,不能把普通 TTL UART 直接连接标准 RS232 接口。

RS232

RS232 是串行通信的电气接口标准,常见信号为 TX + RX + GND,使用单端传输,主要用于点对点通信。

可以简单区分为:UART:串行数据传输RS232:单端物理接口

设备内部常见链路:CPU UART → RS232 收发器 → RS232 接口

因此 Android 上操作的通常仍然是 UART,RS232 收发芯片负责完成电平转换。

RS485

RS485 是差分串行通信接口,常见线路为 A + B,部分设备同时提供 GND。相比 RS232,更适合 较长距离 + 工业现场 + 多设备总线

典型链路:CPU UART → RS485 收发器 → A/B 总线

RS485 只定义物理层,不定义数据内容。Modbus RTU + 厂商私有协议 + 自定义二进制协议 + ASCII 协议 都可以运行在 RS485 上。

对于 Android 应用,两者在软件层可能没有明显区别。例如开发板把 RS232 和 RS485 分别映射到 /dev/ttyS1/dev/ttyS3,应用仍然操作串口设备节点,差异主要位于 UART 后面的收发电路。

RS485 半双工

常见两线 RS485 使用半双工,发送和接收共用 A/B,总线上同一时间通常只有一个节点发送。

RS485 收发器通常存在 DE(Driver Enable)RE(Receiver Enable),典型收发流程:切换发送模式 → 发送数据 → 等待发送完成 → 切换接收模式

部分 Android 工控板已经在 Linux 驱动或硬件层实现自动方向控制,应用只负责正常发送和接收数据。部分设备则需要通过 RTS + GPIO + ioctl + 厂商 API 控制 RS485 收发方向。

是否需要手动控制方向需要查看开发板硬件说明和串口驱动实现。

Android 串口设备节点

Android 底层基于 Linux,板载 UART 通常表现为字符设备。常见设备节点:/dev/ttyS0 + /dev/ttyS1 + /dev/ttyS2 + /dev/ttyS3 + /dev/ttyAMA*

常见含义:/dev/ttyS*:SoC UART 或传统串口/dev/ttyAMA*:部分 ARM 平台 UART/dev/ttyUSB*:USB 转串口设备/dev/ttyACM*:USB CDC ACM 设备

可以通过:

bash 复制代码
ls /dev/tty*

查看系统当前存在的串口设备,但具体哪个设备节点对应 RS232、RS485,需要以开发板原理图、设备树或厂商文档为准。

例如某块 Android 工控板可能定义:/dev/ttyS1 → RS232/dev/ttyS3 → RS485。更换设备后这个对应关系可能完全不同。

串口权限

设备节点存在不代表 Android 应用一定可以访问。访问 /dev/ttyS* 同时受 Linux 文件权限 + SELinux 限制。

查看权限:

bash 复制代码
ls -l /dev/ttyS3

没有设备权限时常见错误:

复制代码
Permission denied

开发调试阶段可以临时修改:

bash 复制代码
chmod 666 /dev/ttyS3

正式设备通常通过 ueventd.rc + init.rc + SELinux Policy + 系统应用权限 + 厂商系统配置 处理。

串口权限与 AndroidManifest 中普通的运行时权限不是同一个问题。

串口配置

串口通信双方至少需要确认:Port + Baud Rate + Data Bits + Stop Bits + Parity + Flow Control

例如:

yaml 复制代码
Port: /dev/ttyS3
Baud Rate: 9600
Data Bits: 8
Stop Bits: 1
Parity: None
Flow Control: None

也可以直接记录为:

bash 复制代码
/dev/ttyS3 9600 8N1

串口开发出现无法通信时,设备节点和这些参数应该先于业务协议检查。

波特率

常见波特率:1200 + 2400 + 4800 + 9600 + 19200 + 38400 + 57600 + 115200 + 460800 + 921600

工业设备常见:9600 + 19200 + 115200

双方波特率不一致时常见现象:乱码 + 字节缺失 + 数据长度异常 + CRC 错误 + 完全无法解析

波特率不是越高越好。线缆长度、现场干扰、收发器性能和硬件设计都会影响可稳定使用的速率。

数据位、校验位、停止位

数据位常见 7bit + 8bit,二进制设备协议基本使用 8bit

校验位常见:N:NoneE:EvenO:Odd

停止位常见:1bit + 2bit

因此会看到:8N1 + 8E1 + 8O1 等配置。

9600 8N19600 8E1 即使波特率相同,也不是相同的串口配置。

流控

常见流控:None + RTS/CTS + XON/XOFF

普通 RS232、RS485 工业设备多数使用 None

部分 RS485 实现会借用 RTS 控制 DE/RE,因此看到 RTS 时还需要确认它属于普通硬件流控还是 RS485 方向控制。

串口收发数据

串口最终传输的是字节。ASCII 协议可能表现为:

复制代码
AT+VERSION?\r\n

二进制协议可能表现为:

复制代码
01 03 00 00 00 02 C4 0B

实际项目中串口 API 只需要完成两件事:发送 byte[] + 接收 byte[]

对于二进制协议,调试日志统一使用 HEX 更方便,例如:

makefile 复制代码
TX: 01 03 00 00 00 02 C4 0B
RX: 01 03 04 00 01 00 02 2A 32

业务层再按照具体协议解析收到的数据。

串口没有数据包边界

串口传输的是连续字节流,不存在 TCP/UDP 那种由 API 保证的数据包概念。

假设设备发送:

复制代码
01 03 04 00 01 00 02 2A 32

应用可能一次收到完整数据,也可能分成:01 03 0400 01 00 022A 32,还可能一次收到当前帧和下一帧。

因此:一次串口回调 ≠ 一帧协议数据

数据帧边界应该由上层协议确定。

数据分帧

常见协议使用这些方式确定完整数据帧:固定长度 + 帧头/帧尾 + 长度字段 + 功能码 + 时间间隔 + CRC

自定义协议例如:

objectivec 复制代码
AA 55 | LEN | CMD | DATA | CRC

解析过程:接收字节 → 写入缓存 → 查找帧头 → 获取长度 → 判断完整帧 → CRC 校验 → 解析命令和数据

串口层负责收发字节,协议层负责分帧和解释数据。

拆包与粘包

拆包:一个完整协议帧被分多次收到

粘包:多个协议帧在一次接收中返回

例如设备连续发送:

复制代码
AA 55 03 01 02 03
AA 55 02 04 05

应用可能一次收到:

复制代码
AA 55 03 01 02 03 AA 55 02 04 05

这不是串口异常,而是字节流读取的正常情况。处理位置应放在协议解析层:缓存 + 分帧 + 校验 + 数据解析

ASCII 与二进制协议

ASCII 协议直接使用可显示字符,例如:

复制代码
AT+VERSION?

二进制协议直接定义每个字节,例如:

复制代码
01 03 00 00 00 02 C4 0B

串口本身不关心数据属于字符串、整数还是设备状态,只负责传输字节。

判断协议时需要查看设备通信文档,而不是根据 RS232 或 RS485 接口判断。

大小端

多字节数值需要确认字节序。

数值 0x1234

复制代码
大端:12 34
小端:34 12

大小端属于上层协议,不属于 UART、RS232 或 RS485 的规定。

同一个 RS485 接口上的不同设备可以使用完全不同的数据格式和字节序。

CRC

工业串口协议经常使用 CRC 检查数据完整性。

以 Modbus RTU 为例,数据结构通常可以记录为:

从站地址 + 功能码 + 数据 + CRC16

Modbus CRC16 在线路上的发送顺序为:CRC Low + CRC High

CRC 校验失败不一定代表 CRC 算法有问题,还需要检查:串口参数 + 数据是否完整 + 分帧是否正确 + 是否丢字节 + 线路干扰

Modbus RTU

Modbus RTU 是 RS485 场景中常见的上层协议,但 RS485 并不要求使用 Modbus。

例如:

复制代码
01 03 00 00 00 02 C4 0B

可以拆成:01:从站地址03:读取保持寄存器00 00:起始寄存器00 02:读取两个寄存器C4 0B:CRC16

完整关系:Android → UART → RS485 → Modbus RTU 数据帧 → 从站设备

其中 UART/RS485 负责数据传输,Modbus RTU 负责定义数据内容。

RS485 多设备通信

RS485 支持在一条总线上连接多个节点,但设备地址并不是 RS485 自己定义的。

例如 Modbus RTU:

erlang 复制代码
01 03 ...
02 03 ...
03 03 ...

分别表示访问不同从站。

因此:RS485 提供多点总线能力 + 上层协议提供设备寻址方式

使用私有协议时,同样需要协议自己定义设备地址或设备 ID。

RS485 A/B

RS485 最常见的接线问题是 A/B 命名不统一。

不同芯片和不同厂商可能使用:A/BD+/D-485+/485- 等名称,而且部分厂商对 A/B 的标识习惯可能相反。

出现完全无法通信时,可以结合设备文档、收发器芯片型号和现场测试确认极性,不能只依赖 A/B 字母名称。

RS485 GND

RS485 使用差分信号,但这不代表任何情况下都不需要 GND。

设备之间存在较大地电位差时,只连接 A/B 可能产生共模电压问题。工业设备通常需要结合接口设计判断是否连接信号地、是否使用隔离收发器以及是否存在保护电路。

因此 RS485 接线不能简单理解成永远只需要两根线。

RS485 终端电阻

RS485 总线较长或者速率较高时需要考虑终端匹配,常见终端电阻为 120Ω,一般放在总线两端。

是否需要终端电阻与 线缆长度 + 波特率 + 拓扑 + 收发器 + 现场干扰 有关。

终端电阻属于物理层问题,协议代码无法解决反射造成的通信异常。

RS485 总线拓扑

RS485 更适合总线型连接:

主干 → 节点 → 节点 → 节点

应尽量减少长距离星形分支。

现场出现 短距离正常 + 拉长线后异常 + 某些节点加入后 CRC 错误增加 时,需要同时检查总线拓扑、终端匹配、偏置、电源隔离和接地,而不只是修改 Android 代码。

串口调试

串口调试至少记录:设备节点 + 串口配置 + 发送 HEX + 接收 HEX + 时间

例如:

yaml 复制代码
Port: /dev/ttyS3
Config: 9600 8N1
TX: 01 03 00 00 00 02 C4 0B
RX: 01 03 04 00 01 00 02 2A 32

调试链路通常可以组合:Android 日志 + PC 串口调试助手 + USB 转串口 + 协议文档 + 示波器/逻辑分析仪

PC 串口助手可以正常通信而 Android 不能通信时,重点比较:波特率 + 数据位 + 校验位 + 停止位 + 实际发送字节 + RS485 方向控制

完全收不到数据

重点检查:串口设备节点 + 权限 + 波特率 + 数据位 + 停止位 + 校验位 + TX/RX + RS485 A/B + RS485 DE/RE + 目标设备地址 + 设备是否需要先发送查询命令

收到乱码

重点检查:波特率 + 数据位 + 校验位 + 停止位 + ASCII/二进制协议判断 + 实际接收 HEX

二进制数据直接作为字符串显示,本身也可能表现为乱码,因此排查时先查看原始 HEX。

只能发送不能接收

RS485 场景重点检查:收发方向控制 + DE/RE + RTS/GPIO + 驱动自动方向控制 + A/B 接线 + 从站是否真正返回数据

RS232 场景重点检查:TX/RX 是否交叉 + GND + 对端发送条件

偶尔 CRC 错误

重点检查:接收帧是否完整 + 拆包粘包处理 + 串口参数 + 丢字节 + RS485 方向切换时机 + 总线终端电阻 + 拓扑 + 接地 + 干扰

如果同一条指令大多数时间正确、偶尔 CRC 错误,不能只检查 CRC 计算代码。

在 ADB Shell 中直接排查串口问题

Android 串口出现无法通信、乱码、收不到数据等问题时,可以直接在 Shell 环境中检查设备节点、串口参数和原始收发数据。可以先排除 Android 业务代码影响,确认 设备节点 + UART 配置 + RS232/RS485 硬件链路 + 外部设备 是否能够正常通信。

查看串口设备

bash 复制代码
ls -l /dev/tty*

常见设备节点:/dev/ttyS*:板载 UART/dev/ttyAMA*:部分 ARM UART/dev/ttyUSB*:USB 转串口/dev/ttyACM*:USB CDC ACM

查看内核中的串口信息:

perl 复制代码
dmesg | grep -i -E "tty|uart|serial|rs485"

查看串口配置

bash 复制代码
stty -F /dev/ttyS3 -a

-F /dev/ttyS3 表示指定需要操作的串口设备。stty 中常见参数含义:speed:波特率cs5/cs6/cs7/cs8:数据位parenb:启用校验-parenb:关闭校验parodd:奇校验-parodd:偶校验cstopb:2 个停止位-cstopb:1 个停止位crtscts:启用 RTS/CTS 硬件流控-crtscts:关闭 RTS/CTS 硬件流控ixon/ixoff:启用 XON/XOFF 软件流控-ixon -ixoff:关闭软件流控raw:原始模式echo:开启回显-echo:关闭回显

常见配置命令结构:

xml 复制代码
stty -F /dev/ttyS3 <波特率> <数据位> <校验> <停止位> <流控> raw -echo

8N1 对应:cs8 + -parenb + -cstopb。配置后可以再次执行 stty -F /dev/ttyS3 -a 确认实际参数。

查看串口接收数据

文本协议可以直接读取:

bash 复制代码
cat /dev/ttyS3

二进制协议更适合直接显示为十六进制:

bash 复制代码
od -An -tx1 -v < /dev/ttyS3

参数含义:-A n:不显示地址偏移-t x1:每个字节以十六进制显示-v:不省略重复数据

如果系统包含 hexdump

bash 复制代码
hexdump -C /dev/ttyS3

-C 表示以十六进制和 ASCII 对照格式显示数据。串口协议排查时优先查看原始 HEX,不要先转换成字符串。

发送串口数据

发送 ASCII:

bash 复制代码
printf 'COMMAND\r\n' > /dev/ttyS3

发送二进制数据:

bash 复制代码
printf '\x01\x02\x03\x04' > /dev/ttyS3

printf 可以直接控制 \r\n 等字符,\xNN 表示发送一个十六进制字节。printf '01' 发送的是字符 01printf '\x01' 发送的才是实际的 0x01 字节。

查看串口权限

bash 复制代码
ls -l /dev/ttyS3

没有设备节点访问权限时通常会出现 Permission denied。调试阶段可以临时修改:

bash 复制代码
chmod 666 /dev/ttyS3

正式系统仍应通过 ueventd.rc + SELinux Policy + 系统权限 配置串口访问权限。

相关推荐
YF02112 小时前
遥控器APP端自动重连方案
android
执明wa3 小时前
Android Studio 打包 APK
android·ide·android studio
waiting9711184 小时前
Ubuntu 26.04 + Android14安装与编译教程
android·linux·ubuntu
hunterandroid4 小时前
Android 测试全景:从单元测试到 UI 自动化的完整实践
android·前端
蜡台4 小时前
Kotlin 五大作用域函数详解|let/run/apply/also/with 选型指南+实战避坑
android·java·kotlin
2401_833269305 小时前
Android registerForActivityResult详解
android
Carson带你学Android5 小时前
9月 Android Drop:Gemini 终于不只是聊天助手了
android·ai编程
Sagittarius_A*5 小时前
【LitCTF2026】lit_ezsql
android·java·数据库
hai_android6 小时前
Messenger:Android 最轻量的 IPC 方式
android·kotlin