Linux Socket CAN

http://www.emtronix.com/article/article2013603.html

一、简述

 

        EM9287工控主板CPU为Freescale 的iMX287,主频454MHz,以具有丰富的通讯接口为特色,可同时支持双网口、7路串口、32路GPIO、SPI、I2C以及CAN通讯等接口。CAN是一种在世界范围内广泛用于自动控制、嵌入式设备和汽车领域的网络技术,EM9287 的CAN通讯接口是通过FlexCAN模块来实现控制局域网络协议(CAN)通信的,FlexCAN模块同时支持CAN协议规范2.0,包括协议所规定的标准帧和扩展帧。

 

        Linux下最早使用CAN的方法是基于字符设备来实现的,在EM9287上移植的Linux-3.9.7内核中FlexCAN模块驱动实现的是Socket CAN方式,Scoket CAN使用了socket接口和Linux网络协议栈,这种方法使得CAN设备驱动可以通过网络接口函数来调用,这样大大地方便了熟悉Linux网络编程的程序员,由于调用的都是标准的socket 函数,也使得应用程序便于移植,而不会因为硬件的调整而修改应用程序,这样加强了应用程序的可维护性。关于Socket CAN在Linux内核文档中有更为详细的介绍(/Linux-3.9.7/Documentation/networtking/can.txt)。

 

        本文将简要介绍EM9287在Linux-3.9.7内核上如何实现CAN驱动以及如何在应用程序中使用Socket CAN。

 

二、Linux内核配置

 

        内核配置中增加以下选项(make menuconfig):

 

        Networking support ---> 
        <*> CAN bus subsystem support ---> 
        <*> Raw CAN Protocol (raw access with CAN-ID filtering) 
        <*> Broadcast Manger CAN Protocol (with content filtering) 
        <*> CAN Gateway/Router (with netlink configuration) 
            CAN Device Drivers ---> 
        <*> Platform CAN drivers with Netlink support 
        [*] CAN bit-timing calculation 
        <*> Support for Freescale FLEXCAN based chips

 

        EM9287移植的是Linux-3.9.7版本,对于硬件的描述和配置都是通过device tree相关文件进行传递,除了内核的配置外,还需要在相应的dst文件中增加can0节点。如:

 

        can0: can@80032000 { 
                compatible = 'fsl,imx28-flexcan', 'fsl,p1010-flexcan'; 
                reg = <0x80032000 0x2000>; 
                interrupts = <8>; 
                clocks = <&clks 58>, <&clks 58>; 
                clock-names = 'ipg', 'per'; 
                pinctrl-names = 'default'; 
                pinctrl-0 = <&can0_pins_a>; 
        };

 

        内核编译成功后,板卡启动显示即表示flexcan驱动加载成功。

        [ 2.022398] CAN device driver interface 
        [ 2.031257] flexcan 80032000.can: device registered (reg_base=f5032000, irq=190)

 

三、Socket CAN的测试

 

        Socket CAN 的使用会用到ip命令工具,由于busybox中的ip没有支持 socket can,所以需要重新移植ip工具,iproute2中的ip可以支持socket can。

 

1、移植iproute2 
        从iproute2官方网站上下载源码,我们这里用到的是iproute2-3.10.0. 
        1) tar jxvf iproute2-3.10.0 
        2) 修改Makefie 
        CC=arm-none-linux-gnueabi-gcc 
        由于只用到ip工具,所以将别编译目录屏蔽: 
        #SUBDIRS=lib ip tc bridge misc netem genl man 
        SUBDIRS=lib ip 
        3) make编译成功后生成 “ip” 
        4) 将ip复制到EM9287的文件系统中,替换原来busybox中的ip

 

        这样ip命令工具就算是移植好了。

 

2、使用ip命令配置can0接口。 
        // 关闭can0接口,以便进行配置 
        ifconfig can0 down 
        // 方法一:配置can0的波特率为250Kbps 
        ip link set can0 type can bitrate 250000 
        // 方法二:配置can0的波特率为250Kbps 
        ip link set can0 type can tp 250 prog-seg 5 phase-seg1 8 phase-seg2 2 sjw 2 
        // 启动can0接口 
        ifconfig can0 up

 

        EM9287的FLEXCAN时钟选用的是24MHz的外部晶体振荡时钟。为了适应各种不同的采样率,我们采用方法二来对can的波特率进行设置,以CiA推荐的采样点在bit的87.5%处,作为基准来计算:

 

