Android 车载 CAN 开发笔记:CAN、SocketCAN、CAN FD、DBC、ISO-TP 与 UDS

1 阅读13分钟

Android 车载 CAN 开发笔记:CAN、SocketCAN、CAN FD、DBC、ISO-TP 与 UDS

Android 车机、T-Box、仪表、工控屏和工程机械终端经常通过 CAN 获取 车速 + 档位 + 点火状态 + 电池状态 + 车门状态 + 电机状态 + BMS 数据 + ECU 诊断数据,也可能向 ECU 发送控制命令。

典型链路:Android 应用 → JNI/NDK → SocketCAN → can0 → CAN Controller → CAN Transceiver → CANH/CANL → ECU

车载 CAN 开发主要涉及:CAN Controller + CAN Transceiver + CANH/CANL + CAN ID + DLC + Data + Bitrate + CAN FD + SocketCAN + DBC + ISO-TP + UDS + J1939 + SELinux + 上层车辆协议

CAN

CAN(Controller Area Network)是汽车电子中常见的多主串行总线协议。CAN 使用 CANH + CANL 差分信号,并定义了 仲裁 + 帧格式 + CRC + ACK + 错误检测 + 自动重发 等机制。

典型车辆网络:ECU ↔ CANH/CANL ↔ BCM ↔ VCU ↔ BMS ↔ 仪表 ↔ Android 终端

可以简单记录为:RS485:主要定义差分物理接口CAN:物理层 + 数据链路层协议

CAN Controller 与 CAN Transceiver

CAN 通信通常包含 CAN Controller + CAN Transceiver

CAN Controller 负责:CAN Frame + Arbitration + CRC + ACK + Error Counter + Bus Off

CAN Transceiver 负责:Controller Logic Signal ↔ CANH/CANL 差分信号

典型链路:SoC → CAN Controller → CAN Transceiver → CANH/CANL

Android 系统存在 can0 只能说明 CAN Controller 和驱动已经注册网络接口,不代表 CANH/CANL 物理总线一定正常,还需要确认 Transceiver + PinMux + Device Tree + 电源 + CANH/CANL + 终端电阻

CAN 仲裁

CAN 是多主总线,多个 ECU 可以同时尝试发送数据,通过 Arbitration ID 逐位进行仲裁。

核心规则:Dominant 0 覆盖 Recessive 1

因此通常可以简单理解为:CAN ID 数值越小 → 总线优先级越高

CAN ID 并不等于设备地址。CAN ID 可以由车辆协议定义为:消息类型 + ECU + 来源 + 目标 + 功能 + 优先级

标准帧与扩展帧

Classical CAN 支持:

Standard Frame:11 bit CAN ID,0x000 ~ 0x7FF

Extended Frame:29 bit CAN ID,0x00000000 ~ 0x1FFFFFFF

SocketCAN 使用 CAN_EFF_FLAG 表示扩展帧,使用 CAN_RTR_FLAG 表示 Remote Frame,使用 CAN_ERR_FLAG 表示 Error Frame。

Android Bionic 当前提供 Linux CAN UAPI,其中包含 CAN_EFF_FLAG + CAN_RTR_FLAG + CAN_ERR_FLAG + CAN_SFF_MASK + CAN_EFF_MASK 等定义。

实际车辆私有协议经常使用 11 bit CAN ID,J1939 等协议大量使用 29 bit Extended CAN ID。

CAN 数据帧

经典 CAN 数据帧可以抽象为:

CAN ID + Control + DLC + Data + CRC + ACK

应用层通常主要关注:

CAN ID + DLC + Data

例如:

ID: 0x123
DLC: 8
DATA: 01 02 03 04 05 06 07 08

经典 CAN 单帧最多携带 8 Byte Data。SocketCAN 中对应 struct can_frame

CAN ID 与车辆信号

一条 CAN Frame 中通常可以包含多个车辆 Signal。

例如:

