嵌入式Linux CAN

前言

这是前篇:

本篇介绍下can的使用, 采用米尔MYD-YA157C板子出厂配置的系统. 板子自带一路CAN:
在这里插入图片描述
连接到CAN分析仪上, 启用120Ω终端电阻:
在这里插入图片描述

CAN终端测试

查看CAN接口:

root@myir:~# ls /sys/class/net/
can0  eth0  lo  usb0  wlan0

或者ifconfig -a:

root@myir:~# ifconfig -a
can0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          NOARP  MTU:16  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:10
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          Interrupt:41

可以先关闭CAN网络, 设置波特率等参数, 然后再打开CAN, 进行发送和接收的测试

root@myir:~# ifconfig can0 down
root@myir:~# ip link set can0 type can bitrate 500000
[  211.454557] m_can 4400e000.can can0: bitrate error 0.7%
root@myir:~# ifconfig can0 up
root@myir:~# cansend can0 18F#11.22.33.44.55.66.77.88
root@myir:~# candump can0

其中, 测试发送的cansend can0 18F#11.22.33.44.55.66.77.88(后面是十六进制), 可以在主机上收到信息, 但测试接收的candump can0, 通过can分析仪发送没有收到数据, 还不太清楚原因.

发送时的格式如下:

    <can_id>#{R|data}          for CAN 2.0 frames
    <can_id>##<flags>{data}    for CAN FD frames

<can_id> can have 3 (SFF) or 8 (EFF) hex chars
{data} has 0..8 (0..64 CAN FD) ASCII hex-values (optionally separated by '.')
<flags> a single ASCII Hex value (0 .. F) which defines canfd_frame.flags

e.g. 5A1#11.2233.44556677.88 / 123#DEADBEEF / 5AA# / 123##1 / 213##311
     1F334455#1122334455667788 / 123#R for remote transmission request.

