Linux CAN测试

一、代码

如下是:100ms发送1次帧ID为0x123的和0x321的CAN报文的代码,用的是定时器,只有一路CAN0。IMX8QM

//g++ can_time.cpp -lrt -o can_send_arm
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <error.h>
 
#include <linux/can.h>
#include <linux/can/raw.h>
 
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/time.h>
#define CAN_ID 0x1
 
int socket_fd_can0 = -1;
struct can_frame frame_send_can0, frame_send_can1;
//事件1
void timeout1(int sig)
{
  	struct timespec mytime;
  	int nbytes;
  	clock_gettime(CLOCK_REALTIME, &mytime);
  	if(socket_fd_can0 != -1)
	{
		printf("can0 write time:%lds %ldns\n",mytime.tv_sec,mytime.tv_nsec);
		nbytes = write(socket_fd_can0, &frame_send_can0, sizeof(struct can_frame));//发送
		nbytes = write(socket_fd_can0, &frame_send_can1, sizeof(struct can_frame));//发送
		if(nbytes > 0)
		{
			printf("can0 Send:ID=0x%X DLC=%d Data ", frame_send_can0.can_id,frame_send_can0.can_dlc);
			for(int i=0;i<sizeof(frame_send_can0.data)/sizeof(frame_send_can0.data[0]);i++)
			{
				printf("%X ",frame_send_can0.data[i]);
			}
			printf("\n");
			for(int i=0;i<sizeof(frame_send_can1.data)/sizeof(frame_send_can1.data[0]);i++)
			{
				printf("%X ",frame_send_can1.data[i]);
			}
			printf("\n");
		}else{
			perror("CAN0 write error:");
		}
	}
}
 
void settime_alarm(void)//定时器配置
{
    static struct itimerval tick;
    signal(SIGALRM, &timeout1);
    memset(&tick, 0, sizeof(tick));
    //第一次调用时间
    tick.it_value.tv_sec = 1;
    tick.it_value.tv_usec = 0;
    //第一次调用后,每次调用间隔时间
    tick.it_interval.tv_sec = 0;
    tick.it_interval.tv_usec = 100000;//100ms
 
    if(setitimer(ITIMER_REAL, &tick, NULL) < 0)
    {
        printf("Set timer failed!\n");
    }
}
 
