CAN
1. Ovieview
CAN, short for Controller Area Network, is a serial communication protocol widely used for real-time data transmission. Originally developed by the German company Bosch for communication between electronic control units (ECUs) in the automotive industry, the CAN protocol has since been widely adopted in other fields such as industrial automation, medical devices, maritime applications, and aerospace.
2 Enabling CAN
The development board has the CAN interface function enabled by default. This example uses the USB-CAN-A module from Waveshare for testing.
3. Common Commands
-
Communicate with the USB-CAN-FD module and set the arbitration domain and data domain bit rates to 1M.
sudo suip link set can0 downip link set can0 type can bitrate 1000000 dbitrate 1000000 fd onip link set can0 up -
CAN FD Sending:
Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123##1DEADBEEFSend (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123##1DEADBEEF -
CAN 2.0 Sending:
Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123#DEADBEEFSend (Standard frame, Remote frame, ID:123): cansend can0 123#RSend (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123#12345678Send (Extended frame, Remote frame, ID:00000123): cansend can0 00000123#R -
CAN 2.0 and CAN FD Receiving:
candump can0
4. CAN Test (Python Program)
If you want to perform CAN bus testing using Python, the python-can library is a very suitable option. It provides a high-level API for CAN communication and supports various lower-level CAN interfaces such as SocketCAN, PCAN, and Kvaser, allowing developers to easily work with CAN bus communication in Python. By using python-can, you can focus on sending and receiving data without delving into the complexities of the underlying details.
-
Install the
python-canlibrary:sudo apt install python3-can -
Verify the library installation:
root@luckfox:/home/luckfox# python3Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>> import can>>> exit() -
Write a sending program (avoid naming the script
can.pyto prevent conflicts with thepython-canlibrary).#!/usr/bin/python3import candef send_can_message():# Connect to can0 using SocketCANbus = can.interface.Bus(channel='can0', bustype='socketcan')# Create a CAN messagemsg = can.Message(arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=False)# Send the CAN messagetry:bus.send(msg)print("Message sent on can0")except can.CanError:print("Message NOT sent")if __name__ == "__main__":send_can_message() -
Run the program by opening a new terminal window:
chmod +x can_test.py./can_test.pyroot@luckfox:/home/luckfox# candump can0can0 123 [8] 01 02 03 04 05 06 07 08
5. CAN Test (C Program)
SocketCAN is a native CAN bus interface supported by Linux. It uses a standard network interface (similar to Ethernet) to configure and communicate with the CAN bus, providing an API similar to common network protocol stacks (e.g., TCP/IP). This makes working with CAN more flexible and streamlined.
- Key Features:
- Unified Interface: Treats CAN devices as network interfaces (e.g.,
can0,can1) accessible via standard networking APIs. - Kernel Integration: Part of the Linux kernel, allowing direct system calls for accessing CAN interfaces using socket functions.
- Protocol Support: Compatible with standard CAN (CAN 2.0) and CAN FD (Flexible Data-rate).
- Device Abstraction: Abstracts CAN controllers as network devices, enabling management with standard tools like
ip.
- Unified Interface: Treats CAN devices as network interfaces (e.g.,
5.1 Programming with SocketCAN
-
Create a socket:
int sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); -
Bind the socket to a CAN interface:
struct ifreq ifr;strcpy(ifr.ifr_name, "can0");ioctl(sockfd, SIOCGIFINDEX, &ifr);struct sockaddr_can addr;addr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)); -
Send and receive CAN frames:
write(sockfd, &frame, sizeof(struct can_frame));read(sockfd, &frame, sizeof(struct can_frame)); -
Complete example for CAN transmission:
#include <stdio.h>#include <string.h>#include <stdlib.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 sockfd;struct sockaddr_can addr;struct ifreq ifr;struct can_frame frame;// Create a CAN socketsockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);if (sockfd < 0) {perror("Error while opening socket");return -1;}// Specify the CAN interface (assuming it is can0)strcpy(ifr.ifr_name, "can0");ioctl(sockfd, SIOCGIFINDEX, &ifr);// Bind the CAN socket to the interfaceaddr.can_family = AF_CAN;addr.can_ifindex = ifr.ifr_ifindex;if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {perror("Error in socket bind");return -2;}// Prepare to send a CAN frameframe.can_id = 0x123; // 11-bit standard frame IDframe.can_dlc = 8; // Data length is 8 bytes// Use a loop to assign CAN data framesfor (int i = 0; i < frame.can_dlc; i++){frame.data[i] = i + 1; // Assign data from 0x01 to 0x08 in sequence}// Send the CAN framefor(int i=0;i<3;i++){if (write(sockfd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {perror("Write error");return -3;}}printf("CAN message sent on can0\n");// Close the socketclose(sockfd);return 0;} -
Compile and run the program.
gcc can.c -o can./can -
Reopen another terminal to receive data.
root@luckfox:/home/luckfox# candump can0can0 123 [8] 01 02 03 04 05 06 07 08can0 123 [8] 01 02 03 04 05 06 07 08can0 123 [8] 01 02 03 04 05 06 07 08
6. Device Tree Overview
-
The CAN DTS source file has been defined in
kernel-6.10/arch/arm64/boot/dts/rockchip/rk3576.dtsi,and we can call it directly.can0: can@2ac00000 {compatible = "rockchip,rk3576-canfd";reg = <0x0 0x2ac00000 0x0 0x1000>;interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru CLK_CAN0>, <&cru HCLK_CAN0>;clock-names = "baudclk", "apb_pclk";resets = <&cru SRST_CAN0>, <&cru SRST_H_CAN0>;reset-names = "can", "can-apb";dmas = <&dmac0 20>;dma-names = "rx";status = "disabled";}; -
The device file path is located in
kernel-6.1/arch/arm64/boot/dts/rockchip/luckfox-omni3576-can.dtsi,the following snippet enablescan0:&can0 {assigned-clocks = <&cru CLK_CAN0>;assigned-clock-rates = <200000000>;pinctrl-names = "default";pinctrl-0 = <&can0m2_pins>;status = "okay";};