1. 简述
对于IMX处理器,NXP已提供CAN驱动,在内核中使能CAN驱动。如果没有CAN控制器,可以通过SPI-CAN芯片拓展。在linux中使用socket进行CAN通信,如果之前开发过linux网络编程,那么开发起来非常顺手。开发SocketCan一定要熟悉内核提供的CAN文档,它是CAN应用开发的依据。canutils提供can测试脚本,可以分析canutils源码,帮助CAN应用开发。
内核版本: 5.4
linux-kernel/Documentation/networking/can.rst
2. 函数
/* 1. 帧结构体 */
/* linux-kernel/include/linux/can.h */
struct can_frame {
canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
__u8 can_dlc; /* frame payload length in byte (0 .. 8) */
__u8 __pad; /* padding */
__u8 __res0; /* reserved / padding */
__u8 __res1; /* reserved / padding */
__u8 data[8] __attribute__((aligned(8)));
};
CAN_ID为32位:
标准帧使用低0~10位;
扩展帧使用0~28位:
29/30/31位用于定义帧类型;
100(0)-扩展帧
010(0)-远程帧
001(0)-错误帧
可以通过与以下宏进行或运算指定帧类型:
#define CAN_EFF_FLAG 0x80000000U /* 扩展帧 */
#define CAN_RTR_FLAG 0x40000000U /* 远程帧 */
#define CAN_ERR_FLAG 0x20000000U /* 错误帧 */
/* 2. 过滤 */
struct can_filter {
canid_t can_id;
canid_t can_mask;
};
样例:
struct can_filter rfilter[2];
rfilter[0].can_id = 0x123;
rfilter[0].can_mask = CAN_SFF_MASK;
rfilter[1].can_id = 0x200;
rfilter[1].can_mask = 0x700;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
2. 代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#define OPEN_CAN "ifconfig can0 up" /* 打开CAN */
#define CLOSE_CAN "ifconfig can0 down" /* 关闭CAN */
#define SET_CAN "ip link set can0 type can bitrate 500000 triple-sampling on" /* 设置CAN */
int main(void)
{
int ret;
int fd = -1;
struct sockaddr_can addr = {0};
struct can_frame frame = {0};
struct ifreq ifr = {0};
/* 1. 打开和设置CAN */
system(OPEN_CAN);
system(SET_CAN);
/* 2. 打开套接字 */
fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (fd < 0)
{
return 0;
}
/* 3. 指定CAN设备 */
strcpy(ifr.ifr_name, "can0");
ioctl(fd, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
/* 4. CAN与套接字进行绑定 */
ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
/* 5. 设置过滤规则 */
setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
/* 6. 设置数据格式 */
frame.data[0] = 0x11;
frame.data[1] = 0x22;
frame.data[2] = 0x33;
frame.data[3] = 0x44;
frame.data[4] = 0x55;
frame.data[5] = 0x66;
frame.data[6] = 0x77;
frame.data[7] = 0x88;
frame.can_dlc = 8; /* 数据长度为8 */
frame.can_id = 0xFF; /* ID为0xFF */
/* 7. 发送数据 */
for(;;)
{
ret = write(fd, &frame, sizeof(frame)); //发送数据
if(sizeof(frame) != ret)
{
perror("write error");
}
sleep(1);
}
/* 8. 关闭套接字 */
close(fd);
/* 9. 关闭CAN */
system(CLOSE_CAN);
return 0;
}
cmake_minimum_required(VERSION 3.0.0)
project(my_can VERSION 0.1.0)
include(CTest)
enable_testing()
add_executable(my_can my_can.c)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
3. 测试
最近CAN卡不在手里,待测试。
4. 工具
canutils(推荐使用buildroot或yocot移植)
不需要编写应用程序,可对CAN做测试,有时间可以分析源码。
ip link set can0 type can bitrate 500000 /* 设置波特率 */
ifconfig can0 up /* 打开CAN */
candump can0 /* 接收数据 */
cansend can0 FF#11.22.33.44.55.66.77.88 //发送数据
/*
FF: 帧ID;
11 22 33 44 55 66 77 88为十六进制数据,用点隔开;
*/
ifconfig can0 down /* 关闭CAN */
ip link set can0 type can bitrate 500000 loopback on /* 回环测试 */