CAN-Bus am Raspberry Pi

Bei meinem HomePi ist der CAN-Bus über einen IC vom Typ MCP2515 direkt an einen Raspberry Pi angebunden. In diesem Beitrage zeige ich, wie der CAN-Bus hard- und softwaremäßig an den Raspberry Pi angebunden wird.

Inhalt

Hardware

Für die Hardware werden der CAN-Controller MCP2515 und der CAN-Transceiver MCP2562 eingesetzt. Der MCP2515 kümmert sich dabei um das Senden und Empfangen der Nachrichten im CAN-Protokoll. Der MCP2562 ist ein Treiber, welcher (einfach ausgedrückt) die Signale des MCP2515 für die Übertragung verstärkt.

Unter Verwendung dieser beiden ICs sind Datenraten von bis zu 1 Mb/s auf dem CAN-Bus möglich.

Die Beschaltung erfolgt gemäß dem folgenden Schaltplan:

Schaltplan

Der CAN-Controller MCP2515 wird mit +3,3 V als Betriebsspannung versorgt. Somit ist eine direkte Verbindung der Datenleitungen des SPI-Bus zum Raspberry Pi möglich.

Der Treiber MCP2562 wird mit +5 V als Betriebsspannung versorgt. Zusätzlich wird er mit +3,3 V versorgt, welche er als Basis für seine RXD und TXD Leitungen verwendet. Hier erfolgt somit automatisch durch den IC die Pegelanpassung zwischen +5 V und den +3,3 V, die für den Raspberry Pi erforderlich sind.

Achtung

Es dürfen keine Leitungen mit +5 V direkt mit dem Raspi verbunden werden, da dieser sonst beschädigt wird.

Als Taktquelle für den MCP2515 dient ein 16 MHz Quarz. Alternativ kann hier auch ein 8 MHz Quarz verwendet werden.

Der Widerstand R2 dient als Abschlusswiderstand zur Terminierung des CAN-Bus. Dieser sollte nur jeweils einmal am Anfang und am Ende des gesamten CAN-Bus vorhanden sein. Bei Busteilnehmern “in der Mitte” darf dieser Widerstand nicht vorhanden sein!

Installation der Software

Als Basissystem dient hier ein normales aktuelles Raspberry Pi OS.

Die nötige Software ist in den Standard-Repositories vom OS bereits enthalten und muss nur noch installiert werden.

Softwarepakete installieren
1
2
sudo apt update
sudo apt install can-utils

Aktivieren der Hardware

Damit die Hardware verwendet wird, müssen die entsprechenden Overlays in der Datei /boot/firmware/config.txt konfiguriert werden. Hierfür werden am Ende der Datei die folgenden Zeilen hinzugefügt:

Zusätzliche Einträge für die config.txt
1
2
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=6

Die 16000000 steht in dem Fall dafür, dass der MCP2515 mit einem Takt von 16 MHz arbeitet. Dies ist abhängig von dem verwendeten Quarz und muss gegebenenfalls angepasst werden.

Die 6 steht für den GPIO-Pin (BCM), an dem die Interrupt-Leitung des MCP2515 angeschlossen ist und muss ebenfalls an die eigene Hardware angepasst werden.

Zum Übernehmen der Änderungen ist ein Neustart das Raspberry Pis erforderlich.

Hinweis

Ein paar Informationen zu anderen Hardwarekonfigurationen habe ich am Ende dieses Beitrags aufgelistet.

Prüfen ob die Hardware erkannt wurde

Ob die Hardware korrekt erkannt wurde kann wie folgt überprüft werden:

Prüfen ob die Hardware erkannt wurde
1
2
3
4
5
6
7
8
9
10
11
12
13
pi@raspberrypi:~ $ ls /sys/bus/spi/devices/spi0.0
driver modalias of_node statistics supplier:platform:3f200000.gpio
driver_override net power subsystem uevent
pi@raspberrypi:~ $ ls /sys/bus/spi/devices/spi0.0/net
can0
pi@raspberrypi:~ $ ls /sys/bus/spi/devices/spi0.0/net/can0
addr_assign_type dev_port name_assign_type speed
address dormant napi_defer_hard_irqs statistics
[...]
device link_mode proto_down
dev_id mtu queues
pi@raspberrypi:~ $ ls /sys/class/net
can0 eth0 lo

