一、项目背景
在车载诊断场景中,LIN 总线常被用于低速从节点(如门控、座椅、灯控 ECU)。
本项目目标是:
✅ 使用ESP32‑S3实现一个LIN Master
✅ 在 LIN 上传输UDS(Unified Diagnostic Services)
✅ 支持多帧 DID 读取(如 VIN)
✅ 可被CANoe 正确解析,无 Spike / 无协议错误
二、系统架构
1. 硬件架构
ESP32‑S3 UART1 ──> LIN 收发器 ──> LIN Bus (TJA1021)- UART:19200 bps,8N1
- UART:19200 bps,8N1
- TX/RX:ESP32‑S3 可映射 IO
- EN:控制 LIN 收发器使能
2. 软件分层
+----------------------+ | UDS Service | ReadDID (0x22) +----------------------+ | ISO‑TP (LIN) | FF / CF 拼包 +----------------------+ | LIN Frame | Header / Data / Checksum +----------------------+ | LIN Physical | Break / Sync / PID +----------------------+三、LIN Master 的关键实现点
1. 为什么不能直接用 Arduino Serial?
在最初实现中,使用了:
Serial1.end(); GPIO 拉低生成 Break; Serial1.begin();👉 结果在 CANoe 中出现:
- Spike(几十 µs 毛刺)
- WakeupRequest 误判
- framing error during bus idle phase
CANoe Trace异常通讯截图如下:
✅根因:
UART 关闭 / 重新初始化 → TX 引脚进入不确定态 → LIN 收发器检测到毛刺
✅ 正确做法:GPIO 生成 Break,但不重启 UART
最终稳定方案
void linBreak() { uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(2)); pinMode(LIN_TX_PIN, OUTPUT); digitalWrite(LIN_TX_PIN, LOW); delayMicroseconds(900); // ≥13bit digitalWrite(LIN_TX_PIN, HIGH); delayMicroseconds(100); // delimiter uart_set_pin(UART_PORT, LIN_TX_PIN, LIN_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); }✅ CANoe 验证结果:
- 无 Spike
- 无 WakeupRequest
- Sync 100% 正确
CANoe Trace正常通讯截图如下:
四、标准 sendFrame 实现(完全符合 LIN 规范)
为避免所有长度错误,最终采用自动补齐 + 自动校验:
void sendFrame(uint8_t id, uint8_t *data, uint8_t len) { uint8_t buf[8]; for (int i = 0; i < 8; i++) buf[i] = (i < len) ? data[i] : 0xFF; // padding sendHeader(id); uart_write_bytes(UART_PORT, (const char*)buf, 8); uint8_t cs = checksum(buf, 8); uart_write_bytes(UART_PORT, (const char*)&cs, 1); }五、UDS over LIN 实现要点
1. LIN 没有 Flow Control
这是一个非常容易混淆的点:
| 协议 | 是否有 FC |
|---|---|
| CAN ISO‑TP | ✅ 有 |
| LIN 诊断 | ❌ 没有 |
👉LIN 的节奏完全由 Master 控制
2. 正确的多帧时序
以 VIN(DID F190 / F187)为例:
Master: 3C → 请求 Master: 3D → FF Master: 3D → CF Master: 3D → CF ...✅ 没有 FC
✅ Slave 不主动发帧
六、ISO‑TP 自动处理(无 FC 版本)
核心逻辑:
- 自动识别 SF / FF / CF
- 自动拼接数据
- Master 周期性发送 3D 触发 Slave
if (pci == 0x1) // FF { tpLen = ((d[1]&0x0F)<<8) | d[2]; tpIndex = 0; memcpy(tpBuf, &d[3], 5); tpIndex += 5; tpActive = true; } else if (pci == 0x2 && tpActive) // CF { memcpy(&tpBuf[tpIndex], &d[2], 6); tpIndex += 6; if (tpIndex >= tpLen) { tpActive = false; // 数据接收完成 } }七、CANoe 中的关键验证点
✅ 正确现象
- 无 Spike
- 无 WakeupRequest
- 无 short response
- Trace 中看到:
MasterReq 3C SlaveResp 3D (FF) SlaveResp 3D (CF) ...❌ 常见错误与原因
| 错误 | 原因 |
|---|---|
| short response | Data Field < 8 |
| invalid sync | Break delimiter 错误 |
| Spike | UART 重启 / IO 抖动 |
| timeout byte 7 | 忘记 padding |
八、阶段总结
✅ 本阶段已经实现:
- ESP32‑S3 稳定 LIN Master
- 标准 LIN Break / Header / Checksum
- UDS ReadDID 服务
- ISO‑TP 多帧自动接收
- CANoe 0 error 解析