Versenden und Empfangen einer Nachricht

canknotenkomplett_klein.jpg

In diesem Artikel gehe ich etwas genauer auf die Grundlagen der Nachrichtenübertragung über den CAN-Bus mit Hilfe eines MCP2515 ein.
Für ein besseres Verständnis des Programmes wäre es hilfreich dieses griffbereit zu haben, da es sehr lang ist und ich es aus diesem Grund nicht komplett hier posten werde.

-> Der Loopback Modus:

Der MCP2515 bietet ein nettes Feature, wodurch es möglich ist die Übertragung ohne zweiten CAN-Controller o.ä. zu testen; den sogenannten „Loopback“ Modus.
Wenn der Controller in diesen Modus versetzt worden ist, kann er Nachrichten versenden und gleichzeitig empfängt er sie.
Um den MCP2515 in den „Loopback“ Modus zu versetzen, verwende ich folgendem Befehl:

Dieser Befehl wird einfach an den Anfang eures Hauptprogramms gesetzt und er modifiziert die „REQOP“ Bits vom „CANCTRL“ Register.
Diese Bits legen den Betriebsmodus des Controllers fest:

MCP2515 Betriebsmodi

Nachdem der CAN-Controller nun in den „Loopback“ Modus versetzt worden ist, können wir mit dem Versenden und Empfangen von Nachrichten beginnen.

-> Aktivieren der Interrupts:

Die erste Nachricht, die wir versenden wird ein Remoteframe sein, da dieser keine Daten enthält und somit etwas leichter zu handhaben ist.
Bevor ich allerdings auf das Programm eingehe muss der Mega32 erstmal vorbereitet werden.
Da ich die gesamte Kommunikation über CAN Interruptgesteuert realisiert habe, muss der INT Pin vom MCP2515 mit einem INT Pin vom Mega32 verbunden werden.
Für meine Versuche habe ich den INT0 gewählt.
Aktiviert wird dieser wie folgt:

Als erstes werden die Interrupts global freigegeben und dann wird der INT0 Pin so eingestellt das er auf fallende Flanken reagiert.
Die Zeile

legt die Sprungmarke für den Interrupt fest. Das Programm springt also bei einer fallenden Flanke in die ISR „MCP2515_Int“.
Als letztes wird der INT0 noch aktiviert.
Anschließend muss noch der Pin D.2 als Eingang geschaltet werden:

Falls keine externen Pull-Up Widerstände verwendet werden, muss der interne Pull-Up mit dem Befehl

eingeschaltet werden.

-> Auswerten eines Interrupts:

Die ISR für den INT0 sieht folgendermaßen aus:

Da es nicht sonderlich effizient ist in einer ISR einen Sprung in ein Unterprogramm zu machen (da die ISR sonst zu lange aktiv ist), setze ich dort nur ein Flag.
Dieses Flag frage ich dann im Hautprogramm ab:

Sobald also ein Interrupt auftritt (was erstmal nur passiert wenn der CAN-Controller eine Nachricht empfängt und den entsprechend ein Rx-Buffer Full Interrupt auslöst), wird eine Meldung im Terminal ausgegeben, das Flag auf 0 gesetzt und in ein Unterprogramm mit dem Namen „MCP2515_Interrupt“ gesprungen.
Dieses Unterprogramm sieht wie folgt aus:

In diesem Unterprogramm findet die eigentliche Analyse des Interrupts statt.
Diese erfolgt in mehreren Schritten:

Als erstes wird mittels

das Bitmuster aus dem Interruptflag Register des CAN-Controllers geladen.
Anschließend werden die einzelnen Bits über eine Reihe von Bitmanipulationen herausgefiltert.
Diese Bits werden anschließend über ein Terminal ausgegeben.
Im letzten Schritt wird das Unterprogramm „Recieve_can“ aufgerufen.
Wofür dieses Unterprogramm da ist erkläre ich später genauer.
Für die ersten Versuche wird es nicht benötigt.
Das Grundgerüst für das Empfangen steht nun. Wir können jetzt erkennen ob der CAN-Controller eine neue Nachricht in seinen Buffern hat sobald dieser einen Interrupt auslöst.

