Arm Linux Can

一:can-utils 安装

在这里插入图片描述
生产如下工具:
在这里插入图片描述

二:can-utils 使用

ip link set can0 down
ip link set can0 type can bitrate 250000 loopback off//设置波特率,关闭回环测试
ip link set can0 up

说明:

  1. loopback off:关闭回环模式。测试发现关闭后,可以实现CAN设备与外界的收发通信。
  2. loopback on:打开回环模式。测试发现打开后,可以实现自发自收,不依赖外部CAN设备。

can网络关闭

ip link set can0 down

can波特率设置

ip link set can0 type can bitrate 500000 loopback off

查询can0设备的参数设置

ip -details link show can0

can网络开启

ip link set can0 up

查询工作状态

ip -details -statistics link show can0

can发送数据

数据发送格式:
cansend can0 XXX#11223344
eg:
cansend can0 12345678#112233
XXX:表示标识符ID,ID越小优先级越高CAN数据冲突时,优先发送。
112233344:表示发送的数据,以16进制表示。最小发送数据单位是字节,可发0~8个字节。

can接受数据

candump can0 或 candump can0 &

三:can回环测试

ip link set down can0
ip link set can0 type can loopback on
ip link set up can0
candump can0 -L &
cansend can0 123#1122334455667788
ip -details link show can0

四:C语言CAN编程

由于系统将 CAN 设备作为网络设备进行管理,因此在 CAN 总线应用开发方面, Linux 提供了SocketCAN 接口,使得 CAN 总线通信近似于和以太网的通信,应用程序开发接口更加通用,也更加灵活。

初始化

SocketCAN 中大部分的数据结构和函数在头文件 linux/can.h 中进行了定义。 CAN 总线套接字的创建采用标准的网络套接字操作来完成。网络套接字在头文件 sys/socket.h 中定义。 套接字的初始化方法如下:

int init(char *drvcan_name)
{
    int ret;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_filter rfilter[1];
    int fd;
    char can_down_cmd[64] = "";
    char can_init_cmd[64] = "";
    char can_up_cmd[64] = "";

    if (drvcan_name == NULL)
    {
        printf("drvcan_name is null");
        return -1;
    }

    sprintf(can_down_cmd, CAN_DOWN, drvcan_name);
    sprintf(can_init_cmd, CAN_INIT, drvcan_name);
    sprintf(can_up_cmd, CAN_UP, drvcan_name);

    system(can_down_cmd);
    system(can_init_cmd);
    system(can_up_cmd);

    fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (fd < 0)
    {
        printf("socket error\n");
        return -2;
    }

    sprintf(ifr.ifr_name, "%s", drvcan_name);
    ret = ioctl(fd, SIOCGIFINDEX, &ifr);
    if (ret < 0)
    {
        printf("ioctl error ret:%d", ret);
        return -3;
    }

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

    /*设置过滤规则*/
    // rfilter[0].can_id = 0x2;
    // rfilter[0].can_mask = 0;
    // Setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
    // setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, NULL);

    /*启动接收线程*/
    thread_exit_state = EM_THREAD_STATE_RUN;
    pthread_t _threadID;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&_threadID, &attr, can_recv_thread, fd);
    pthread_attr_destroy(&attr);
    return fd;
}

数据结构

在数据收发的内容方面,CAN 总线与标准套接字通信稍有不同,每一次通信都采用 can_frame 结构体将数据封装成帧。 结构体定义

/**
 * struct can_frame - basic CAN frame structure
 * @can_id:  CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
 * @can_dlc: frame payload length in byte (0 .. 8) aka data length code
 *           N.B. the DLC field from ISO 11898-1 Chapter 8.4.2.3 has a 1:1
 *           mapping of the 'data length code' to the real payload length
 * @__pad:   padding
 * @__res0:  reserved / padding
 * @__res1:  reserved / padding
 * @data:    CAN frame payload (up to 8 byte)
 */
struct can_frame {
	canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
	__u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
	__u8    __pad;   /* padding */
	__u8    __res0;  /* reserved / padding */
	__u8    __res1;  /* reserved / padding */
	__u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));
};

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

/* special address description flags for the CAN_ID */
#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB 扩展帧*/
#define CAN_RTR_FLAG 0x40000000U /* remote transmission request 远程帧*/
#define CAN_ERR_FLAG 0x20000000U /* error message frame 错误帧*/

/* valid bits in CAN ID for frame formats */
#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */

数据发送

struct can_frame frame;
frame.can_id = 0x123;//如果为扩展帧,那么 frame.can_id = CAN_EFF_FLAG | 0x123;
frame.can_dlc = 1; //数据长度为 1
frame.data[0] = 0xAB; //数据内容为 0xAB
int nbytes = write(s, &frame, sizeof(frame)); //发送数据
if(nbytes != sizeof(frame)) //如果 nbytes 不等于帧长度,就说明发送失败
printf("Error\n!");

如果要发送远程帧(标识符为 0x123),可采用如下方法进行发送

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

当然, 套接字数据收发时常用的 send、 sendto、 sendmsg 以及对应的 recv 函数也都可以用于 CAN总线数据的收发

错误处理