int main(void)
{
  	//can0数据
 	frame_send_can0.can_id  = 0x123;
	frame_send_can0.can_dlc = 8;
	frame_send_can0.data[0] = 0x11;
	frame_send_can0.data[1] = 0x11;
	frame_send_can0.data[2] = 0x11;
	frame_send_can0.data[3] = 0x11;
	frame_send_can0.data[4] = 0x11;
	frame_send_can0.data[5] = 0x11;
	frame_send_can0.data[6] = 0x66;
  	frame_send_can0.data[7] = 0x77;
  	//can1数据
  	frame_send_can1.can_id  = 0x321;
	frame_send_can1.can_dlc = 8;
	frame_send_can1.data[0] = 0x11;
	frame_send_can1.data[1] = 0x11;
	frame_send_can1.data[2] = 0x22;
	frame_send_can1.data[3] = 0x33;
	frame_send_can1.data[4] = 0x44;
	frame_send_can1.data[5] = 0x55;
	frame_send_can1.data[6] = 0x66;
  	frame_send_can1.data[7] = 0x77;
 
	int nbytes;
	struct sockaddr_can addr_can0;
  	struct timespec mytime;
 
	
	//定时器配置
	settime_alarm();
	//can0配置
	if((socket_fd_can0 = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
			perror("can0 Error while opening socket");
			return -1;
	}
	const char *ifname0 = "can0";
  	struct ifreq ifr0;
	strcpy(ifr0.ifr_name, ifname0);
	ioctl(socket_fd_can0, SIOCGIFINDEX, &ifr0);
	addr_can0.can_family  = AF_CAN;
	addr_can0.can_ifindex = ifr0.ifr_ifindex;
	printf("can0 %s at index %d\n", ifname0, ifr0.ifr_ifindex);
	if(bind(socket_fd_can0, (struct sockaddr *)&addr_can0, sizeof(addr_can0)) < 0) {
		perror("can0 Error in socket bind");
		return -2;
	}
  	while(1)
	{  
		usleep(100);
  	}	
	return 0;
}

如下是:100ms发送1次帧ID为0x123的和0x134的CAN报文的代码,用的是定时器。2路CAN,CAN1和CAN0。Xavier

//g++ can.cpp -lrt -o can
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <error.h>
 
#include <linux/can.h>
#include <linux/can/raw.h>
 
#include <time.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/time.h>
#define CAN_ID 0x1
 
int socket_fd_can0 = -1;
int socket_fd_can1 = -1;
struct can_frame frame_send_can0, frame_send_can1;
//struct can_frame frame_recv;
 
//事件1
void timeout1(int sig)
{
  struct timespec mytime;
  int nbytes;
  clock_gettime(CLOCK_REALTIME, &mytime);
  if(socket_fd_can0 != -1)
  {
    printf("can0 write time:%lds %ldns\n",mytime.tv_sec,mytime.tv_nsec);
    nbytes = write(socket_fd_can0, &frame_send_can0, sizeof(struct can_frame));//发送
	  if(nbytes > 0)
	  {
      printf("can0 Send:ID=0x%X DLC=%d Data ", frame_send_can0.can_id,frame_send_can0.can_dlc);
	    for(int i=0;i<sizeof(frame_send_can0.data)/sizeof(frame_send_can0.data[0]);i++){
	      printf("%X ",frame_send_can0.data[i]);
	    }
	    printf("\n");
	  }
	  else{
		perror("CAN0 write error:");
       // printf("CAN0 write error:%s\n", strerror(error));
	  }
  }
  if(socket_fd_can1 != -1)
  {
    printf("can1 write time:%lds %ldns\n",mytime.tv_sec,mytime.tv_nsec);
    nbytes = write(socket_fd_can1, &frame_send_can1, sizeof(struct can_frame));//发送
    if(nbytes > 0)
	  {
      printf("can1 Send:ID=0x%X DLC=%d Data ", frame_send_can1.can_id,frame_send_can1.can_dlc);
	    for(int i=0;i<sizeof(frame_send_can1.data)/sizeof(frame_send_can1.data[0]);i++){
	      printf("%X ",frame_send_can1.data[i]);
	    }
	    printf("\n");
	  }else{
perror("CAN1 write error:");
       // printf("CAN1 write error:%s\n", strerror(error));
	  }
  }
}
 
void settime_alarm(void)//定时器配置
{
    static struct itimerval tick;
    
    signal(SIGALRM, &timeout1);
    memset(&tick, 0, sizeof(tick));
 
    //第一次调用时间
    tick.it_value.tv_sec = 1;
    tick.it_value.tv_usec = 0;
	
    //第一次调用后,每次调用间隔时间
    tick.it_interval.tv_sec = 0;
    tick.it_interval.tv_usec = 100000;//100ms
 
    if(setitimer(ITIMER_REAL, &tick, NULL) < 0)
    {
        printf("Set timer failed!\n");
    }
}
 
int main(void)
{
  //can0数据
 	frame_send_can0.can_id  = 0x123;
	frame_send_can0.can_dlc = 8;
	frame_send_can0.data[0] = 0x11;
	frame_send_can0.data[1] = 0x11;
	frame_send_can0.data[2] = 0x11;
	frame_send_can0.data[3] = 0x11;
	frame_send_can0.data[4] = 0x11;
	frame_send_can0.data[5] = 0x11;
	frame_send_can0.data[6] = 0x66;
  frame_send_can0.data[7] = 0x77;
  //can1数据
  frame_send_can1.can_id  = 0x134;
	frame_send_can1.can_dlc = 8;
	frame_send_can1.data[0] = 0x11;
	frame_send_can1.data[1] = 0x11;
	frame_send_can1.data[2] = 0x22;
	frame_send_can1.data[3] = 0x33;
	frame_send_can1.data[4] = 0x44;
	frame_send_can1.data[5] = 0x55;
	frame_send_can1.data[6] = 0x66;
  frame_send_can1.data[7] = 0x77;
 
	int nbytes;
	struct sockaddr_can addr_can0, addr_can1;
  struct timespec mytime;
 
	
  //定时器配置
  settime_alarm();
  //can0配置
  if((socket_fd_can0 = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("can0 Error while opening socket");
		return -1;
	}
	const char *ifname0 = "can0";
  struct ifreq ifr0;
	strcpy(ifr0.ifr_name, ifname0);
	ioctl(socket_fd_can0, SIOCGIFINDEX, &ifr0);
	addr_can0.can_family  = AF_CAN;
	addr_can0.can_ifindex = ifr0.ifr_ifindex;
	printf("can0 %s at index %d\n", ifname0, ifr0.ifr_ifindex);
	if(bind(socket_fd_can0, (struct sockaddr *)&addr_can0, sizeof(addr_can0)) < 0) {
		perror("can0 Error in socket bind");
		return -2;
	}
  //can1配置
  if((socket_fd_can1 = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
		perror("can1 Error while opening socket");
		return -1;
	}
  const char *ifname1 = "can1";
  struct ifreq ifr1;
	strcpy(ifr1.ifr_name, ifname1);
	ioctl(socket_fd_can1, SIOCGIFINDEX, &ifr1);
	addr_can1.can_family  = AF_CAN;
	addr_can1.can_ifindex = ifr1.ifr_ifindex;
	printf("can1 %s at index %d\n", ifname1, ifr1.ifr_ifindex);
	if(bind(socket_fd_can1, (struct sockaddr *)&addr_can1, sizeof(addr_can1)) < 0) {
		perror("can1 Error in socket bind");
		return -2;
	}
   
  while(1)
	{  
    /*
		nbytes = read(socket_fd_can0, &frame_recv, sizeof(struct can_frame)); //接收报文
    if(CAN_ID != frame_recv.can_id) continue;
    //显示报文
    if(nbytes > 0)
    {
      clock_gettime(CLOCK_REALTIME, &mytime);
      printf("read time:%lds---%ldns\n",mytime.tv_sec,mytime.tv_nsec);
      printf("Recv:ID=0x%X DLC=%d Data ", frame_recv.can_id, frame_recv.can_dlc);
      for(int i=0;i<sizeof(frame_recv.data)/sizeof(frame_recv.data[0]);i++){
        printf("%X ",frame_recv.data[i]);
      }
      printf("\n");
    }*/
  }	
	return 0;
}

二、涉及的技术

2.1 自启动

第一种方法是在/etc/profile.d改

经过反复多轮的测试,这种方法遇见很多异常问题,比如,自启了两遍程序(这个是在Xavier上测试的)。还有一个是在IMX8QM上的,遇到的问题更加离谱,只发一次,用ssh连接进入系统后,才是正常的。

方法:在/etc/profile.d目录下创建一个sh文件,比如can_start.sh,内容为:

Linux开机自启_偏安一隅,占山为王的博客-CSDN博客_linux开机自启动

第二种方法是/etc/rc.local文件中改,

cd /home/root && ./can

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.


echo 30000 >  /proc/sys/vm/min_free_kbytes

########在这里加
cd /home/root && chmod 777 can &&./can
###也可以换成这样的
chmod 777 /home/root/can && ./home/root/can
exit 0

2.2 定时器中断

见程序代码

三、开发遇到的问题

3.1 CAN 发送抛出错误 “No buffer space available”

由于缓冲队列空间不足 

设置即可

echo  4096 > /sys/class/net/can0/tx_queue_len

首先,你查看你原来的tx_queue_len,应该默认是10,设置之后,再cat查看一下,

cat /sys/class/net/can0/tx_queue_len

最后应该是4096。

注意事项:

网上还有一种做法是:暂时没找到命令,但是设置之后,程序运行一会,会卡死在一个位置。然后我又把缓冲区大小设置为1000

3.2 测试干扰条件下,产生错误帧

云控的CAN测试没通过,远程协助后解决了开机不连续发的问题,但在测试中,当测试干扰频率达到19MHZ左右出现错误帧,错误帧累计到一定数量后(第一次是224个,第二次是63个)控制器停发数据,干扰撤除后不恢复,重新上电,控制器恢复CAN数据发送。

错误帧:

CAN总线基础知识(4)——CAN的错误帧_Easy Code的博客-CSDN博客_错误帧

CAN总线错误帧详解_freshcoolman的博客-CSDN博客_can错误帧

CAN总线抗干扰的六种解决方案_e调布鲁斯的博客-CSDN博客_can总线抗干扰

linux can简介_pingis58的博客-CSDN博客_can linux

Linux CAN编程详解_panfei263031的博客-CSDN博客_linux can编程

CAN开发:

Linux应用开发【第十四章】CAN编程应用开发_韦东山的博客-CSDN博客_can编程

插播

12、选择好的无限循环

在编程中,我们常常需要用到无限循环,常用的两种方法是while (1) 和 for (;;)。这两种方法效果完全一样,但那一种更好呢?然我们看看它们编译后的代码:

编译前:
 

while (1);

编译后:
mov eax,1

test eax,eaxje foo+23hjmp foo+18h编译前:
for (;;);

编译后:

jmp foo+23h

显然,for (;;)指令少,不占用寄存器,而且没有判断,跳转,比while (1)好。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_42475191

谢谢老板

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值