一、代码
如下是: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,1test eax,eax
je foo+23h
jmp foo+18h
编译前:for (;;);
编译后:
jmp foo+23h
显然,for (;;)指令少,不占用寄存器,而且没有判断,跳转,比while (1)好。