CAN ID: 0x123
DATA: 10 27 00 64 01 00 00 00

车辆协议可能定义:Byte0~1:MotorSpeedByte2~3:VehicleSpeedByte4 Bit0:DoorOpen

完整解析过程通常是:

CAN Frame → CAN ID → Signal Start Bit/Length → Endianness → Raw Value → Factor/Offset → Physical Value

因此 SocketCAN 只能获得原始 CAN Frame,并不知道某两个 Byte 代表车速还是电机转速。

DBC

DBC 是车载 CAN 中常见的 CAN Database 文件,用于描述:Message + CAN ID + DLC + Signal + Start Bit + Length + Intel/Motorola + Signed/Unsigned + Factor + Offset + Unit + Enum

完整链路:

SocketCAN → CAN Frame → DBC → Signal → Vehicle Data

例如:

0x123 + Raw Data → VehicleSpeed = 60 km/h

DBC 解决的是“原始 CAN Byte 如何转换成实际车辆信号”,不属于 SocketCAN 本身。

Intel 与 Motorola

DBC 中常见两种 Signal 字节序:

Intel:Little Endian

Motorola:Big Endian

CAN Signal 还同时涉及 Start Bit + Bit Length + Bit Numbering。特别是 Motorola Signal 跨 Byte 时,不能只按照普通整数大小端交换字节。

使用 DBC 时应按照 DBC Signal 定义解析,不要直接根据 Byte 顺序猜测。

CAN 周期报文

车辆 CAN 中大量数据不是请求一次返回一次,而是 ECU 周期广播。

常见周期:10ms + 20ms + 50ms + 100ms + 500ms + 1s

分析 CAN 时通常同时关注:CAN ID + DLC + Period + Data Change

例如车速可能 20ms 周期发送,车门状态可能使用低频周期或状态变化上报。

CAN 波特率

经典 CAN 常见 Bitrate:125 kbit/s + 250 kbit/s + 500 kbit/s + 1 Mbit/s

同一条 CAN Bus 上的节点必须使用兼容的 Bit Timing。

除了 bitrate,CAN Bit Timing 还包括:Sample Point + Time Quantum + Propagation Segment + Phase Segment + SJW

Linux SocketCAN 通常可以根据 Bitrate 自动计算 Bit Timing。

Bitrate 不一致常见表现:收不到数据 + Error Frame + ACK Error + Bus Error + Bus Off

CAN ACK

正常 CAN Data Frame 需要其他节点在 ACK Slot 确认。

如果测试环境只有 Android CAN 节点,没有第二个工作在相同 Bitrate 的节点,发送端可能持续产生 ACK Error。

因此:

cansend 执行成功 ≠ CAN 总线通信正常

还需要检查:ACK + Error Counter + Bus State

CAN 错误状态

CAN Controller 自带:Bit Error + Stuff Error + CRC Error + Form Error + ACK Error

节点错误状态通常为:

Error Active → Error Passive → Bus Off

错误持续累积后 Controller 可能进入 Bus Off,此时 can0 仍然存在,但已经不能正常发送 CAN Frame。

终端电阻

高速 CAN 总线通常在主干两端分别配置:

120Ω

典型结构:

120Ω → CAN Bus → ECU → ECU → ECU → 120Ω

断电测量 CANH 与 CANL,两个 120Ω 并联后的理论值接近:

60Ω

如果测到约 120Ω,通常需要检查是否只存在一个终端电阻;明显偏离 60Ω 时继续检查总线终端和拓扑。

CAN 总线拓扑

CAN 通常采用线型主干:

120Ω → Main Bus → Node → Node → Node → 120Ω

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

出现 短线正常 + 长线异常 + 接入更多 ECU 后错误增加 时,重点检查:终端电阻 + Stub Length + 线缆 + 接地 + 屏蔽 + Bitrate + Transceiver

CAN FD

