📺 影片版(約 5 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
剛好有客戶用到 USB/CAN Adapter要來驗證CANBUS上的通訊訊號!!YouTube:https://youtu.be/5Y_xPVi-LqU(winson-DIY 頻道)
⚡ Shorts 精華版(39 秒):https://youtube.com/shorts/cOeMpwUNWq0
⚡ Shorts 精華版(39 秒):https://youtube.com/shorts/cOeMpwUNWq0
所以就先搭上 之前的CAR ECU Simulator 跟之前用Arduino Mega2560加MCP2515 CAN Module做的 CAN BUS 診斷器!
如圖上的device 依照腳位定義將CANH與 CANL並接一起
arduino 上的code MCP2515有提供sample code 很好開發
最主要是initial這段 我試蠻久的為了跟ECU做通訊!!
回應為ID: 7E8的資料!!
補充一、測試架構
這次是三個節點掛在同一條 CAN bus 上,速率都是 500 kbit/s:
PC(PCAN-View) PC(客戶的軟體)
│ USB │ USB
PCAN-USB Gridconnect USB/CAN Adapter(待驗證的設備)
│ CANH / CANL │ CANH / CANL
───┴────────────┬───────────┴────────────┬────── CAN bus(500 kbit/s)
│ │
CAR ECU Simulator Arduino Mega2560 + MCP2515
(回應 OBD-II 查詢) (自製 CAN BUS 診斷器)
PCAN-View 下方狀態列顯示 Bit rate: 500 kBit/s、Status: OK、Overruns: 0,代表 bus 上沒有錯誤;如果速率設錯或少了終端電阻,這裡會變成 Bus Warning / Bus Off。補充二、接線要點
| 介面 | CANH | CANL | GND |
|---|---|---|---|
| DB9(PCAN-USB、多數 USB/CAN 轉接器,CiA 303-1) | pin 7 | pin 2 | pin 3 |
| OBD-II 16 pin 接頭(車上 / ECU 模擬器) | pin 6 | pin 14 | pin 4、5 |
| MCP2515 模組 | 端子 H | 端子 L | GND |
- CANH 接 CANH、CANL 接 CANL,所有設備並接在一起;接反不會燒,但完全收不到。
- 終端電阻:bus 兩端各一顆 120 Ω。斷電量 CANH 和 CANL 之間應該是約 60 Ω;量到 120 Ω 表示只有一端有,量到 40 Ω 表示多了一顆。MCP2515 模組上有 J1 跳線可以接上板子的 120 Ω,不在 bus 兩端的節點就不要接。
- 共地:不同電源的設備最好把 GND 也接在一起,比較不會有莫名其妙的錯誤。
- Mega2560 的 SPI 腳位和 UNO 不同:MISO 50、MOSI 51、SCK 52;CS 和 INT 依程式設定(例如 CS 53、INT 2),也可以從 ICSP 排針接。
補充三、為什麼 initial 這段要試很久
文中那段
CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ),三個參數都有坑:| 參數 | 意思 | 設錯會怎樣 |
|---|---|---|
MCP_ANY | 不過濾,所有 ID 都收 | 開了遮罩 / 過濾器卻沒設對,會只收到部分或完全收不到 |
CAN_500KBPS | bus 速率 | 要跟 ECU、PCAN 一樣;車上的 OBD-II CAN 多半是 500k,少數 250k |
MCP_8MHZ | 模組上的晶振頻率 | 常見的藍色 MCP2515 模組是 8 MHz 晶振,範例程式常寫 16 MHz;設錯的話實際速率只有一半(設 500k 變 250k),init 會成功但永遠收不到正確資料 |
另外兩個重點:
setMode(MCP_NORMAL)一定要叫:begin() 之後還不是正常模式,要切到 Normal,MCP2515 才會回 ACK、真正參與 bus。只有兩個節點時,如果另一邊沒有 ACK,送出的那一方會一直重送,最後進入錯誤狀態。- 失敗要能重試:文中的寫法是 init 失敗就把
flag.CANFlag留在 0 下次再試,成功才vTaskResume(hcan)把負責收送 CAN 的 FreeRTOS task 叫起來,這樣模組晚一點上電也不會整個卡死。
補充四、OBD-II 查詢車速的封包解讀
文中的兩張截圖,就是一次標準的 OBD-II(ISO 15765-4)查詢與回應:
送出 ID 0x7DF DLC 8 02 01 0D 00 00 00 00 00 回應 ID 0x7E8 DLC 4 03 41 0D 6D
| 位元組 | 查詢 | 回應 |
|---|---|---|
| CAN ID | 0x7DF:廣播查詢,所有 ECU 都會收 | 0x7E8:1 號 ECU(引擎)的回應(回應 ID = 請求 ID 0x7E0 + 8) |
| Byte 0 | 02:後面有 2 個有效位元組 | 03:後面有 3 個有效位元組 |
| Byte 1 | 01:Mode 01,讀即時資料 | 41:0x01 + 0x40,代表肯定回應 |
| Byte 2 | 0D:PID 0x0D,車速 | 0D:回的是哪個 PID |
| Byte 3 | — | 6D:車速 = 0x6D = 109 km/h |
Serial Monitor 上另一筆是
03 41 0D 6E,就是 110 km/h。模擬器回的 DLC 是 4,真車的 ECU 多半會補滿 8 個位元組(後面填 00、AA 或 55),解析時要看 Byte 0 的長度,不要看 DLC。常用的 Mode 01 PID:
| PID | 項目 | 回應位元組 | 換算 |
|---|---|---|---|
| 0x00 | 支援哪些 PID(01~20) | 4(A B C D) | 32 個 bit,每個 bit 代表一個 PID |
| 0x04 | 引擎負載 | 1(A) | A × 100 / 255 % |
| 0x05 | 冷卻水溫 | 1(A) | A − 40 °C |
| 0x0C | 引擎轉速 | 2(A B) | (A × 256 + B) / 4 rpm |
| 0x0D | 車速 | 1(A) | A km/h |
| 0x11 | 節氣門位置 | 1(A) | A × 100 / 255 % |
補充五、Arduino 查詢車速的完整範例
用 MCP2515 的 mcp_can 函式庫(Library Manager 搜尋
mcp_can,coryjfowler 版),每秒查一次車速並解析回應:#include <SPI.h>
#include <mcp_can.h>
#define CAN0_CS 53 // Mega2560 的 SS;依實際接線修改
#define CAN0_INT 2 // MCP2515 的 INT 腳
MCP_CAN CAN0(CAN0_CS);
void setup() {
Serial.begin(115200);
while (CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) != CAN_OK) {
Serial.println("CAN init fail, retry...");
delay(1000);
}
CAN0.setMode(MCP_NORMAL); // 切到正常模式才會回 ACK
pinMode(CAN0_INT, INPUT);
Serial.println("CAN init ok");
}
unsigned long last = 0;
void loop() {
// 每秒送一次:Mode 01、PID 0x0D(車速)
if (millis() - last >= 1000) {
last = millis();
byte req[8] = {0x02, 0x01, 0x0D, 0, 0, 0, 0, 0};
CAN0.sendMsgBuf(0x7DF, 0, 8, req); // 0 = 標準 11-bit ID
}
// INT 腳拉低代表有資料
if (digitalRead(CAN0_INT) == LOW) {
long unsigned int id;
byte len, buf[8];
CAN0.readMsgBuf(&id, &len, buf);
// 0x7E8~0x7EF 是各 ECU 的回應;41 0D = 車速的肯定回應
if (id >= 0x7E8 && id <= 0x7EF && buf[1] == 0x41 && buf[2] == 0x0D) {
Serial.print("Speed: ");
Serial.print(buf[3]);
Serial.println(" km/h");
}
}
}
要查轉速就把 0x0D 換成 0x0C,回應改用 (buf[3] * 256 + buf[4]) / 4 計算。補充六、收不到資料時的檢查順序
- 速率:所有節點同一個 bit rate(PCAN-View 連線時選的速率、Arduino 的
CAN_500KBPS、ECU 的設定)。 - 晶振:看 MCP2515 模組上晶振的刻字(8.000 或 16.000),程式裡的
MCP_8MHZ/MCP_16MHZ要對上。 - 終端電阻:斷電量 CANH–CANL 約 60 Ω。
- 接線:CANH / CANL 有沒有接反、GND 有沒有共地。
- 模式:有沒有
setMode(MCP_NORMAL);bus 上至少要有兩個 Normal 模式的節點,才有人回 ACK。 - 用 PCAN-View 當第三者:先讓 PCAN 看得到 ECU 的回應,再加入 Arduino 或待測設備,這樣出問題時比較容易分辨是哪一邊。這次驗證客戶的 USB/CAN Adapter 也是同樣做法:ECU、PCAN、Arduino 都確定正常,剩下的差異就只會在待測設備。
延伸閱讀


沒有留言:
張貼留言