2019年4月10日 星期三

CAN BUS 通訊研究

📺 影片版(約 5 分鐘):想先看實際操作的話可以直接看這支影片,完整步驟、設定與程式碼在下方文字版。
YouTube:https://youtu.be/5Y_xPVi-LqU(winson-DIY 頻道)
⚡ Shorts 精華版(39 秒):https://youtube.com/shorts/cOeMpwUNWq0
剛好有客戶用到 USB/CAN Adapter要來驗證CANBUS上的通訊訊號!!
所以就先搭上 之前的CAR ECU Simulator 跟之前用Arduino Mega2560加MCP2515 CAN Module做的 CAN BUS 診斷器!

如圖上的device 依照腳位定義將CANH與 CANL並接一起


arduino 上的code MCP2515有提供sample code 很好開發
最主要是initial這段 我試蠻久的為了跟ECU做通訊!!

上圖為詢問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。

補充二、接線要點
介面CANHCANLGND
DB9(PCAN-USB、多數 USB/CAN 轉接器,CiA 303-1)pin 7pin 2pin 3
OBD-II 16 pin 接頭(車上 / ECU 模擬器)pin 6pin 14pin 4、5
MCP2515 模組端子 H端子 LGND
  • 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_500KBPSbus 速率要跟 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 ID0x7DF:廣播查詢,所有 ECU 都會收0x7E8:1 號 ECU(引擎)的回應(回應 ID = 請求 ID 0x7E0 + 8)
Byte 002:後面有 2 個有效位元組03:後面有 3 個有效位元組
Byte 101:Mode 01,讀即時資料41:0x01 + 0x40,代表肯定回應
Byte 20D: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 計算。

補充六、收不到資料時的檢查順序
  1. 速率:所有節點同一個 bit rate(PCAN-View 連線時選的速率、Arduino 的 CAN_500KBPS、ECU 的設定)。
  2. 晶振:看 MCP2515 模組上晶振的刻字(8.000 或 16.000),程式裡的 MCP_8MHZ / MCP_16MHZ 要對上。
  3. 終端電阻:斷電量 CANH–CANL 約 60 Ω。
  4. 接線:CANH / CANL 有沒有接反、GND 有沒有共地。
  5. 模式:有沒有 setMode(MCP_NORMAL);bus 上至少要有兩個 Normal 模式的節點,才有人回 ACK。
  6. 用 PCAN-View 當第三者:先讓 PCAN 看得到 ECU 的回應,再加入 Arduino 或待測設備,這樣出問題時比較容易分辨是哪一邊。這次驗證客戶的 USB/CAN Adapter 也是同樣做法:ECU、PCAN、Arduino 都確定正常,剩下的差異就只會在待測設備。



延伸閱讀

沒有留言:

張貼留言