Sieht die Ausgabe so aus, wie hier dargestellt, dann ist alles in Ordnung und der Raspberry Pi kann mit dem MCP2515 kommunizieren. Sollte ein Problem vorliegen, dann liefert mindestens einer der Befehle einen Fehler zurück.

Einrichtung des CAN-Interfaces

Der CAN-Bus erscheint auf dem Raspberry Pi als Netzwerkinterface. Ein Aufruf von ip addr sollte das Interface can0 anzeigen, wobei es jedoch noch als DOWN markiert ist.

Auszug der Ausgabe von ip addr
1
2
3
ip link show type can
3: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN group default qlen 10
link/can

Über den folgenden Befehl kann das Interface manuell aktiviert werden:

CAN-Interface manuell aktivieren
1
sudo ip link set can0 up type can bitrate 500000

Hierbei wird eine Bitrate von 500 kb/s verwendet. Diese Bitrate kann bei Bedarf natürlich angepasst werden und sollte bei allen Busteilnehmern gleich sein.

Ein erneuter Aufruf von ip addr zeigt nun, dass das Interface aktiv ist.

Auszug der Ausgabe von ip addr mit aktivem Interface
1
2
3
ip link show type can
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP group default qlen 10
link/can

Automatisches Aktivieren des Interfaces beim Systemstart

Damit das can0 Interface beim Systemstart automatisch aktiviert wird, müssen die folgenden Einträge in der Datei /etc/network/interfaces hinzugefügt werden. Dies funktioniert auch dann, wenn die “normalen” Netzwerkinterfaces vom NetworkManager oder dhcpcd verwaltet werden.

CAN-Bus Einträge in /etc/network/interfaces
1
2
3
4
# CAN-Bus
auto can0
iface can0 can static
bitrate 500000

Die 500000 ist hierbei wieder die zu verwendende Bitrate.

Testen des CAN-Bus

Zum Testen des CAN-Bus können nun über ein Terminal Nachrichten gesendet oder empfangen werden.

Achtung

Es muss mindestens ein weiterer Teilnehmer am Bus sein. Sonst bleiben vom CAN-Controller erwartete Rückmeldungen auf dem Bus aus und der Controller geht somit in einen Fehlerzustand. Alternativ kann zum Testen der Loopback-Modus aktiviert werden.

Senden von CAN-Nachrichten

Zum Senden von Nachrichten kann das Tool cansend verwendet werden, wobei Standard Frames mit 11-Bit Nachrichten-IDs und Extended Frames mit 29-Bit Nachrichten-IDs unterstützt werden.

Für Standard Frames sieht der Aufruf wie folgt aus:

Senden einer Nachricht als Standard Frame
1
cansend can0 123#42ff

123 ist in diesem Fall die ID der Nachricht in Hex-Schreibweise und muss immer 3 Zeichen lang sein. 42ff sind die Daten, wiederum in Hex, mit einer Länge von 0 bis 8 Byte.

Bei Extended Frames besteht die ID aus 8 Zeichen. Der Rest verhält sich analog zu den Standard Frames.

Senden einer Nachricht als Extended Frame
1
cansend can0 012EA567#42ff

Eine kurze Hilfe zu cansend erhält man über die Befehle cansend --help oder man cansend.

Empfangen von CAN-Nachrichten

Über den Befehl candump könne empfange Nachrichten angezeigt werden.

Empfangene Nachrichten anzeigen mittels Candump
1
2
3
4
5
6
pi@raspberrypi:~ $ candump can0
can0 123 [2] 42 FF
can0 012EA567 [2] 42 FF
can0 1FFFFFFF [8] 00 11 22 33 44 55 66 77
can0 001 [0]
can0 00001337 [4] DE AD BE EF

Optional können über die Angabe von beispielsweise -tA -x zusätzliche Informationen angezeigt werden:

Empfangene Nachrichten anzeigen mittels Candump und zusätzlichen Infos
1
2
3
4
5
pi@raspberrypi:~ $ candump -tA -x can0
(2024-04-25 11:48:30.715527) can0 RX - - 05004004 [8] 00 00 08 41 00 00 00 41
(2024-04-25 11:49:00.202991) can0 TX - - 1F000001 [7] 14 18 04 19 0B 31 00
(2024-04-25 11:49:00.715446) can0 RX - - 05004004 [8] 00 00 08 41 00 00 00 41
(2024-04-25 11:49:30.715394) can0 RX - - 05004004 [8] 00 00 04 41 00 00 02 41

Dabei sind gesendete Nachrichten dann mit TX und empfangene mit RX gekennzeichnet. Dahinter folgen das BRS und das ESI Bit.
Ist das ESI Bit gesetzt, bedeutet dies, dass der Sender der Nachricht im Error Passive Zustand ist.

Details zum Status des CAN-Interfaces anzeigen

Genaue Details zu dem can0 Interface können wie folgt angezeigt werden:

Details zum CAN-Interface anzeigen lassen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pi@raspberrypi:~ $ ip -details -statistics link show can0
3: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
can state ERROR-ACTIVE restart-ms 0
bitrate 500000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 brp 1
mcp251x: tseg1 3..16 tseg2 2..8 sjw 1..4 brp 1..64 brp_inc 1
clock 8000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 tso_max_size 65536 tso_max_segs 65535 gro_max_size 65536 parentbus spi parentdev spi0.0
RX: bytes packets errors dropped missed mcast
3 1 0 0 0 0
TX: bytes packets errors dropped carrier collsns
3 1 0 0 0 0

Interessant sind hier vor allem der aktuelle Zustand in Zeile 4 (hier ERROR-ACTIVE), sowie die Anzahl der empfangenen und gesendeten Pakete in den letzten 4 Zeilen.

Über die Anzahl der empfangenen und gesendeten Pakete, besonders dabei den errors und dropped lässt sich schnell ein Überblick über eventuelle Probleme auf dem Bus gewinnen.

Tipp

Nicht von clock 8000000 verwirren lassen. Ich nutze beispielsweise einen 16 MHz Quarz, sowie die passenden Einträge in der config.txt und es werden hier trotzdem 8 MHz angezeigt.

Mögliche Zustände der Bus-Teilnehmer

Grundsätzlich gibt es beim CAN-Bus drei mögliche (Fehler-) Zustände für einen Teilnehmer:

  • Error-active
    Der Teilnehmer nimmt aktiv am Bus teil und kann Nachrichten und aktive Fehlernachrichten (active error frames) senden. Dies sollte der Normalzustand sein.
  • Error-passive
    Der Teilnehmer nimmt am Bus teil, aber sendet keine aktiven Fehlernachrichten mehr, sondern nur noch passive (passive error frames). Dieser Zustand tritt ein, wenn der Teilnehmer Fehler auf dem Bus erkannt hat und soll verhindern, dass er den Bus mit möglichweise falschen Nachrichten stört.
  • Bus-off
    Bei zu vielen Fehlern koppelt sich der Teilnehmer selbstständig vom Bus ab, um den Bus nicht zu stören. In diesem Zustand können keine Nachrichten empfangen oder gesendet werden.

Loopback-Modus

Normalerweise sendet der CAN-Controller die Nachrichten an den Bus und erwartet eine entsprechende Rückmeldung. Bleibt diese aus, so geht der Controller von einem Fehler aus.

Möchte man nur lokal etwas testen so kann man den Lookback-Modus aktivieren. Dieser leitet die zu sendenden Nachrichten direkt wieder an den Controller zurück. Der Controller spricht quasi mit sich selbst, ohne etwas wirklich zu senden.

Aktiviert werden kann der Loopback-Modus durch Erweiterung des Befehls um loopback on. War das Interface bereits aktiv, so muss es zuvor gestoppt werden.

Interface im Loopback-Modus aktivieren
1
2
sudo ip link set can0 down
sudo ip link set can0 up type can bitrate 500000 loopback on

In den Details zum Interface ist dies dann auch durch den Zusatz <LOOPBACK> deutlich gekennzeichnet:

Details zum Interface bei aktivem Loopback-Modus
1
2
3
4
5
pi@raspberrypi:~ $ ip -details -statistics link show can0
3: can0: <NOARP,ECHO> mtu 16 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 10
link/can promiscuity 0 allmulti 0 minmtu 0 maxmtu 0
can <LOOPBACK> state STOPPED restart-ms 0
[...]