-> Verschicken eines Remoteframes:

Jetzt geht es an das Versenden einer Nachricht.
Wie oben bereits erwähnt habe ich für meine ersten Versuche einen Remoteframe verschickt.
Dies geschieht mit folgendem Unterprogramm:

Es wird wie folgt aufgerufen:

Das Unterprogramm bekommt als Übergabeparameter die ID der zukünftigen Nachricht, sowie die Nachrichtenlänge übergeben.
Im Unterprogramm wird als erstes geprüft ob die Nachrichtenlänge mehr als 8 Zeichen beträgt und ob der Identifier größer als 2047 (11 Bit) ist.
Wenn dies der Fall ist wird das Unterprogramm verlassen und ein Fehler ausgegeben.
Wenn alles in Ordnung ist, wird der Identifier zerlegt, geshiftet und in die beiden Register für den Identifier geschrieben (einmal die höherwertigen 8 Bit und einmal die niederwertigen 3 Bit).
Im nächsten Schritt wird die Datenlänge in das „Datalenght“-Register geschrieben. Zusätzlich wird dort noch das Remotebit gesetzt, damit der CAN-Controller weiß das die nächste Nachricht ein Remoteframe sein wird.
Als letztes wird mit dem Befehl:

die Übertragung ausgelöst.
Wenn ihr das Unterprogramm nun z.B. wie folgt aufruft (z.B. 1x die Sekunde mittels Timer)

sollte nach dem Versenden der Nachricht eine Ausgabe im Terminal erscheinen.
Wenn diese Meldung im Terminal erscheint, habt ihr alles richtig gemacht.
Dann wurde der Remoteframe erfolgreich gesendet und der CAN-Controller hat ihn auch erfolgreich empfangen.

-> Auswerten der Nachricht:

Natürlich ist dies etwas wenig für eine Kommunikation. Die nächsten Schritte finden anschließend im Unterprogramm „Recieve_can“ statt.
Dort wird als aller erstes mit diesem Befehl

das Statusregister der Recievebuffer ausgelesen.
Anschließend werden wieder über eine Reihe von Bitmanipulationen alle notwendigen Informationen aus dem im Buffer gespeicherten Bitmuster gewonnen:

Danach wird geprüft in welchem Buffer die Nachricht liegt.
Dies geschieht über eine einfache If-Else Abfrage und nachdem der Empfangsbuffer bestimmt worden ist, geschieht dasselbe nochmal für den Frame um zu erkennen ob es sich um einen Standard Datenframe oder um einen Standard Remote Frame handelt.
Da ich in dem Beispiel erstmal nur einen Remoteframe gesendet habe, werde ich auch erstmal nur auf diesen Teil der Abfrage eingehen.
Da nun bestimmt ist, dass die Nachricht in Buffer 0 liegt und es sich um einen Standard Remote Frame handelt, kann mit dem Befehl

das Lesen von Buffer 0 ausgelöst werden.
Sobald dieser Befehl abgeschickt wurde, gibt der CAN-Controller alle in dem Frame enthaltenen Informationen über das SPI Interface aus.
Diese werden nun eingelesen:

Die ersten beiden Bytes sind die beiden Teile der ID. Diese werden in separaten Variablen gespeichert. Bei den Werten die unter „Dummy“ gespeichert werden handelt es sich um Bytes für den Extended Identifier, welche sofort verworfen werden, da ich sie nicht verwenden möchte.
Das letzte Byte ist dann die Länge.
Sobald der Einlesevorgang abgeschlossen ist müssen die empfangenen Daten noch wieder richtig zusammen gesetzt werden.
Dies geschieht so:

