Professional Documents
Culture Documents
Mình làm series về điện tử ô tô dành cho các bạn sinh viên thích tìm
tòi khám phá. Hy vọng thông tin cung cấp sẽ giúp ích cho các bạn trong quá trình học tập.
Bài đầu tiên xin chia sẻ với các bạn cách kết nối Arduino với canbus của ô tô thông qua cổng
OBD2 để đọc các thông số tốc độ, vòng tua, nhiệt độ, … có rất nhiều thông số có thể đọc qua
can bus nhưng hiện tại trong ví dụ này mình chỉ đọc vòng tua và nhiệt độ nước làm mát thôi nhé.
Các thông số khác đọc tương tự.
Lý do mình chọn arduino vì đây là module rất thông dụng, có rất nhiều thư viện hỗ trợ. Người sử
dụng hầu như chỉ cần ghép nối các thư viện, chỉnh sửa chút ít thông tin là có thể hoàn thành đồ
án một cách nhanh chóng.
Arduino là module dựa vào vi điều khiển AVR nhung câu lệnh đã được chuyển thế sang ngôn
ngữ C dễ nhớ hơn.
Nhưng riêng Arduino due là dùng chip của ARM nhé.
Các bước chuẩn bị như sau.
1. Mua mạch Arduino UNO, module can bus loại kết nối trực tiếp với Arduino (mình dùng
Sunflower can shield V3).
Các bạn có thể dùng module có hình dạng sau, cách giao tiếp vẫn tương tự.
Luu ý khi mua Arduino thì phải mua cả cáp nạp nhé. Arduino nạp luôn trên mạch nên
không cần mạch nạp. Arduino quá thông dụng rồi nên mình không giới thiệu nhiều nữa.
2. Sau khi các bạn có các module này rồi, bước tiếp theo sẽ tải IDE của Arduino. Đây là
giao diện lập trình cho Arduino bằng ngôn ngữ C rất trực quan.
Link: https://en.wikipedia.org/wiki/OBD-II_PIDs
Ví dụ: ta sẽ đọc được giá trị A và B thông quá Can bus. Vòng tua RPM sẽ được tính qua
công thức (256A+B)/4.
Bởi vì giá trị đọc về được là giá trị 8 byte (64 bit). A sẽ là giá trị byte thứ 3, B là giá trị
tại byte thứ 4.
Nhân tiện chúng ta tham khảo cấu trúc bức điện của 1 tin nhắn, giải thích như sau.
Bức điện request: 7DF 02 01 0D 55 55 55 55 55
Bức điện trả lời: 7E8 03 41 0D 32 xx xx xx xx
Identifier:
- 7DF: định nghĩa đây là bức điện yêu cầu thông tin
- 7E8 7EF: định nghĩa đây là bức điện hồi đáp.
Byte (length)
- 02 (thông tin bao gồm 02 dữ liệu: mode và địa chỉ thông tin 01 và 0D):
cho bức điện request
- 03 (bởi vì lúc này thông tin bao gồm 03 dữ liệu: mod, địa chỉ và dữ
liệu phản hồi): cho bức điện phản hồi.
Mode
Request: là 01
Response: 41 (chuyển 04)
PID: chứa địa chỉ các biến cần lấy thông tin.
Ví dụ: RPM thì địa chỉ lấy thông tin là 0C, speed địa chỉ lấy thông tin là 0D.
Ah, Bh,Ch,Dh: dữ liệu đọc về lưu dưới dạng HEX
6. Sơ đồ kết nối.
- Nếu các bạn dùng Sunflower CAN shield thì cách kết nối rất đơn giản, chỉ ghép hai
module vào mới nhau như hình bên dưới. Kết nối qua cổng com có sẵn trên CAN bus
shield như hình chụp. Nhược điểm của kết nối này là mạch Arduino UNO dùng
nguồn 12V từ xe nên IC nguồn rất nóng. Các bạn có thể dùng nguồn ngoài <12 để
thay thế.
Nếu không có cổng DB9 có thể kết nối vào domino như bên dưới. cấp nguồn cho
Arduino là CAN shield có nguồn, không cần phải cấp hai mạch riêng.
- Nếu các bạn dùng module rời nhứ hình dưới thì cách kết nối như sau.
Để kiểm tra lại sơ đồ của Arduino uno, các bạn có thể vào google tra từ “Arduino uno
schematic”
7. Giải thích một số câu lệnh quan trọng.
- #define PID_COOLANT_TEMP 0x05 //địa chỉ để lấy dữ liệu nhiệt đồ
- #define CAN_ID_PID 0x7DF //identifier 0x7DF để chỉ ra đây là bứ điện
yêu cầu thông tin.
- unsigned char tmp[8] = {0x02, 0x01, __pid, 0, 0, 0, 0, 0};
--pid: chưa địa chỉ dữ liệu cần yêu cầu.
Các thông tin khác tham khảo mục trên.
- CAN.sendMsgBuf(CAN_ID_PID, 0, 8, tmp);
CAN_ID_PID: địa chỉ xuất phát của dữ liệu
0: standard frame, 1-extended frame
8: chiều dài dữ liệu là 8 byte
Tmp: nội dung của tin nhắn.
- CAN.readMsgBuf(&len, buf);
&len: chiều dài dữ liệu
Buf: bộ đệm chứa dữ liệu
- while (CAN_OK != CAN.begin(CAN_500KBPS))
Khởi tạo giao thức can bus với tốc độ 500KBPS.
8. Chương trình chính.
#include <SPI.h>
#include "mcp_can.h"
/*SAMD core*/
#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL SerialUSB
#else
#define SERIAL Serial
#endif
void set_mask_filt()
{
/*
* set mask, set both the mask to 0x3ff
*/
CAN.init_Mask(0, 0, 0x7FC);
CAN.init_Mask(1, 0, 0x7FC);
/*
* set filter, we can receive id from 0x04 ~ 0x09
*/
CAN.init_Filt(0, 0, 0x7E8);
CAN.init_Filt(1, 0, 0x7E8);
CAN.init_Filt(2, 0, 0x7E8);
CAN.init_Filt(3, 0, 0x7E8);
CAN.init_Filt(4, 0, 0x7E8);
CAN.init_Filt(5, 0, 0x7E8);
}
void setup()
{
SERIAL.begin(115200);
while (CAN_OK != CAN.begin(CAN_500KBPS)) // init can bus : baudrate = 500k
{
SERIAL.println("CAN BUS Shield init fail");
SERIAL.println(" Init CAN BUS Shield again");
delay(100);
}
SERIAL.println("CAN BUS Shield init ok!");
set_mask_filt();
}
void taskCanRecv1()
{
unsigned char len = 0;
unsigned char buf[8];
SERIAL.println("\r\n------------------------------------------------------------------");
SERIAL.print("Get Data From id: 0x");
SERIAL.println(CAN.getCanId(), HEX);
for(int i = 0; i<len; i++) // print the data
{
SERIAL.print("0x");
SERIAL.print(buf[i]);
SERIAL.print("\t");
}
SERIAL.println("\n");
SERIAL.print("toc do...:");
SERIAL.println((buf[3]*256+buf[4])/4);
}
}
void taskCanRecv2()
SERIAL.println("\r\n------------------------------------------------------------------");
SERIAL.print("Get Data From id: 0x");
SERIAL.println(CAN.getCanId(), HEX);
for(int i = 0; i<len; i++) // print the data
{
SERIAL.print("0x");
SERIAL.print(buf[i]);
SERIAL.print("\t");
}
SERIAL.println("\n");
SERIAL.print("nhiet do...:");
SERIAL.println(buf[3]-40);
}
}
void taskDbg()
{
while(SERIAL.available())
{
char c = SERIAL.read();
}
else if(c>='A' && c<='F')
{
PID_INPUT *= 0x10;
PID_INPUT += 10+c-'A';
}
else if(c>='a' && c<='f')
{
PID_INPUT *= 0x10;
PID_INPUT += 10+c-'a';
}
else if(c == '\n') // END
{
getPid = 1;
Luu ý cho các bạn muốn phát triển ứng dụng mạnh, cần tốc độ xử lý cao, bộ nhớ
trong lớn thì các bạn sinh viên nên học ARM. Đây là vi điều khiển 32 bit tốc độ xử lý
cao, bộ nhớ trong lớn, có sẵn nhiều giao thức giao tiếp on chip SPI, I2C, CAN… sẽ
đáp ứng được tât cả các yêu cầu của các bạn.