CAN FD(CAN with Flexible Data-Rate)是在 Classical CAN 上的扩展。

主要区别:

Classical CAN:最多 8 Byte

CAN FD:最多 64 Byte

CAN FD 还可以让 Arbitration Phase 和 Data Phase 使用不同 Bitrate,例如:

Nominal Bitrate = 500 kbit/s

Data Bitrate = 2 Mbit/s

SocketCAN 对应使用 struct canfd_frame。Android Bionic 当前 CAN UAPI 中定义了 CANFD_MAX_DLEN = 64struct canfd_frame

SocketCAN

SocketCAN 是 Linux CAN 网络栈,将 CAN Controller 表示为网络接口。

常见接口:

can0
can1
vcan0

因此:

UART → /dev/ttyS*

CAN → can0/can1

Android 基于 Linux,只要 Kernel 启用了 SocketCAN、CAN Controller Driver 正常并完成设备注册,就可以出现 can0

Android CAN 系统 API

Android Framework 没有类似:

CanManager
CanDevice
CanSocket

这样的官方公开 Java CAN API。

车载 Android 直接操作 SocketCAN 时,常见方式是:

Java/Kotlin → JNI → Android NDK/Bionic → PF_CAN Socket → can0

Android Bionic 当前提供 Linux CAN 用户态 Header,包括:

<linux/can.h>
<linux/can/raw.h>

其中包含 CAN_RAW + CAN_RAW_FILTER + CAN_RAW_ERR_FILTER + CAN_RAW_FD_FRAMES 等 SocketCAN 定义。

因此 Android 车载 CAN 的“系统 API”主要是 Linux Socket API,而不是 Android Framework Java API。

NDK Socket API

CAN RAW Socket 的核心入口:

socket(PF_CAN, SOCK_RAW, CAN_RAW);

常用系统调用:socket() + bind() + read()/recv()/recvmsg() + write()/send()/sendmsg() + setsockopt() + ioctl()/if_nametoindex() + close()

相关数据结构:

struct sockaddr_can
struct can_frame
struct canfd_frame
struct can_filter

Android 应用通常在 Native 层封装成自己的:

openCan + closeCan + sendFrame + receiveFrame + setFilter

再通过 JNI 暴露给 Java/Kotlin。

sockaddr_can

SocketCAN 使用:

struct sockaddr_can

绑定 CAN Interface。

其中:

can_family:AF_CAN

can_ifindex:can0/can1 对应的 Network Interface Index

因此 SocketCAN 操作的是 Linux Network Interface,不是设备文件。

CAN RAW Filter

CAN RAW Socket 支持 Kernel Filter。

结构:

struct can_filter {
    canid_t can_id;
    canid_t can_mask;
};

通过:

setsockopt(..., SOL_CAN_RAW, CAN_RAW_FILTER, ...)

配置。

匹配规则:

received_can_id & mask == can_id & mask

如果业务只关注少量 CAN ID,应优先使用 Kernel Filter,而不是先让整条车辆总线的数据进入 Android 再过滤。Android Bionic 当前的 <linux/can/raw.h> 提供 CAN_RAW_FILTER 等 Raw Socket Option。

Error Frame

SocketCAN 可以向应用提供 CAN Error Frame。

常见错误:Bus Error + Controller Error + Protocol Error + Lost Arbitration + Bus Off + Restarted

应用需要通过 CAN_RAW_ERR_FILTER 订阅需要关注的 Error Frame。

车机出现“CAN 数据突然全部消失”时,Error Frame 往往比业务 CAN ID 日志更容易确认是否已经发生 Bus Off。

CAN FD Socket

CAN RAW Socket 默认需要显式开启 FD Frame:

CAN_RAW_FD_FRAMES

然后根据接收长度区分:

CAN_MTU → Classical CAN

CANFD_MTU → CAN FD

Android Bionic 当前 Raw CAN Header 中仍提供 CAN_RAW_FD_FRAMES

ISO-TP