Als erstes wird die Aufteilung des Identifiers rückgängig gemacht. Hierfür werden die Bitmuster wieder in die richtige Position geschoben und anschließend verodert und an der ersten Stelle des Arrays für die Nachricht gespeichert.
Bei dem Bitmuster für die Länge muss noch das Remotebit entfernt werden.
Dies geschieht über eine einfache Subtraktion
Die komplette Nachricht wird anschließend ausgegeben:

Wenn auch hier alles funktioniert, erhaltet ihr eine Nachricht mit exakt den Werten die ihr abgeschickt habt.
Das verschicken, empfangen und auswerten eines Remoteframes klappt nun also bereits.
Im nächsten Artikel erkläre ich euch dann wie ihr einen Datenframe sendet.

-> Verschicken eines Datenframes:

Das Versenden eines Datenframes geschieht auf ähnliche Weise wie das Versenden eines Remoteframes.
Das Unterprogramm hierfür sieht so aus:

Das Unterprogramm wird wie folgt aufgerufen:

In das Unterprogramm werden die ID, die Datenlänge und die einzelnen Datenbytes der Nachricht übergeben.
Auch hier wird erst geprüft ob ein gültiger Parameter für die ID und für die Datenlänge übergeben wurde.
Anschließend wird, wie auch beim Remoteframe, das Bitmuster für das Identifierregister erzeugt und die Datenlänge in das „Datalenght“-Register geschrieben.
Danach beginnt eine Zählschleife, die die Datenregister des CAN-Controllers mit den übergebenen Daten füttert.
Diese Schleife sieht so aus:

Als erstes wird die Variable „Transmitadress“ auf die Adresse vom Datenregister 0 gesetzt.
Danach beginnt eine Schleife die von 1 bis zu der Anzahl an übergebenen Daten zählt (also maximal 8) und jedes mal die Variable „Transmitadress“ um eins erhöht.
Anschließend wird das Register mit der, durch die Variable „Transmitadress“ angegebenen, Adresse mit einem durch den Zähler markierten Datenbyte gefüllt.
Danach wird der Zähler um eins erhöht und alles beginnt von vorne.
Sobald die Schleife durchlaufen wurde, wird die Übertragung ausgelöst.
Dies geschieht auf die selbe Weise wie beim Remoteframe.

-> Auswerten der Nachricht:

Das Auswerten der Nachricht geschieht ebenfalls in dem Unterprogramm „Recieve_can“ und ist bis zu der If-Else Abfrage identisch mit der Auswertung eines Remoteframes.
Sobald das Programm aber feststellt das es sich um einen Datenframe handelt, springt es in den Teil der Abfrage der für die Auswertung eines Datenframes zuständig ist.
Als erstes wird mit

das Lesen des Empfangsbuffers ausgelöst und die beiden Bytes für die ID, sowie die beiden bytes für den Extended Identifier eingelesen.
Anschließend wird die Länge der Daten eingelesen und aufbereitet:

Spiin Laenge , 1
Zaehler_laenge = Laenge + 2
Laenge = Laenge And &H0F

Diese Länge verwende ich nun für eine Schleife, die bei jedem Durchlauf ein Datenbyte einließt und es in einem Array speichert:

Nachdem alle Daten eingelesen wurde, werden die beiden Bytes der Message ID wieder zusammen gesetzt um die ursprüngliche Nachrichten ID zu erhalten. Anschließend werden alle Informationen in einem Nachrichtenarray gespeichert:

Als letztes wird die komplette Nachricht über UART ausgegeben:

Das ganze kann nun getestet werden, indem mit folgendem Befehl eine Übertragung gestartet wird:

Dadurch wird ein Datenframe mit der ID 7, einer Länge von 4 Byte und den Datenbytes 2, 5, 7 und 8 verschickt.

 

-> Zurück zum CAN-Bus

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Time limit is exhausted. Please reload CAPTCHA.