can口通信詳解
can口分爲:
標準幀:使用can_id的0~10位作爲標識符
擴展幀:使用can_id的0~28位作爲標識符
遠程幀:由總線上的節點發出,用於請求其他節點發送具有同一標識符的數據幀。當某個節點需要數據時,可以發送遠程幀請求另一節點發送相應數據幀。與數據幀相比,遠程幀沒有數據場(主要用來避免數據同時發送造成衝突,數據幀的優先級大於遠程幀)
數據幀:數據幀攜帶數據從發送器至接收器
錯誤幀:任何單元,一旦檢測到總線錯誤就發出錯誤幀。錯誤幀由兩個不同的場組成,第一個場是由不同站提供的錯誤標誌的疊加(錯誤標誌),第二個場是錯誤界定符
can_id的第29、30、31位是幀的標誌位,用來定義幀的類型
#define CAN_EFF_FLAG 0x80000000U //擴展幀的標識
#define CAN_RTR_FLAG 0x40000000U //遠程幀的標識
#define CAN_ERR_FLAG 0x20000000U //錯誤幀的標識,用於錯誤檢查
使用can口時,要先設置速率,只有速率一致才能收發到數據。
- 使用IP命令 設置波特率
ifconfig can0 down //一定要先關閉才能設置
ip link set can0 type can bitrate 125000 //設置波特率
ifconfig can0 up //打開can接口
- 使用canconfig命令 設置波特率
ifconfig can0 down //一定要先關閉才能設置
canconfig can0 bitrate 125000 ctrlmode triple-sampling on //設置波特率
canconfig can0 start
ifconfig can0 up //打開can接口
使用canconfig命令時,如果沒有需要交叉編譯;
先下載 canutils和 libsocketcan下載鏈接
首先編譯ibsocketcan庫
tar -xvf canutils-4.0.6.tar.bz2
cd libsocketcan-0.0.10/
mkdir out
./configure --prefix=/home/qt/test/libsocketcan-0.0.10/out --host=arm-none-linux-gnueabi//這裏選擇自己的交叉編譯器
make
make install
生成的庫在 out/lib 下,將這些生成的庫複製到你板子的文件系統內,放在 lib 目錄下也行,放在 usr/lib 裏面也可以
然後編譯canutils庫:
tar -xvf canutils-4.0.6.tar.bz2
cd canutils-4.0.6/
mkdir out
./configure --host=arm-none-linux-gnueabi --prefix=/home/qt/test/canutils-4.0.6/out libsocketcan_LIBS=-lsocketcan LDFLAGS="-L/home/qt/test/libsocketcan-0.0.10/out/lib/" libsocketcan_CFLAGS="-I/home/qt/test/libsocketcan-0.0.10/out/include" //注意選擇自己的路徑和自己的編譯器
make
make install
然後就編譯成功,生成了canconfig,cansend,candump等小工具都在out目錄中
cansend can0 -i 0x800 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 -e
-e 表示擴展幀,CAN_ID最大29bit,標準幀CAN_ID最大11bit -i表示CAN_ID
candump can0
監聽CAN0數據
C語言代碼實例:
can口初始化函數
int canInit(int fd, char *dev, char id_1, char id_2)
{
int ret;
struct ifreq ifr;
struct sockaddr_can addr;
struct can_filter rfilter[2] = {{0}}; //這裏要接收兩個can_id號,所以建了兩個結構體
char * cmd = (char *)calloc(125, sizeof(char));
memset(cmd, 0, 125);
snprintf(cmd, 125, "ifconfig %s down", dev);
system(cmd);
memset(cmd, 0, 125); //配置can口速率
snprintf(cmd, 125, "canconfig %s bitrate 125000 ctrlmode triple-sampling on", dev);
system(cmd);
memset(cmd, 0, 125);
snprintf(cmd, 125, "canconfig %s start", dev);
system(cmd);
memset(cmd, 0, 125);
snprintf(cmd, 125, "ifconfig %s up", dev);
system(cmd);
fd = socket(PF_CAN,SOCK_RAW,CAN_RAW);
strcpy(ifr.ifr_name,dev); //映射實際的can接口
ret = ioctl(fd ,SIOCGIFINDEX ,&ifr);
if(ret < 0) {
ERR_DEBUG("ioctl error[%d]", ret);
ES_FREE(cmd);
return CAN_IOCTL_ERROR;
}
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(fd, (const struct sockaddr *)&addr, sizeof(addr));
rfilter[0].can_id = id_1; // 要接收的can_id
rfilter[0].can_mask = CAN_SFF_MASK;
rfilter[1].can_id = id_2; //要接收的can_id 這裏這兩個id都能監聽到
rfilter[1].can_mask = CAN_SFF_MASK;
setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(struct can_filter));
ESdata->can_p->fd = fd;
ES_FREE(cmd);
return 0;
}
can口發送函數
int can_send(int sock_fd, int can_id/*發送can_id*/, char * data/*發送的數據*/)
{
int i, j, ret;
struct can_frame frame;
memset(&frame, 0, sizeof(struct can_frame));
if(data == NULL){
ERR_DEBUG("can data is null");
return CAN_DATA_NULL;
}
if(can_id != 0){
frame.can_id = can_id;
frame.can_dlc = 8;
for(i=0;i<8;i++)
frame.data[i] = data[i];
ret = write(sock_fd, &frame, sizeof(struct can_frame));
if(ret != sizeof(frame)) {
ERR_DEBUG("send message error");
return CAN_SEND_ERROR;
}
}
DEBUG("send [0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x]",frame.data[0],frame.data[1],frame.data[2],frame.data[3],frame.data[4],frame.data[5],frame.data[6],frame.data[7]);
return 0;
}
can口接收函數
int can_recv(const int fd, char * recv_data/*接收buf*/)
{
int i;
int nbytes;
struct can_frame frame;
nbytes = read(fd, &frame, sizeof(frame));
if(nbytes > 0){
memset(recv_data, 0, CAN_LEN);
DEBUG("recv from [0x%x] dlc [%d]",frame.can_id,frame.can_dlc);
for(i = 0; i < nbytes; i++)
recv_data[i] = frame.data[i];
DEBUG("recv [%x %x %x %x %x %x %x %x]", recv_data[0], recv_data[1], recv_data[2], recv_data[3], recv_data[4], recv_data[5], recv_data[6], recv_data[7]);
}else{
ERR_DEBUG("can receive error");
return CAN_RECV_ERROR;
}
return 0;
}