• FPGA Zynq MPSOC ZU5EV 下CAN 应用编程


    FPGA Zynq MPSOC ZU5EV 下CAN 应用编程

    声明:本文是学习赛灵思 Zynq UltraScale+ MPSoC 5EV过程中写的笔记,便于以后复习,参考《 ug1144-petalinux-tools-reference-guide》、黑金Zynq UltraScale+ MPSoC 5EV开发板资料、正点原子Linux C应用编程。

    1、CAN设备配置

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

    在这里插入图片描述

    1.2、配置开启CAN及配置波特率

    sudo ip link set can4 down  // 先关闭CAN4 再设备波特率
    sudo ip link set can4 type can bitrate 500000 // 设置波特率为500000
    ip -details link show can4  // 查看 can4配置信息
    sudo ip link set can4 up    // 开启 can4
    
    • 1
    • 2
    • 3
    • 4

    2、SocketCan应用编程基础知识

    2.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

    2.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

    2.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

    2.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

    2.5、数据接收

    使用read()函数实现:

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

    3、FPGA Zynq MPSOC CAN 发送数据

    3.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

    3.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

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

    ./can_updata 4 // 4是对应的CAN 设备号 MPSOC 5EV 可以引出多路CAN
    
    • 1

    在这里插入图片描述

    3.4、把CANCAN_HMPSOC 5EV CAN4_HCANCAN_LMPSOC 5EV CAN4_L

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

    在这里插入图片描述

    CAN卡会收到FPGA MPSOC 5EV 发送过来的如下报文

    在这里插入图片描述

    4、FPGA Zynq MPSOC 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的学习站"

  • 相关阅读:
    ACL2022国内部分论文分享内容总结1
    【前端】-css的详解
    kali当中不同的python版本切换(超简单)
    169. 多数元素
    python日期处理
    【老生谈算法】matlab实现图像锐化处理及边缘检测算法源码——图像锐化处理及边缘检测
    Nginx 配置 https
    IDEA集成Git
    小波神经网络的基本原理,小波神经网络设计方案
    《吐血整理》高级系列教程-吃透Fiddler抓包教程(25)-Fiddler如何优雅地在正式和测试环境之间来回切换-下篇
  • 原文地址:https://blog.csdn.net/qq_44705488/article/details/127650048