经典 CAN 单帧最多只有 8 Byte,而车辆诊断经常需要传输更长的数据,因此 ISO-TP(ISO 15765-2)负责 CAN 上的长数据传输。

典型关系:

CAN → ISO-TP → UDS

ISO-TP 定义:Single Frame + First Frame + Consecutive Frame + Flow Control

如果 Android Kernel 开启了 CAN ISO-TP,可以直接创建对应的 CAN Socket,不需要应用自己实现所有 ISO-TP 分包和重组。

Android Bionic 当前 CAN UAPI 定义了:

CAN_ISOTP

作为 CAN Protocol。

UDS

UDS(Unified Diagnostic Services,ISO 14229)常用于 ECU 诊断,通常运行在 ISO-TP 上。

典型链路:

Android → SocketCAN → ISO-TP → UDS → ECU

常见 UDS Service:0x10 DiagnosticSessionControl0x11 ECUReset0x14 ClearDiagnosticInformation0x19 ReadDTCInformation0x22 ReadDataByIdentifier0x27 SecurityAccess0x2E WriteDataByIdentifier0x31 RoutineControl0x34 RequestDownload

可以简单记录为:

CAN:负责 Frame

ISO-TP:负责长数据传输

UDS:负责 ECU 诊断业务

J1939

SAE J1939 常用于:卡车 + 工程机械 + 农业机械 + 商用车

J1939 主要运行在 29 bit Extended CAN ID 上,常见结构涉及:

Priority + PGN + Source Address

业务信号进一步通过:

PGN + SPN

描述。

Android/Linux CAN UAPI 当前定义了:

CAN_J1939

如果 Kernel 开启对应 J1939 Stack,可以直接使用 J1939 Socket。

OBD-II

OBD-II 是车辆诊断接口和诊断体系,不等于 CAN 本身。

支持 CAN 的车辆经常通过 ISO 15765-4 进行 OBD 通信。

典型用途:读取 PID + DTC + ECU 信息 + 排放相关诊断

可以简单记录为:

CAN:底层车辆总线

ISO-TP:诊断传输

OBD-II/UDS:上层诊断协议

USB-CAN

车载 Android 还经常通过 USB-CAN 接入车辆总线。

如果 Linux 已经存在对应 USB-CAN Driver:

USB-CAN → Kernel Driver → SocketCAN → can0

应用继续使用 PF_CAN。

如果 USB-CAN 使用厂商私有 USB Protocol:

Android → UsbManager → USB-CAN Vendor Protocol → CAN

这时不会出现 can0,需要通过 Android USB Host API 和厂商协议收发 CAN Frame。

因此首先需要确认:

USB-CAN 是否已经注册成 SocketCAN Interface

CAN 权限

SocketCAN 使用 Network Socket 权限模型,不是 /dev/tty* 文件权限。

Android 应用直接创建 PF_CAN Socket 还会受到:SELinux + App UID + Domain + Kernel Config + Vendor Policy 影响。

因此:

Shell 下 candump 正常 + App PF_CAN 失败

优先检查:

SELinux + sepolicy + Application Domain

而不是重新检查 CANH/CANL。

车载量产系统通常由固定 System Service、Native Service 或系统应用统一访问 Raw CAN,不会默认给所有普通 APK 整车 CAN Bus 权限。

can-utils

SocketCAN 常用调试工具来自 can-utils

常见命令:candump:接收 CAN Framecansend:发送 CAN Framecangen:生成测试流量canplayer:回放 CAN Logcansniffer:观察 CAN Data 变化isotpsend/isotprecv:ISO-TP 调试

Android 系统默认不一定包含 can-utils,定制车载系统可以自行集成。

查看 CAN 接口

ip -details link show can0

主要关注:state + bitrate + sample-point + tq + sjw + restart-ms + berr-counter

查看统计:

ip -details -statistics link show can0

