• NVIDIA Jetson Xavier CAN 开机启动及应用编程


    NVIDIA XAVIER CAN 配置及开机启动

    在这里插入图片描述

    1、CAN配置

    1.1、使用modprobe 工具 加载CAN设备

    sudo modprobe can
    sudo modprobe can_raw
    sudo modprobe mttcan
    
    • 1
    • 2
    • 3

    1.2、配置CAN参数

    //①、关闭CAN
    sudo ip link set down can0  
    sudo ip link set down can1
    
    //②、设备CAN波特率
    sudo ip link set can0 type can bitrate 500000
    sudo ip link set can1 type can bitrate 500000
    
    //③、打开CAN
    sudo ip link set up can0
    sudo ip link set up can1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    1.3、使用 ifconfig -a 命令查看CAN驱动是否加载成功

    2、CAN开机自启

    2.1、编写脚本 enable_CAN.sh

    touch enable_CAN.sh  // 创建脚本文件
    sudo chmod 777 enable_CAN.sh  //给脚本文件赋权限
    
    • 1
    • 2

    2.2、编写脚本

    sudo vi enable_CAN.sh
    
    • 1
    #!/bin/sh
    
    sudo modprobe can
    sudo modprobe can_raw
    sudo modprobe mttcan
    
    sudo ip link set down can0
    sudo ip link set down can1
    
    sudo ip link set can0 type can bitrate 500000
    sudo ip link set can1 type can bitrate 500000
    
    sudo ip link set up can0
    sudo ip link set up can1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BgOqvFbp-1667201642664)(E:\形深智能工作资料\工作记录\S32K144学习笔记\Linux\88.png)]

    2.3、配置开机启动

    ①、将enable.sh脚本文件拷贝到/usr/locak/ 目录下

    sudo cp enable_CAN.sh /usr/local/
    
    • 1

    ②、配置 service 文件

    cd /etc/systemd/system   //进入文件目录下
    sudo touch enable_CAN.service  //新建 enable_CAN.service
    sudo vi enable_CAN.service     //配置文件如下
    
    • 1
    • 2
    • 3
    [Unit]
    Description=Can
    
    [Service]
    ExecStart=/usr/local/enable_CAN.sh start
    
    [Install]
    WantBy=multi-user.target
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ③、设置服务

    sudo systemctl daemon-reload
    sudo systemctl enable enable_CAN.service
    sudo systemctl start enable_CAN.service
    
    • 1
    • 2
    • 3

    ④、重启XAVIER 后 使用 ifconfig -a 查看 CAN设备是否启动成功

    在这里插入图片描述

    3、SocketCan应用编程基础知识

    3.1、创建socket套接字

    CAN总线套接字采用标准的网络套接字操作完成,网络套接字在头文件中有定义。创建CAN套接字方式如下:

    int s = -1;
    
    /* 创建套接字 */
    s = socket(PF_CAN,SOCK_RAW,CAN_RAW);
    if(0 > s)
    {
    	perror("socket error");
    	exit(EXIT_FAILURE);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.2、将套接字与CAN设备进行绑定

    struct ifreq ifr = {0};
    struct sockaddr_can can_addr = {0};
    int ret;
    
    
    strcpy(ifr.ifr_name,"can0");//指定名字
    //灵活设置CAN口
    sprintf(ifr.ifr_name,"can%d",1);
    
    ioctl(s,SIOCGIFINDEX,&ifr);
    
    can_addr.can_family = AF_CAN;
    can_addr.can_ifindex = ifr.ifr_ifindex;
    
    /* 将套接字与 can0 进行绑定 */
    ret = bind(s,(struct sockaddr *)&can_addr,sizeof(can_addr));
    if(0 > ret)
    {
    	perror("bind error");
    	close(s);
    	exit(EXIT_FAILURE);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.3、设置过滤规则

    struct can_filter rfilter[2]; // 定义一个can_filter 结构体对象
    
    // 填充过滤规则,只接收ID为(can_id&can_mask)的报文
    rfilter[0].can_id = 0x60A;
    rfilter[0].can_mask = 0x7FF;
    rfilter[1].can_id = 0x60B;
    rfilter[1].can_mask = 0x7FF;
    
    // 调用 setsockopt 设置过滤规则
    setsockopt(s,SOL_CAN_RAW,CAN_RAW_FILTER,&rfilter,sizeof(rfilter));
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果应用程序功能仅是发送数据,则可省去接收队列,setsockopt()函数的第4个参数设为NULL,第五个参数设置为0:

    setsockopt(s,SOL_CAN_RAW,CAN_RAW_FILTER,NULL,0);
    
    • 1

    3.4、数据发送

    CAN总线每一次通信使用struct can_frame 结构体将数据封装成帧,struct can_frame定义如下:

    struct can_frame {
     canid_t can_id; /* CAN 标识符 */
     __u8 can_dlc; /* 数据长度(最长为 8 个字节) */
     __u8 __pad; /* padding */
     __u8 __res0; /* reserved / padding */
     __u8 __res1; /* reserved / padding */
     __u8 data[8]; /* 数据 */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    can_id 为帧的标识符,如果是标准帧,就使用 can_id 的低 11 位;如果为扩展帧,就使用 0~28 位。can_id 的第 29、30、31 位是帧的标志位,用来定义帧的类型,定义如下:

    #define CAN_EFF_FLAG 0x80000000U /* 扩展帧的标识 */
    #define CAN_RTR_FLAG 0x40000000U /* 远程帧的标识 */
    #define CAN_ERR_FLAG 0x20000000U /* 错误帧的标识,用于错误检查 */
    /* mask */
    #define CAN_SFF_MASK 0x000007FFU /* 获取标准帧 ID */
    #define CAN_EFF_MASK 0x1FFFFFFFU /* 获取标准帧 ID */
    #define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    数据发送使用write()函数实现,发送报文ID为0x123,数据为:0xA0、0xB0、0xC0;定义如下:

    struct can_frame frame; //定义一个 can_frame 变量
    int ret;
    frame.can_id = 123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 123;
    frame.can_dlc = 3; //数据长度为 3
    frame.data[0] = 0xA0; //数据内容为 0xA0
    frame.data[1] = 0xB0; //数据内容为 0xB0
    frame.data[2] = 0xC0; //数据内容为 0xC0
    ret = write(sockfd, &frame, sizeof(frame)); //发送数据
    if(sizeof(frame) != ret) //如果 ret 不等于帧长度,就说明发送失败
     perror("write error");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    要发送远程帧(帧 ID 为 123),可采用如下方法进行发送:

    struct can_frame frame;
    frame.can_id = CAN_RTR_FLAG | 123;
    write(sockfd, &frame, sizeof(frame));
    
    • 1
    • 2
    • 3

    3.5、数据接收

    使用read()函数实现:

    struct can_frame frame;
    int ret = read(sockfd, &frame, sizeof(frame));
    
    • 1
    • 2

    4、NVIDIA Jetson Xavier CAN 发送数据

    4.1、can_send.cCAN发送数据具体实现如下:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int s;
    
    int main(int argc, char *argv[])
    {
        int cannum = atoi(argv[1]);
        char buf[60];
    	sprintf(buf,"ifconfig can%d down",cannum);// 关闭CAN
    	system(buf);
    	sprintf(buf,"ip link set can%d type can bitrate 500000",cannum);// 设置CAN 波特率500000
    	system(buf);
    	sprintf(buf,"ifconfig can%d up",cannum);//开启CAN
    	system(buf);
        
        struct can_frame frame1[2] = {{0}};
    	struct sockaddr_can addr;
    	struct ifreq ifr;
    
        s = socket(PF_CAN, SOCK_RAW, CAN_RAW); //创建套接字
        if(0 > s)
        {
            perror("socket error");
            exit(EXIT_FAILURE);
        }
    
        sprintf(ifr.ifr_name, "can%d",cannum);
        ioctl(s, SIOCGIFINDEX, &ifr); //指定can设备--can0 can1...
        addr.can_family = AF_CAN;
        addr.can_ifindex = ifr.ifr_ifindex;
    
        int ret = bind(s, (struct sockaddr *)&addr, sizeof(addr)); //将套接字与can%d 绑定
        if(0 > ret)
        {
            perror("bind error");
            close(s);
            exit(EXIT_FAILURE);
        }
    
        //待发送的数据
        struct can_frame frame = {0};
        frame.data[0] = 0xA0;
        frame.data[1] = 0xB0;
        frame.data[2] = 0xC0;
        frame.data[3] = 0xD0;
        frame.data[4] = 0xE0;
        frame.data[5] = 0xF0;
        frame.can_dlc = 6;  // 数据字节数
        frame.can_id = 0x123;  // 报文ID
    
        //设置过滤规则: 仅发送数据 不接收报文*/
        setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &frame, sizeof(frame)); 
    
        for(;;)
        {
            //发送数据
            ret = write(s,&frame,sizeof(frame));
            if(sizeof(frame) != ret) //如果ret不等于报文长度 说明发送失败
            {
                perror("write error");
                goto out;
            }
    
            sleep(1); //发送时延 1s发一次
        }
    
    out:
        close(s);
        exit(EXIT_SUCCESS);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    4.2、使用aarch64-linux-gnu-gcc 交叉编译器编译程序

    aarch64-linux-gnu-gcc -o can_send can_send.c
    
    • 1

    如果没有ARM 交叉编译环境,则使用如下命令安装:

    sudo apt-get install gcc-9-aarch64-linux-gnu
    sudo apt-get install gcc-aarch64-linux-gnu
    
    • 1
    • 2

    查看aarch64-linux-gnu-gcc版本号

    aarch64-linux-gnu-gcc -v
    
    • 1

    4.3、将生成的可执行文件can_send 拷贝到/home/root/目录下;使用如下命令运行can_send`

    ./can_updata 1 // 1是对应的CAN 设备号 Xavier 可以引出两路CAN
    
    • 1

    4.4、把CANCAN_HMPSOC 5EV CAN4_HCANCAN_LMPSOC 5EV CAN4_L

    4.5、设置CAN卡参数,打开对应通道,启用终端电阻;

    在这里插入图片描述

    CAN卡会收到Xavier 发送过来的如下报文

    在这里插入图片描述

    4、NVIDIA Jetson Xavier CAN 接收数据

    4.1、can_read.cCAN接收数据源码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    int main(int argc, char *argv[])
    {
        int cannum = atoi(argv[1]);
        char buf[60];
    	sprintf(buf,"ifconfig can%d down",cannum);// 关闭CAN
    	system(buf);
    	sprintf(buf,"ip link set can%d type can bitrate 500000",cannum);// 设置CAN 波特率500000
    	system(buf);
    	sprintf(buf,"ifconfig can%d up",cannum);//开启CAN
    	system(buf);
        
        struct ifreq ifr = {0};
        struct sockaddr_can can_addr = {0};
        struct can_frame frame = {0};
        int sockfd = -1;
        int i;
        int ret;
    
        sockfd = socket(PF_CAN,SOCK_RAW,CAN_RAW);
        if(0 > sockfd)
        {
            perror("socket error");
            exit(EXIT_FAILURE);
        }
    
        sprintf(ifr.ifr_name, "can%d",cannum);
        //strcpy(ifr.ifr_name, "can4");
        ioctl(sockfd, SIOCGIFINDEX, &ifr); //指定can设备--can0 can1...
        can_addr.can_family = AF_CAN;
        can_addr.can_ifindex = ifr.ifr_ifindex;
    
        ret = bind(sockfd, (struct sockaddr *)&can_addr, sizeof(can_addr)); //将套接字与can%d 绑定
        if(0 > ret)
        {
            perror("bind error");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
    
        for(;;)
        {
            if(0 > read(sockfd,&frame,sizeof(struct can_frame)))
            {
                perror("read error");
                break;
            }
    
            if(frame.can_id & CAN_ERR_FLAG)
            {
                printf("Error frame!\n");
                break;
            }
    
    
            if(frame.can_id & CAN_EFF_FLAG)
            {
                printf("扩展帧 <0x%08x>",frame.can_id & CAN_EFF_MASK);
            }
            else
            {
                printf("标准帧 <0x%03x>",frame.can_id & CAN_SFF_MASK);
            }
    
            if(frame.can_id & CAN_RTR_FLAG)
            {
                printf("remote request\n");
                continue;
            }
    
    
            printf("[%d]",frame.can_dlc);
    
            for(int i = 0; i < frame.can_dlc; i++)
            {
                printf("%02x",frame.data[i]);
            }
            printf("\n");
            
        }
        
        close(sockfd);
        exit(EXIT_SUCCESS);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    使用aarch64-linux-gnu-gcc 交叉编译器编译程序

    aarch64-linux-gnu-gcc -o can_read can_read.c
    
    • 1

    将生成的可执行文件can_read 拷贝到/home/root/目录下;使用如下命令运行can_read

    Welcome to follow my weixingongzhonghao "Kevin的学习站"

  • 相关阅读:
    ZZULIOJ1039: n个数求和
    vite项目、vue-cli项目环境配置
    五、【React-Router6】路由表 useRoutes() + Outlet
    对双STA-双连接的一些思考
    第一类和第二类增值电信业务经营许可证的区别
    听潮汐,筑灯塔,聚千帆:智慧港口全球创新实验室启航时
    一文详解ATK Loss论文复现与代码实战
    从 DevOps 到平台工程:软件开发的新范式
    Android开发自测应用monkey常用命令参数
    Spring-重新认识IoC二
  • 原文地址:https://blog.csdn.net/qq_44705488/article/details/127616710