CAN communication principle learning

CAN communication

One: Basic overview

1.1 What is the can bus

CAN is the abbreviation of Controller Area Network, which is an ISO international standardized serial communication protocol. In layman's terms, the CAN bus is a line for transmitting data, which is used to transmit data between different ECUs.
CAN(Controller Area Network) is an ISO international standardized serial communication protocol. Widely used in automobiles, ships, etc. Has been recognized by everyone for its high performance and reliability. The CAN controller determines the level of the bus through the potential difference of the
two lines (CAN-H and CAN-L) that make up the bus . At any time, there are two levels on the bus: dominant level and recessive level. "Dominant" means "priority", as long as one unit outputs dominant level, the bus is dominant level, and "recessive" means "inclusive", only all units output hidden When the level is active, it is recessive level on the bus. (dominant level is stronger than recessive level). When performing logical "AND" on the bus, the logic value of the dominant level is "0", and the recessive level is "1". The figure below shows a typical CAN topology connection diagram.



insert image description here

All units connected to the bus can send information. If more than one unit is sending information at the same time, the unit with the highest priority is eligible to send, and all other units perform receiving operations.

The topology of can:

insert image description here


1.2 Features of can bus protocol

The CAN bus protocol has the following characteristics:

1) Multi-master control

When the bus is free, all units connected to the bus can start sending information, which is the concept of so-called multi-master control.
The device that occupies the bus first is eligible to send information on the bus. This is the so-called CSMA/CR (Carrier Sense Multiple Access/Collosion Avoidance) method.
If multiple devices start sending information at the same time, the device that sends the highest priority ID message is eligible to send.

2) Sending of information

In the CAN protocol, all sent information must meet a predefined format. When the bus is not occupied, any device connected to the bus can start the transmission of new information. If two or more devices start the transmission of information at the same time, the priority is determined by ID. The ID does not indicate the destination of the information, but indicates the priority of the information. If two or more devices start the transmission of information at the same time, they compete on the bus according to each bit of the ID contained in the information, and the device that wins the competition (that is, the information with the highest priority) can continue to send, The loser immediately stops sending and enters the receiving operation. Because there can only be one sender on the bus at the same time, and the others are all in the receiving state, so the concept of address does not need to be defined in the underlying protocol.

3) System flexibility

Units connected to the bus do not have identifiers like addresses, so adding or removing a device does not require changing software and hardware, or application layer software for other devices.

4) Communication speed

Any communication speed can be set to suit the network size.
For a network, all units must have the same communication speed. If they are different, errors will occur and prevent network communication. However, different communication speeds can be different between different networks.

5) Remote data request

It is possible to request other units to send data by sending a "remote control frame".

6) Error detection, error notification, error recovery function

All units can detect errors (error detection function).
A unit that detects an error immediately and simultaneously notifies all other units (error notification function). If a unit detects an error while sending a message, it will forcefully terminate the message transmission, notify all other devices of the error, and then retransmit until the message is transmitted normally (error recovery function).

7) Error isolation

There are two types of errors on the CAN bus: temporary errors (the data on the bus is temporarily erroneous due to noise); caused by). CAN can distinguish between these two types. On the one hand, the communication priority of the frequent error unit is reduced to prevent the impact on other normal devices. On the other hand, if it is a persistent error, the device is isolated from the bus.

8) connect

The CAN bus allows multiple devices to be connected to the bus at the same time and there is no logical limit on the number. However, due to the limitation of delay and load capacity, the actual devices that can be connected are still limited, and the number of connected devices can be increased by reducing the communication speed. On the contrary, if fewer devices are connected, the communication speed can be increased.


1.3 can's network communication structure

1.3.1

In fact, the bottom layer of the CAN bus network only uses the data link layer and the transmission layer in the OSI basic reference model . In the CAN network, only the application layer of the OSI basic reference model is used.
insert image description here


1.3.2 can protocol network level

In the CAN protocol, the ISO standard only stipulates the data link layer and the physical layer. For a part of the data link layer and the physical layer, the provisions of ISO11898 and ISO11519-2 are the same, but the PMD sub-layer and the MDI sub-layer of the physical layer are different.
insert image description here

In the CAN bus, the items defined in each layer of the network are as follows:
insert image description here


2: Application of socket can in communication network

socket canApplication instance
server side:

#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
int can_recv() {
    
    
    int sock_fd;
    unsigned long nbytes, len;
    struct sockaddr_can addr;
    struct ifreq ifr;
    /*为了能够接收CAN报文,我们需要定义一个CAN数据格式的结构体变量*/
    struct can_frame frame;
    struct can_frame *ptr_frame;

    /* 建立套接字,设置为原始套接字,原始CAN协议 */
    sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    /* 对CAN接口进行初始化,设置CAN接口名,即当我们用ifconfig命令时显示的名字 */
    strcpy(ifr.ifr_name, "can0");
    ioctl(sock_fd, SIOCGIFINDEX, &ifr);
    /*设置CAN协议 */
    addr.can_family = AF_CAN;
    addr.can_ifindex = 0;
    /*将刚生成的套接字与网络地址进行绑定*/
    bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr));
    /*开始接收数据*/
    nbytes = recvfrom(sock_fd, &frame, sizeof(struct can_frame), 0,
                      (struct sockaddr *)&addr, (socklen_t *)&len);
    /*get interface name of the received CAN frame*/
    ifr.ifr_ifindex = addr.can_ifindex;
    ioctl(sock_fd, SIOCGIFNAME, &ifr);
    printf("Received a CAN frame from interface %s\n", ifr.ifr_name);
    /*将接收到的CAN数据打印出来,其中ID为标识符,DLC为CAN的字节数,DATA为1帧报文的字节数*/
    printf("CAN frame:\nID = %x\nDLC = %x\nDATA = %s\n", frame.can_id,
           frame.can_dlc, frame.data);

    ptr_frame = &frame;

    return 0;
}

clientend

#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
int can_send() {
    
    
    int sock_fd;
    unsigned long nbytes;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;
    /*建立套接字,设置为原始套接字,原始CAN协议 */
    sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    /* 对CAN接口进行初始化,设置CAN接口名,即当我们用ifconfig命令时显示的名字 */
    strcpy((char *)(ifr.ifr_name), "can0");
    ioctl(sock_fd, SIOCGIFINDEX, &ifr);
    printf("can0 can_ifindex = %x\n", ifr.ifr_ifindex);
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    /*将刚生成的套接字与CAN套接字地址进行绑定*/
    bind(sock_fd, (struct sockaddr *)&addr, sizeof(addr));
    /*设置CAN帧的ID号,可区分为标准帧和扩展帧的ID号*/
    frame.can_id = 0x1122;
    strcpy((char *)frame.data, "hello");
    frame.can_dlc = strlen((char *)frame.data);
    printf("Send a CAN frame from interface %s\n", ifr.ifr_name);
    /*开始发送数据*/
    nbytes = sendto(sock_fd, &frame, sizeof(struct can_frame), 0,
                    (struct sockaddr *)&addr, sizeof(addr));

    return 0;
}

After reading the above two programs, you may have doubts, why these two programs do not have listen() and accept() functions?
In fact, these two programs run independently, unlike byte stream sockets (SOCK_STREAM) and datagram sockets (SOCK_DGRAM), which need to run the server to listen first. The two server and client programs of SOCK_STREAM and SOCK_DGRAM send and receive data through the network.
The object of the CAN socket server and client program to send and receive data is CAN总线. The server receives data from the CAN bus, and the client sends the data to the CAN bus. When there is data on the CAN bus, the server can receive the data. When the CAN bus is idle, the client can send the data.


trinity program

I recently wrote a program-based linux socket canprogram that I think is good. The main functions are:

1. The program has all CAN functions, including CAN standard frame/extended frame receiving and sending, CAN bus error judgment, loopback and other functions. 2. It is
suitable for CAN interface based on LINUX SOCKET mechanism, and can be used for CAN testing of embedded LINUX
. The program adopts the standard LINUX command line parameter option form and accepts user parameters