当帧接收后,可以通过判断 can_id 中的 CAN_ERR_FLAG 位来判断接收的帧是否为错误帧。 如果为错误帧,可以通过 can_id 的其他符号位来判断错误的具体原因。
错误帧的符号位在头文件 linux/can/error.h 中定义。

过滤规则

在数据接收时,系统可以根据预先设置的过滤规则,实现对报文的过滤。过滤规则使用 can_filter 结构体来实现,定义如下:

/**
 * struct can_filter - CAN ID based filter in can_register().
 * @can_id:   relevant bits of CAN ID which are not masked out.
 * @can_mask: CAN mask (see description)
 *
 * Description:
 * A filter matches, when
 *
 *          <received_can_id> & mask == can_id & mask
 *
 * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
 * filter for error message frames (CAN_ERR_FLAG bit set in mask).
 */
struct can_filter {
	canid_t can_id;
	canid_t can_mask;
};

接收到的数据帧的 can_id & mask == can_id & mask。
通过这条规则可以在系统中过滤掉所有不符合规则的报文,使得应用程序不需要对无关的报文进行处理。

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));//设置规则

在极端情况下,如果应用程序不需要接收报文,可以禁用过滤规则。这样的话,原始套接字就会忽略所有接收到的报文。在这种仅仅发送数据的应用中,可以在内核中省略接收队列,以此减少 CPU 资源的消耗。禁用方法如下:

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

回环设置

在默认情况下, 本地回环功能是开启的,可以使用下面的方法关闭回环/开启功能:

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

在本地回环功能开启的情况下,所有的发送帧都会被回环到与 CAN 总线接口对应的套接字上。 默认情况下,发送 CAN 报文的套接字不想接收自己发送的报文,因此发送套接字上的回环功能是关闭的。可以在需要的时候改变这一默认行为:

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

五:Linux 系统中CAN 接口应用程序示例

报文发送程序

#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 s, nbytes;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame[2] = {{0}};

    s = socket(PF_CAN, SOCK_RAW, CAN_RAW); // 创建套接字
    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr); // 指定 can0 设备
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(s, (struct sockaddr *)&addr, sizeof(addr)); // 将套接字与 can0 绑定

    // 禁用过滤规则,本进程不接收报文,只负责发送
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

    // 生成两个报文
    frame[0].can_id = 0x11;
    frame[0].can_dlc = 1;
    frame[0].data[0] = 'Y';
    frame[0].can_id = 0x22;
    frame[0].can_dlc = 1;
    frame[0].data[0] = 'N';

    // 循环发送两个报文
    while (1)
    {
        nbytes = write(s, &frame[0], sizeof(frame[0])); // 发送 frame[0]
        if (nbytes != sizeof(frame[0]))
        {
            printf("Send Error frame[0]\n!");
            break; // 发送错误,退出
        }

        sleep(1);

        nbytes = write(s, &frame[1], sizeof(frame[1])); // 发送 frame[1]

        if (nbytes != sizeof(frame[0]))

        {

            printf("Send Error frame[1]\n!");

            break;
        }

        sleep(1);
    }

    close(s);
    return 0;
}

报文过滤接收程序

/* 2. 报文过滤接收程序 */
#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 s, nbytes;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    struct can_filter rfilter[1];

    s = socket(PF_CAN, SOCK_RAW, CAN_RAW); // 创建套接字
    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr); // 指定 can0 设备

    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    bind(s, (struct sockaddr *)&addr, sizeof(addr)); // 将套接字与 can0 绑定

    // 定义接收规则,只接收表示符等于 0x11 的报文
    rfilter[0].can_id = 0x11;
    rfilter[0].can_mask = CAN_SFF_MASK;

    // 设置过滤规则
    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
    while (1)
    {
        nbytes = read(s, &frame, sizeof(frame)); // 接收报文
        // 显示报文
        if (nbytes > 0)
        {
            printf(“ID = 0x % X DLC = % d data[0] = 0x % X\n”, frame.can_id,
                   frame.can_dlc, frame.data[0]);
        }
    }
    close(s);
    return 0;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
As an AI language model, I do not have personal experience using web servers. However, I can provide you with some information on setting up a web server on ARM Linux. 1. Choose your ARM Linux distribution: There are several ARM Linux distributions available, such as Raspbian, Ubuntu MATE, and Arch Linux ARM. Choose one that is suitable for your ARM device and has good support for web servers. 2. Install the web server software: There are several web server software available for Linux, such as Apache, Nginx, and Lighttpd. Choose one that is suitable for your needs and install it using the package manager of your ARM Linux distribution. 3. Configure the web server: After installing the web server software, configure it according to your needs. This involves setting up the website files, defining the virtual hosts, configuring SSL if required, and setting up access controls. 4. Test the web server: After configuring the web server, test it by accessing the website using a web browser. Make sure that everything is working as expected. 5. Secure the web server: Finally, secure the web server by configuring firewalls, using SSL certificates, and following best practices for web server security. Note that setting up a web server on ARM Linux may require some technical knowledge and may vary depending on the specific ARM device and Linux distribution being used. It is always recommended to research and follow best practices for web server security to ensure that your website and data are secure.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值