概述

CAN 协议通信技术在自动化领域,嵌入式器件编程,和汽车领域等具有广泛的应用。Socketcan 套接字是 Linux 下 CAN 协议的实现方法,使用socket API将CAN器件驱动程序实现为网络接口,最终以套接字接口呈现给用户。

CAN常用操作命令

可以使用ip命令来查看或设置CAN,使用ifconfig或ip命令来开启/关闭CAN,canconfig工具来配置和调试CAN,cansend 和 candump用于收发CAN报文。
#ifconfig -a //查到当前can网络 can0 can1,包括收发包数量、是否有错误等等
#ip link set canX down //关闭can设备;或使用ifconfig canX down
#ip link set canX up //开启can设备;或使用ifconfig canX up
#ip -details link show canX //显示can设备详细信息;
#ip link set canX up type can bitrate 250000 //设置can波特率
#canconfig canX ctrlmode loopback on //回环测试;
#canconfig canX restart // 重启can设备;
#canconfig canX stop //停止can设备;
#canecho canX //查看can设备总线状态;
#candump canX //接收can总线发来的数据;
#cansend canX --identifier=ID+数据 //发送数据;
#candump canX --filter=ID:mask//使用滤波器接收ID匹配的数据
应用示例:

# ifconfig
can0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          UP RUNNING NOARP  MTU:16  Metric:1
          RX packets:302165 errors:3 dropped:0 overruns:0 frame:1
          TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:10 
          RX bytes:2417320 (2.3 MiB)  TX bytes:32 (32.0 B)
          Interrupt:48 

can1      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          UP RUNNING NOARP  MTU:16  Metric:1
          RX packets:551384 errors:111248 dropped:0 overruns:0 frame:0
          TX packets:10 errors:145 dropped:1 overruns:0 carrier:1
          collisions:0 txqueuelen:10 
          RX bytes:4411072 (4.2 MiB)  TX bytes:88 (88.0 B)
          Interrupt:49 
#ip link set can0 down
#ip link set can0 up type can bitrate 1000000
at91_can f000c000.can can0: writing AT91_BR: 0x00050232
IPv6: ADDRCONF(NETDEV_CHANGE): can0: link becomes ready
#ip -details link show can0
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 
          bitrate 1000000 sample-point 0.727 
          tq 90 prop-seg 3 phase-seg1 4 phase-seg2 3 sjw 1
          at91_can: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 2..128 brp-inc 1
          clock 66000000
#cansend can0 123#1122334455667788
#candump can0
  can0  0000F102   [8]  00 00 00 00 00 00 00 00
  can0  0000F103   [8]  00 00 00 00 00 00 00 00
  can0  0000F104   [8]  00 00 00 00 00 00 00 00

CAN应用编程

由于系统将 CAN 设备作为网络设备进行管理,因此在 CAN 总线应用开发方面, Linux 提供了SocketCAN 接口,使得 CAN 总线通信近似于和以太网的通信,应用程序开发接口 更加通用,也更加灵活。使用socketCAN就像使用TCP/IP一样。
实现SocketCAN通信步骤如下:
步骤一:使用socket函数,创建一个CAN套接字
CAN套接字需要使用到一个新的协议族,所以调任用socket(2)这个系统函数的时候需要将 PF_CAN 作为第一个参数。当前有两个 CAN 的协议可以选择,一个是原始套接字协议( raw socket protocol),另一个是广播管理协议 BCM(broadcast manager)。你可以这样来打开一个套接字:

s = socket(PF_CAN, SOCK_RAW, CAN_RAW);或
s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);

步骤二:将CAN套接字绑定某个CAN接口
在成功创建套接字之后,调用ioctl来指定can设备,然后使用 bind(2)函数将套接字绑定在某个 CAN 设备接口上。
步骤三:CAN数据的收发
在绑定 (CAN_RAW)或连接(CAN_BCM) 套接字之后,你可以在套接字上使用 read(2)/write(2) ,也可以使用send(2)/sendto(2)/sendmsg(2)和对应的 recvfrom操作。当然也会有 CAN 特有的套接字选项。CAN 帧结构体和套接字地址结构体定义在 include/linux/can.h中。
每一次通信都采用 can_ frame 结构体将数据封装成帧。

struct can_frame {
canid_t can_id;/* 32 bit CAN_ID + EFF/RTR/ERR flags /
__u8 can_dlc; /
data length code: 0 … 8 */
__u8 data[8] attribute((aligned(8)));
};

can_id:0~28bit为扩展ID(11位基本ID+18位扩展ID)+29bit错误帧标志(0=data frame,1=error frame)+30bit为远程帧请求标志(1=rtr frame)+31bit帧格式标志(0 = standard, 1 = extended)。
can_dlc:CAN报文数据长度,0~8范围。
data: data[]数组,它的字节对齐是 64bit 的。所以,用户通过定义自己的结构体和共同体,可以轻松的访问 CAN 数据载荷。

CAN发送程序示例

CAN发送应用程序示例:

/* 1. 报文发送程序 */
#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[2] = {{0}};
    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 绑定
    //禁用过滤规则,本进程不接收报文,只负责发送
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
    //生成两个报文
    frame[0].can_id = 0x11;
    frame[0]. can_dlc = 1;
    frame[0].data[0] = 'Y';
    frame[1].can_id = 0x22;
    frame[1]. can_dlc = 1;
    frame[1].data[0] = 'N';
    //循环发送两个报文
    while(1)
    {
        nbytes = write(s, &frame[0], sizeof(frame[0])); //发送 frame[0]
        if(nbytes != sizeof(frame[0]))
        {
            printf("Send Error frame[0]\n!");
            break; //发送错误,退出
        }
        sleep(1);
        nbytes = write(s, &frame[1], sizeof(frame[1])); //发送 frame[1]
        if(nbytes != sizeof(frame[0]))
        {
            printf("Send Error frame[1]\n!");
            break;
        }
        sleep(1);
    }
    close(s);
    return 0;
}

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

CAN过滤器的使用

在绑定原始套接字的时候将会默认的filter将会接收所有的数据,修改该特性,必须包含<linux/can/raw.h>。过滤规则(过滤器)的定义在 include/linux/can.h 中:
struct can_filter {
canid_t can_id;canid_t can_mask;
};
过滤器匹配规则:<received_can_id> & mask == can_id & mask
(完)

友情链接 Linux CAN说明文档:https://blog.csdn.net/jirryzhang/article/details/73005707

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