When configuring CAN communication rate and loopback mode operations later, the ip command needs to be used. However, when using the ip command compiled by default with PetaLinux, the following error message appears:
xxxxxxxxxxip: either "dev" is duplicate, or "type" is garbage
According to the instructions in the reference material, the iproute2 component needs to be correctly configured in the rootfs to obtain full-featured ip command support:
xxxxxxxxxxpetalinux-config -c rootfs
#In the menuconfig for the root filesystem, navigate to the location below and add iproute2:Filesystem Packages ---> base ---> iproute2 ---> [*] iproute2Reference material:
65243 - PetaLinux 2014.4 - The "ip" Command Fails to Properly Locate CAN Devices
After completing the previous step 'petalinux-config -c rootfs', use the following commands to recompile and package:
xxxxxxxxxxpetalinux-build
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --u-boot --fpga --forceClear the contents of the previous second partition of the TF card, and extract the ./image/rootfs.tar.gz file to the second partition of the TF card using the following command:
xxxxxxxxxxsudo tar -xzf ./images/linux/rootfs.tar.gz -C /media/mind/rootfs/syncInsert the TF card into the board, select the SD card boot mode with the jumper cap, connect the serial port to the board, and power it on.
Right-click on src to create a new folder:
After creating the relevant .c and .h files, it should look like the following:
Add the folder containing .h files to the path:
Open the CAN channel and set the baud rate using commands. The relevant commands are as follows:
xxxxxxxxxx #Check if can0 has been added to network devices: ifconfig -a #Set the baud rate of can0, here set to 100kbps ip link set can0 type can bitrate 100000 #After setting, query the parameters of the can0 device with the following command ip -details link show can0 #After setting is complete, enable the can0 device with the following command ifconfig can0 up #Use the following command to disable the can0 device ifconfig can0 down #During device operation, use the following command to query the working status ip -d -s link show can0 #Set can0 to loopback mode, self-transmit and self-receive ip link set can0 up type can loopback on
#Baud rate and mode can be set together ip link set can0 type can bitrate 200000 loopback on ip link set can0 type can bitrate 200000 loopback off #-e indicates extended frame, CAN_ID maximum 29bit, standard frame CAN_ID maximum 11bit, -i indicates CAN_ID # 0x800 corresponds to frame ID, 0x11~0x88 corresponds to the transmitted data cansend can0 -i 0x800 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 -e #--loop indicates sending 20 packets cansend can0 -i 0x02 0x11 0x12--loop=20 #Receive CAN0 data candump can0When changing the working state of CAN, must first turn off can0, otherwise it will report "Device or resource busy"
The Linux system manages CAN devices as network devices and provides the SocketCAN interface, making CAN bus communication similar to Ethernet, with more universal and flexible application development interfaces.
CAN initialization and configuration are completed by using the system() system call to execute the corresponding commands mentioned earlier.
xxxxxxxxxxbool can_open(CAN_Handle_t *h, const char *ifname){ uint8_t cmd_cfg[64]; int ret = -1;
can_close(h); can_is_up = false; can_send_active = false; can_rcv_status = false;
sprintf(cmd_cfg, "ip link set can0 type can bitrate %d loopback %s", can_baud_rate, (can_mode == 0) ?"off":"on"); ret = system(cmd_cfg);
if (ret != -1) { ret = system("ip link set can0 up"); }
if (ret == -1) { perror("open can interface failed"); return false; }
struct ifreq ifr; struct sockaddr_can addr;
if (!h || !ifname) return false;
h->sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (h->sockfd < 0) { perror("socket(PF_CAN)"); return false; }
strncpy(h->ifname, ifname, sizeof(h->ifname) - 1);
memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); if (ioctl(h->sockfd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl(SIOCGIFINDEX)"); close(h->sockfd); h->sockfd = -1; return false; }
addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex;
if (bind(h->sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); close(h->sockfd); h->sockfd = -1; return false; }
can_is_up = true; can_send_active = false; can_rcv_status = false; return true;}
void can_close(CAN_Handle_t *h){ const char *cmd = {"ip link set can0 down"}; int ret = system(cmd); if (ret == -1) { perror("system"); }
if (h && h->sockfd >= 0) { close(h->sockfd); h->sockfd= -1; }}Most data structures and functions in SocketCAN are defined in linux/can.h, and the creation of CAN bus sockets is done using standard network sockets.
Each time the CAN bus receives data, it is in units of can_frame. This structure is defined as follows:
xxxxxxxxxxstruct canfd_frame { canid_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ __u8 len; /* frame payload length in byte */ __u8 flags; /* additional flags for CAN FD */ __u8 __res0; /* reserved / padding */ __u8 __res1; /* reserved / padding */ __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));};can_id is the frame identifier. If sending a standard frame, use the lower 11 bits of can_id; if it's an extended frame, use bits 0~28. The lower 29th, 30th, and 31st bits of can_id are frame identifier bits used to define the frame type, as shown below:
xxxxxxxxxx/* special address description flags for the CAN_ID *//* EFF/SFF is set in the MSB *//* remote transmission request *//* error message frame */Data transmission uses the write function. To send a data frame with identifier 0x123 and data of one byte 0xAB, the method is as follows:
xxxxxxxxxxstruct can_frame frame;frame.can_id = 0x123;frame.can_dlc = 1;frame.data[0] = 0xAB;int nbytes = write(s, &frame, sizeof(frame));if(nbytes != sizeof(frame)) printf("Error\n");If sending a remote frame, then frame.can_id = CAN_RTR_FLAG | 0x123
Data reception uses the read function, implemented as follows:
xxxxxxxxxxstruct can_frame frame;int nbytes = read(s, &frame, sizeof(frame))When testing CAN communication programs under Linux, it is usually necessary to create a thread responsible for reception. Using threads can fully utilize the task scheduling and resource reuse provided by the Linux kernel.
pthread_t recv_tid;pthread_create(&recv_tid, NULL, can_recv_thread, &g_can_handle);
void *can_recv_thread(void *arg){ CAN_Handle_t *h = (CAN_Handle_t *)arg; uint32_t id = 0;
while (1) { if (can_is_up && (can_recv(h, &id, can_rcv_buf, &can_rcv_len) > 0)) { printf("Received CAN frame: ID=0x%X, Data=", id); for (int i = 0; i < can_rcv_len; i++) { printf("%02X ", can_rcv_buf[i]); } can_rcv_status = true; printf("\n"); } else { usleep(1000); } }
printf("CAN receive thread exiting.\n"); return NULL;}