波特率PRESDIV 
-> fTq
TSEG1TSEG2TQ采样点
PROPSEG+PSEG1+2PSEG2+1
10001 -> 12MHz(0 + 7 + 2)= 9(1+1)= 21283.3%
8001 -> 12MHz(3 + 7 + 2)= 12(1+1)= 21586.6%
5002 -> 8MHz(4 + 7 + 2)= 13(1+1)= 21687.5%
2505 -> 4MHz(4 + 7 + 2)= 13(1+1)= 21687.5%
12511 -> 2MHz(4 + 7 + 2)= 13(1+1)= 21687.5%
10014 -> 1.6MHz(4 + 7 + 2)= 13(1+1)= 21687.5%
6024 -> 960KHz(4 + 7 + 2)= 13(1+1)= 21687.5%
5029 -> 800KHz(4 + 7 + 2)= 13(1+1)= 21687.5%
2074 -> 320KHz(4 + 7 + 2)= 13(1+1)= 21687.5%
10149 -> 160KHz(4 + 7 + 2)= 13(1+1)= 21687.5%

 

3、Scoket CAN测试代码

 

        就像TCP/IP协议一样,在使用CAN网络之前首先需要打开一个套接字。CAN的套接字使用到了一个新的协议族PF_CAN,所以在调用socket( )这个系统函数的时候需要将PF_CAN作为第一个参数。当前有两个CAN的协议可以选择,一个是原始套接字协议( raw socket protocol),另一个是广播管理协议BCM(broadcast manager)。作为一般的工业应用我们选用原始套接字协议:

        s = socket(PF_CAN, SOCK_RAW, CAN_RAW);

 

        在成功创建一个套接字之后,通常需要使用bind( )函数将套接字绑定在某个CAN接口上。在绑定 (CAN_RAW)套接字之后,就可以在套接字上使用read( )/write( )进行数据收发的操作。

 

        基本的CAN帧结构体和套接字地址结构体定义在include/linux/can.h

 

        /* 
         * 扩展格式识别符由 29 位组成。其格式包含两个部分:11 位基本 ID、18 位扩展 ID。 
         * Controller Area Network Identifier structure 
         * 
         * bit 0-28 : CAN识别符 (11/29 bit) 
         * bit 29 : 错误帧标志 (0 = data frame, 1 = error frame) 
         * bit 30 : 远程发送请求标志 (1 = rtr frame) 
         * bit 31 :帧格式标志 (0 = standard 11 bit, 1 = extended 29 bit) 
        */ 
        typedef __u32 canid_t; 
        struct can_frame { 
                canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ 
                __u8 can_dlc; /* 数据长度: 0 .. 8 */  
                __u8 data[8] __attribute__((aligned(8))); 
        };

 

        以下为相关的测试代码:

 

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

                printf( 'SocketCAN Test V1.0\n' ); 
                // 解析命令行参数, CAN波特率 
                if( argc > 1 ) 
                { 
                        baudrate = atoi( argv[1] ); 
                } 
                else 
                { 
                        baudrate = 250000; 
                } 
                printf( 'bitrate is %d\n', baudrate ); 
                set_can_bittiming( baudrate ); 

                s = socket(PF_CAN, SOCK_RAW, CAN_RAW); 
                printf( 'SOCK_RAW can sockfd:%d\n', s ); 
                if( s < 0 ) 
                { 
                        return -1; 
                } 

                int loopback = 0; /* 0 = disabled, 1 = enabled (default) */ 
                setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)); 

                strcpy(ifr.ifr_name, 'can0' ); 
                ret = ioctl(s, SIOCGIFINDEX, &ifr); 
                if( ret < 0 ) 
                { 
                        return -1; 
                } 

                addr.can_family = AF_CAN; 
                addr.can_ifindex = ifr.ifr_ifindex; 
                bind(s, (struct sockaddr *)&addr, sizeof(addr)); 

                for( i1=0; i1<10; i1++ ) 
                { 
                        nbytes = read(s, &frame, sizeof(struct can_frame)); 
                        if (nbytes < 0) { 
                                perror('can raw socket read'); 
                                return 1; 
                        } 
                        if( nbytes < (int)sizeof(struct can_frame)) 
                        { 
                                perror('read: incomplete CAN frame\n'); 
                                return 1; 
                        } 
                        /* do something with the received CAN frame: send back */ 
                        nbytes = write(s, &frame, sizeof(struct can_frame)); 
                        printf( '%d sendbytes: %d\n', i1+1, nbytes ); 
                } 
                close( s ); 
                return 0; 
        }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值