主要关注:bus-errors + error-warn + error-pass + bus-off + restarted + RX/TX dropped

配置 CAN

常见命令结构:

ip link set can0 down
ip link set can0 type can bitrate <bitrate>
ip link set can0 up

can0:CAN Interfacetype can:设置 CAN Link 参数bitrate:CAN 仲裁阶段 Bitrate

CAN FD:

ip link set can0 type can bitrate <bitrate> dbitrate <dbitrate> fd on

其中:bitrate:Arbitration Bitratedbitrate:Data Phase Bitratefd on:开启 CAN FD

Bus Off 自动恢复:

ip link set can0 type can restart-ms <milliseconds>

实际 Bitrate 必须以目标车辆网络定义为准。

查看 CAN 数据

监听:

candump can0

常见结果:

can0  123   [8]  01 02 03 04 05 06 07 08

对应:Interface can0 + CAN ID 0x123 + DLC 8 + Data

显示时间戳:

candump -t a can0

车辆 CAN 报文数量较大时,应优先设置 Filter,不要长期抓取整条总线全部 Frame。

CAN 报文过滤

candump 支持:

candump can0,<can_id>:<mask>

过滤逻辑:

received_can_id & mask == can_id & mask

实际应用同样应尽可能使用 SocketCAN Kernel Filter。

发送 CAN Frame

格式:

cansend <interface> <can_id>#<data>

例如结构:

can0 123#01020304

对应:can0 + CAN ID 0x123 + 4 Byte Data

车载实际总线上不要随意使用未知 CAN ID 测试发送,因为 CAN Frame 可能对应真实车辆控制命令。

cansniffer

cansniffer 适合分析周期 CAN Data 中发生变化的 Byte。

车载总线上可能同时存在大量 10ms、20ms、100ms 周期 Frame,直接查看 candump 很难观察某个操作对应哪一段数据变化。

cansniffer 可以辅助定位:

按键动作 → CAN ID → Byte/Bit Change

但最终 Signal 定义仍应以 DBC 或车辆协议为准。

vcan

SocketCAN 提供 Virtual CAN:

vcan0

可以测试:SocketCAN API + CAN Frame 编解码 + Filter + DBC 解析 + 应用业务逻辑

vcan 不模拟:CANH/CANL + ACK + 仲裁物理过程 + Bus Off + 终端电阻 + 线路干扰

因此它只能验证软件层。

在 ADB Shell 中直接排查 CAN 问题

查看 CAN 接口

ip -details link show can0
ip -details -statistics link show can0

用于确认 can0 是否存在,并查看 state + bitrate + berr-counter + bus-errors + error-pass + bus-off + RX/TX dropped

配置 CAN

ip link set can0 down
ip link set can0 type can bitrate 500000
ip link set can0 up

CAN FD:

ip link set can0 type can bitrate 500000 dbitrate 2000000 fd on

bitrate 必须与目标 CAN 总线一致。

接收 CAN 数据

candump can0
candump -t a can0

正常输出类似:

can0  123   [8]  01 02 03 04 05 06 07 08

用于绕过 Android 应用层,直接确认驱动和 CAN 总线是否能够收到原始 Frame。

过滤 CAN ID

candump can0,123:7FF

用于只监听指定 CAN ID,避免整条总线大量周期报文影响观察。

发送 CAN 数据

cansend can0 123#0102030405060708

用于验证发送链路,但 cansend 执行成功不代表物理总线正常,还需要检查 ACK、Error Counter 和 Bus Off;真实车辆总线上不要发送未知 CAN ID。

观察数据变化

cansniffer can0

适合执行按键、开关、档位等操作时观察 CAN ID + Byte/Bit 的变化,用于定位可能对应的车辆信号。

其他调试指令

cangen can0
canplayer <log>
isotpsend ...
isotprecv ...

cangen 用于生成测试流量,canplayer 用于回放 CAN Log,isotpsend/isotprecv 用于 ISO-TP 调试。