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;
}