CAN
1. Introduction
CAN, short for Controller Area Network, is a serial communication protocol widely used for real-time data communication. Initially developed by the German company Bosch for communication between electronic control units (ECUs) in automobiles, CAN protocol's efficiency and reliability have led to its extensive use in other fields, such as industrial automation, medical devices, marine systems, and aerospace.
2. Enabling CAN
The RM_IO offers a high degree of flexibility, allowing any RMIO pin to be designated as a CAN interface. Given that different customers have significantly varied requirements for using the CAN interface, the pre - enabled settings we provide may not meet the actual needs of all customers. Therefore, the CAN interface function is not enabled by default in the factory image. It is recommended that customers download the SDK on their own, add the device tree provided at the end of this section to the device tree, and then compile the image to enable the CAN interface according to their needs. This example uses the USB-CAN-A module from Waveshare for testing.
3. Common Commands
Communicate with the USB-CAN-FD module, setting arbitration and data bitrates to 1 Mbps.
sudo su
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 1000000 fd on
ip link set can0 upCAN FD transmission:
Sending (standard frame, data frame, ID: 123, data: DEADBEEF): cansend can0 123##1DEADBEEF
Sending (extended frame, data frame, ID: 00000123, data: DEADBEEF): cansend can0 00000123##1DEADBEEFCAN 2.0 transmission:
Sending (standard frame, data frame, ID: 123, data: DEADBEEF): cansend can0 123#DEADBEEF
Sending (standard frame, remote frame, ID: 123): cansend can0 123#R
Sending (extended frame, data frame, ID: 00000123, data: DEADBEEF): cansend can0 00000123#12345678
Sending (extended frame, remote frame, ID: 00000123): cansend can0 00000123#RReceiving CAN 2.0 and CAN FD frames:
candump can0
4. CAN Testing (Python Program)
If you want to test the CAN bus using Python, the python-can library is a highly suitable choice. It provides a high-level abstraction for CAN communication, supporting various backend interfaces like SocketCAN, PCAN, and Kvaser. With python-can, developers can focus on data transmission and reception without delving into low-level complexities.
Verify library installation:
root@luckfox:~# python3
Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import can
>>> exit()Example transmission script (Avoid naming your script
can.pyto prevent conflicts with thepython-canlibrary):#!/usr/bin/python3
import can
def send_can_message():
# Connect to can0 using SocketCAN
bus = can.interface.Bus(channel='can0', bustype='socketcan')
# Create a CAN message
msg = can.Message(arbitration_id=0x123, data=[1, 2, 3, 4, 5, 6, 7, 8], is_extended_id=False)
# Send the CAN message
try:
bus.send(msg)
print("Message sent on can0")
except can.CanError:
print("Message NOT sent")
if __name__ == "__main__":
send_can_message()Run the script and open another terminal to monitor data:
chmod +x can_test.py
./can_test.py
root@luckfox:/home/luckfox# candump can0
can0 123 [8] 01 02 03 04 05 06 07 08
5. CAN Testing (C Program)
SocketCAN is a native Linux interface for CAN bus communication. It uses a standard network API, providing a flexible and simplified approach to using CAN, similar to typical network protocols like TCP/IP
- 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 <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
int main() {
int sockfd;
struct sockaddr_can addr;
struct ifreq ifr;
struct can_frame frame;
sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (sockfd < 0) {
perror("Error opening socket");
return -1;
}
strcpy(ifr.ifr_name, "can0");
ioctl(sockfd, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
frame.can_id = 0x123;
frame.can_dlc = 8;
for (int i = 0; i < frame.can_dlc; i++) {
frame.data[i] = i + 1;
}
for (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(sockfd);
return 0;
}Cross-compile the program. For setup, refer to sections Program Compilation or GPIO.
arm-none-linux-gnueabihf-gcc can.c -o canOpen another terminal to monitor received data:
root@luckfox:/home/luckfox# candump can0
can0 123 [8] 01 02 03 04 05 06 07 08
can0 123 [8] 01 02 03 04 05 06 07 08
can0 123 [8] 01 02 03 04 05 06 07 08
6. Device Tree Overview
The device tree file is located at
kernel-6.1/arch/arm/boot/dts/rk3506g-luckfox-lyra.dts. The following code snippet shows how to enableCAN:&can0 {
assigned-clocks = <&cru CLK_CAN0>;
assigned-clock-rates = <200000000>;
pinctrl-names = "default";
pinctrl-0 = <&rm_io30_can0_tx &rm_io31_can0_rx>;
status = "okay";
};