1.先启动can
1.在linux下先使用ifconfig -a
命令
如图显示出来can0和can1。此时can0是打开的,can1是关闭的。
2.使用shell脚本打开你要打开的can口,并设置波特率。
#/bin/sh
ifconfig canX down #把canX关闭
# set bitrate
/sbin/ip link set canX type can bitrate xxxxxx
#设置canX的波特率位xxxxxx
ifconfig canX up #把canX打开
canX:代表你要打开的can口(can0或者can1)。
xxxxxx:代表你要给can口设置的波特率(这个波特率和串口的不一样)。
3.在使用ifconfig -a
命令查看can口状态
2.在确定canX已经开启后开始编写程序(本次使用can0)
#include <stdio.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <linux/socket.h>
#include <linux/can.h>
#include <linux/can/error.h>
#include <linux/can/raw.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
int main(int argc, char *argv[])
{
int s;
int ret;
struct sockaddr_can addr;
struct ifreq ifr;
int master = 3;
int frameId = 0;
if(argc >= 2) {
master = atoi(argv[1]);
}
if(argc >= 3)
frameId = (int)strtol(argv[2], NULL, 0);
if(master < 1)
master = 1;
srand(time(NULL));
s = socket(AF_CAN, SOCK_RAW, CAN_RAW); /* 创建套接字 */
//can编程在linux里是和网络编程一样的,PF_CAN是can的代号,可见下图
if (s < 0) {
perror("socket PF_CAN failed");
return 1;
}
/* 把套接字绑定到can0接口 */
/* 如需要使用can1,将"can0"替换为"can1"*/
strcpy(ifr.ifr_name, "can0");
ret = ioctl(s, SIOCGIFINDEX, &ifr);
if (ret < 0) {
perror("ioctl failed");
return 1;
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
ret = bind(s, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
perror("bind failed");
return 1;
}
/* 设置过滤规则,可要可不要 */
{
struct can_filter filter[2];
/* 第1个规则是可以接收ID为0x200 & 0xFFF的数据帧 */
filter[0].can_id = 0x200 | CAN_EFF_FLAG;
filter[0].can_mask = 0xFFF;
/* 第2个规则是可以接收ID为0x20F& 0xFFF的数据帧 */
filter[1].can_id = 0x20F | CAN_EFF_FLAG;
filter[1].can_mask = 0xFFF;
/* 启用过滤规则,只要CAN0接收到的数据帧满足上面2个规则中的任何一个也被接受*/
ret = setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter));
//sizeof(filter)这个参数为零时,代表禁用规则
if (ret < 0) {
perror("setsockopt failed");
return 1;
}
}
test_can_rw(s, master, frameId); /*在这里对can的报文进行收发*/
close(s);
return 0;
}
static int test_can_rw(int fd, int master, int frameId)
{
int ret;
struct can_frame frdup;
struct timeval tv;
fd_set rset;
while (1)
{
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&rset);
FD_SET(fd, &rset);
/* 可以使用select进行can口的多路复用*/
ret = select(fd+1, &rset, NULL, NULL, NULL);
if (ret == 0) {
myerr("select time out");
return -1;
}
/* select调用无错返回时,表示有符合规则的数据帧到达 */
ret = read(fd, &frdup, sizeof(frdup));
if (ret < sizeof(frdup)) {
myerr("read failed");
return -1;
}
if (frdup.can_id & CAN_ERR_FLAG) { /* 检查数据帧是否错误 */
handle_err_frame(&frdup);
myerr("CAN device error");
continue;
}
print_frame(&frdup); /* 打印数据帧信息 */
ret = write(fd, &frdup, sizeof(frdup)); /* 把接收到的数据帧发送出去 */
if (ret < 0) {
myerr("write failed");
return -1;
}
sleep(1);
}
return 0;
}
/*can是高速、实时的通讯协议,可以精确到毫秒进行传输*/
/*这里进行了毫秒级的打印*/
static void print_frame(struct can_frame *fr)
{
int i;
static char timestr[200] ={0};
struct tm * pTempTm;
struct timeval time1;
/*打印时间(毫秒级的)*/
gettimeofday(&time1,NULL);
pTempTm = localtime(&time1.tv_sec);
if( NULL != pTempTm )
{
snprintf(timestr,199,"%04d-%02d-%02d %02d:%02d:%02d.%03ld",
pTempTm->tm_year+1900,
pTempTm->tm_mon+1,
pTempTm->tm_mday,
pTempTm->tm_hour,
pTempTm->tm_min,
pTempTm->tm_sec,
time1.tv_usec/1000);
printf("timestr = %s\n",timestr);
}
/*打印接收到的数据*/
{
printf("recv: can.id=0x%08x ", fr->can_id & CAN_EFF_MASK);
printf("%08x ", fr->can_id);
printf("dlc = %d ", fr->can_dlc);
printf("data = ");
for (i = 0; i < fr->can_dlc; i++)
printf("%02x ", fr->data[i]);
printf("\n");
}
}