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

2、SocketCan应用编程基础知识

2.1、创建socket套接字

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

int s = -1;

/* 创建套接字 */
s = socket(PF_CAN,SOCK_RAW,CAN_RAW);
if(0 > s)
{
	perror("socket error");
	exit(EXIT_FAILURE);
    
}

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

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

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

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

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]; /* 数据 */
};

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 /* <can_id & CAN_SFF_MASK>获取标准帧 ID */
#define CAN_EFF_MASK 0x1FFFFFFFU /* <can_id & CAN_EFF_MASK>获取标准帧 ID */
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */

数据发送使用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");

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

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

2.5、数据接收

使用read()函数实现:

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

3、FPGA Zynq MPSOC CAN 发送数据

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

#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>
#include <linux/fs.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdbool.h>

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

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

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

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

sudo apt-get install gcc-9-aarch64-linux-gnu
sudo apt-get install gcc-aarch64-linux-gnu

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

aarch64-linux-gnu-gcc -v

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

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

在这里插入图片描述

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

}

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

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

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

在这里插入图片描述
Welcome to follow my weixingongzhonghao "Kevin的学习站"

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kevin的学习站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值