Wieder deaktivieren lässt sich der Loopback-Modus durch loopback off am Ende des Befehls.

Mögliche Leitungslängen

Die maximal mögliche Leitungslänge beim CAN-Bus hängt vor allem von der verwendeten Bitrate ab. Je höher die Bitrate, desto geringer die maximale Leitungslänge.

Selbstverständlich sollten bei größeren Längen immer entsprechend qualitativ hochwertige und geschirmte Kabel verwendet werden.

Die hier angegebenen Längen sind Richtwerte. Die tatsächlich erreichbare Länge hängt von vielen Faktoren ab.

BitrateLeitungslänge
10 kb/s6,7 km
20 kb/s3,3 km
50 kb/s1,0 km
125 kb/s500 m
250 kb/s250 m
500 kb/s125 m
1 Mb/s25 m

Andere Hardwarekonfigurationen

Grundsätzlich ist zu beachten, welcher GPIO und was für ein Quarz von der Hardware verwendet werden. Beides muss entsprechend in der config.txt angepasst werden.

Hier noch eine kurze Zusammenstellung der Einträge für andere Hardwarekonfigurationen und kaufbaren Module.

Waveshare RS485 CAN HAT

Hier gibt es zwei Versionen, die über den aufgelöteten Quarz unterschieden werden können. Die ältere Version vor August 2019 hat einen 8 MHz Quarz (Aufschrift 8.000) und die neuere Version einen 12 MHz Quarz (Aufschrift 12.000).
Beide Version nutzen den GPIO 25 (BCM) als Interrupt.

config.txt Einträge für Waveshare RS485 CAN HAT mit 8 MHz Quarz
1
2
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25,spimaxfrequency=1000000
config.txt Einträge für Waveshare RS485 CAN HAT mit 12 MHz Quarz
1
2
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25,spimaxfrequency=2000000

Zwei CAN-Controller (z.B. PiCAN Duo)

Für die gleichzeitige Nutzung von zwei MCP2515 CAN-Controllern an einem SPI-Bus könnten die Einträge in /boot/config.txt so aussehen:

config.txt Einträge für zwei CAN-Controller
1
2
3
4
dtparam=spi=on
dtoverlay=spi0-2cs,cs0_pin=8,cs1_pin=7
dtoverlay=mcp2515-can0,oscillator=8000000,interrupt=25
dtoverlay=mcp2515-can1,oscillator=8000000,interrupt=24

Die erzeugt dann die Interfaces can0 und can1 im System.
Die letzte Zeile aktiviert die Unterstützung von zwei CS-Leitungen für den SPI-Bus.

Fehlersuche

Wenn die Datei /boot/firmware/config.txt nicht vorhanden ist, dann wird noch die alte Variante /boot/config.txt verwendet. Alle Overlay Einstellungen müssen dann dort hinein.

Bei Fehlern hilft oftmals in Blick in das Kernel Log.

Kernel Log nach mcp2515 durchsuchen
1
sudo dmesg | grep mcp2515

Im Idealfall sollte hier mcp251x spi0.0 can0: MCP2515 successfully initialized stehen. Andernfalls gibt es Probleme bei der Kommunikation mit dem MCP2515 CAN-Controller.

Bei älteren Installationen wird teilweise folgende Zeile zusätzlich in der config.txt benötigt:

Zusätzlich benötigtes Overlay bei älteren Installationen
1
dtoverlay=spi-bcm2835-overlay

Bei Problemen kann es teilweise helfen die Geschwindigkeit vom SPI-Bus zu reduzieren, indem dem man bei dtoverlay=mcp2515-can0,... am Ende noch ,spimaxfrequency=500000 hinzufügt.

Weitere Informationen

Hier noch ein paar Links zu Hardware- und Softwarekomponenten von mir zum CAN-Bus:

  • CAN-Shield - CAN-Bus Platine mit MCP2515 für Raspberry Pi und Arduino
  • MCP-CAN-Boot - CAN-Bus Bootloader für AVR Mikrocontroller
  • ioBroker.canbus - ioBroker Adapter zur Einbindung vom CAN-Bus