Navigation
MSP430 und CAN:

In diesem Tutorial möchte ich zeigen, wie man den MSP430 CAN-Bus-fähig machen kann. Standardmäßig existieren am MSP430 nur "gewöhnliche" serielle Schnittstellen wie USART (z.B. als RS232 und RS485). Wie man dem MSP430 beibringt, mit I2C-Geräten zu kommunizieren, habe ich ja schon in einigen Beispielen erklärt (siehe die entsprechenden News auf der Startseite).

Zu den Grundlagen des CAN-Busses möchte ich hier nichts mehr schreiben, dazu gibt es schon genügend gute Webseiten. Eine vorzügliche Einführung findet ihr z.B. auf Wikipedia.

Für dieses Tutorial gehe ich nun von einer komplett neuen Hardware aus, d.h. einem "leeren" MSP430-Headerboard auf einer Trägerplatine mit Versorgungsspannung. Alle Pinbelegungen in diesem Tutorial beziehen sich also NICHT mehr auf mein Standard-Board aus all den vorhergegangenen Tutorials!

OK, dann kann es ja losgehen ... Zu dem MSP430 kommen als erstes noch zwei weitere Bausteine, nämlich ein CAN-Driver MCP2510 von Microchip, der an den USART0 des MSP430 angeschlossen wird, sowie ein CAN-Transceiver SN65HVD230D, der wiederum an den MCP2510 angeschlossen wird. Vermutlich werdet ihr Probleme haben, den MCP2510 zu finden, da er nicht mehr hergestellt wird und bei den meisten Distris schon ausgelistet ist. In diesem Fall könnt ihr auf den MCP2515 ausweichen, welcher der überarbeitete Nachfolger des MCP2510 ist. Ausserdem verwende ich statt des weit verbreiteten PCA82C250/251 von Philips den oben erwähnten SN65HVD230D von Texas Instruments, da dieser Chip genau wie der MSP430 mit 3,3V versorgt werden kann und es somit keine Pegel-Probleme gibt. Wie man dem Datenblatt des Herstellers entnehmen kann, benötigt der MCP2515 als externe Beschaltung lediglich einen Quarz; in diesem Beispiel wird ein 2MHz-Oszillator verwendet.

Als dritten Baustein setzen wir jetzt einen MAX3233 (MAX233 in 3,3V-Technologie) dazu, welcher an den USART1 angeschlossen wird. Wer gerne Elkos lötet, kann natürlich auch einen MAX232 einsetzen. ;-) Die CS-Leitung des MCP2515 wird an P2.0 angeschlossen. Zu Testzwecken werden ferner 8 Low-Current-LEDs direkt über passende Vorwiderstände an die 8 Pins von Port 1 angeschlossen. An Port 4 hängt schließlich noch ein 8-bittiger R2R-DAC, mit dem ich später analoge Spannungen ausgeben möchte.

Während der Entwicklungsphase des Projektes sollte man als Test-CAN-Bus vielleicht nicht unbedingt sein Auto benutzen, sondern beispielsweise auf einen PC-Dongle ausweichen, der an den Parallelport des PCs angeschlossen wird. So kann man bequem an seinem Computer beliebige CAN-Nachrichten erzeugen -- und dann hoffentlich mit dem MSP430 empfangen! Empfehlenswert ist meiner Meinung nach die Seite CAN200, welche zeigt, wie man sich für ca. 15 Euro selber ein solches Dongle zusammenlöten kann. Die passenden Windows-Treiber liefert der Autor auf seiner Seite direkt mit.

Nun wird es langsam Zeit, sich mal die Software anzugucken! Ich habe die einzelnen Sourcecode-Dateien zu einem ZIP-Archiv zusammengefasst, welches ihr euch hier herunterladen könnt. Das Archiv enthält die eigentliche CAN-Bibliothek ("CAN_new.c"), eine Datei, welche lediglich den USART0 und USART1 initialisiert ("hw-init.c"), eine Datei mit ein paar nützlichen Hilfsunktionen ("tools.c"), eine Header-Datei mit alles #defines der MCP-Register ("MCP_Reg.h") und ein kurzes Hauptrogramm, welches die verwendete Hardware initialisiert und anschließend auf dem CAN-Bus nach gültigen Nachrichten lauscht ("main.c").

Die Datei "hw-init.c" initialisiert also die beiden verwendeten USARTs, und zwar den USART0 (an dem der MCP2515 hängt) mit 1MHz im SPI-Modus. Der MCP kann laut Datenblatt bis zu 10MHz, was jedoch bei dem verwendeten 2MHz-Quarz ein bisschen kritisch werden könnte. Wenn man einen höhergetakteten Quarz verwendet, sind aber sicherlich auch 10MHz machbar. USART1 wird als "normale" serielle RS232-Schnittstelle mit 57,6KBaud initialisiert. So kann man während der Testphase den MSP430 einfach vom PC aus per Terminal debuggen.

