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:MotorSpeed,Byte2~3:VehicleSpeed,Byte4 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 = 64 和 struct 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 DiagnosticSessionControl,0x11 ECUReset,0x14 ClearDiagnosticInformation,0x19 ReadDTCInformation,0x22 ReadDataByIdentifier,0x27 SecurityAccess,0x2E WriteDataByIdentifier,0x31 RoutineControl,0x34 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 Frame,cansend:发送 CAN Frame,cangen:生成测试流量,canplayer:回放 CAN Log,cansniffer:观察 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 Interface,type can:设置 CAN Link 参数,bitrate:CAN 仲裁阶段 Bitrate。
CAN FD:
ip link set can0 type can bitrate <bitrate> dbitrate <dbitrate> fd on
其中:bitrate:Arbitration Bitrate,dbitrate:Data Phase Bitrate,fd 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 调试。