需要注意的是:

  • 标准帧是3个字节, 扩展帧是8个字节(如cansend can0 018F0000#11.22.33.44.55.66.77.88, 前面的0不能少).
  • CAN 2.0是一个#, CAN FD是两个#
  • 配置的参数掉电就会消失, 可以写成脚本放到开机启动里面, 参考TX2/Linux下can总线的接收与发送详解

SocketCAN

SocketCAN - Controller Area Network, 这是Linux内核SocketCAN的介绍.

扫描二维码关注公众号,回复: 10048179 查看本文章

SocketCAN使用Berkeley套接字API, Linux网络堆栈, 将CAN设备驱动程序实现为网络接口, 这样可以用bind等, 避免了复杂的操作, 但与TCP / IP和以太网网络不同的是,CAN总线仅仅是广播媒介,没有像以太网一样的MAC层寻址。CAN标识符(can_id)用于在CAN总线上进行仲裁(ID越小优先级越高)。因此,最好将CAN-ID视为一种源地址。

C语言CAN发送示例

米尔官方的can_send例子需要./can_test -d can0 -i 123 11 2233 44 55 66 77 88, 这里简化下, 编写can_test.c用于测试CAN发送:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <getopt.h>

#define CAN_DEVICE "can0"
#define CAN_ID0 0x18f
#define CAN_DLC 8u
int can_data[8] = {250, 2, 3, 4, 5, 6, 7, 8};

int main(int argc, char *argv[])
{
    int s, nbytes, i;
    struct ifreq ifr;
    struct sockaddr_can addr;
    struct can_frame frame[1];

    /* create a socket */
    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    strcpy(ifr.ifr_name, CAN_DEVICE);
    /* determine the interface index */
    ioctl(s, SIOCGIFINDEX, &ifr);
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    /* bind the socket to a CAN interface */
    bind(s, (struct sockaddr *)&addr, sizeof(addr));
    /* Set the filter rules */
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
    /* generate CAN frames */
    frame[0].can_id = CAN_ID0;
    frame[0].can_dlc = CAN_DLC;
    for (i = 0; i < frame[0].can_dlc; i++)
    {
        frame[0].data[i] = can_data[i];
    }

    /* send CAN frames */
    while (1)
    {
        nbytes = write(s, &frame[0], sizeof(frame[0]));
        if (nbytes < 0)
        {
            perror("can raw socket write");
            return 1;
        }

        ++frame[0].data[7];

        /* paranoid check ... */
        if (nbytes < sizeof(struct can_frame))
        {
            fprintf(stderr, "read: incomplete CAN frame\n");
            return 1;
        }
        usleep(1000000);
    }
    close(s);
    return 0;
}

交叉编译arm-linux-gnueabihf-gcc can_test.c -o can_test2, 放到板子上运行./can_test2, 默认的回环开启:
在这里插入图片描述
CAN分析仪收到的数据:
在这里插入图片描述

C语言CAN接收示例

直接把米尔官方的can_receive.c挪过来:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <net/if.h> 
#include <sys/ioctl.h> 
#include <sys/socket.h> 
#include <linux/can.h> 
#include <linux/can/raw.h> 
#include <getopt.h>

void print_usage (FILE *stream, int exit_code)
{
    fprintf(stream, "Usage: option [ dev... ] \n");
    fprintf(stream,
            "\t-h  --help     Display this usage information.\n"
            "\t-d  --device   The device can[0-1]\n"
	    	"\t-i  --id		  Set the can id that want to receive\n");
    exit(exit_code);
}

/**
 * @brief: main function  
 * @Param: argc: number of parameters
 * @Param: argv: parameters list
 */
int main()  
{  
	int s, nbytes, i;
	char *device="can0";
	int id, next_option, device_flag=0, id_flag=0;  
	struct sockaddr_can addr;  
	struct ifreq ifr;  
	struct can_frame frame;  
	struct can_filter rfilter[1];
	const char *const short_options = "hd:i:";
	const struct option long_options[] = {
		{ "help",   0, NULL, 'h'},
		{ "device", 1, NULL, 'd'},
		{ "id", 1, NULL, 'i'},
		{ NULL,     0, NULL, 0  }
	};
	
	
	/* create a socket */  
	s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
	strcpy(ifr.ifr_name, device);
	/* determine the interface index */  
	ioctl(s, SIOCGIFINDEX, &ifr);                    
	addr.can_family = AF_CAN;  
	addr.can_ifindex = ifr.ifr_ifindex;
	/* bind the socket to a CAN interface */    
	bind(s, (struct sockaddr *)&addr, sizeof(addr));
	
	if (id_flag) {     
		/* define the filter rules */   
		rfilter[0].can_id   = id;  
		rfilter[0].can_mask = CAN_SFF_MASK;  
		/* Set the filter rules */	  
		setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); 
	}  
	while(1) {
		/* receive frame */  
		nbytes = read(s, &frame, sizeof(frame));            
		/* printf the received frame */  
		if (nbytes > 0) { 
			printf("%s  %#x  [%d]  ", ifr.ifr_name, frame.can_id, frame.can_dlc);
			for (i = 0; i < frame.can_dlc; i++)
				printf("%#x ", frame.data[i]); 
			printf("\n"); 
		}  
	}  
	close(s);  
	return 0;  
}  

然而, 上面终端测试没有收到, 这个自然也没有收到, 原因待排查.

如果想要加滤波器, 搬来知乎的这篇写的非常好的文章 Linux CAN编程详解中的例子:

/* 2. 报文过滤接收程序 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main()
{
    int s, nbytes;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    struct can_filter rfilter[1];
    s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字
    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr); //指定 can0 设备
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与 can0 绑定
    //定义接收规则,只接收表示符等于 0x11 的报文
    rfilter[0].can_id = 0x11;
    rfilter[0].can_mask = CAN_SFF_MASK;
    //设置过滤规则
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
    while (1)
    {
        nbytes = read(s, &frame, sizeof(frame)); //接收报文
        //显示报文
        if (nbytes > 0)
        {
            printf("ID = 0x %X DLC = %d data[0] = 0x %X\n", frame.can_id, frame.can_dlc, frame.data[0]);
        }
    }
    close(s);
    return 0;
}

微信公众号

欢迎扫描关注我的微信公众号, 及时获取最新文章:
在这里插入图片描述

发布了203 篇原创文章 · 获赞 105 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/weifengdq/article/details/104951115