文章摘要:本文描述了关于CAN总线应用程序的编程。
内核版本:linux-3.8.13
硬件平台:AM335x


内核配置: 添加CAN驱动程序及协议

[*] Networking support --->
    <*> CAN bus subsystem support  --->
        <*> Raw CAN Protocol (raw access with CAN-ID filtering)
        <*> Broadcast Manager CAN Protocol (with content filtering)
            CAN Device Drivers  --->
                <*> Bosch D_CAN devices  --->
                    <*>   Generic Platform Bus based D_CAN driver

AM335x CAN控制器驱动程序位于:drivers/net/can/c_can目录下
c_can_platform.c
c_can.c


开机自动配置: /etc/network/interface

auto can0
iface can0 inet manual
pre-up /ip link set $IFACE type can bitrate 125000 triple-sampling on
up /sbin/ifconfig $IFACE up
down /sbin/ifconfig $IFACE down

查看可用列表: 查看/sys/class/net/目录

$ ls /sys/class/net/can*

设置波特率:

# echo 500000 > /sys/class/net/can%d/can_bittiming/bitrate

系统设置:命令及说明:

配置CAN总线参数

# ip link set can0 type can bitrate 500000 triple-sampling on

或者

# canconfig can0 bitrate 500000 ctrlmode triple-sampling on

bitrate参数用于设置波特率,此处设置为500K bps.
loopback on/off - 使能/禁止回环(除了测试,一般都关闭回环功能)

使能CAN总线接口

# ip link set can0 up

或者

# canconfig can0 start 

此命令完成后,可通过ifconfig查看到相关接口。

关闭CAN总线接口

# ip link set can0 down

或者

# canconfig can0 stop

查询当前工作状态:

# ip -details -statistics link show can0

发送测试:

# cansend can0 7ff#112233

接收测试:

# candump can0

C语言示例代码:

#include <stdio.h>
#include <string.h>
#include <linux/can.h>
#include <linux/sockios.h>
#include <net/if.h>    

/*
 * 初始化CAN控制器
 */
int can_open(char* name)
{
    int fd;
    struct ifreq can_ifr;
    struct sockaddr_can can_addr; 
    // int flag = 1; 
       
    // 初始化socket套接字用于CAN设备
    fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);     
            
    strcpy(can_ifr.ifr_name, name);     
    ioctl(fd, SIOCGIFINDEX, &can_ifr);        // 指定can设备   
      
    can_addr.can_family  = AF_CAN;            // 指定协议族
    can_addr.can_ifindex = can_ifr.ifr_ifindex;  
             
    bind(fd, (struct sockaddr *)&can_addr, sizeof(can_addr)); // 绑定
    //ioctl(fd, FIONBIO, &flag);        //设置为非阻塞模式  
    return fd;  
}
/*
 *  向CAN总线发送数据
 */
int can_send(int can, int id, unsigned char* buff)
{
    int ret; 
    struct can_frame can_frm = {0};
    
    can_frm.can_id  = id;    
    // 如果为扩展帧则为: can_frm.can_id = CAN_EFF_FLAG | id;
    can_frm.can_dlc = 8;  
    memcpy(can_frm.data, buff, 8);
    
    ret = write(can, &can_frm, sizeof(can_frm));    
    if(ret != sizeof(can_frm)) 
    {   
        printf("can send error: id = %X\r\n", id);  
    }

    return 0;
}
/*
 *  向CAN总线读取数据
 */
int can_recv(int can, struct can_frame* pcf)
{
    int rxlen;
    
    // 正常情况下返回值为16(struct can_frame的长度)
    rxlen = read(can, pcf, sizeof(struct can_frame));
    if(rxlen != sizeof(struct can_frame))    
    {           
        return -1;    // 读取失败
    }

    return 0;
}

发送远程帧:

struct can_frame frame;
frame.can_id = CAN_RTR_FLAG | id;
write(s, &frame, sizeof(frame));

设置过滤规则: 可以设置多条

struct can_filter rfilter[2];
rfilter[0].can_id   = 0x123;
rfilter[0].can_mask = CAN_SFF_MASK; //#define CAN_SFF_MASK 0x000007FFU
rfilter[1].can_id   = 0x200;
rfilter[1].can_mask = 0x700;
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));//设置规则

// 过滤所有报文(只发不接)
setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0); 

// 过滤错误帧
can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, err_mask, sizeof(err_mask));

错误处理:
通过can_id 中的 CAN_ERR_FLAG 位来判断接收的帧是否为错误帧。
如果为错误帧,可以通过 can_id 的其他符号位来判断错误的具体原因。


关闭/开启回环功能:

// 0 表示关闭, 1 表示开启( 默认)
int loopback = 0; 
setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

http://emb.hqyj.com/Column/Column596.htm
https://public.pengutronix.de/software/libsocketcan/
http://www.pengutronix.de/software/socket-can/download/canutils
http://www.kernel.org/pub/linux/utils/net/iproute2/