int main(int argc, char **argv)
{
    
    
    S_CanFrame sendframe, recvframe;
    byte *psendframe = (byte *)&sendframe;
    byte *precvframe = (byte *)&recvframe;
    u_canframe_data_t *psend_data = (u_canframe_data_t *)sendframe.data;
    const int can_frame_len = sizeof(S_CanFrame); 

    pid_t pid = -1;
    int   status;

    int  ret = 0;
    char buf[128] = {
    
    0};
    bool carry_bit = false;// 进位标志

    int segment_id;//id for shared memo


    if (parse_options(argc, argv))
    {
    
    
        usage();    return  0;
    }

    if (!find_can(port))
    {
    
    
        sprintf(buf, "\n\t错误:CAN%d设备不存在\n\n", port + 1);
        panic(buf);
        return  -1;
    }

    close_can(port);// 必须先关闭CAN,才能成功设置CAN波特率
    set_bitrate(port, bitrate);// 操作CAN之前,先要设置波特率
    open_can(port, bitrate);

    send_socket_fd = socket_connect(port);
    recv_socket_fd = socket_connect(port);
    //printf("send_socket_fd = %d, recv_socket_fd = %d\n", send_socket_fd, recv_socket_fd);
    if (send_socket_fd < 0 || send_socket_fd < 0)
    {
    
    
        disconnect(&send_socket_fd);
        disconnect(&recv_socket_fd);
        panic("\n\t打开socket can错误\n\n");
        return  -1;
    }
    set_can_filter();
    set_can_loopback(send_socket_fd, lp);

    printf_head();

    memset(&sendframe, 0x00, sizeof(sendframe));
    memset(&recvframe, 0x00, sizeof(recvframe));

    if (extended_frame) // 指定发送帧类型:扩展帧或标准帧
    {
    
    
        sendframe.can_id = (send_frame_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
    } 
    else
    {
    
    
        sendframe.can_id = (send_frame_id & CAN_SFF_MASK);
    }
    sendframe.can_dlc = dlc;
    memcpy(sendframe.data, send_frame_data, dlc);

    
    segment_id = shmget(IPC_PRIVATE, sizeof(int), S_IRUSR | S_IWUSR);// allocate memo
    pframeno = (int *)shmat(segment_id, NULL, 0);// attach the memo
    if (pframeno == NULL)
    {
    
    
        panic("\n\t创建共享内存失败\n\n");
        return  -1;
    }
    *pframeno = 1;

    run = true;

    pid = fork();
    if(pid == -1) 
    {
    
     
        panic("\n\t创建进程失败\n\n");
        return  -1;
    }
    else if(pid == 0) // 子进程,用于发送CAN帧
    {
    
    
        while (run && (send_frame_times > 0))
        {
    
    
            ret = send_frame(send_socket_fd, (char *)&sendframe, sizeof(sendframe));
            printf_frame(sendframe.can_id & CAN_EFF_MASK, sendframe.data, sendframe.can_dlc, 
                ((sendframe.can_id & CAN_EFF_FLAG) ? true : false),
                ret > 0 ? true : false, 
                true);
            delay_ms(send_frame_freq_ms);

            if (send_frame_id_inc_en)
            {
    
    
                sendframe.can_id++;
                if (extended_frame)
                {
    
    
                    sendframe.can_id = (sendframe.can_id & CAN_EFF_MASK) | CAN_EFF_FLAG;
                } 
                else
                {
    
    
                    sendframe.can_id = (sendframe.can_id & CAN_SFF_MASK);
                }
            }

            if (send_frame_data_inc_en && dlc > 0)
            {
    
    
                if (dlc > 4 && psend_data->s.dl == ((__u32)0xFFFFFFFF))
                {
    
    
                    carry_bit = true;// 发生进位
                }
                psend_data->s.dl++;

                if (dlc <= 4)
                {
    
    
                    if (psend_data->s.dl >= (1 << (dlc * 8)))
                    {
    
    
                        psend_data->s.dl = 0;
                    }
                }
                else if (dlc <= 8)
                {
    
    
                    if (carry_bit)
                    {
    
    
                        psend_data->s.dh++;
                        if (psend_data->s.dh >= (1 << ((dlc - 4) * 8)))
                        {
    
    
                            psend_data->s.dh = 0;
                        }

                        carry_bit = false;
                    }
                }
            }

            send_frame_times--;
        }

        exit(0);
    }
    else // 父进程,接收CAN帧
    {
    
    
        install_sig();

        while (run)
        {
    
    
            memset(precvframe, 0x00, can_frame_len);
            ret = recv_frame(recv_socket_fd, precvframe, can_frame_len, 5 * 1000);
            if (ret > 0)
            {
    
    
                printf_frame(recvframe.can_id & CAN_EFF_MASK, recvframe.data, recvframe.can_dlc, 
                    ((recvframe.can_id & CAN_EFF_FLAG) ? true : false),
                    true, 
                    false);
            }
        }

        while(((pid = wait(&status)) == -1) && (errno == EINTR))
        {
    
    
            delay_ms(10);
        }
    }

    disconnect(&send_socket_fd);
    disconnect(&recv_socket_fd);

    shmdt(pframeno);// detach memo
    shmctl(segment_id, IPC_RMID, NULL);// remove

    return  0;
}

Guess you like

Origin blog.csdn.net/weixin_48433164/article/details/126926258