Auf die eigentliche CAN-Bibliothek "CAN_new.c" möchte ich nun nicht Zeile für Zeile eingehen, da die Kommentare im Sourcecode eigentlich für's Verständnis der Funktionen ausreichend sein sollten. In der Funktion can_init() ist vielleicht erwähnenswert, dass die drei Register CNF1, CNF2 und CNF3 die verwendete CAN-Baudrate unter Berücksichtigung des Quarzes festlegen -- hier 125KBaud. Die Definition der einzelnen Register steht in der Header-Datei "MCP_Reg.h", welche ich bei Microchip gefunden und weitestgehend übernommen habe. Die wichtigsten der jeweils 1 Byte großen Register sind die Transmit-Register TXBxD0 bis TXBxD7 sowie die entsprechenden Receive-Register RXBxD0 bis RXBxD7. In den Registern TXBxDLC bzw. RXBxDLC steht der DLC (Data Length Code), also die Information über die Länge des zu sendenden/empfangenen CAN-Frames. Weiterhin gibt es verschiedene Kontroll- und Interrupt-Register.

Wie funktioniert nun das Senden eines Frames? Nun, schauen wir uns hierzu einmal die Sendefunktion can_tx0() an. Zuerst muss die CAN-ID des zu sendenden Frames festgelegt werden. Da diese 2 Byte lang ist, sind in der Funktion can_write_id() u.a. einige Shift-Operationen nötig. Anschließend wird in das TXBxDLC-Register die Länge des zu sendenden Frames geschrieben (maximal 8 Byte). Nun füllt man die Register TXBxD0 bis TXBxD7 mit den Bytes, die man versenden möchte. Ist auch dieses geschehen, wird der Sende-Befehl übermittelt. Wenn alles klappt, sollte der Frame nun auf dem CAN-Bus liegen. :-)

Das Empfangen von Daten ist ein wenig umfangreicher, aber nicht viel schwerer. Zuerst prüft man mithilfe der Funktion can_chk(), ob einer der beiden CAN-Receiver einen Frame empfangen hat (empfangene Daten werden durch einen Interrupt im Register CANINTF angezeigt!). Hat beispielsweise der erste Receiver (RX0) Daten empfangen, muss man anschließend über die Funktion can_rx0() die CAN-ID und den DLC des emfangenen Frames auslesen. Kennt man die Länge der Daten, so kann man schließlich mit can_read_many_registers() die Register RXBxD0 bis RXBxD+Datenlänge auslesen. Anhand der ID kann man allerhand machen, z.B. nur auf bestimmte IDs reagieren, bestimmte Bytes lesen usw. Hat man die Receive-Register ausgelesen, so sollte man tunlichst nicht vergessen, das RXxIF-Register zu löschen, da sonst keine neuen Frames empfangen werden können.

Zu guter letzt schauen wir uns an einem konkreten Beispiel ("main.c") mal an, wie eine solche Empfangs- und Sendeprozedur aussehen kann: Als erstes wird in einer Endlosschleife der Status des MCP abgefragt. Kommt ein Wert ungleich Null zurück, so wurde ein Interrupt ausgelöst. Mithilfe der switch()-Anweisung wird anschließend festgestellt, ob Receiver 1 oder Receiver 2 einen Datenframe empfangen haben oder etwas anderes den Interrupt ausgelöst hat (was einfach ignoriert wird). In jedem "gültigen" Empfangsfall wird nun mittels can_rxX die ID des Frames und die Länge des Datenpaketes ausgelesen. Anschließend werden die Daten aus dem Register geholt. In diesem Beispiel wird nun geprüft, ob die ID des gelesenen Frames 0x22 ist. Ist dies der Fall, so wird das erste Byte des Frames auf den R2R-DAC geschrieben, was bewirkt, dass man dort eine Spannung zwischen 0 Volt und Vcc messen kann (je nach empfangenem Wert). Ist die ID 0x29, so wird das zweite Byte des Frames auf Port 1 geschrieben, was zur Folge hat, dass die dort angeschlossenen LEDs den Wert des Bytes binär ausgeben. Anschließend wird noch das dritte Byte des Frames, unabhängig von der ID, auf den Port 5 geschrieben. Nun habe ich zu Debugzwecken das, was ich empfangen habe, mit einer neuen ID versehen und die Daten zurück geschrieben, sozusagen ein CAN-Echo :-). So bin ich in der Lage, alles, was ich mit dem Dongle auf den Bus schreibe, auch wieder zurück lesen zu können. Als letztes muss der Interrupt des Receivers wieder gelöscht werden, damit das nächste Frame empfangen werden kann. Dies geschieht durch die Funktion can_clear_rxX().