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 su
ip link set can0 down
ip link set can0 type can bitrate 1000000 dbitrate 1000000 fd on
ip link set can0 upCAN FD Sending:
Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123##1DEADBEEF
Send (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123##1DEADBEEFCAN 2.0 Sending:
Send (Standard frame, Data frame, ID:123, Data:DEADBEEF): cansend can0 123#DEADBEEF
Send (Standard frame, Remote frame, ID:123): cansend can0 123#R
Send (Extended frame, Data frame, ID:00000123, Data:DEADBEEF): cansend can0 00000123#12345678
Send (Extended frame, Remote frame, ID:00000123): cansend can0 00000123#RCAN 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-canVerify the library installation:
root@luckfox:/home/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()Write a sending program (avoid naming the 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 program by opening a new terminal window:
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 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 socket
sockfd = 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 interface
addr.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 frame
frame.can_id = 0x123; // 11-bit standard frame ID
frame.can_dlc = 8; // Data length is 8 bytes
// Use a loop to assign CAN data frames
for (int i = 0; i < frame.can_dlc; i++)
{
frame.data[i] = i + 1; // Assign data from 0x01 to 0x08 in sequence
}
// Send the CAN frame
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 the socket
close(sockfd);
return 0;
}Compile and run the program.
gcc can.c -o can
./canReopen another terminal to receive 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 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";
};