醬是創客的ESP32教學主題第20篇,以NodeMCU-32S(使用Arduino IDE)來實作教學,本篇教學Arduino透過SPI介面傳送/接收CAN bus通訊協議,來傳送接收Standard/Extended/Remote request Frame
以下是我們今天的目標
- 傳送接收Standard/Extended/Remote request Frame
- 了解CAN bus ID/DLC/DATA的關聯
設備:
安信可原廠/副廠NodeMCU-32S #露天拍賣 #蝦皮購物
醬是創客CAN bus模組 #露天拍賣 #蝦皮購物
CAN bus發送端接線方式:
NodeMCU-32S(GPIO5)–SPI訊號–醬是創客CAN bus模組(CS)
NodeMCU-32S(GPIO18)–SPI訊號–醬是創客CAN bus模組(SCK)
NodeMCU-32S(GPIO19)–SPI訊號–醬是創客CAN bus模組(SO)
NodeMCU-32S(GPIO23)–SPI訊號–醬是創客CAN bus模組(SI)
DC 5V(+)–醬是創客CAN bus模組(VCC)
DC 0V(-)–醬是創客CAN bus模組(GND)
發送端醬是創客CAN bus模組(H)-CAN訊號–接收端醬是創客CAN bus模組(H)
發送端醬是創客CAN bus模組(L)-CAN訊號–接收端醬是創客CAN bus模組(L)
CAN bus接收端接線方式:
NodeMCU-32S(GPIO5)–SPI訊號–醬是創客CAN bus模組(CS)
NodeMCU-32S(GPIO18)–SPI訊號–醬是創客CAN bus模組(SCK)
NodeMCU-32S(GPIO19)–SPI訊號–醬是創客CAN bus模組(SO)
NodeMCU-32S(GPIO21)–DI訊號–醬是創客CAN bus模組(INT)
NodeMCU-32S(GPIO23)–SPI訊號–醬是創客CAN bus模組(SI)
DC 5V(+)–醬是創客CAN bus模組(VCC)
DC 0V(-)–醬是創客CAN bus模組(GND)
發送端醬是創客CAN bus模組(H)-CAN訊號–接收端醬是創客CAN bus模組(H)
發送端醬是創客CAN bus模組(L)-CAN訊號–接收端醬是創客CAN bus模組(L)
Ai-Thinker安信可NodeMCU-32S的腳位圍(資料來源:安信可)
醬是創客CAN bus模組相關資料
CAN bus的速率是依靠晶片與振盪器而定,以MCP2515這顆晶片來說它理論可以達到1Mbps,這個前提是配合16MHz或20MHz振盪器,如果需要跑到1Mbps需求者,需自行將振盪器元件轉焊換成16MHz或20MHz,一般來說這個模組使用8MHz振盪器,速度會選用125Kbps,以我們做過的專案來說125Kbps其實就很夠用
Arduino IDE mcp2515.h 安裝說明:
1. 下載此ZIP檔案https://github.com/autowp/arduino-mcp2515/archive/master.zip
2. 打開Arduino IDE >> 草稿碼 >> 匯入程式庫 >> 加入.ZIP程式庫,並選擇剛剛下載的ZIP檔案
3. 安裝好後,關閉Arduino IDE,並再重新啟動Arduino IDE
Arduino CAN bus發送端範例程式碼如下,請注意程式碼中的 & 需自行轉換成&符號
//醬是創客 開發實作的好夥伴 #include "WiFi.h" #include <SPI.h> #include <mcp2515.h> struct can_frame canMsg1; struct can_frame canMsg2; //定義使用GPIO5當SPI的CS MCP2515 mcp2515(5); void setup() { //standard frame canMsg1.can_id = 0x0F6; canMsg1.can_dlc = 8; canMsg1.data[0] = 0x8E; canMsg1.data[1] = 0x87; canMsg1.data[2] = 0x32; canMsg1.data[3] = 0xFA; canMsg1.data[4] = 0x26; canMsg1.data[5] = 0x8E; canMsg1.data[6] = 0xBE; canMsg1.data[7] = 0x86; //extended frame canMsg2.can_id = 0x12345678 | CAN_EFF_FLAG; canMsg2.can_dlc = 8; canMsg2.data[0] = 0x0E; canMsg2.data[1] = 0x00; canMsg2.data[2] = 0x00; canMsg2.data[3] = 0x08; canMsg2.data[4] = 0x01; canMsg2.data[5] = 0x00; canMsg2.data[6] = 0x00; canMsg2.data[7] = 0xA0; Serial.begin(115200); mcp2515.reset(); //使用8MHz振盪器並搭配125Kbps速度 mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ); //使用CAN bus NormalMode mcp2515.setNormalMode(); Serial.println("Chosemaker: Write to CAN"); } void loop() { //送出standard frame mcp2515.sendMessage(&canMsg1); delay(1000); //送出extended frame mcp2515.sendMessage(MCP2515::TXB1,&canMsg2); delay(1000); }
Arduino CAN bus接收端範例程式碼如下,請注意程式碼中的 & 需自行轉換成&符號
//醬是創客 開發實作的好夥伴 #include "WiFi.h" #include <SPI.h> #include <mcp2515.h> struct can_frame canMsg; //定義CAN_INT使用GPIO21 #define CAN_INT 21 //定義使用GPIO5當SPI的CS MCP2515 mcp2515(5); char msgString[128]; void setup() { Serial.begin(115200); mcp2515.reset(); //使用8MHz振盪器並搭配125Kbps速度 mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ); //使用CAN bus NormalMode mcp2515.setNormalMode(); //CAN_INT為INPUT模式 pinMode(CAN_INT, INPUT); Serial.println("------- CAN Read ----------"); Serial.println("ID DLC DATA"); } void loop() { //CAN_INT低電位時開始收資料 if(!digitalRead(CAN_INT)) { //讀取CAN Frame資料 mcp2515.readMessage(&canMsg); //判斷ID是standard (11 bits) frame還是extended (29 bits)frame if((canMsg.can_id & 0x80000000) == 0x80000000) sprintf(msgString, "Extended ID: 0x%.8lX DLC: %1d Data:", (canMsg.can_id & 0x1FFFFFFF), canMsg.can_dlc); else sprintf(msgString, "Standard ID: 0x%.3lX DLC: %1d Data:", canMsg.can_id, canMsg.can_dlc); Serial.print(msgString); //判斷ID是否為remote request frame if((canMsg.can_id & 0x40000000) == 0x40000000){ sprintf(msgString, " REMOTE REQUEST FRAME"); Serial.print(msgString); } else { //取出frame資料 for(byte i = 0; i<canMsg.can_dlc; i++){ sprintf(msgString, " 0x%.2X", canMsg.data[i]); Serial.print(msgString); } } Serial.println(); } }
我們在發送端每隔一秒鐘持續發出standard frame和extended frame,在CAN bus設計中standard frame ID有11 bits,extended frame ID有29 bits,DLC代表著資料的長度,DATA的部分常用都是8組0~255
entry 0x400806ac ------- CAN Read ---------- ID DLC DATA Extended ID: 0x12345678 DLC: 8 Data: 0x0E 0x00 0x00 0x08 0x01 0x00 0x00 0xA0 Standard ID: 0x0F6 DLC: 8 Data: 0x8E 0x87 0x32 0xFA 0x26 0x8E 0xBE 0x86